Merge "Create a GrGLDefaultInterface for Android."
diff --git a/Android.mk b/Android.mk
index cb5f89e..6461d9b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -30,6 +30,7 @@
 	src/core/SkCordic.cpp \
 	src/core/SkDebug.cpp \
 	src/core/SkFloatBits.cpp \
+	src/core/SkFontHost.cpp \
 	src/core/SkPoint.cpp \
 	src/core/SkRect.cpp \
 	src/core/SkRegion.cpp \
@@ -181,6 +182,7 @@
 	src/utils/SkBoundaryPatch.cpp \
 	src/utils/SkCamera.cpp \
 	src/utils/SkDumpCanvas.cpp \
+	src/utils/SkEGLContext_none.cpp \
 	src/utils/SkInterpolator.cpp \
 	src/utils/SkLayer.cpp \
 	src/utils/SkOSFile.cpp \
@@ -290,7 +292,6 @@
   gpu/src/GrInOrderDrawBuffer.cpp \
   gpu/src/GrMatrix.cpp \
   gpu/src/GrMemory.cpp \
-  gpu/src/GrPath.cpp \
   gpu/src/GrPathUtils.cpp \
   gpu/src/GrRectanizer_fifo.cpp \
   gpu/src/GrResource.cpp \
diff --git a/android_sample/SampleApp/Android.mk b/android_sample/SampleApp/Android.mk
new file mode 100644
index 0000000..6d62c35
--- /dev/null
+++ b/android_sample/SampleApp/Android.mk
@@ -0,0 +1,71 @@
+######################################
+# Build the app.
+######################################
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+        $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := SampleApp
+
+LOCAL_JNI_SHARED_LIBRARIES := libskia-sample
+
+include $(BUILD_PACKAGE)
+
+######################################
+# Build the shared library.
+######################################
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES += \
+    external/skia/include/core \
+    external/skia/include/config \
+    external/skia/include/effects \
+    external/skia/include/images \
+    external/skia/include/utils \
+    external/skia/include/utils/android \
+    external/skia/include/views \
+    external/skia/samplecode \
+    external/skia/include/xml \
+    external/skia/include/gpu \
+    external/skia/src/core \
+    external/skia/gpu/include \
+    frameworks/base/core/jni/android/graphics \
+    frameworks/base/native/include/android \
+    $(LOCAL_PATH)/jni
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libutils \
+    libskia \
+    libandroid_runtime \
+    libGLESv2
+
+LOCAL_STATIC_LIBRARIES := \
+    libskiagpu
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE := libskia-sample
+
+LOCAL_SRC_FILES := \
+    ../../src/ports/SkXMLParser_empty.cpp \
+    jni/sample-jni.cpp
+
+include external/skia/src/views/views_files.mk
+LOCAL_SRC_FILES += $(addprefix ../../src/views/, $(SOURCE))
+
+include external/skia/src/xml/xml_files.mk
+LOCAL_SRC_FILES += $(addprefix ../../src/xml/, $(SOURCE))
+
+include external/skia/samplecode/samplecode_files.mk
+LOCAL_SRC_FILES += $(addprefix ../../samplecode/, $(SOURCE))
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/android_sample/SampleApp/AndroidManifest.xml b/android_sample/SampleApp/AndroidManifest.xml
new file mode 100644
index 0000000..e323246
--- /dev/null
+++ b/android_sample/SampleApp/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 Skia
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.skia.sampleapp"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <uses-sdk android:minSdkVersion="3" />
+    <application android:label="@string/app_name"
+                 android:debuggable="true">
+        <activity android:name=".SampleApp"
+                  android:theme="@android:style/Theme.Holo.NoActionBar"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest> 
diff --git a/android_sample/SampleApp/README.txt b/android_sample/SampleApp/README.txt
new file mode 100644
index 0000000..a7d8e54
--- /dev/null
+++ b/android_sample/SampleApp/README.txt
@@ -0,0 +1,25 @@
+Building the sample app for Android using an Android tree:
+
+Copy this folder into an Android tree in packages/apps. In addition to jni,
+res, and src, there needs to be a fourth folder named "skia_extra".  This
+will include the skia files which are not part of an Android checkout. It
+should have three folders: include, samplecode, and src.
+
+skia/trunk/include/views -> skia_extra/include/views
+skia/trunk/include/xml -> skia_extra/include/xml
+
+skia/trunk/samplecode -> skia_extra/samplecode
+
+skia/trunk/src/views -> skia_extra/src/views
+skia/trunk/src/ports/SkXMLParser_empty.cpp -> skia_extra/src/ports/
+skia/trunk/src/xml -> skia_extra/src/xml
+
+skia/trunk/include/utils/android/AndroidKeyToSkKey.h -> jni/
+
+From packages/apps/SampleApp, type "mm" to build, and install the
+resulting apk.
+
+(It may be necessary to remove samples that do not build from
+skia_extra/samplecode/samplecode_files.mk)
+
+TODO: Instructions for building from SDK/NDK
diff --git a/android_sample/SampleApp/jni/sample-jni.cpp b/android_sample/SampleApp/jni/sample-jni.cpp
new file mode 100644
index 0000000..529047c
--- /dev/null
+++ b/android_sample/SampleApp/jni/sample-jni.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2011 Skia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <jni.h>
+
+#include "SkCanvas.h"
+#include "GraphicsJNI.h"
+#include "SkEvent.h"
+#include "SkWindow.h"
+#include "SkApplication.h"
+#include "utils/android/AndroidKeyToSkKey.h"
+
+///////////////////////////////////////////
+///////////////// Globals /////////////////
+///////////////////////////////////////////
+
+struct ActivityGlue {
+    JNIEnv* m_env;
+    jweak m_obj;
+    jmethodID m_setTitle;
+    ActivityGlue() {
+        m_env = NULL;
+        m_obj = NULL;
+        m_setTitle = NULL;
+    }
+} gActivityGlue;
+
+struct WindowGlue {
+    jweak m_obj;
+    jmethodID m_inval;
+    WindowGlue() {
+        m_obj = NULL;
+        m_inval = NULL;
+    }
+} gWindowGlue;
+
+SkOSWindow* gWindow;
+
+///////////////////////////////////////////
+///////////// SkOSWindow impl /////////////
+///////////////////////////////////////////
+
+void SkOSWindow::onSetTitle(const char title[])
+{
+    if (gActivityGlue.m_env) {
+        JNIEnv* env = gActivityGlue.m_env;
+        jstring string = env->NewStringUTF(title);
+        env->CallVoidMethod(gActivityGlue.m_obj, gActivityGlue.m_setTitle,
+                string);
+        env->DeleteLocalRef(string);
+    }
+}
+
+void SkOSWindow::onHandleInval(const SkIRect& rect)
+{
+    if (!gActivityGlue.m_env || !gWindowGlue.m_inval || !gWindowGlue.m_obj) {
+        return;
+    }
+    gActivityGlue.m_env->CallVoidMethod(gWindowGlue.m_obj, gWindowGlue.m_inval,
+            rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
+}
+
+///////////////////////////////////////////
+/////////////// SkEvent impl //////////////
+///////////////////////////////////////////
+
+void SkEvent::SignalQueueTimer(SkMSec) {}
+
+void SkEvent::SignalNonEmptyQueue() {}
+
+///////////////////////////////////////////
+////////////////// JNI ////////////////////
+///////////////////////////////////////////
+
+static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[],
+        const char signature[])
+{
+    jmethodID m = env->GetMethodID(clazz, name, signature);
+    if (!m) SkDebugf("Could not find Java method %s\n", name);
+    return m;
+}
+
+extern "C" {
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_drawToCanvas(
+        JNIEnv* env, jobject thiz, jobject jcanvas);
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init(
+        JNIEnv* env, jobject thiz);
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term(
+        JNIEnv* env, jobject thiz);
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize(
+        JNIEnv* env, jobject thiz, jint w, jint h);
+JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown(
+        JNIEnv* env, jobject thiz, jint keyCode, jint uni);
+JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp(
+        JNIEnv* env, jobject thiz, jint keyCode);
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick(
+        JNIEnv* env, jobject thiz, jint x, jint y, jint state);
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow(
+        JNIEnv* env, jobject thiz, jobject jsampleView);
+};
+
+JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown(
+        JNIEnv* env, jobject thiz, jint keyCode, jint uni)
+{
+    bool handled = gWindow->handleKey(AndroidKeycodeToSkKey(keyCode));
+    handled |= gWindow->handleChar((SkUnichar) uni);
+    return handled;
+}
+
+JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp(JNIEnv* env,
+        jobject thiz, jint keyCode)
+{
+    return gWindow->handleKeyUp(AndroidKeycodeToSkKey(keyCode));
+}
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick(JNIEnv* env,
+        jobject thiz, jint x, jint y, jint jstate)
+{
+    SkView::Click::State state;
+    switch(jstate) {
+        case 0:     // MotionEvent.ACTION_DOWN
+            state = SkView::Click::kDown_State;
+            break;
+        case 1:     // MotionEvent.ACTION_UP
+        case 3:     // MotionEvent.ACTION_CANCEL
+            state = SkView::Click::kUp_State;
+            break;
+        case 2:     // MotionEvent.ACTION_MOVE
+            state = SkView::Click::kMoved_State;
+            break;
+        default:
+            SkDebugf("motion event ignored\n");
+            return;
+    }
+    gWindow->handleClick(x, y, state);
+}
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize(JNIEnv* env,
+        jobject thiz, jint w, jint h)
+{
+    gWindow->resize(w, h);
+}
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow(
+        JNIEnv* env, jobject thiz, jobject jsampleView)
+{
+    gWindow = create_sk_window(NULL);
+    // Only using a method on View.
+    jclass clazz = gActivityGlue.m_env->FindClass("android/view/View");
+    gWindowGlue.m_obj = gActivityGlue.m_env->NewWeakGlobalRef(jsampleView);
+    gWindowGlue.m_inval = GetJMethod(gActivityGlue.m_env, clazz, "invalidate",
+            "(IIII)V");
+    gActivityGlue.m_env->DeleteLocalRef(clazz);
+}
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init(JNIEnv* env,
+        jobject thiz)
+{
+    gActivityGlue.m_env = env;
+    // Only using a method on Activity.
+    jclass clazz = env->FindClass("android/app/Activity");
+    gActivityGlue.m_obj = env->NewWeakGlobalRef(thiz);
+    gActivityGlue.m_setTitle = GetJMethod(env, clazz, "setTitle",
+            "(Ljava/lang/CharSequence;)V");
+    env->DeleteLocalRef(clazz);
+
+    application_init();
+}
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term(JNIEnv* env,
+        jobject thiz)
+{
+    application_term();
+    if (gWindowGlue.m_obj) {
+        env->DeleteWeakGlobalRef(gWindowGlue.m_obj);
+        gWindowGlue.m_obj = NULL;
+    }
+    if (gActivityGlue.m_obj) {
+        env->DeleteWeakGlobalRef(gActivityGlue.m_obj);
+        gActivityGlue.m_obj = NULL;
+    }
+    delete gWindow;
+    gWindow = NULL;
+}
+
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_drawToCanvas(
+        JNIEnv* env, jobject thiz, jobject jcanvas)
+{
+    if (!gWindow) return;
+    gWindow->update(NULL);
+    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
+    canvas->drawBitmap(gWindow->getBitmap(), 0, 0);
+}
diff --git a/android_sample/SampleApp/res/layout/layout.xml b/android_sample/SampleApp/res/layout/layout.xml
new file mode 100644
index 0000000..d71116b
--- /dev/null
+++ b/android_sample/SampleApp/res/layout/layout.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 Skia
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/holder"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+    <TextView android:id="@+id/title_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        />
+</LinearLayout>
+
diff --git a/android_sample/SampleApp/res/values/strings.xml b/android_sample/SampleApp/res/values/strings.xml
new file mode 100644
index 0000000..72d3bc8
--- /dev/null
+++ b/android_sample/SampleApp/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 Skia
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <string name="app_name">SampleApp</string>
+</resources>
diff --git a/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java b/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java
new file mode 100644
index 0000000..b02c4d7
--- /dev/null
+++ b/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 Skia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.skia.sampleapp;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class SampleApp extends Activity
+{
+    private TextView mTitle;
+
+    public class SampleView extends View {
+        public SampleView(Context context) {
+            super(context);
+            createOSWindow(this);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            drawToCanvas(canvas);
+        }
+
+        @Override
+        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+            updateSize(w, h);
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            final int x = (int) event.getX();
+            final int y = (int) event.getY();
+            final int action = event.getAction();
+            handleClick(x, y, action);
+            return true;
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+
+        init();
+        setContentView(R.layout.layout);
+        mTitle = (TextView) findViewById(R.id.title_view);
+        LinearLayout holder = (LinearLayout) findViewById(R.id.holder);
+        View view = new SampleView(this);
+        holder.addView(view, new LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+    }
+
+    @Override
+    public void onDestroy()
+    {
+        term();
+        super.onDestroy();
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        switch (event.getAction()) {
+            case KeyEvent.ACTION_DOWN:
+                int uni = event.getUnicodeChar(event.getMetaState());
+                return handleKeyDown(event.getKeyCode(), uni);
+            case KeyEvent.ACTION_UP:
+                return handleKeyUp(event.getKeyCode());
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        mTitle.setText(title);
+    }
+
+    private native void drawToCanvas(Canvas canvas);
+    private native void init();
+    private native void term();
+    // Currently depends on init having already been called.
+    private native void createOSWindow(SampleView view);
+    private native void updateSize(int w, int h);
+    private native void handleClick(int x, int y, int state);
+    private native boolean handleKeyDown(int key, int uni);
+    private native boolean handleKeyUp(int key);
+
+    static {
+        System.loadLibrary("skia-sample");
+    }
+}
diff --git a/bench/Android.mk b/bench/Android.mk
index b20aef9..71523af 100644
--- a/bench/Android.mk
+++ b/bench/Android.mk
@@ -6,6 +6,9 @@
 	BitmapBench.cpp \
   DecodeBench.cpp \
   FPSBench.cpp \
+  GradientBench.cpp \
+  MatrixBench.cpp \
+  PathBench.cpp \
 	RectBench.cpp \
 	RepeatTileBench.cpp \
 	TextBench.cpp \
@@ -17,7 +20,8 @@
     ../src/utils/SkNWayCanvas.cpp \
     ../src/utils/SkParse.cpp
 
-LOCAL_SHARED_LIBRARIES := libcutils libskia
+LOCAL_SHARED_LIBRARIES := libcutils libskia libGLESv2
+LOCAL_STATIC_LIBRARIES := libskiagpu
 LOCAL_C_INCLUDES := \
     external/skia/include/config \
     external/skia/include/core \
diff --git a/bench/MatrixBench.cpp b/bench/MatrixBench.cpp
new file mode 100644
index 0000000..d963bc7
--- /dev/null
+++ b/bench/MatrixBench.cpp
@@ -0,0 +1,229 @@
+#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);
+    }
+
+    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 = 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);
+        always_do(m0.getType());
+        always_do(m1.getType());
+        always_do(m2.getType());
+    }
+private:
+    typedef MatrixBench INHERITED;
+};
+
+class ScaleMatrixBench : public MatrixBench {
+public:
+    ScaleMatrixBench(void* param) : INHERITED(param, "scale") {
+
+        fM0.reset();
+        fM1.setScale(fSX, fSY);
+        fM2.setTranslate(fSX, fSY);
+        fSX = fSY = SkFloatToScalar(1.5f);
+    }
+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;
+};
+
+
+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 BenchRegistry gReg0(M0);
+static BenchRegistry gReg1(M1);
+static BenchRegistry gReg2(M2);
+static BenchRegistry gReg3(M3);
+static BenchRegistry gReg4(M4);
diff --git a/bench/bench_compare.py b/bench/bench_compare.py
new file mode 100644
index 0000000..f6909b1
--- /dev/null
+++ b/bench/bench_compare.py
@@ -0,0 +1,140 @@
+'''
+Created on May 16, 2011
+
+@author: bungeman
+'''
+import sys
+import getopt
+import re
+
+def parse(lines):
+    """Takes iterable lines of bench output, returns {bench:{config:time}}."""
+    
+    benches = {}
+    current_bench = None
+    
+    for line in lines:
+        #see if this line starts a new bench
+        new_bench = re.search('running bench \[\d+ \d+\] (.{28})', 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('  (.{4}): msecs = (\d+\.\d+)', line):
+                current_config = new_config.group(1)
+                current_time = float(new_config.group(2))
+                if current_bench in benches:
+                    benches[current_bench][current_config] = current_time
+                else:
+                    benches[current_bench] = {current_config : current_time}
+    
+    return benches
+
+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 '-f <fieldSpec> which fields to output and in what order.'
+    print '   Not specifying is the same as -f "bcondp".'
+    print '  b: bench'
+    print '  c: config'
+    print '  o: old time'
+    print '  n: new time'
+    print '  d: diff'
+    print '  p: percent diff'
+    
+    
+def main():
+    """Parses command line and writes output."""
+    
+    try:
+        opts, args = getopt.getopt(sys.argv[1:], "f:o:n:h")
+    except getopt.GetoptError, err:
+        print str(err) 
+        usage()
+        sys.exit(2)
+    
+    column_formats = {
+        'b' : '{bench: >28} ',
+        'c' : '{config: <4} ',
+        'o' : '{old_time: >10.2f} ',
+        'n' : '{new_time: >10.2f} ',
+        'd' : '{diff: >+10.2f} ',
+        'p' : '{diffp: >+7.1%} ',
+    }
+    header_formats = {
+        'b' : '{bench: >28} ',
+        'c' : '{config: <4} ',
+        'o' : '{old_time: >10} ',
+        'n' : '{new_time: >10} ',
+        'd' : '{diff: >10} ',
+        'p' : '{diffp: >7} ',
+    }
+    
+    old = None
+    new = None
+    column_format = ""
+    header_format = ""
+    columns = 'bcondp'
+    header = False
+    
+    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
+        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'
+            , old_time='old'
+            , new_time='new'
+            , diff='diff'
+            , diffp='diffP'
+        )
+    
+    old_benches = parse(open(old, 'r'))
+    new_benches = parse(open(new, 'r'))
+    
+    for old_bench, old_configs in old_benches.items():
+        if old_bench in new_benches:
+            new_configs = new_benches[old_bench]
+            for old_config, old_time in old_configs.items():
+                if old_config in new_configs:
+                    new_time = new_configs[old_config]
+                    old_time = old_configs[old_config]
+                    print column_format.format(
+                        bench=old_bench.strip()
+                        , config=old_config.strip()
+                        , old_time=old_time
+                        , new_time=new_time
+                        , diff=(old_time - new_time)
+                        , diffp=((old_time-new_time)/old_time)
+                    )
+    
+    
+if __name__ == "__main__":
+    main()
diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp
index 4f6d81c..066573a 100644
--- a/bench/benchmain.cpp
+++ b/bench/benchmain.cpp
@@ -6,6 +6,9 @@
 #include "SkPicture.h"
 #include "SkString.h"
 #include "SkTime.h"
+#include "GrContext.h"
+#include "SkGpuDevice.h"
+#include "SkEGLContext.h"
 
 #include "SkBenchmark.h"
 
@@ -30,6 +33,7 @@
     }
 }
 
+#if 0
 static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) {
     if (bm1.width() != bm2.width() ||
         bm1.height() != bm2.height() ||
@@ -43,9 +47,9 @@
             return false;
         }
     }
-
     return true;
 }
+#endif
 
 class Iter {
 public:
@@ -62,7 +66,7 @@
         }
         return NULL;
     }
-    
+
 private:
     const BenchRegistry* fBench;
     void* fParam;
@@ -102,7 +106,7 @@
             *p++ = c | (SK_A32_MASK << SK_A32_SHIFT);
         }
     }
-
+    
     SkString str;
     make_filename(name, &str);
     str.appendf("_%s.png", config);
@@ -118,7 +122,7 @@
     r.set(SkIntToScalar(10), SkIntToScalar(10),
           SkIntToScalar(w*2/3), SkIntToScalar(h*2/3));
     canvas->clipRect(r, SkRegion::kIntersect_Op);
-
+    
     r.set(SkIntToScalar(w/3), SkIntToScalar(h/3),
           SkIntToScalar(w-10), SkIntToScalar(h-10));
     canvas->clipRect(r, SkRegion::kXOR_Op);
@@ -143,21 +147,6 @@
     canvas->translate(-x, -y);
 }
 
-static void compare_pict_to_bitmap(SkPicture* pict, const SkBitmap& bm) {
-    SkBitmap bm2;
-    
-    bm2.setConfig(bm.config(), bm.width(), bm.height());
-    bm2.allocPixels();
-    erase(bm2);
-
-    SkCanvas canvas(bm2);
-    canvas.drawPicture(*pict);
-
-    if (!equal(bm, bm2)) {
-        SkDebugf("----- compare_pict_to_bitmap failed\n");
-    }
-}
-
 static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) {
     if (argv < stop) {
         *var = atoi(*argv) != 0;
@@ -166,16 +155,43 @@
     return false;
 }
 
+enum Backend {
+    kRaster_Backend,
+    kGPU_Backend,
+    kPDF_Backend,
+};
+
+static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size,
+                             Backend backend, GrContext* context) {
+    SkDevice* device = NULL;
+    SkBitmap bitmap;
+    bitmap.setConfig(config, size.fX, size.fY);
+    
+    switch (backend) {
+        case kRaster_Backend:
+            bitmap.allocPixels();
+            erase(bitmap);
+            device = new SkDevice(NULL, bitmap, true);
+            break;
+        case kGPU_Backend:
+            device = new SkGpuDevice(context, bitmap, SkGpuDevice::Current3DApiRenderTarget());
+//            device->clear(0xFFFFFFFF);
+            break;
+        case kPDF_Backend:
+        default:
+            SkASSERT(!"unsupported");
+    }
+    return device;
+}
+
 static const struct {
     SkBitmap::Config    fConfig;
     const char*         fName;
+    Backend             fBackend;
 } gConfigs[] = {
-    { SkBitmap::kARGB_8888_Config,  "8888" },
-    { SkBitmap::kRGB_565_Config,    "565",  },
-#if 0
-    { SkBitmap::kARGB_4444_Config,  "4444", },
-    { SkBitmap::kA8_Config,         "A8",   }
-#endif
+    { SkBitmap::kARGB_8888_Config,  "8888",     kRaster_Backend },
+    { SkBitmap::kRGB_565_Config,    "565",      kRaster_Backend },
+    { SkBitmap::kARGB_8888_Config,  "GPU",      kGPU_Backend },
 };
 
 static int findConfig(const char config[]) {
@@ -189,7 +205,7 @@
 
 int main (int argc, char * const argv[]) {
     SkAutoGraphics ag;
-
+    
     SkTDict<const char*> defineDict(1024);
     int repeatDraw = 1;
     int forceAlpha = 0xFF;
@@ -199,16 +215,16 @@
     bool doScale = false;
     bool doRotate = false;
     bool doClip = false;
-    bool doPict = false;
     const char* matchStr = NULL;
     bool hasStrokeWidth = false;
     float strokeWidth;
-
+    
     SkString outDir;
     SkBitmap::Config outConfig = SkBitmap::kNo_Config;
     const char* configName = "";
+    Backend backend = kRaster_Backend;  // for warning
     int configCount = SK_ARRAY_COUNT(gConfigs);
-
+    
     char* const* stop = argv + argc;
     for (++argv; argv < stop; ++argv) {
         if (strcmp(*argv, "-o") == 0) {
@@ -219,8 +235,6 @@
                     outDir.append("/");
                 }
             }
-        } else if (strcmp(*argv, "-pict") == 0) {
-            doPict = true;
         } else if (strcmp(*argv, "-repeat") == 0) {
             argv++;
             if (argv < stop) {
@@ -290,6 +304,7 @@
                 if (index >= 0) {
                     outConfig = gConfigs[index].fConfig;
                     configName = gConfigs[index].fName;
+                    backend = gConfigs[index].fBackend;
                     configCount = 1;
                 } else {
                     SkString str;
@@ -316,7 +331,7 @@
             return -1;
         }
     }
-
+    
     // report our current settings
     {
         SkString str;
@@ -324,7 +339,13 @@
                    forceAlpha, forceAA, forceFilter);
         log_progress(str);
     }
-                   
+    
+    GrContext* context = NULL;
+    SkEGLContext eglContext;
+    if (eglContext.init(1024, 1024)) {
+        context = GrContext::CreateGLShaderContext();
+    }
+    
     Iter iter(&defineDict);
     SkBenchmark* bench;
     while ((bench = iter.next()) != NULL) {
@@ -340,32 +361,34 @@
         if (hasStrokeWidth) {
             bench->setStrokeWidth(strokeWidth);
         }
-
+        
         // only run benchmarks if their name contains matchStr
         if (matchStr && strstr(bench->getName(), matchStr) == NULL) {
             continue;
         }
-
+        
         {
             SkString str;
             str.printf("running bench [%d %d] %28s", dim.fX, dim.fY,
                        bench->getName());
             log_progress(str);
         }
-
+        
         for (int configIndex = 0; configIndex < configCount; configIndex++) {
             if (configCount > 1) {
                 outConfig = gConfigs[configIndex].fConfig;
                 configName = gConfigs[configIndex].fName;
+                backend = gConfigs[configIndex].fBackend;
             }
             
-            SkBitmap bm;
-            bm.setConfig(outConfig, dim.fX, dim.fY);
-            bm.allocPixels();
-            erase(bm);
-
-            SkCanvas canvas(bm);
-
+            if (kGPU_Backend == backend && NULL == context) {
+                continue;
+            }
+            
+            SkDevice* device = make_device(outConfig, dim, backend, context);
+            SkCanvas canvas(device);
+            device->unref();
+            
             if (doClip) {
                 performClip(&canvas, dim.fX, dim.fY);
             }
@@ -375,33 +398,27 @@
             if (doRotate) {
                 performRotate(&canvas, dim.fX, dim.fY);
             }
-
+            
+            //warm up caches if needed
             if (repeatDraw > 1) {
                 SkAutoCanvasRestore acr(&canvas, true);
                 bench->draw(&canvas);
+                if (kGPU_Backend == backend && context) {
+                    context->flush();
+                    glFinish();
+                }
             }
-
+            
             SkMSec now = SkTime::GetMSecs();
             for (int i = 0; i < repeatDraw; i++) {
-                SkCanvas* c = &canvas;
-
-                SkNWayCanvas nway;
-                SkPicture* pict = NULL;
-                if (doPict) {
-                    pict = new SkPicture;
-                    nway.addCanvas(pict->beginRecording(bm.width(), bm.height()));
-                    nway.addCanvas(&canvas);
-                    c = &nway;
-                }
-
-                SkAutoCanvasRestore acr(c, true);
-                bench->draw(c);
-                
-                if (pict) {
-                    compare_pict_to_bitmap(pict, bm);
-                    pict->unref();
-                }
+                SkAutoCanvasRestore acr(&canvas, true);
+                bench->draw(&canvas);
             }
+            if (kGPU_Backend == backend && context) {
+                context->flush();
+                glFinish();
+            }
+            
             if (repeatDraw > 1) {
                 double duration = SkTime::GetMSecs() - now;
                 SkString str;
@@ -409,7 +426,8 @@
                 log_progress(str);
             }
             if (outDir.size() > 0) {
-                saveFile(bench->getName(), configName, outDir.c_str(), bm);
+                saveFile(bench->getName(), configName, outDir.c_str(),
+                         device->accessBitmap(false));
             }
         }
         log_progress("\n");
diff --git a/gm/Android.mk b/gm/Android.mk
index b3aeadf..acfb4a5 100644
--- a/gm/Android.mk
+++ b/gm/Android.mk
@@ -8,6 +8,7 @@
   complexclip.cpp \
   filltypes.cpp \
   gradients.cpp \
+  nocolorbleed.cpp \
   pathfill.cpp \
   points.cpp \
   poly2poly.cpp \
diff --git a/gm/bitmapfilters.cpp b/gm/bitmapfilters.cpp
index 0487fe6..3903913 100644
--- a/gm/bitmapfilters.cpp
+++ b/gm/bitmapfilters.cpp
@@ -3,15 +3,20 @@
 namespace skiagm {
 
 static void make_bm(SkBitmap* bm) {
-    const SkColor colors[] = {
+    const SkColor colors[4] = {
         SK_ColorRED, SK_ColorGREEN,
         SK_ColorBLUE, SK_ColorWHITE
     };
-    SkColorTable* ctable = new SkColorTable(colors, 4);
+    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;
@@ -57,7 +62,7 @@
     canvas->translate(SkIntToScalar(48), 0);
 
     canvas->scale(SkIntToScalar(scale), SkIntToScalar(scale));
-    
+
     x += draw_set(canvas, bm, 0, &paint);
     paint.reset();
     paint.setAlpha(0x80);
@@ -90,7 +95,7 @@
 
         SkScalar x = SkIntToScalar(10);
         SkScalar y = SkIntToScalar(10);
-        
+
         canvas->translate(x, y);
         y = draw_row(canvas, fBM8);
         canvas->translate(0, y);
@@ -100,7 +105,7 @@
         canvas->translate(0, y);
         draw_row(canvas, fBM32);
     }
-    
+
 private:
     typedef GM INHERITED;
 };
diff --git a/gm/blurs.cpp b/gm/blurs.cpp
index 26fdc79..c934178 100644
--- a/gm/blurs.cpp
+++ b/gm/blurs.cpp
@@ -5,14 +5,14 @@
 
 class BlursGM : public GM {
 public:
-	BlursGM() {}
+    BlursGM() {}
 
 protected:
     virtual SkString onShortName() {
         return SkString("blurs");
     }
 
-	virtual SkISize onISize() {
+    virtual SkISize onISize() {
         return make_isize(700, 500);
     }
 
@@ -37,8 +37,8 @@
 
         SkPaint paint;
         paint.setAntiAlias(true);
-        paint.setTextSize(25);
-        canvas->translate(-40, 0);
+        paint.setTextSize(SkIntToScalar(25));
+        canvas->translate(SkIntToScalar(-40), SkIntToScalar(0));
 
         SkBlurMaskFilter::BlurFlags flags = SkBlurMaskFilter::kNone_BlurFlag;
         for (int j = 0; j < 2; j++) {
@@ -46,27 +46,32 @@
             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);
+                    SkMaskFilter* mf = SkBlurMaskFilter::Create(
+                            SkIntToScalar(20), gRecs[i].fStyle, flags
+                    );
                     paint.setMaskFilter(mf)->unref();
                 } else {
                     paint.setMaskFilter(NULL);
                 }
-                canvas->drawCircle(200 + gRecs[i].fCx*100,
-                                   200 + gRecs[i].fCy*100, 50, paint);
+                canvas->drawCircle(SkIntToScalar(200 + gRecs[i].fCx*100)
+                                   , SkIntToScalar(200 + gRecs[i].fCy*100)
+                                   , SkIntToScalar(50)
+                                   , paint);
             }
             // draw text
             {
-                SkMaskFilter* mf = SkBlurMaskFilter::Create(4,
-                                                            SkBlurMaskFilter::kNormal_BlurStyle,
-                                                            flags);
+                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);
+                canvas->drawText("Hamburgefons Style", 18
+                                 , x, y + SkIntToScalar(50), paint);
                 paint.setMaskFilter(NULL);
                 paint.setColor(SK_ColorWHITE);
                 x -= SkIntToScalar(2);
@@ -75,7 +80,7 @@
             }
             canvas->restore();
             flags = SkBlurMaskFilter::kHighQuality_BlurFlag;
-            canvas->translate(350, 0);
+            canvas->translate(SkIntToScalar(350), SkIntToScalar(0));
         }
     }
 
diff --git a/gm/gm_files.mk b/gm/gm_files.mk
index e867820..fec20b6 100644
--- a/gm/gm_files.mk
+++ b/gm/gm_files.mk
@@ -3,6 +3,7 @@
     blurs.cpp \
     filltypes.cpp \
     gradients.cpp \
+    nocolorbleed.cpp \
     pathfill.cpp \
     points.cpp \
     poly2poly.cpp \
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index 6267fd4..ea205e3 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -441,7 +441,7 @@
     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)
 
-    bool doReplay = false;
+    bool doReplay = true;
     bool doSerialize = false;
     const char* const commandName = argv[0];
     char* const* stop = argv + argc;
@@ -461,8 +461,8 @@
             if (argv < stop && **argv) {
                 diffPath = *argv;
             }
-        } else if (strcmp(*argv, "--replay") == 0) {
-            doReplay = true;
+        } else if (strcmp(*argv, "--noreplay") == 0) {
+            doReplay = false;
         } else if (strcmp(*argv, "--serialize") == 0) {
             doSerialize = true;
         } else {
diff --git a/gm/nocolorbleed.cpp b/gm/nocolorbleed.cpp
new file mode 100755
index 0000000..3dec7cc
--- /dev/null
+++ b/gm/nocolorbleed.cpp
@@ -0,0 +1,75 @@
+#include "gm.h"
+
+namespace skiagm {
+
+class NoColorBleedGM : public GM {
+public:
+    NoColorBleedGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("nocolorbleed");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(200, 200);
+    }
+
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        drawBG(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/pathfill.cpp b/gm/pathfill.cpp
index ec56942..713847f 100644
--- a/gm/pathfill.cpp
+++ b/gm/pathfill.cpp
@@ -6,14 +6,15 @@
 typedef SkScalar (*MakePathProc)(SkPath*);
 
 static SkScalar make_frame(SkPath* path) {
-    SkRect r = { 10, 10, 630, 470 };
-    path->addRoundRect(r, 15, 15);
+    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(5);
+    paint.setStrokeWidth(SkIntToScalar(5));
     paint.getFillPath(*path, path);
-    return 15;
+    return SkIntToScalar(15);
 }
 
 static SkScalar make_triangle(SkPath* path) {
@@ -24,21 +25,23 @@
     path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3]));
     path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5]));
     path->close();
-    path->offset(10, 0);
+    path->offset(SkIntToScalar(10), SkIntToScalar(0));
     return SkIntToScalar(30);
 }
 
 static SkScalar make_rect(SkPath* path) {
-    SkRect r = { 10, 10, 30, 30 };
+    SkRect r = { SkIntToScalar(10), SkIntToScalar(10),
+                 SkIntToScalar(30), SkIntToScalar(30) };
     path->addRect(r);
-    path->offset(10, 0);
+    path->offset(SkIntToScalar(10), SkIntToScalar(0));
     return SkIntToScalar(30);
 }
 
 static SkScalar make_oval(SkPath* path) {
-    SkRect r = { 10, 10, 30, 30 };
+    SkRect r = { SkIntToScalar(10), SkIntToScalar(10),
+                 SkIntToScalar(30), SkIntToScalar(30) };
     path->addOval(r);
-    path->offset(10, 0);
+    path->offset(SkIntToScalar(10), SkIntToScalar(0));
     return SkIntToScalar(30);
 }
 
@@ -56,8 +59,8 @@
         x += dx;
         path->lineTo(x, y + dy);
     }
-    path->lineTo(x, y + 2 * dy);
-    path->lineTo(x0, y + 2 * dy);
+    path->lineTo(x, y + (2 * dy));
+    path->lineTo(x0, y + (2 * dy));
     path->close();
     return SkIntToScalar(30);
 }
@@ -100,7 +103,7 @@
     SkPath  fPath[N];
     SkScalar fDY[N];
 public:
-	PathFillGM() {
+    PathFillGM() {
         for (size_t i = 0; i < N; i++) {
             fDY[i] = gProcs[i](&fPath[i]);
         }
@@ -111,7 +114,7 @@
         return SkString("pathfill");
     }
 
-	virtual SkISize onISize() {
+    virtual SkISize onISize() {
         return make_isize(640, 480);
     }
 
@@ -127,7 +130,7 @@
         
         for (size_t i = 0; i < N; i++) {
             canvas->drawPath(fPath[i], paint);
-            canvas->translate(0, fDY[i]);
+            canvas->translate(SkIntToScalar(0), fDY[i]);
         }
     }
 
diff --git a/gm/shadertext.cpp b/gm/shadertext.cpp
index 1cf562c..ea87823 100644
--- a/gm/shadertext.cpp
+++ b/gm/shadertext.cpp
@@ -11,7 +11,7 @@
     bm->eraseColor(0);
 
     SkCanvas    canvas(*bm);
-    SkScalar s = w < h ? w : h;
+    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 };
@@ -121,7 +121,7 @@
 
         const char text[] = "Shaded Text";
         const int textLen = SK_ARRAY_COUNT(text) - 1;
-        static int pointSize = SkIntToScalar(48);
+        const int pointSize = 48;
 
         int w = pointSize * textLen;
         int h = pointSize;
@@ -194,5 +194,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
-
diff --git a/gm/shadows.cpp b/gm/shadows.cpp
index 5afde49..bba997f 100644
--- a/gm/shadows.cpp
+++ b/gm/shadows.cpp
@@ -1,32 +1,29 @@
 #include "gm.h"
-#include "SkPicture.h"
-#include "SkRectShape.h"
 #include "SkBlurDrawLooper.h"
 
 namespace skiagm {
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class ShadowsGM : public GM {
+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;
-    SkPaint fPaint;
-    SkRectShape fRectShape;
+    SkRect fRect;
+
     ShadowsGM() {
         fCirclePath.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(10) );
-    fPaint.setStrokeWidth(SkIntToScalar(4));
-    fPaint.setAntiAlias(true);
-    fPaint.setColor(0xFF00FF00);
-    fPaint.setStyle(SkPaint::kStroke_Style); 
-    SkRect rect;
-    rect.set(SkIntToScalar(10), SkIntToScalar(10),
-             SkIntToScalar(30), SkIntToScalar(30));
-    fRectShape.setRect(rect);
-    fRectShape.paint().setColor(SK_ColorRED);
-    }
-
-    virtual ~ShadowsGM() {
+        fRect.set(SkIntToScalar(10), SkIntToScalar(10),
+                  SkIntToScalar(30), SkIntToScalar(30));
     }
 
 protected:
@@ -47,43 +44,61 @@
 
     SkBlurDrawLooper* shadowLoopers[5];
     shadowLoopers[0] =
-        new SkBlurDrawLooper (10, 5, 10, 0xFF0000FF, 
+        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 (10, 5, 10, 0xFF0000FF, 
+        new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5),
+                              SkIntToScalar(10), 0xFF0000FF,
                               SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                               SkBlurDrawLooper::kOverrideColor_BlurFlag );
     SkAutoUnref aurL1(shadowLoopers[1]);
     shadowLoopers[2] =
-        new SkBlurDrawLooper (5, 5, 10, 0xFF000000,
+        new SkBlurDrawLooper (SkIntToScalar(5), SkIntToScalar(5),
+                              SkIntToScalar(10), 0xFF000000,
                               SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                               SkBlurDrawLooper::kHighQuality_BlurFlag  );
     SkAutoUnref aurL2(shadowLoopers[2]);
     shadowLoopers[3] =
-        new SkBlurDrawLooper (5, -5 ,-10, 0x7FFF0000, 
+        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 (0, 5, 5, 0xFF000000, 
+        new SkBlurDrawLooper (SkIntToScalar(0), SkIntToScalar(5),
+                              SkIntToScalar(5), 0xFF000000,
                               SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                               SkBlurDrawLooper::kOverrideColor_BlurFlag |
                               SkBlurDrawLooper::kHighQuality_BlurFlag  );
     SkAutoUnref aurL4(shadowLoopers[4]);
 
-    for (int looper = 0; looper < 5; looper ++)
-    {
-        fRectShape.paint().setLooper(shadowLoopers[looper]);
-        canvas->resetMatrix();
-        canvas->translate(SkIntToScalar(looper*40), SkIntToScalar(0));
-        canvas->drawShape(&fRectShape);
-        fPaint.setLooper(shadowLoopers[looper]); 
+    static const struct {
+        SkColor fColor;
+        SkScalar fStrokeWidth;
+    } gRec[] = {
+        { SK_ColorRED,      -SK_Scalar1 },
+        { SK_ColorGREEN,    SkIntToScalar(4) },
+    };
+
+    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(i*40), SkIntToScalar(0));
+        setup(&paint, gRec[0].fColor, gRec[0].fStrokeWidth);
+        canvas->drawRect(fRect, paint);
+
         canvas->translate(SkIntToScalar(0), SkIntToScalar(40));
-        canvas->drawPath(fCirclePath, fPaint);
+        setup(&paint, gRec[1].fColor, gRec[1].fStrokeWidth);
+        canvas->drawPath(fCirclePath, paint);
     }
 }
 
diff --git a/gm/shapes.cpp b/gm/shapes.cpp
index 324ce7e..5daea0a 100644
--- a/gm/shapes.cpp
+++ b/gm/shapes.cpp
@@ -57,6 +57,8 @@
         for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
             SkSafeRef(fMatrixRefs[i] = fGroup.getShapeMatrixRef(i));
         }
+        SkScalar c = SkIntToScalar(50);
+        fMatrixRefs[3]->preRotate(SkIntToScalar(30), c, c);
     }
 
     virtual ~ShapesGM() {
@@ -81,10 +83,6 @@
     virtual void onDraw(SkCanvas* canvas) {
         this->drawBG(canvas);
 
-        SkMatrix saveM = *fMatrixRefs[3];
-        SkScalar c = SkIntToScalar(50);
-        fMatrixRefs[3]->preRotate(SkIntToScalar(30), c, c);
-
         SkMatrix matrix;
 
         SkGroupShape* gs = new SkGroupShape;
@@ -111,8 +109,6 @@
         canvas->drawPicture(*pict);
         pict->unref();
 #endif
-
-        *fMatrixRefs[3] = saveM;
 }
 
 private:
diff --git a/gm/strokerects.cpp b/gm/strokerects.cpp
index b716407..891b95a 100644
--- a/gm/strokerects.cpp
+++ b/gm/strokerects.cpp
@@ -29,14 +29,14 @@
 
 class StrokeRectGM : public GM {
 public:
-	StrokeRectGM() {}
+    StrokeRectGM() {}
     
 protected:
     virtual SkString onShortName() {
         return SkString("strokerects");
     }
 
-	virtual SkISize onISize() {
+    virtual SkISize onISize() {
         return make_isize(W*2, H*2);
     }
 
@@ -63,7 +63,10 @@
 
                 SkAutoCanvasRestore acr(canvas, true);
                 canvas->translate(SW * x, SH * y);
-                canvas->clipRect(SkRect::MakeLTRB(2, 2, SW - 2, SH - 2));
+                canvas->clipRect(SkRect::MakeLTRB(
+                        SkIntToScalar(2), SkIntToScalar(2)
+                        , SW - SkIntToScalar(2), SH - SkIntToScalar(2)
+                ));
 
                 SkRandom rand;
                 for (int i = 0; i < N; i++) {
diff --git a/gpu/include/GrClipIterator.h b/gpu/include/GrClipIterator.h
index 1fcbdd1..f7f74a7 100644
--- a/gpu/include/GrClipIterator.h
+++ b/gpu/include/GrClipIterator.h
@@ -47,7 +47,7 @@
      * Return the current path. It is an error to call this when isDone() is
      * true or when getType() is kRect_Type.
      */
-    virtual GrPathIter* getPathIter() = 0;
+    virtual const GrPath* getPath() = 0;
 
     /**
      * Return the fill rule for the path. It is an error to call this when
diff --git a/gpu/include/GrConfig.h b/gpu/include/GrConfig.h
index f1f2437..9ee37c7 100644
--- a/gpu/include/GrConfig.h
+++ b/gpu/include/GrConfig.h
@@ -189,7 +189,7 @@
 // debug -vs- release
 //
 
-extern void GrPrintf(const char format[], ...);
+extern GR_API void GrPrintf(const char format[], ...);
 
 /**
  *  GR_STRING makes a string of X where X is expanded before conversion to a string
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index 951c0e6..58c53ba 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -25,7 +25,6 @@
 class GrFontCache;
 class GrGpu;
 struct GrGpuStats;
-class GrPathIter;
 class GrVertexBufferAllocPool;
 class GrIndexBufferAllocPool;
 class GrInOrderDrawBuffer;
@@ -191,35 +190,16 @@
      *         on failure.
      */
     GrResource* createPlatformSurface(const GrPlatformSurfaceDesc& desc);
-
     /**
-     * DEPRECATED, WILL BE REMOVED SOON. USE createPlatformSurface.
-     *
-     * Wraps an externally-created rendertarget in a GrRenderTarget.
-     * @param platformRenderTarget  3D API-specific render target identifier
-     *                              e.g. in GL platforamRenderTarget is an FBO
-     *                              id.
-     * @param stencilBits           the number of stencil bits that the render
-     *                              target has.
-     * @param isMultisampled        specify whether the render target is 
-     *                              multisampled.
-     * @param width                 width of the render target.
-     * @param height                height of the render target.
-     */
-    GrRenderTarget* createPlatformRenderTarget(intptr_t platformRenderTarget,
-                                               int stencilBits,
-                                               bool isMultisampled,
-                                               int width, int height);
-
-    /**
-     * DEPRECATED, WILL BE REMOVED SOON. USE createPlatformSurface.
-     *
      * Reads the current target object (e.g. FBO or IDirect3DSurface9*) and
      * viewport state from the underlying 3D API and wraps it in a
      * GrRenderTarget. The GrRenderTarget will not attempt to delete/destroy the
      * underlying object in its destructor and it is up to caller to guarantee
      * that it remains valid while the GrRenderTarget is used.
      *
+     * Will not detect that the render target is also a texture. If you need
+     * to also use the render target as a GrTexture use createPlatformSurface.
+     *
      * @return the newly created GrRenderTarget
      */
     GrRenderTarget* createRenderTargetFrom3DApiState();
@@ -323,22 +303,14 @@
      * Draws a path.
      *
      * @param paint         describes how to color pixels.
-     * @param pathIter      the path to draw
+     * @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,
-                  GrPathIter* pathIter,
-                  GrPathFill fill,
+    void drawPath(const GrPaint& paint, const GrPath& path, GrPathFill fill,
                   const GrPoint* translate = NULL);
-    /**
-     * Helper version of drawPath that takes a GrPath
-     */
-    void drawPath(const GrPaint& paint,
-                  const GrPath& path,
-                  GrPathFill fill,
-                  const GrPoint* translate = NULL);
+
     /**
      * Draws vertices with a paint.
      *
@@ -585,16 +557,9 @@
 
     void drawClipIntoStencil();
 
-    GrPathRenderer* getPathRenderer(const GrDrawTarget* target,
-                                    GrPathIter* path,
-                                    GrPathFill fill);
+    GrPathRenderer* getPathRenderer(const GrDrawTarget*, const GrPath&, GrPathFill);
 
     struct OffscreenRecord;
-    // we currently only expose stage 0 through the paint so use stage 1. We
-    // use stage 1 for the offscreen.
-    enum {
-        kOffscreenStage = 1,
-    };
 
     bool doOffscreenAA(GrDrawTarget* target, 
                        const GrPaint& paint,
@@ -613,6 +578,15 @@
                           const GrIRect& boundRect,
                           OffscreenRecord* record);
     
+    // computes vertex layout bits based on the paint. If paint expresses
+    // a texture for a stage, the stage coords will be bound to postitions
+    // unless hasTexCoords[s]==true in which case stage s's input coords
+    // are bound to tex coord index s. hasTexCoords == NULL is a shortcut
+    // for an array where all the values are false.
+    static int PaintStageVertexLayoutBits(
+                                    const GrPaint& paint,
+                                    const bool hasTexCoords[GrPaint::kTotalStages]);
+    
 };
 
 /**
diff --git a/gpu/include/GrContext_impl.h b/gpu/include/GrContext_impl.h
index fae4e92..c79a191 100644
--- a/gpu/include/GrContext_impl.h
+++ b/gpu/include/GrContext_impl.h
@@ -40,19 +40,15 @@
                                           const COL_SRC* colorSrc,
                                           const IDX_SRC* idxSrc) {
 
-    GrVertexLayout layout = 0;
-
     GrDrawTarget::AutoReleaseGeometry geo;
 
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
 
-    if (NULL != paint.getTexture()) {
-        if (NULL != texCoordSrc) {
-            layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
-        } else {
-            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
-        }
-    }
+    bool hasTexCoords[GrPaint::kTotalStages] = {
+        NULL != texCoordSrc, // texCoordSrc provides explicit stage 0 coords
+        0                    // remaining stages use positions
+    };
+    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, hasTexCoords);
 
     if (NULL != colorSrc) {
         layout |= GrDrawTarget::kColor_VertexLayoutBit;
diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h
index 93b381d..cd70d3e 100644
--- a/gpu/include/GrDrawTarget.h
+++ b/gpu/include/GrDrawTarget.h
@@ -32,7 +32,6 @@
 class GrClipIterator;
 class GrVertexBuffer;
 class GrIndexBuffer;
-class GrEffect;
 
 class GrDrawTarget : public GrRefCnt {
 public:
@@ -51,10 +50,21 @@
      * or not.
      */
     enum {
-        kNumStages = 2,
+        kNumStages = 3,
         kMaxTexCoords = kNumStages
     };
 
+
+    /**
+     * The absolute maximum number of edges that may be specified for
+     * a single draw call when performing edge antialiasing.  This is used for
+     * the size of several static buffers, so implementations of getMaxEdges()
+     * (below) should clamp to this value.
+     */
+    enum {
+        kMaxEdges = 32
+    };
+
     /**
      *  Bitfield used to indicate which stages are in use.
      */
@@ -79,9 +89,6 @@
         kNoColorWrites_StateBit = 0x08, //<! If set it disables writing colors.
                                         //   Useful while performing stencil
                                         //   ops.
-        kEdgeAA_StateBit        = 0x10, //<! Perform edge anti-aliasing.
-                                        //   Requires the edges to be passed in
-                                        //   setEdgeAAData().
 
         // subclass may use additional bits internally
         kDummyStateBit,
@@ -129,6 +136,20 @@
         fCurrDrawState.fStencilSettings.setDisabled();
     }
 
+    class Edge {
+      public:
+        Edge() {}
+        Edge(float x, float y, float z) : fX(x), fY(y), fZ(z) {}
+        GrPoint intersect(const Edge& other) {
+            return GrPoint::Make(
+                (fY * other.fZ - other.fY * fZ) /
+                  (fX * other.fY - other.fX * fY),
+                (fX * other.fZ - other.fX * fZ) /
+                  (other.fX * fY - fX * other.fY));
+        }
+        float fX, fY, fZ;
+    };
+
 protected:
 
     struct DrState {
@@ -137,19 +158,26 @@
             // all DrState members should default to something
             // valid by the memset
             memset(this, 0, sizeof(DrState));
-            // This is an exception to our memset, since it will
-            // result in no change.
+            
+            // memset exceptions
             fColorFilterXfermode = SkXfermode::kDstIn_Mode;
+            fFirstCoverageStage = kNumStages;
+
+            // pedantic assertion that our ptrs will
+            // be NULL (0 ptr is mem addr 0)
             GrAssert((intptr_t)(void*)NULL == 0LL);
+
+            // default stencil setting should be disabled
             GrAssert(fStencilSettings.isDisabled());
+            fFirstCoverageStage = kNumStages;
         }
         uint32_t                fFlagBits;
         GrBlendCoeff            fSrcBlend;
         GrBlendCoeff            fDstBlend;
         GrColor                 fBlendConstant;
         GrTexture*              fTextures[kNumStages];
-        GrEffect*               fEffects[kNumStages];
         GrSamplerState          fSamplerStates[kNumStages];
+        int                     fFirstCoverageStage;
         GrRenderTarget*         fRenderTarget;
         GrColor                 fColor;
         DrawFace                fDrawFace;
@@ -158,7 +186,8 @@
 
         GrStencilSettings       fStencilSettings;
         GrMatrix                fViewMatrix;
-        float                   fEdgeAAEdges[18];
+        Edge                    fEdgeAAEdges[kMaxEdges];
+        int                     fEdgeAANumEdges;
         bool operator ==(const DrState& s) const {
             return 0 == memcmp(this, &s, sizeof(DrState));
         }
@@ -245,6 +274,18 @@
     }
 
     /**
+     * Shortcut for preConcatSamplerMatrix on all stages in mask with same 
+     * matrix
+     */
+    void preConcatSamplerMatrices(int stageMask, const GrMatrix& matrix) {
+        for (int i = 0; i < kNumStages; ++i) {
+            if ((1 << i) & stageMask) {
+                this->preConcatSamplerMatrix(i, matrix);
+            }
+        }
+    }
+
+    /**
      * Gets the matrix of a stage's sampler
      *
      * @param stage     the stage to of sampler to get
@@ -343,6 +384,26 @@
     void setDrawFace(DrawFace face) { fCurrDrawState.fDrawFace = face; }
 
     /**
+     * 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) { 
+        fCurrDrawState.fFirstCoverageStage = firstCoverageStage; 
+    }
+
+    /**
+     * Gets the index of the first coverage-computing stage.
+     */
+    int getFirstCoverageStage() const { 
+        return fCurrDrawState.fFirstCoverageStage; 
+    }
+
+    /**
      * Gets whether the target is drawing clockwise, counterclockwise,
      * or both faces.
      * @return the current draw face(s).
@@ -392,7 +453,7 @@
      * @param srcCoef coeffecient applied to the src color.
      * @param dstCoef coeffecient applied to the dst color.
      */
-    void setBlendFunc(GrBlendCoeff srcCoef, GrBlendCoeff dstCoef);
+    void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff);
 
     /**
      * Sets the blending function constant referenced by the following blending
@@ -498,7 +559,7 @@
      * @param edges       3 * 6 float values, representing the edge
      *                    equations in Ax + By + C form
      */
-     void setEdgeAAData(const float edges[18]);
+     void setEdgeAAData(const Edge* edges, int numEdges);
 
 private:
     static const int TEX_COORD_BIT_CNT = kNumStages*kMaxTexCoords;
@@ -766,6 +827,15 @@
      */
     virtual void clear(const GrIRect* rect, GrColor color) = 0;
 
+    /**
+     * Returns the maximum number of edges that may be specified in a single
+     * draw call when performing edge antialiasing.  This is usually limited
+     * by the number of fragment uniforms which may be uploaded.  Must be a
+     * minimum of six, since a triangle's vertices each belong to two boundary
+     * edges which may be distinct.
+     */
+    virtual int getMaxEdges() const { return 6; }
+
     ///////////////////////////////////////////////////////////////////////////
 
     class AutoStateRestore : ::GrNoncopyable {
diff --git a/gpu/include/GrFontScaler.h b/gpu/include/GrFontScaler.h
index 77730d7..ab73ea4 100644
--- a/gpu/include/GrFontScaler.h
+++ b/gpu/include/GrFontScaler.h
@@ -21,7 +21,7 @@
 #include "GrGlyph.h"
 #include "GrKey.h"
 
-class GrPath;
+class SkPath;
 
 /**
  *  This is a virtual base class which Gr's interface to the host platform's
@@ -37,7 +37,7 @@
     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, GrPath*) = 0;
+    virtual bool getGlyphPath(uint16_t glyphID, SkPath*) = 0;
 };
 
 #endif
diff --git a/gpu/include/GrGLDefines.h b/gpu/include/GrGLDefines.h
index 29e56f3..6c2483b 100644
--- a/gpu/include/GrGLDefines.h
+++ b/gpu/include/GrGLDefines.h
@@ -69,6 +69,12 @@
 /*      GL_DST_ALPHA */
 /*      GL_ONE_MINUS_DST_ALPHA */
 
+/* ExtendedBlendFactors */
+#define GR_GL_SRC1_COLOR                     0x88F9
+#define GR_GL_ONE_MINUS_SRC1_COLOR           0x88FA
+/*      GL_SRC1_ALPHA */
+#define GR_GL_ONE_MINUS_SRC1_ALPHA           0x88FB
+
 /* BlendEquationSeparate */
 #define GR_GL_FUNC_ADD                       0x8006
 #define GR_GL_BLEND_EQUATION                 0x8009
@@ -318,6 +324,8 @@
 #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
diff --git a/gpu/include/GrGLInterface.h b/gpu/include/GrGLInterface.h
index 591ab8c..150e8e4 100644
--- a/gpu/include/GrGLInterface.h
+++ b/gpu/include/GrGLInterface.h
@@ -63,6 +63,7 @@
 typedef unsigned char GrGLboolean;
 typedef unsigned int GrGLbitfield;
 typedef signed char GrGLbyte;
+typedef char GrGLchar;
 typedef short GrGLshort;
 typedef int GrGLint;
 typedef int GrGLsizei;
@@ -199,6 +200,9 @@
     // Buffer mapping (extension in ES).
     typedef GrGLvoid* (GR_GL_FUNCTION_TYPE *GrGLMapBufferProc)(GrGLenum target, GrGLenum access);
     typedef GrGLboolean (GR_GL_FUNCTION_TYPE *GrGLUnmapBufferProc)(GrGLenum target);
+
+    // Dual source blending
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBindFragDataLocationIndexedProc)(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name);
 }  // extern "C"
 
 /*
@@ -333,6 +337,9 @@
     GrGLMapBufferProc fMapBuffer;
     GrGLUnmapBufferProc fUnmapBuffer;
 
+    // Dual Source Blending
+    GrGLBindFragDataLocationIndexedProc fBindFragDataLocationIndexed;
+
     // Code that initializes this struct using a static initializer should
     // make this the last entry in the static initializer. It can help to guard
     // against failing to initialize newly-added members of this struct.
diff --git a/gpu/include/GrGpu.h b/gpu/include/GrGpu.h
index e44956f..574a430 100644
--- a/gpu/include/GrGpu.h
+++ b/gpu/include/GrGpu.h
@@ -60,6 +60,21 @@
 
 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_BlendCoeff = kPublicBlendCoeffCount,
+        kIS2C_BlendCoeff,
+        kS2A_BlendCoeff,
+        kIS2A_BlendCoeff,
+
+        kTotalBlendCoeffCount
+    };
+
+    /**
      *  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.
@@ -109,20 +124,6 @@
      */
     GrTexture* createTexture(const GrTextureDesc& desc,
                              const void* srcData, size_t rowBytes);
-    /**
-     * Wraps an externally-created rendertarget in a GrRenderTarget.
-     * @param platformRenderTarget  handle to the the render target in the
-     *                              underlying 3D API. Interpretation depends on
-     *                              GrGpu subclass in use.
-     * @param stencilBits           number of stencil bits the target has
-     * @param isMultisampled        specify whether the RT is multisampled
-     * @param width                 width of the render target
-     * @param height                height of the render target
-     */
-     GrRenderTarget* createPlatformRenderTarget(intptr_t platformRenderTarget,
-                                                int stencilBits,
-                                                bool isMultisampled,
-                                                int width, int height);
 
     GrResource* createPlatformSurface(const GrPlatformSurfaceDesc& desc);
 
@@ -204,6 +205,15 @@
     bool supports4x4DownsampleFilter() const { return f4X4DownsampleFilterSupport; }
 
     /**
+     * Does this instance support dual-source blending? Required for proper
+     * blending with partial coverage with certain blend modes (dst coeff is
+     * not 1, ISA, or ISC)
+     */
+    bool supportsDualSourceBlending() const { 
+        return fDualSourceBlendingSupport; 
+    }
+
+    /**
      * Gets the minimum width of a render target. If a texture/rt is created
      * with a width less than this size the GrGpu object will clamp it to this
      * value.
@@ -385,6 +395,7 @@
     bool fAALineSupport;
     bool fFSAASupport;
     bool f4X4DownsampleFilterSupport; // supports GrSamplerState::k4x4Downsample_Filter
+    bool fDualSourceBlendingSupport;
 
     // set by subclass to true if index and vertex buffers can be locked, false
     // otherwise.
@@ -427,11 +438,6 @@
                                        const void* srcData,
                                        size_t rowBytes) = 0;
     virtual GrResource* onCreatePlatformSurface(const GrPlatformSurfaceDesc& desc) = 0;
-    virtual GrRenderTarget* onCreatePlatformRenderTarget(
-                                                intptr_t platformRenderTarget,
-                                                int stencilBits,
-                                                bool isMultisampled,
-                                                int width, int height) = 0;
     virtual GrRenderTarget* onCreateRenderTargetFrom3DApiState() = 0;
     virtual GrVertexBuffer* onCreateVertexBuffer(uint32_t size,
                                                  bool dynamic) = 0;
@@ -513,8 +519,7 @@
     void prepareIndexPool();
 
     // determines the path renderer used to draw a clip path element.
-    GrPathRenderer* getClipPathRenderer(GrPathIter* path,
-                                        GrPathFill fill);
+    GrPathRenderer* getClipPathRenderer(const SkPath& path, GrPathFill fill);
 
     void handleDirtyContext() {
         if (fContextIsDirty) {
diff --git a/gpu/include/GrPaint.h b/gpu/include/GrPaint.h
index 3035ca1..a7923f5 100644
--- a/gpu/include/GrPaint.h
+++ b/gpu/include/GrPaint.h
@@ -25,12 +25,17 @@
 
 /**
  * The paint describes how pixels are colored when the context draws to
- * them.
+ * them. TODO: Make this a "real" class with getters and setters, default
+ * values, and documentation.
  */
 class GrPaint {
 public:
+    enum {
+        kMaxTextures = 1,
+        kMaxMasks    = 1,
+    };
 
-    // All the paint fields are public except texture (it's ref-counted)
+    // All the paint fields are public except textures/samplers
     GrBlendCoeff                fSrcBlendCoeff;
     GrBlendCoeff                fDstBlendCoeff;
     bool                        fAntiAlias;
@@ -38,22 +43,75 @@
 
     GrColor                     fColor;
 
-    GrSamplerState              fSampler;
-
     GrColor                     fColorFilterColor;
     SkXfermode::Mode            fColorFilterXfermode;
 
-    void setTexture(GrTexture* texture) {
+    void setTexture(int i, GrTexture* texture) {
+        GrAssert((unsigned)i < kMaxTextures);
         GrSafeRef(texture);
-        GrSafeUnref(fTexture);
-        fTexture = texture;
+        GrSafeUnref(fTextures[i]);
+        fTextures[i] = texture;
     }
 
-    GrTexture* getTexture() const { return fTexture; }
+    GrTexture* getTexture(int i) const { 
+        GrAssert((unsigned)i < kMaxTextures);
+        return fTextures[i]; 
+    }
+
+    GrSamplerState* getTextureSampler(int i) {
+        GrAssert((unsigned)i < kMaxTextures);
+        return fTextureSamplers + i;
+    }
+
+    const GrSamplerState* getTextureSampler(int i) const {
+        GrAssert((unsigned)i < kMaxTextures);
+        return fTextureSamplers + i;
+    }
+
+    // The mask can be alpha-only or per channel. It is applied
+    // after the colorfilter
+    void setMask(int i, GrTexture* mask) {
+        GrAssert((unsigned)i < kMaxMasks);
+        GrSafeRef(mask);
+        GrSafeUnref(fMaskTextures[i]);
+        fMaskTextures[i] = mask;
+    }
+
+    GrTexture* getMask(int i) const { 
+        GrAssert((unsigned)i < kMaxMasks);
+        return fMaskTextures[i]; 
+    }
+
+    // mask's sampler matrix is always applied to the positions
+    // (i.e. no explicit texture coordinates)
+    GrSamplerState* getMaskSampler(int i) {
+        GrAssert((unsigned)i < kMaxMasks);
+        return fMaskSamplers + i;
+    }
+
+    const GrSamplerState* getMaskSampler(int i) const {
+        GrAssert((unsigned)i < kMaxMasks);
+        return fMaskSamplers + i;
+    }
+
+    // pre-concats sampler matrices for non-NULL textures and masks
+    void preConcatActiveSamplerMatrices(const GrMatrix& matrix) {
+        for (int i = 0; i < kMaxTextures; ++i) {
+            fTextureSamplers[i].preConcatMatrix(matrix);
+        }
+        for (int i = 0; i < kMaxMasks; ++i) {
+            fMaskSamplers[i].preConcatMatrix(matrix);
+        }
+    }
 
     // uninitialized
     GrPaint() {
-        fTexture = NULL;
+        for (int i = 0; i < kMaxTextures; ++i) {
+            fTextures[i] = NULL;
+        }
+        for (int i = 0; i < kMaxMasks; ++i) {
+            fMaskTextures[i] = NULL;
+        }
     }
 
     GrPaint(const GrPaint& paint) {
@@ -67,22 +125,35 @@
         fColorFilterColor = paint.fColorFilterColor;
         fColorFilterXfermode = paint.fColorFilterXfermode;
 
-        fSampler = paint.fSampler;
-        fTexture = paint.fTexture;
-        GrSafeRef(fTexture);
+        for (int i = 0; i < kMaxTextures; ++i) {
+            fTextureSamplers[i] = paint.fTextureSamplers[i];
+            fTextures[i] = paint.fTextures[i];
+            GrSafeRef(fTextures[i]);
+        }
+        for (int i = 0; i < kMaxMasks; ++i) {
+            fMaskSamplers[i] = paint.fMaskSamplers[i];
+            fMaskTextures[i] = paint.fMaskTextures[i];
+            GrSafeRef(fMaskTextures[i]);
+        }
     }
 
     ~GrPaint() {
-        GrSafeUnref(fTexture);
+        for (int i = 0; i < kMaxTextures; ++i) {
+            GrSafeUnref(fTextures[i]);
+        }
+        for (int i = 0; i < kMaxMasks; ++i) {
+            GrSafeUnref(fMaskTextures[i]);
+        }
     }
 
-    // sets paint to src-over, solid white, no texture
+    // sets paint to src-over, solid white, no texture, no mask
     void reset() {
-        resetBlend();
-        resetOptions();
-        resetColor();
-        resetTexture();
-        resetColorFilter();
+        this->resetBlend();
+        this->resetOptions();
+        this->resetColor();
+        this->resetTextures();
+        this->resetColorFilter();
+        this->resetMasks();
     }
 
     void resetColorFilter() {
@@ -90,8 +161,60 @@
         fColorFilterColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
     }
 
+    bool hasTexture() const {
+        return 0 != this->getActiveTextureStageMask();
+    }
+
+    bool hasMask() const {
+        return 0 != this->getActiveMaskStageMask();
+    }
+
+    bool hasTextureOrMask() const {
+        return this->hasTexture() || this->hasMask();
+    }
+
+    // helpers for GrContext, GrTextContext
+    int getActiveTextureStageMask() const {
+        int mask = 0;
+        for (int i = 0; i < kMaxTextures; ++i) {
+            if (NULL != fTextures[i]) {
+                mask |= 1 << (i + kFirstTextureStage);
+            }
+        }
+        return mask;
+    }
+
+    int getActiveMaskStageMask() const {
+        int mask = 0;
+        for (int i = 0; i < kMaxMasks; ++i) {
+            if (NULL != fMaskTextures[i]) {
+                mask |= 1 << (i + kFirstMaskStage);
+            }
+        }
+        return mask;
+    }
+    
+    int getActiveStageMask() const {
+        return this->getActiveTextureStageMask() |
+                this->getActiveMaskStageMask();
+    }
+
+    // internal use
+    // GrPaint's textures and masks map to the first N stages
+    // of GrDrawTarget in that order (textures followed by masks)
+    enum {
+        kFirstTextureStage = 0,
+        kFirstMaskStage = kMaxTextures,
+        kTotalStages = kMaxTextures + kMaxMasks,
+    };
+
 private:
-    GrTexture*      fTexture;
+
+    GrSamplerState              fTextureSamplers[kMaxTextures];
+    GrSamplerState              fMaskSamplers[kMaxMasks];
+
+    GrTexture*      fTextures[kMaxTextures];
+    GrTexture*      fMaskTextures[kMaxMasks];
 
     void resetBlend() {
         fSrcBlendCoeff = kOne_BlendCoeff;
@@ -107,11 +230,19 @@
         fColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
     }
 
-    void resetTexture() {
-        setTexture(NULL);
-        fSampler.setClampNoFilter();
+    void resetTextures() {
+        for (int i = 0; i < kMaxTextures; ++i) {
+            this->setTexture(i, NULL);
+            fTextureSamplers[i].setClampNoFilter();
+        }
     }
 
+    void resetMasks() {
+        for (int i = 0; i < kMaxMasks; ++i) {
+            this->setMask(i, NULL);
+            fMaskSamplers[i].setClampNoFilter();
+        }
+    }
 };
 
 #endif
diff --git a/gpu/include/GrPath.h b/gpu/include/GrPath.h
index f958329..c23cfc4 100644
--- a/gpu/include/GrPath.h
+++ b/gpu/include/GrPath.h
@@ -18,91 +18,10 @@
 #ifndef GrPath_DEFINED
 #define GrPath_DEFINED
 
-#include "GrPathSink.h"
-#include "GrPathIter.h"
-#include "GrTDArray.h"
-#include "GrPoint.h"
-#include "GrRect.h"
+#include "GrTypes.h"
+#include "SkPath.h"
 
-class GrPath : public GrPathSink {
-public:
-    GrPath();
-    GrPath(const GrPath&);
-    explicit GrPath(GrPathIter&);
-    virtual ~GrPath();
-
-    GrConvexHint getConvexHint() const { return fConvexHint; }
-    void setConvexHint(GrConvexHint hint) { fConvexHint = hint; }
-
-    const GrRect& getConservativeBounds() const { return fConservativeBounds; }
-
-    void resetFromIter(GrPathIter*);
-
-    bool operator ==(const GrPath& path) const;
-    bool operator !=(const GrPath& path) const { return !(*this == path); }
-    // overrides from GrPathSink
-
-    virtual void moveTo(GrScalar x, GrScalar y);
-    virtual void lineTo(GrScalar x, GrScalar y);
-    virtual void quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1);
-    virtual void cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1,
-                         GrScalar x2, GrScalar y2);
-    virtual void close();
-
-    /**
-     *  Offset the path by (tx, ty), adding tx to the horizontal position
-     *  and adds ty to the vertical position of every point.
-     */
-    void offset(GrScalar tx, GrScalar ty);
-
-    class Iter : public GrPathIter {
-    public:
-        /**
-         * Creates an uninitialized iterator
-         */
-        Iter();
-
-        Iter(const GrPath& path);
-
-        // overrides from GrPathIter
-        virtual GrPathCmd next(GrPoint points[]);
-        virtual GrConvexHint convexHint() const;
-        virtual GrPathCmd next();
-        virtual void rewind();
-        virtual bool getConservativeBounds(GrRect* rect) const;
-
-        /**
-         * Sets iterator to begining of path
-         */
-        void reset(const GrPath& path);
-    private:
-        const GrPath* fPath;
-        GrPoint       fLastPt;
-        int           fCmdIndex;
-        int           fPtIndex;
-    };
-
-    static void ConvexUnitTest();
-
-private:
-
-    GrTDArray<GrPathCmd>    fCmds;
-    GrTDArray<GrPoint>      fPts;
-    GrConvexHint            fConvexHint;
-    GrRect                  fConservativeBounds;
-
-    // this ensures we have a moveTo at the start of each contour
-    inline void ensureMoveTo();
-
-    bool wasLastVerb(GrPathCmd cmd) const {
-        int count = fCmds.count();
-        return count > 0 && cmd == fCmds[count - 1];
-    }
-
-    friend class Iter;
-
-    typedef GrPathSink INHERITED;
-};
+typedef SkPath GrPath;
 
 #endif
 
diff --git a/gpu/include/GrPathIter.h b/gpu/include/GrPathIter.h
deleted file mode 100644
index e67ff69..0000000
--- a/gpu/include/GrPathIter.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-    Copyright 2010 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 GrPathIter_DEFINED
-#define GrPathIter_DEFINED
-
-#include "GrRect.h"
-
-/**
- 2D Path iterator. Porting layer creates a subclass of this. It allows Ganesh to
- parse the top-level API's 2D paths. Supports lines, quadratics, and cubic
- pieces and moves (multi-part paths).
- */
-class GrPathIter {
-public:
-
-    virtual ~GrPathIter() {};
-
-    /**
-     * Iterates through the path. Should not be called after
-     * kEnd_Command has been returned once. This version retrieves the
-     * points for the command.
-     * @param points  The points relevant to returned commend. See Command
-     *               enum for number of points valid for each command.
-     * @return The next command of the path.
-     */
-    virtual GrPathCmd next(GrPoint points[4]) = 0;
-
-    /**
-     * If the host API has knowledge of the convexity of the path
-     * it can be communicated by this hint. Gr can analyze the path
-     * as it is iterated. So it is not necessary to do additional work to
-     * compute convexity status if it isn't already determined.
-     *
-     * @return a hint about the convexity of the path.
-     */
-    virtual GrConvexHint convexHint() const = 0;
-
-     /**
-      * Iterates through the path. Should not be called after
-      * kEnd_Command has been returned once. This version does not retrieve the
-      * points for the command.
-      * @return The next command of the path.
-      */
-     virtual GrPathCmd next() = 0;
-
-     /**
-      * Returns conservative bounds on the path points. If returns false then
-      * no bounds are available.
-      */
-     virtual bool getConservativeBounds(GrRect* rect) const = 0;
-
-    /**
-     Restarts iteration from the beginning.
-     */
-    virtual void rewind() = 0;
-
-};
-
-#endif
diff --git a/gpu/include/GrPathRenderer.h b/gpu/include/GrPathRenderer.h
index 21cab6b..1ebad4f 100644
--- a/gpu/include/GrPathRenderer.h
+++ b/gpu/include/GrPathRenderer.h
@@ -19,7 +19,7 @@
 
 #include "GrDrawTarget.h"
 
-class GrPathIter;
+class SkPath;
 struct GrPoint;
 
 /**
@@ -37,8 +37,7 @@
      *
      * @return  true if the path can be drawn by this object, false otherwise.
      */
-    virtual bool canDrawPath(const GrDrawTarget* target,
-                             GrPathIter* path,
+    virtual bool canDrawPath(const GrDrawTarget* target, const SkPath& path,
                              GrPathFill fill) const = 0;
 
     /**
@@ -57,7 +56,7 @@
      */
     virtual void drawPath(GrDrawTarget* target,
                           GrDrawTarget::StageBitfield stages,
-                          GrPathIter* path,
+                          const SkPath& path,
                           GrPathFill fill,
                           const GrPoint* translate) = 0;
 
@@ -80,7 +79,7 @@
      *         clips.
      */
     virtual bool requiresStencilPass(const GrDrawTarget* target,
-                                     GrPathIter* path,
+                                     const SkPath& path,
                                      GrPathFill fill) const { return false; }
 
     /**
@@ -102,7 +101,7 @@
      *                              the path. NULL means (0,0).
      */
     virtual void drawPathToStencil(GrDrawTarget* target,
-                                   GrPathIter* path,
+                                   const SkPath& path,
                                    GrPathFill fill,
                                    const GrPoint* translate) {
         GrCrash("Unexpected call to drawPathToStencil.");
@@ -113,7 +112,7 @@
      * having FSAA enabled for a render target)
      */
     virtual bool supportsAA(GrDrawTarget* target,
-                            GrPathIter* path,
+                            const SkPath& path,
                             GrPathFill fill) { return false; }
 
     /**
@@ -138,26 +137,26 @@
                           bool stencilWrapOpsSupport);
 
     virtual bool canDrawPath(const GrDrawTarget* target,
-                             GrPathIter* path,
+                             const SkPath& path,
                              GrPathFill fill) const { return true; }
 
     virtual void drawPath(GrDrawTarget* target,
                           GrDrawTarget::StageBitfield stages,
-                          GrPathIter* path,
+                          const SkPath& path,
                           GrPathFill fill,
                           const GrPoint* translate);
     virtual bool requiresStencilPass(const GrDrawTarget* target,
-                                     GrPathIter* path,
+                                     const SkPath& path,
                                      GrPathFill fill) const;
     virtual void drawPathToStencil(GrDrawTarget* target,
-                                   GrPathIter* path,
+                                   const SkPath& path,
                                    GrPathFill fill,
                                    const GrPoint* translate);
 private:
 
     void onDrawPath(GrDrawTarget* target,
                     GrDrawTarget::StageBitfield stages,
-                    GrPathIter* path,
+                    const SkPath& path,
                     GrPathFill fill,
                     const GrPoint* translate,
                     bool stencilOnly);
diff --git a/gpu/include/GrSamplerState.h b/gpu/include/GrSamplerState.h
index dd47c53..373ea94 100644
--- a/gpu/include/GrSamplerState.h
+++ b/gpu/include/GrSamplerState.h
@@ -94,6 +94,7 @@
         fSampleMode = kNormal_SampleMode;
         fFilter = filter;
         fMatrix.setIdentity();
+        fTextureDomain.setEmpty();
     }
 
     GrSamplerState(WrapMode wx, WrapMode wy, Filter filter) {
@@ -102,6 +103,7 @@
         fSampleMode = kNormal_SampleMode;
         fFilter = filter;
         fMatrix.setIdentity();
+        fTextureDomain.setEmpty();
     }
 
     GrSamplerState(WrapMode wx, WrapMode wy, 
@@ -111,6 +113,7 @@
         fSampleMode = kNormal_SampleMode;
         fFilter = filter;
         fMatrix = matrix;
+        fTextureDomain.setEmpty();
     }
 
     GrSamplerState(WrapMode wx, WrapMode wy, SampleMode sample, 
@@ -120,12 +123,15 @@
         fSampleMode = sample;
         fMatrix = matrix;
         fFilter = filter;
+        fTextureDomain.setEmpty();
     }
 
     WrapMode getWrapX() const { return fWrapX; }
     WrapMode getWrapY() const { return fWrapY; }
     SampleMode getSampleMode() const { return fSampleMode; }
     const GrMatrix& getMatrix() const { return fMatrix; }
+    const GrRect& getTextureDomain() const { return fTextureDomain; }
+    bool hasTextureDomain() const {return SkIntToScalar(0) != fTextureDomain.right();}
     Filter getFilter() const { return fFilter; }
 
     bool isGradient() const {
@@ -146,6 +152,13 @@
     void setMatrix(const GrMatrix& matrix) { fMatrix = matrix; }
     
     /**
+     * Sets the sampler's texture coordinate domain to a 
+     * custom rectangle, rather than the default (0,1).
+     * This option is currently only supported with kClamp_WrapMode
+     */
+    void setTextureDomain(const GrRect& textureDomain) { fTextureDomain = textureDomain; }
+
+    /**
      *  Multiplies the current sampler matrix  a matrix
      *
      *  After this call M' = M*m where M is the old matrix, m is the parameter
@@ -169,6 +182,7 @@
         fSampleMode = kNormal_SampleMode;
         fFilter = kNearest_Filter;
         fMatrix.setIdentity();
+        fTextureDomain.setEmpty();
     }
 
     GrScalar getRadial2CenterX1() const { return fRadial2CenterX1; }
@@ -198,6 +212,7 @@
     SampleMode  fSampleMode;
     Filter      fFilter;
     GrMatrix    fMatrix;
+    GrRect      fTextureDomain;
 
     // these are undefined unless fSampleMode == kRadial2_SampleMode
     GrScalar    fRadial2CenterX1;
diff --git a/gpu/include/GrScalar.h b/gpu/include/GrScalar.h
index a26b67c..35cd61a 100644
--- a/gpu/include/GrScalar.h
+++ b/gpu/include/GrScalar.h
@@ -40,6 +40,7 @@
 #define GrScalarHalf(a)     SkScalarHalf(a)
 #define GrScalarAve(a,b)    SkScalarAve(a,b)
 #define GrMul(a,b)          SkScalarMul(a,b)
+#define GrScalarDiv(a,b)    SkScalarDiv(a, b)
 #define GrScalarToFloat(a)  SkScalarToFloat(a)
 #define GrFloatToScalar(a)  SkScalarToFloat(a)
 #define GrIntToScalar(a)    SkIntToScalar(a)
diff --git a/gpu/include/GrStencil.h b/gpu/include/GrStencil.h
index b014ee5..44a390d 100644
--- a/gpu/include/GrStencil.h
+++ b/gpu/include/GrStencil.h
@@ -159,7 +159,7 @@
         return kKeep_StencilOp == fFrontPassOp   &&
                kKeep_StencilOp == fBackPassOp    &&
                kKeep_StencilOp == fFrontFailOp   &&
-               kKeep_StencilOp == fFrontFailOp   &&
+               kKeep_StencilOp == fBackFailOp   &&
                kAlways_StencilFunc == fFrontFunc &&
                kAlways_StencilFunc == fBackFunc;
     }
diff --git a/gpu/include/GrTesselatedPathRenderer.h b/gpu/include/GrTesselatedPathRenderer.h
index accd114..e37e66b 100644
--- a/gpu/include/GrTesselatedPathRenderer.h
+++ b/gpu/include/GrTesselatedPathRenderer.h
@@ -25,22 +25,22 @@
 
     virtual void drawPath(GrDrawTarget* target,
                           GrDrawTarget::StageBitfield stages,
-                          GrPathIter* path,
+                          const GrPath& path,
                           GrPathFill fill,
                           const GrPoint* translate);
     virtual bool canDrawPath(const GrDrawTarget* target,
-                             GrPathIter* path,
+                             const GrPath& path,
                              GrPathFill fill) const;
 
     virtual bool requiresStencilPass(const GrDrawTarget* target,
-                                     GrPathIter* path,
+                                     const GrPath& path,
                                      GrPathFill fill) const { return false; }
     virtual void drawPathToStencil(GrDrawTarget* target,
-                                   GrPathIter* path,
+                                   const GrPath& path,
                                    GrPathFill fill,
                                    const GrPoint* translate);
     virtual bool supportsAA(GrDrawTarget* target,
-                            GrPathIter* path,
+                            const GrPath& path,
                             GrPathFill fill);
 };
 
diff --git a/gpu/include/GrTypes.h b/gpu/include/GrTypes.h
index 08b10f0..9348375 100644
--- a/gpu/include/GrTypes.h
+++ b/gpu/include/GrTypes.h
@@ -18,11 +18,9 @@
 #ifndef GrTypes_DEFINED
 #define GrTypes_DEFINED
 
+#include "SkTypes.h"
 #include "GrConfig.h"
 
-#include <memory.h>
-#include <string.h>
-
 ////////////////////////////////////////////////////////////////////////////////
 
 /**
@@ -53,7 +51,7 @@
  *  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)     (((n) + 3) >> 2 << 2)
+#define GrALIGN4(n)     SkAlign4(n)
 #define GrIsALIGN4(n)   (((n) & 3) == 0)
 
 template <typename T> const T& GrMin(const T& a, const T& b) {
@@ -111,7 +109,7 @@
 /**
  *  Count elements in an array
  */
-#define GR_ARRAY_COUNT(array)  (sizeof(array) / sizeof(array[0]))
+#define GR_ARRAY_COUNT(array)  SK_ARRAY_COUNT(array)
 
 //!< allocate a block of memory, will never return NULL
 extern void* GrMalloc(size_t bytes);
@@ -232,7 +230,7 @@
     kConstA_BlendCoeff,  //<! constant color alpha
     kIConstA_BlendCoeff, //<! one minus constant color alpha
 
-    kBlendCoeffCount
+    kPublicBlendCoeffCount
 };
 
 /**
diff --git a/gpu/src/GrBinHashKey.h b/gpu/src/GrBinHashKey.h
index 683528b..565b460 100644
--- a/gpu/src/GrBinHashKey.h
+++ b/gpu/src/GrBinHashKey.h
@@ -28,19 +28,20 @@
 public:
     GrBinHashKeyBuilder() {}
     virtual ~GrBinHashKeyBuilder() {}
-    virtual void keyData(const uint8_t *dataToAdd, size_t len) = 0;
+    virtual void keyData(const uint32_t *dataToAdd, size_t len) = 0;
 };
 
 /**
  *  Hash function class than can take a data stream of indeterminate length.
- *  It also has the ability to recieve data in several chunks (steamed). The 
- *  hash function used is Adler-32.
+ *  It also has the ability to recieve data in several chunks (steamed). The
+ *  hash function used is the One-at-a-Time Hash
+ *  (http://burtleburtle.net/bob/hash/doobs.html).
  *
- *  Keys are built in two passes the first pass builds the key until the 
+ *  Keys are built in two passes the first pass builds the key until the
  *  allocated storage for the key runs out, raw data accumulation stops, but
  *  the calculation of the 32-bit hash value and total key length continue.
  *  The second pass is only necessary if storage ran-out during the first pass.
- *  If that is the case, the heap storage portion of the key will be 
+ *  If that is the case, the heap storage portion of the key will be
  *  re-allocated so that the entire key can be stored in the second pass.
  *
  *  Code for building a key:
@@ -57,9 +58,8 @@
 template<typename Entry, size_t StackSize>
 class GrBinHashKey : public GrBinHashKeyBuilder {
 public:
-    GrBinHashKey() 
-        : fA(1)
-        , fB(0)
+    GrBinHashKey()
+        : fA(0)
         , fLength(0)
         , fHeapData(NULL)
         , fPhysicalSize(StackSize)
@@ -77,15 +77,15 @@
     // errors with template classes, which are hard to trace back to the use
     // of assignment.
     GrBinHashKey(const GrBinHashKey<Entry, StackSize>&) {}
-    GrBinHashKey<Entry, StackSize>& operator=(const GrBinHashKey<Entry, 
+    GrBinHashKey<Entry, StackSize>& operator=(const GrBinHashKey<Entry,
         StackSize>&) {
         return this;
     }
 
-public:    
+public:
     void copyAndTakeOwnership(GrBinHashKey<Entry, StackSize>& key) {
-        memcpy(this, &key, sizeof(*this));
         GrAssert(key.fIsValid);
+        copyFields(key);
         if (fUseHeap) {
             key.fHeapData = NULL;  // ownership transfer
         }
@@ -98,7 +98,7 @@
 
     void deepCopyFrom(const GrBinHashKey<Entry, StackSize>& key) {
         GrAssert(key.fIsValid);
-        memcpy(this, &key, sizeof(key));
+        copyFields(key);
         if (fUseHeap) {
             fHeapData = reinterpret_cast<uint8_t*>(
                 GrMalloc(sizeof(uint8_t) * fPhysicalSize));
@@ -121,7 +121,7 @@
         if (1 == fPass) {
             bool passNeeded = false;
             if (fLength > fPhysicalSize) {
-                // If the first pass ran out of space the we need to 
+                // If the first pass ran out of space the we need to
                 // re-allocate and perform a second pass
                 GrFree(fHeapData);
                 fHeapData = reinterpret_cast<uint8_t*>(
@@ -132,14 +132,15 @@
                 fLength = 0;
             }
             fPass++;
-            return passNeeded;       
+            return passNeeded;
         }
         return false;
     }
 
-    void keyData(const uint8_t *dataToAdd, size_t len) {
+    void keyData(const uint32_t *dataToAdd, size_t len) {
         GrAssert(fIsValid);
         GrAssert(fPass);
+        GrAssert(GrIsALIGN4(len));
         if (fUseHeap) {
             GrAssert(fHeapData);
             GrAssert(fLength + len <= fPhysicalSize);
@@ -155,13 +156,18 @@
         fLength += len;
 
         if (1 == fPass) {
-            // update the 32-bit hash
-            while (len) {
-                fA = (fA + *dataToAdd) % kBigPrime;
-                fB = (fB + fA) % kBigPrime;
-                dataToAdd++;
-                len--;
+            uint32_t a = fA;
+            while (len >= 4) {
+                a += *dataToAdd++;
+                a += (a << 10);
+                a ^= (a >> 6);
+                len -= 4;
             }
+            a += (a << 3);
+            a ^= (a >> 11);
+            a += (a << 15);
+
+            fA = a;
         }
     }
 
@@ -175,34 +181,44 @@
                 return memcmp(fStackData, key.fStackData, fLength);
             }
         }
-        
+
         return (fLength - key.fLength);
     }
 
-    static bool 
+    static bool
     EQ(const Entry& entry, const GrBinHashKey<Entry, StackSize>& key) {
         GrAssert(key.fIsValid);
         return 0 == entry.compare(key);
     }
-    
-    static bool 
+
+    static bool
     LT(const Entry& entry, const GrBinHashKey<Entry, StackSize>& key) {
         GrAssert(key.fIsValid);
-        return entry.compare(key) < 0;    
+        return entry.compare(key) < 0;
     }
 
     uint32_t getHash() const {
         GrAssert(fIsValid);
-        return (fB << 16) | fA;
+        return fA;
     }
 
 private:
-    // For computing the Adler-32 hash
-    enum Constants {
-        kBigPrime = 65521 // largest prime smaller than 2^16
-    };
+    void copyFields(const GrBinHashKey<Entry, StackSize>& src) {
+        if (fUseHeap) {
+            GrFree(fHeapData);
+        }
+        // We do a field-by-field copy because this is a non-POD
+        // class, and therefore memcpy would be bad
+        fA = src.fA;
+        fLength = src.fLength;
+        memcpy(fStackData, src.fStackData, StackSize);
+        fHeapData = src.fHeapData;
+        fPhysicalSize = src.fPhysicalSize;
+        fUseHeap = src.fUseHeap;
+        fPass = src.fPass;
+    }
+
     uint32_t                fA;
-    uint32_t                fB;
 
     // For accumulating the variable length binary key
     size_t              fLength;                // length of data accumulated so far
diff --git a/gpu/src/GrClip.cpp b/gpu/src/GrClip.cpp
index 2d1680c..c2613bb 100644
--- a/gpu/src/GrClip.cpp
+++ b/gpu/src/GrClip.cpp
@@ -132,7 +132,7 @@
                     }
                     break;
                 case kPath_ClipType:
-                    e.fPath.resetFromIter(iter->getPathIter());
+                    e.fPath = *iter->getPath();
                     if (tx || ty) {
                         e.fPath.offset(tx, ty);
                     }
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 8cb932b..dae1cd1 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -19,14 +19,16 @@
 #include "GrTextureCache.h"
 #include "GrTextStrike.h"
 #include "GrMemory.h"
-#include "GrPathIter.h"
 #include "GrClipIterator.h"
 #include "GrIndexBuffer.h"
 #include "GrInOrderDrawBuffer.h"
 #include "GrBufferAllocPool.h"
 #include "GrPathRenderer.h"
 
-#define ENABLE_OFFSCREEN_AA 0
+// larger than this, and we don't AA. set to 0 for no AA
+#ifndef GR_MAX_OFFSCREEN_AA_DIM
+    #define GR_MAX_OFFSCREEN_AA_DIM    0
+#endif
 
 #define DEFER_TEXT_RENDERING 1
 
@@ -107,6 +109,26 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+int GrContext::PaintStageVertexLayoutBits(
+                            const GrPaint& paint,
+                            const bool hasTexCoords[GrPaint::kTotalStages]) {
+    int stageMask = paint.getActiveStageMask();
+    int layout = 0;
+    for (int i = 0; i < GrPaint::kTotalStages; ++i) {
+        if ((1 << i) & stageMask) {
+            if (NULL != hasTexCoords && hasTexCoords[i]) {
+                layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(i, i);
+            } else {
+                layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
+            }
+        }
+    }
+    return layout;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
 enum {
     kNPOTBit    = 0x1,
     kFilterBit  = 0x2,
@@ -360,22 +382,7 @@
     return fGpu->createPlatformSurface(desc);
 }
 
-GrRenderTarget* GrContext::createPlatformRenderTarget(intptr_t platformRenderTarget,
-                                                      int stencilBits,
-                                                      bool isMultisampled,
-                                                      int width, int height) {
-#if GR_DEBUG
-    GrPrintf("Using deprecated createPlatformRenderTarget API.");
-#endif
-    return fGpu->createPlatformRenderTarget(platformRenderTarget, 
-                                            stencilBits, isMultisampled, 
-                                            width, height);
-}
-
 GrRenderTarget* GrContext::createRenderTargetFrom3DApiState() {
-#if GR_DEBUG
-    GrPrintf("Using deprecated createRenderTargetFrom3DApiState API.");
-#endif
     return fGpu->createRenderTargetFrom3DApiState();
 }
 
@@ -447,7 +454,7 @@
 bool GrContext::doOffscreenAA(GrDrawTarget* target, 
                               const GrPaint& paint,
                               bool isLines) const {
-#if !ENABLE_OFFSCREEN_AA
+#if GR_MAX_OFFSCREEN_AA_DIM==0
     return false;
 #else
     if (!paint.fAntiAlias) {
@@ -477,7 +484,7 @@
                                       bool requireStencil,
                                       const GrIRect& boundRect,
                                       OffscreenRecord* record) {
-    GrAssert(ENABLE_OFFSCREEN_AA);
+    GrAssert(GR_MAX_OFFSCREEN_AA_DIM > 0);
 
     GrAssert(NULL == record->fEntry0);
     GrAssert(NULL == record->fEntry1);
@@ -577,6 +584,10 @@
     GrTexture* src = record->fEntry0->texture();
     int scale;
 
+    enum {
+        kOffscreenStage = GrPaint::kTotalStages,
+    };
+
     if (OffscreenRecord::k4x4TwoPass_Downsample == record->fDownsample) {
         GrAssert(NULL != record->fEntry1);
         scale = 2;
@@ -606,11 +617,14 @@
     }
 
     // setup for draw back to main RT
+    int stageMask = paint.getActiveStageMask();
+
     target->restoreDrawState(record->fSavedState);
-    if (NULL != paint.getTexture()) {
+
+    if (stageMask) {
         GrMatrix invVM;
         if (target->getViewInverse(&invVM)) {
-            target->preConcatSamplerMatrix(0, invVM);
+            target->preConcatSamplerMatrices(stageMask, invVM);
         }
     }
     target->setViewMatrix(GrMatrix::I());
@@ -624,7 +638,7 @@
     target->setSamplerState(kOffscreenStage, sampler);
 
     GrRect dstRect;
-    int stages = (1 << kOffscreenStage) | (NULL == paint.getTexture() ? 0 : 1);
+    int stages = (1 << kOffscreenStage) | stageMask;
     dstRect.set(boundRect);
     target->drawSimpleRect(dstRect, NULL, stages);
 
@@ -662,11 +676,14 @@
 }
 
 static GrColor getColorForMesh(const GrPaint& paint) {
-    if (NULL == paint.getTexture()) {
-        return paint.fColor;
-    } else {
+    // FIXME: This was copied from SkGpuDevice, seems like
+    // we should have already smeared a in caller if that
+    // is what is desired.
+    if (paint.hasTexture()) {
         unsigned a = GrColorUnpackA(paint.fColor);
         return GrColorPackRGBA(a, a, a, a);
+    } else {
+        return paint.fColor;
     }
 }
 
@@ -742,10 +759,8 @@
                            const GrPaint& paint,
                            const GrRect& devRect) {
 
-    GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit;
-    if (NULL != paint.getTexture()) {
-        layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
-    }
+    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL) |
+                            GrDrawTarget::kColor_VertexLayoutBit;
 
     size_t vsize = GrDrawTarget::VertexSize(layout);
 
@@ -783,11 +798,8 @@
     const GrScalar rx = GrMul(dx, GR_ScalarHalf);
     const GrScalar ry = GrMul(dy, GR_ScalarHalf);
 
-    GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit;
-
-    if (NULL != paint.getTexture()) {
-        layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
-    }
+    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL) |
+                            GrDrawTarget::kColor_VertexLayoutBit;
 
     GrScalar spare;
     {
@@ -903,9 +915,9 @@
                          GrScalar width,
                          const GrMatrix* matrix) {
 
-    bool textured = NULL != paint.getTexture();
 
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
+    int stageMask = paint.getActiveStageMask();
 
     GrRect devRect = rect;
     GrMatrix combinedMatrix;
@@ -914,10 +926,10 @@
 
     if (doAA) {
         GrDrawTarget::AutoViewMatrixRestore avm(target);
-        if (textured) {
+        if (stageMask) {
             GrMatrix inv;
             if (combinedMatrix.invert(&inv)) {
-                target->preConcatSamplerMatrix(0, inv);
+                target->preConcatSamplerMatrices(stageMask, inv);
             }
         }
         target->setViewMatrix(GrMatrix::I());
@@ -941,9 +953,8 @@
         // TODO: consider making static vertex buffers for these cases.
         // Hairline could be done by just adding closing vertex to
         // unitSquareVertexBuffer()
-        GrVertexLayout layout = textured ?
-                            GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :
-                            0;
+        GrVertexLayout layout =  PaintStageVertexLayoutBits(paint, NULL);
+
         static const int worstCaseVertCount = 10;
         GrDrawTarget::AutoReleaseGeometry geo(target, layout, worstCaseVertCount, 0);
 
@@ -974,17 +985,14 @@
         if (NULL != matrix) {
             avmr.set(target);
             target->preConcatViewMatrix(*matrix);
-            if (textured) {
-                target->preConcatSamplerMatrix(0, *matrix);
-            }
+            target->preConcatSamplerMatrices(stageMask, *matrix);
         }
 
         target->drawNonIndexed(primType, 0, vertCount);
     } else {
         #if GR_STATIC_RECT_VB
-            GrVertexLayout layout = (textured) ?
-                            GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :
-                            0;
+            GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
+
             target->setVertexSourceToBuffer(layout,
                                             fGpu->getUnitSquareVertexBuffer());
             GrDrawTarget::AutoViewMatrixRestore avmr(target);
@@ -998,13 +1006,11 @@
             }
 
             target->preConcatViewMatrix(m);
-
-            if (textured) {
-                target->preConcatSamplerMatrix(0, m);
-            }
+            target->preConcatSamplerMatrices(stageMask, m);
+ 
             target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
         #else
-            target->drawSimpleRect(rect, matrix, textured ? 1 : 0);
+            target->drawSimpleRect(rect, matrix, stageMask);
         #endif
     }
 }
@@ -1015,7 +1021,8 @@
                                const GrMatrix* dstMatrix,
                                const GrMatrix* srcMatrix) {
 
-    if (NULL == paint.getTexture()) {
+    // srcRect refers to paint's first texture
+    if (NULL == paint.getTexture(0)) {
         drawRect(paint, dstRect, -1, dstMatrix);
         return;
     }
@@ -1024,8 +1031,8 @@
 
 #if GR_STATIC_RECT_VB
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
-
-    GrVertexLayout layout = GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
+    
+    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
     GrDrawTarget::AutoViewMatrixRestore avmr(target);
 
     GrMatrix m;
@@ -1038,13 +1045,20 @@
     }
     target->preConcatViewMatrix(m);
 
+    // srcRect refers to first stage
+    int otherStageMask = paint.getActiveStageMask() & 
+                         (~(1 << GrPaint::kFirstTextureStage));
+    if (otherStageMask) {
+        target->preConcatSamplerMatrices(otherStageMask, m);
+    }
+
     m.setAll(srcRect.width(), 0,                srcRect.fLeft,
              0,               srcRect.height(), srcRect.fTop,
              0,               0,                GrMatrix::I()[8]);
     if (NULL != srcMatrix) {
         m.postConcat(*srcMatrix);
     }
-    target->preConcatSamplerMatrix(0, m);
+    target->preConcatSamplerMatrix(GrPaint::kFirstTextureStage, m);
 
     target->setVertexSourceToBuffer(layout, fGpu->getUnitSquareVertexBuffer());
     target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
@@ -1074,26 +1088,22 @@
                              const GrColor colors[],
                              const uint16_t indices[],
                              int indexCount) {
-    GrVertexLayout layout = 0;
-    int vertexSize = sizeof(GrPoint);
 
     GrDrawTarget::AutoReleaseGeometry geo;
 
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
 
-    if (NULL != paint.getTexture()) {
-        if (NULL == texCoords) {
-            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
-        } else {
-            layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
-            vertexSize += sizeof(GrPoint);
-        }
-    }
+    bool hasTexCoords[GrPaint::kTotalStages] = {
+        NULL != texCoords,   // texCoordSrc provides explicit stage 0 coords
+        0                    // remaining stages use positions
+    };
+
+    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, hasTexCoords);
 
     if (NULL != colors) {
         layout |= GrDrawTarget::kColor_VertexLayoutBit;
-        vertexSize += sizeof(GrColor);
     }
+    int vertexSize = GrDrawTarget::VertexSize(layout);
 
     bool doAA = false;
     OffscreenRecord record;
@@ -1106,9 +1116,9 @@
         }
         int texOffsets[GrDrawTarget::kMaxTexCoords];
         int colorOffset;
-        int vsize = GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
-                                                            texOffsets,
-                                                            &colorOffset);
+        GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
+                                                texOffsets,
+                                                &colorOffset);
         void* curVertex = geo.vertices();
 
         for (int i = 0; i < vertexCount; ++i) {
@@ -1120,7 +1130,7 @@
             if (colorOffset > 0) {
                 *(GrColor*)((intptr_t)curVertex + colorOffset) = colors[i];
             }
-            curVertex = (void*)((intptr_t)curVertex + vsize);
+            curVertex = (void*)((intptr_t)curVertex + vertexSize);
         }
     } else {
         // we don't do offscreen AA when we have per-vertex tex coords or colors
@@ -1155,10 +1165,8 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void GrContext::drawPath(const GrPaint& paint,
-                         GrPathIter* path,
-                         GrPathFill fill,
-                         const GrPoint* translate) {
+void GrContext::drawPath(const GrPaint& paint, const GrPath& path,
+                         GrPathFill fill, const GrPoint* translate) {
 
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
     GrPathRenderer* pr = this->getPathRenderer(target, path, fill);
@@ -1180,9 +1188,10 @@
                 return;
             }
         }
-        GrRect pathBounds;
-        if (path->getConservativeBounds(&pathBounds)) {
-            GrIRect pathIBounds;
+
+        GrRect pathBounds = path.getBounds();
+        GrIRect pathIBounds;
+        if (!pathBounds.isEmpty()) {
             target->getViewMatrix().mapRect(&pathBounds, pathBounds);
             pathBounds.roundOut(&pathIBounds);
             if (!bound.intersect(pathIBounds)) {
@@ -1190,29 +1199,27 @@
             }
         }
 
+        // for now, abort antialiasing if our bounds are too big, so we don't
+        // hit the FBO size limit
+        if (pathIBounds.width() > GR_MAX_OFFSCREEN_AA_DIM ||
+            pathIBounds.height() > GR_MAX_OFFSCREEN_AA_DIM) {
+            goto NO_AA;
+        }
+
         if (this->setupOffscreenAAPass1(target, needsStencil, bound, &record)) {
             pr->drawPath(target, 0, path, fill, translate);
             this->offscreenAAPass2(target, paint, bound, &record);
             return;
         }
-    } 
-    GrDrawTarget::StageBitfield enabledStages = 0;
-    if (NULL != paint.getTexture()) {
-        enabledStages |= 1;
     }
 
+// we can fall out of the AA section for some reasons, and land here
+NO_AA:
+    GrDrawTarget::StageBitfield enabledStages = paint.getActiveStageMask();
+
     pr->drawPath(target, enabledStages, path, fill, translate);
 }
 
-void GrContext::drawPath(const GrPaint& paint,
-                         const GrPath& path,
-                         GrPathFill fill,
-                         const GrPoint* translate) {
-    GrPath::Iter iter(path);
-    this->drawPath(paint, &iter, fill, translate);
-}
-
-
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrContext::flush(int flagsBitfield) {
@@ -1320,8 +1327,21 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrContext::SetPaint(const GrPaint& paint, GrDrawTarget* target) {
-    target->setTexture(0, paint.getTexture());
-    target->setSamplerState(0, paint.fSampler);
+
+    for (int i = 0; i < GrPaint::kMaxTextures; ++i) {
+        int s = i + GrPaint::kFirstTextureStage;
+        target->setTexture(s, paint.getTexture(i));
+        target->setSamplerState(s, *paint.getTextureSampler(i));
+    }
+
+    target->setFirstCoverageStage(GrPaint::kFirstMaskStage);
+
+    for (int i = 0; i < GrPaint::kMaxMasks; ++i) {
+        int s = i + GrPaint::kFirstMaskStage;
+        target->setTexture(s, paint.getMask(i));
+        target->setSamplerState(s, *paint.getMaskSampler(i));
+    }
+
     target->setColor(paint.fColor);
 
     if (paint.fDither) {
@@ -1483,7 +1503,7 @@
 }
 
 GrPathRenderer* GrContext::getPathRenderer(const GrDrawTarget* target,
-                                           GrPathIter* path,
+                                           const GrPath& path,
                                            GrPathFill fill) {
     if (NULL != fCustomPathRenderer &&
         fCustomPathRenderer->canDrawPath(target, path, fill)) {
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index 518b4ee..2848999 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -19,6 +19,8 @@
 #include "GrGpuVertex.h"
 #include "GrTexture.h"
 
+namespace {
+
 // recursive helper for creating mask with all the tex coord bits set for
 // one stage
 template <int N>
@@ -26,16 +28,16 @@
     return GrDrawTarget::StageTexCoordVertexLayoutBit(stage, N) |
            stage_mask_recur<N+1>(stage);
 }
-template<> // linux build doesn't like static on specializations
+template<> 
 int stage_mask_recur<GrDrawTarget::kNumStages>(int) { return 0; }
 
 // mask of all tex coord indices for one stage
-static int stage_tex_coord_mask(int stage) {
+int stage_tex_coord_mask(int stage) {
     return stage_mask_recur<0>(stage);
 }
 
 // mask of all bits relevant to one stage
-static int stage_mask(int stage) {
+int stage_mask(int stage) {
     return stage_tex_coord_mask(stage) |
            GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(stage);
 }
@@ -47,11 +49,11 @@
     return GrDrawTarget::StageTexCoordVertexLayoutBit(N, texCoordIdx) |
            tex_coord_mask_recur<N+1>(texCoordIdx);
 }
-template<> // linux build doesn't like static on specializations
+template<> 
 int tex_coord_mask_recur<GrDrawTarget::kMaxTexCoords>(int) { return 0; }
 
 // mask of all bits relevant to one texture coordinate index
-static int tex_coord_idx_mask(int texCoordIdx) {
+int tex_coord_idx_mask(int texCoordIdx) {
     return tex_coord_mask_recur<0>(texCoordIdx);
 }
 
@@ -66,6 +68,8 @@
     return true;
 }
 
+} //unnamed namespace
+
 size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
     GrAssert(check_layout(vertexLayout));
 
@@ -370,10 +374,34 @@
     fCurrDrawState.fFlagBits &= ~(bits);
 }
 
-void GrDrawTarget::setBlendFunc(GrBlendCoeff srcCoef,
-                                GrBlendCoeff dstCoef) {
-    fCurrDrawState.fSrcBlend = srcCoef;
-    fCurrDrawState.fDstBlend = dstCoef;
+void GrDrawTarget::setBlendFunc(GrBlendCoeff srcCoeff,
+                                GrBlendCoeff dstCoeff) {
+    fCurrDrawState.fSrcBlend = srcCoeff;
+    fCurrDrawState.fDstBlend = dstCoeff;
+#if GR_DEBUG
+    switch (dstCoeff) {
+    case kDC_BlendCoeff:
+    case kIDC_BlendCoeff:
+    case kDA_BlendCoeff:
+    case kIDA_BlendCoeff:
+        GrPrintf("Unexpected dst blend coeff. Won't work correctly with"
+                 "coverage stages.\n");
+        break;
+    default:
+        break;
+    }
+    switch (srcCoeff) {
+    case kSC_BlendCoeff:
+    case kISC_BlendCoeff:
+    case kSA_BlendCoeff:
+    case kISA_BlendCoeff:
+        GrPrintf("Unexpected src blend coeff. Won't work correctly with"
+                 "coverage stages.\n");
+        break;
+    default:
+        break;
+    }
+#endif
 }
 
 void GrDrawTarget::setColor(GrColor c) {
@@ -478,10 +506,16 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool GrDrawTarget::canDisableBlend() const {
-    // If we're using edge antialiasing, we can't force blend off.
-    if (fCurrDrawState.fFlagBits & kEdgeAA_StateBit) {
+    // If we compute a coverage value (using edge AA or a coverage stage) then
+    // we can't force blending off.
+    if (fCurrDrawState.fEdgeAANumEdges > 0) {
         return false;
     }
+    for (int s = fCurrDrawState.fFirstCoverageStage; s < kNumStages; ++s) {
+        if (this->isStageEnabled(s)) {
+            return false;
+        }
+    }
 
     if ((kOne_BlendCoeff == fCurrDrawState.fSrcBlend) &&
         (kZero_BlendCoeff == fCurrDrawState.fDstBlend)) {
@@ -506,8 +540,8 @@
         return false;
     }
 
-    // ...and there isn't a texture with an alpha channel...
-    for (int s = 0; s < kNumStages; ++s) {
+    // ...and there isn't a texture stage with an alpha channel...
+    for (int s = 0; s < fCurrDrawState.fFirstCoverageStage; ++s) {
         if (this->isStageEnabled(s)) {
             GrAssert(NULL != fCurrDrawState.fTextures[s]);
 
@@ -531,8 +565,10 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-void GrDrawTarget::setEdgeAAData(const float edges[18]) {
-    memcpy(fCurrDrawState.fEdgeAAEdges, edges, sizeof(fCurrDrawState.fEdgeAAEdges));
+void GrDrawTarget::setEdgeAAData(const Edge* edges, int numEdges) {
+    GrAssert(numEdges <= kMaxEdges);
+    memcpy(fCurrDrawState.fEdgeAAEdges, edges, numEdges * sizeof(Edge));
+    fCurrDrawState.fEdgeAANumEdges = numEdges;
 }
 
 
diff --git a/gpu/src/GrGLEffect.h b/gpu/src/GrGLEffect.h
deleted file mode 100644
index ef00df8..0000000
--- a/gpu/src/GrGLEffect.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-    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 GrGLEffect_DEFINED
-#define GrGLEffect_DEFINED
-
-#include "GrGLInterface.h"
-#include "GrStringBuilder.h"
-
-class GrEffect;
-
-struct ShaderCodeSegments {
-    GrStringBuilder fVSUnis;
-    GrStringBuilder fVSAttrs;
-    GrStringBuilder fVaryings;
-    GrStringBuilder fFSUnis;
-    GrStringBuilder fVSCode;
-    GrStringBuilder fFSCode;
-};
-
-/**
- * This class is currently a stub.  This will be a base class for "effects", 
- * which extend the data model of GrPaint and extend the capability of
- * GrGLProgram in a modular fashion.
- */
-class GrGLEffect {
-protected:
-    GrGLEffect(GrEffect* effect) {}
-public:    
-    virtual ~GrGLEffect() {}
-    static GrGLEffect* Create(GrEffect* effect) { return NULL; }
-    void genShaderCode(ShaderCodeSegments* segments) {}
-    bool doGLSetup(GrPrimitiveType type, GrGLint program) { return true; }
-    bool doGLPost() { return true; }
-    void buildKey(GrBinHashKeyBuilder& key) const {}
-    GrGLEffect* nextEffect() { return NULL; }
-};
-
-#endif
diff --git a/gpu/src/GrGLInterface.cpp b/gpu/src/GrGLInterface.cpp
index 0825a3f..5ecf8eb 100644
--- a/gpu/src/GrGLInterface.cpp
+++ b/gpu/src/GrGLInterface.cpp
@@ -333,6 +333,15 @@
         }
     }
 
+    // Dual source blending
+    if (kDesktop_GrGLBinding == fBindingsExported  &&
+        (has_gl_extension_from_string("GL_ARB_blend_func_extended", ext) ||
+         (3 < major) || (3 == major && 3 <= minor))) {
+        if (NULL == fBindFragDataLocationIndexed) {
+            return false;
+        }
+    }
+
     return true;
 }
 
diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp
index 5d2d8b3..ecb4753 100644
--- a/gpu/src/GrGLProgram.cpp
+++ b/gpu/src/GrGLProgram.cpp
@@ -18,7 +18,6 @@
 
 #include "GrBinHashKey.h"
 #include "GrGLConfig.h"
-#include "GrGLEffect.h"
 #include "GrMemory.h"
 
 #include "SkXfermode.h"
@@ -86,6 +85,23 @@
     return ALL[count];
 }
 
+static inline const char* all_ones_vec(int count) {
+    static const char* ONESVEC[] = {"ERROR", "1.0", "vec2(1,1)",
+                                    "vec3(1,1,1)", "vec4(1,1,1,1)"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ONESVEC));
+    return ONESVEC[count];
+}
+
+static inline const char* all_zeros_vec(int count) {
+    static const char* ZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)",
+                                    "vec3(0,0,0)", "vec4(0,0,0,0)"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ZEROSVEC));
+    return ZEROSVEC[count];
+}
+
+static inline const char* declared_color_output_name() { return "fsColorOut"; }
+static inline const char* dual_source_output_name() { return "dualSourceOut"; }
+
 static void tex_matrix_name(int stage, GrStringBuilder* s) {
 #if GR_GL_ATTRIBUTE_MATRICES
     *s = "aTexM";
@@ -120,113 +136,194 @@
     s->appendS32(stage);
 }
 
+static void tex_domain_name(int stage, GrStringBuilder* s) {
+    *s = "uTexDom";
+    s->appendS32(stage);
+}
+
 GrGLProgram::GrGLProgram() {
-    for(int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) {
-        fStageEffects[stage] = NULL;
-    }
 }
 
 GrGLProgram::~GrGLProgram() {
 }
 
+void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
+                                GrBlendCoeff* dstCoeff) const {
+    switch (fProgramDesc.fDualSrcOutput) {
+        case ProgramDesc::kNone_DualSrcOutput:
+            break;
+        // the prog will write a coverage value to the secondary
+        // output and the dst is blended by one minus that value.
+        case ProgramDesc::kCoverage_DualSrcOutput:
+        case ProgramDesc::kCoverageISA_DualSrcOutput:
+        case ProgramDesc::kCoverageISC_DualSrcOutput:
+        *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_BlendCoeff;
+        break;
+        default:
+            GrCrash("Unexpected dual source blend output");
+            break;
+    }
+}
+
 void GrGLProgram::buildKey(GrBinHashKeyBuilder& key) const {
     // Add stage configuration to the key
-    key.keyData(reinterpret_cast<const uint8_t*>(&fProgramDesc), sizeof(ProgramDesc));
+    key.keyData(reinterpret_cast<const uint32_t*>(&fProgramDesc), sizeof(ProgramDesc));
+}
 
-    for(int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) {
-        // First pass: count effects and write the count to the key.
-        // This may seem like  we are adding redundant data to the
-        // key, but in ensures the one key cannot be a prefix of
-        // another key, or identical to the key of a different program.
-        GrGLEffect* currentEffect = fStageEffects[stage];
-        uint8_t effectCount = 0;
-        while (currentEffect) {
-            GrAssert(effectCount < 255); // overflow detection
-            ++effectCount;
-            currentEffect = currentEffect->nextEffect();
-        }
-        key.keyData(reinterpret_cast<const uint8_t*>(&effectCount), sizeof(uint8_t));
+// assigns modulation of two vars to an output var
+// vars can be vec4s or floats (or one of each)
+// result is always vec4
+// if either var is "" then assign to the other var
+// if both are "" then assign all ones
+static inline void modulate_helper(const char* outputVar,
+                                   const char* var0,
+                                   const char* var1,
+                                   GrStringBuilder* code) {
+    GrAssert(NULL != outputVar);
+    GrAssert(NULL != var0);
+    GrAssert(NULL != var1);
+    GrAssert(NULL != code);
 
-        // Second pass: continue building key using the effects
-        currentEffect = fStageEffects[stage];
-        while (currentEffect) {
-            fStageEffects[stage]->buildKey(key);
-        }
+    bool has0 = '\0' != *var0;
+    bool has1 = '\0' != *var1;
+
+    if (!has0 && !has1) {
+        code->appendf("\t%s = %s;\n", outputVar, all_ones_vec(4));
+    } else if (!has0) {
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
+    } else if (!has1) {
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
+    } else {
+        code->appendf("\t%s = vec4(%s * %s);\n", outputVar, var0, var1);
     }
 }
 
-bool GrGLProgram::doGLSetup(GrPrimitiveType type, 
-                            GrGLProgram::CachedData* programData) const {
-    for (int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) {
-        GrGLEffect* effect = fStageEffects[stage];
-        if (effect) {
-            if (!effect->doGLSetup(type, programData->fProgramID)) {
-                return false;
-            }
-        }
-    }
+// assigns addition of two vars to an output var
+// vars can be vec4s or floats (or one of each)
+// result is always vec4
+// if either var is "" then assign to the other var
+// if both are "" then assign all zeros
+static inline void add_helper(const char* outputVar,
+                              const char* var0,
+                              const char* var1,
+                              GrStringBuilder* code) {
+    GrAssert(NULL != outputVar);
+    GrAssert(NULL != var0);
+    GrAssert(NULL != var1);
+    GrAssert(NULL != code);
 
-    return true;
+    bool has0 = '\0' != *var0;
+    bool has1 = '\0' != *var1;
+
+    if (!has0 && !has1) {
+        code->appendf("\t%s = %s;\n", outputVar, all_zeros_vec(4));
+    } else if (!has0) {
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
+    } else if (!has1) {
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
+    } else {
+        code->appendf("\t%s = vec4(%s + %s);\n", outputVar, var0, var1);
+    }
 }
 
-void GrGLProgram::doGLPost() const {
-    for (int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) {
-        GrGLEffect* effect = fStageEffects[stage];
-        if (effect) {
-            effect->doGLPost(); 
-        }    
+// 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 text coefficient to be used in fragment shader code.
+ * Create a blend_coeff * value string to be used in shader code. Sets empty
+ * string if result is trivially zero.
  */
-static void coefficientString(GrStringBuilder* str, SkXfermode::Coeff coeff,
-            const char* src, const char* dst) {
+static void blendTermString(GrStringBuilder* str, SkXfermode::Coeff coeff,
+                             const char* src, const char* dst,
+                             const char* value) {
     switch (coeff) {
     case SkXfermode::kZero_Coeff:    /** 0 */
-        *str = "0.0";
+        *str = "";
         break;
     case SkXfermode::kOne_Coeff:     /** 1 */
-        *str = "1.0";
-        break;
-    case SkXfermode::kSA_Coeff:      /** src alpha */
-        str->appendf("%s.a", src);
-        break;
-    case SkXfermode::kISA_Coeff:     /** inverse src alpha (i.e. 1 - sa) */
-        str->appendf("(1.0 - %s.a)", src);
-        break;
-    case SkXfermode::kDA_Coeff:      /** dst alpha */
-        str->appendf("%s.a", dst);
-        break;
-    case SkXfermode::kIDA_Coeff:     /** inverse dst alpha (i.e. 1 - da) */
-        str->appendf("(1.0 - %s.a)", dst);
+        *str = value;
         break;
     case SkXfermode::kSC_Coeff:
-        str->append(src);
+        str->printf("(%s * %s)", src, value);
+        break;
+    case SkXfermode::kISC_Coeff:
+        str->printf("((%s - %s) * %s)", all_ones_vec(4), src, value);
+        break;
+    case SkXfermode::kDC_Coeff:
+        str->printf("(%s * %s)", dst, value);
+        break;
+    case SkXfermode::kIDC_Coeff:
+        str->printf("((%s - %s) * %s)", all_ones_vec(4), dst, value);
+        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(GrStringBuilder* FSCode, const char * outputVar,
-            SkXfermode::Mode colorFilterXfermode, const char* dstColor) {
-    SkXfermode::Coeff srcCoeff, dstCoeff;
-    SkDEBUGCODE(bool success =)
-    SkXfermode::ModeAsCoeff(colorFilterXfermode, &srcCoeff, &dstCoeff);
-    // We currently do not handle modes that cannot be represented as
-    // coefficients.
-    GrAssert(success);
-    GrStringBuilder srcCoeffStr, dstCoeffStr;
-    coefficientString(&srcCoeffStr, srcCoeff, COL_FILTER_UNI_NAME, dstColor);
-    coefficientString(&dstCoeffStr, dstCoeff, COL_FILTER_UNI_NAME, dstColor);
-    FSCode->appendf("\t%s = %s*%s + %s*%s;\n", outputVar, srcCoeffStr.c_str(),
-            COL_FILTER_UNI_NAME, dstCoeffStr.c_str(), dstColor);
+static void addColorFilter(GrStringBuilder* fsCode, const char * outputVar,
+                           SkXfermode::Coeff uniformCoeff,
+                           SkXfermode::Coeff colorCoeff,
+                           const char* inColor) {
+    GrStringBuilder colorStr, constStr;
+    blendTermString(&colorStr, colorCoeff, COL_FILTER_UNI_NAME,
+                    inColor, inColor);
+    blendTermString(&constStr, uniformCoeff, COL_FILTER_UNI_NAME,
+                    inColor, COL_FILTER_UNI_NAME);
+
+    add_helper(outputVar, colorStr.c_str(), constStr.c_str(), fsCode);
 }
 
 bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
@@ -236,6 +333,39 @@
 
     programData->fUniLocations.reset();
 
+    SkXfermode::Coeff colorCoeff, uniformCoeff;
+    // The rest of transfer mode color filters have not been implemented
+    if (fProgramDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
+        GR_DEBUGCODE(bool success =)
+            SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode>
+                                    (fProgramDesc.fColorFilterXfermode),
+                                    &uniformCoeff, &colorCoeff);
+        GR_DEBUGASSERT(success);
+    } else {
+        colorCoeff = SkXfermode::kOne_Coeff;
+        uniformCoeff = SkXfermode::kZero_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.
+    const char* fsColorOutput;
+    bool dualSourceOutputWritten = false;
+    bool usingDeclaredOutputs = ProgramDesc::kNone_DualSrcOutput !=
+                                fProgramDesc.fDualSrcOutput;
+    if (usingDeclaredOutputs) {
+        GrAssert(0 == segments.fHeader.size());
+        segments.fHeader.printf("#version 150\n");
+        fsColorOutput = declared_color_output_name();
+        segments.fFSOutputs.appendf("out vec4 %s;\n", fsColorOutput);
+    } else {
+        fsColorOutput = "gl_FragColor";
+    }
+
 #if GR_GL_ATTRIBUTE_MATRICES
     segments.fVSAttrs += "attribute mat3 " VIEW_MATRIX_NAME ";\n";
     programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
@@ -253,25 +383,23 @@
     // incoming color to current stage being processed.
     GrStringBuilder inColor;
 
-    switch (fProgramDesc.fColorType) {
-    case ProgramDesc::kAttribute_ColorType:
-        segments.fVSAttrs.append( "attribute vec4 " COL_ATTR_NAME ";\n");
-        segments.fVaryings.append("varying vec4 vColor;\n");
-        segments.fVSCode.append(    "\tvColor = " COL_ATTR_NAME ";\n");
-        inColor = "vColor";
-        break;
-    case ProgramDesc::kUniform_ColorType:
-        segments.fFSUnis.append(  "uniform vec4 " COL_UNI_NAME ";\n");
-        programData->fUniLocations.fColorUni = kUseUniform;
-        inColor = COL_UNI_NAME;
-        break;
-    case ProgramDesc::kNone_ColorType:
-        inColor = "";
-        break;
-    }
-
-    if (fProgramDesc.fUsesEdgeAA) {
-        segments.fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "[6];\n");
+    if (needComputedColor) {
+        switch (fProgramDesc.fColorType) {
+            case ProgramDesc::kAttribute_ColorType:
+                segments.fVSAttrs.append( "attribute vec4 " COL_ATTR_NAME ";\n");
+                segments.fVaryings.append("varying vec4 vColor;\n");
+                segments.fVSCode.append(    "\tvColor = " COL_ATTR_NAME ";\n");
+                inColor = "vColor";
+                break;
+            case ProgramDesc::kUniform_ColorType:
+                segments.fFSUnis.append(  "uniform vec4 " COL_UNI_NAME ";\n");
+                programData->fUniLocations.fColorUni = kUseUniform;
+                inColor = COL_UNI_NAME;
+                break;
+            default:
+                GrAssert(ProgramDesc::kNone_ColorType == fProgramDesc.fColorType);
+                break;
+        }
     }
 
     if (fProgramDesc.fEmitsPointSize){
@@ -289,109 +417,204 @@
         }
     }
 
-    bool useColorFilter = 
-            // The rest of transfer mode color filters have not been implemented
-            fProgramDesc.fColorFilterXfermode <= SkXfermode::kMultiply_Mode
-            // This mode has no effect.
-            && fProgramDesc.fColorFilterXfermode != SkXfermode::kDst_Mode;
-    bool onlyUseColorFilter = useColorFilter
-            && (fProgramDesc.fColorFilterXfermode == SkXfermode::kClear_Mode
-            || fProgramDesc.fColorFilterXfermode == SkXfermode::kSrc_Mode);
-    if (useColorFilter) {
-        // Set up a uniform for the color
-        segments.fFSUnis.append(  "uniform vec4 " COL_FILTER_UNI_NAME ";\n");
-        programData->fUniLocations.fColorFilterUni = kUseUniform;
-    }
+    ///////////////////////////////////////////////////////////////////////////
+    // compute the final color
 
-    // for each enabled stage figure out what the input coordinates are
-    // and count the number of stages in use.
-    const char* stageInCoords[GrDrawTarget::kNumStages];
-    int numActiveStages = 0;
+    // if we have color stages string them together, feeding the output color
+    // of each to the next and generating code for each stage.
+    if (needComputedColor) {
+        GrStringBuilder outColor;
+        for (int s = 0; s < fProgramDesc.fFirstCoverageStage; ++s) {
+            if (fProgramDesc.fStages[s].isEnabled()) {
+                // create var to hold stage result
+                outColor = "color";
+                outColor.appendS32(s);
+                segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
 
-    if (!onlyUseColorFilter) {
-        for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
-            if (fProgramDesc.fStages[s].fEnabled) {
-                if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) {
-                    stageInCoords[s] = POS_ATTR_NAME;
+                const char* inCoords;
+                // figure out what our input coords are
+                if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) &
+                    layout) {
+                    inCoords = POS_ATTR_NAME;
                 } else {
                     int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
                      // we better have input tex coordinates if stage is enabled.
                     GrAssert(tcIdx >= 0);
                     GrAssert(texCoordAttrs[tcIdx].size());
-                    stageInCoords[s] = texCoordAttrs[tcIdx].c_str();
-                }
-                ++numActiveStages;
-            }
-        }
-    }
-
-    // if we have active stages string them together, feeding the output color
-    // of each to the next and generating code for each stage.
-    if (numActiveStages) {
-        int currActiveStage = 0;
-        GrStringBuilder outColor;
-        for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
-            if (fProgramDesc.fStages[s].fEnabled) {
-                if (currActiveStage < (numActiveStages - 1) || useColorFilter) {
-                    outColor = "color";
-                    outColor.appendS32(currActiveStage);
-                    segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
-                } else {
-                    outColor = "gl_FragColor";
+                    inCoords = texCoordAttrs[tcIdx].c_str();
                 }
 
                 genStageCode(s,
                              fProgramDesc.fStages[s],
                              inColor.size() ? inColor.c_str() : NULL,
                              outColor.c_str(),
-                             stageInCoords[s],
+                             inCoords,
                              &segments,
                              &programData->fUniLocations.fStages[s]);
-                ++currActiveStage;
                 inColor = outColor;
             }
         }
-        if (useColorFilter) {
-            addColorFilter(&segments.fFSCode, "gl_FragColor",
-                    fProgramDesc.fColorFilterXfermode, outColor.c_str());
+    }
+
+    // if have all ones for the "dst" input to the color filter then we can make
+    // additional optimizations.
+    if (needColorFilterUniform && !inColor.size() &&
+        (SkXfermode::kIDC_Coeff == uniformCoeff ||
+         SkXfermode::kIDA_Coeff == uniformCoeff)) {
+          uniformCoeff = SkXfermode::kZero_Coeff;
+          bool bogus;
+          needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff,
+                          &needColorFilterUniform, &bogus);
+    }
+    if (needColorFilterUniform) {
+        segments.fFSUnis.append(  "uniform vec4 " COL_FILTER_UNI_NAME ";\n");
+        programData->fUniLocations.fColorFilterUni = kUseUniform;
+    }
+
+    bool wroteFragColorZero = false;
+    if (SkXfermode::kZero_Coeff == uniformCoeff &&
+        SkXfermode::kZero_Coeff == colorCoeff) {
+        segments.fFSCode.appendf("\t%s = %s;\n",
+                                 fsColorOutput,
+                                 all_zeros_vec(4));
+        wroteFragColorZero = true;
+    } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) {
+        segments.fFSCode.appendf("\tvec4 filteredColor;\n");
+        const char* color = inColor.size() ? inColor.c_str() : all_ones_vec(4);
+        addColorFilter(&segments.fFSCode, "filteredColor", uniformCoeff,
+                       colorCoeff, color);
+        inColor = "filteredColor";
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // compute the partial coverage (coverage stages and edge aa)
+
+    GrStringBuilder inCoverage;
+
+    // we don't need to compute coverage at all if we know the final shader
+    // output will be zero and we don't have a dual src blend output.
+    if (!wroteFragColorZero ||
+        ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+        if (fProgramDesc.fEdgeAANumEdges > 0) {
+            segments.fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "[");
+            segments.fFSUnis.appendS32(fProgramDesc.fEdgeAANumEdges);
+            segments.fFSUnis.append("];\n");
+            programData->fUniLocations.fEdgesUni = kUseUniform;
+            int count = fProgramDesc.fEdgeAANumEdges;
+            segments.fFSCode.append(
+                "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n");
+            for (int i = 0; i < count; i++) {
+                segments.fFSCode.append("\tfloat a");
+                segments.fFSCode.appendS32(i);
+                segments.fFSCode.append(" = clamp(dot(" EDGES_UNI_NAME "[");
+                segments.fFSCode.appendS32(i);
+                segments.fFSCode.append("], pos), 0.0, 1.0);\n");
+            }
+            segments.fFSCode.append("\tfloat edgeAlpha = ");
+            for (int i = 0; i < count - 1; i++) {
+                segments.fFSCode.append("min(a");
+                segments.fFSCode.appendS32(i);
+                segments.fFSCode.append(" * a");
+                segments.fFSCode.appendS32(i + 1);
+                segments.fFSCode.append(", ");
+            }
+            segments.fFSCode.append("a");
+            segments.fFSCode.appendS32(count - 1);
+            segments.fFSCode.append(" * a0");
+            for (int i = 0; i < count - 1; i++) {
+                segments.fFSCode.append(")");
+            }
+            segments.fFSCode.append(";\n");
+            inCoverage = "edgeAlpha";
         }
 
-    } else {
-        if (fProgramDesc.fUsesEdgeAA) {
-            // FIXME:  put the a's in a loop
-            segments.fFSCode.append(
-                "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n"
-                "\tfloat a0 = clamp(dot(uEdges[0], pos), 0.0, 1.0);\n"
-                "\tfloat a1 = clamp(dot(uEdges[1], pos), 0.0, 1.0);\n"
-                "\tfloat a2 = clamp(dot(uEdges[2], pos), 0.0, 1.0);\n"
-                "\tfloat a3 = clamp(dot(uEdges[3], pos), 0.0, 1.0);\n"
-                "\tfloat a4 = clamp(dot(uEdges[4], pos), 0.0, 1.0);\n"
-                "\tfloat a5 = clamp(dot(uEdges[5], pos), 0.0, 1.0);\n"
-                "\tfloat edgeAlpha = min(min(a0 * a1, a2 * a3), a4 * a5);\n");
-            if (inColor.size()) {
-                inColor.append(" * edgeAlpha");
-            } else {
-                inColor = "vec4(edgeAlpha)";
+        GrStringBuilder outCoverage;
+        const int& startStage = fProgramDesc.fFirstCoverageStage;
+        for (int s = startStage; s < GrDrawTarget::kNumStages; ++s) {
+            if (fProgramDesc.fStages[s].isEnabled()) {
+                // create var to hold stage output
+                outCoverage = "coverage";
+                outCoverage.appendS32(s);
+                segments.fFSCode.appendf("\tvec4 %s;\n", outCoverage.c_str());
+
+                const char* inCoords;
+                // figure out what our input coords are
+                if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) {
+                    inCoords = POS_ATTR_NAME;
+                } else {
+                    int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
+                        // we better have input tex coordinates if stage is enabled.
+                    GrAssert(tcIdx >= 0);
+                    GrAssert(texCoordAttrs[tcIdx].size());
+                    inCoords = texCoordAttrs[tcIdx].c_str();
+                }
+
+                genStageCode(s,
+                             fProgramDesc.fStages[s],
+                             inCoverage.size() ? inCoverage.c_str() : NULL,
+                             outCoverage.c_str(),
+                             inCoords,
+                             &segments,
+                             &programData->fUniLocations.fStages[s]);
+                inCoverage = outCoverage;
             }
         }
-        // we may not have any incoming color
-        const char * incomingColor = (inColor.size() ? inColor.c_str()
-                : "vec4(1,1,1,1)");
-        if (useColorFilter) {
-            addColorFilter(&segments.fFSCode, "gl_FragColor",
-                    fProgramDesc.fColorFilterXfermode, incomingColor);
-        } else {
-            segments.fFSCode.appendf("\tgl_FragColor = %s;\n", incomingColor);
+        if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+            segments.fFSOutputs.appendf("out vec4 %s;\n",
+                                        dual_source_output_name());
+            bool outputIsZero = false;
+            GrStringBuilder coeff;
+            if (ProgramDesc::kCoverage_DualSrcOutput !=
+                fProgramDesc.fDualSrcOutput && !wroteFragColorZero) {
+                if (!inColor.size()) {
+                    outputIsZero = true;
+                } else {
+                    if (fProgramDesc.fDualSrcOutput ==
+                        ProgramDesc::kCoverageISA_DualSrcOutput) {
+                        coeff.printf("(1 - %s.a)", inColor.c_str());
+                    } else {
+                        coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str());
+                    }
+                }
+            }
+            if (outputIsZero) {
+                segments.fFSCode.appendf("\t%s = %s;\n",
+                                         dual_source_output_name(),
+                                         all_zeros_vec(4));
+            } else {
+                modulate_helper(dual_source_output_name(),
+                                coeff.c_str(),
+                                inCoverage.c_str(),
+                                &segments.fFSCode);
+            }
+            dualSourceOutputWritten = true;
         }
     }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // combine color and coverage as frag color
+
+    if (!wroteFragColorZero) {
+        modulate_helper(fsColorOutput,
+                         inColor.c_str(),
+                         inCoverage.c_str(),
+                         &segments.fFSCode);
+    }
+
     segments.fVSCode.append("}\n");
     segments.fFSCode.append("}\n");
 
+    ///////////////////////////////////////////////////////////////////////////
+    // compile and setup attribs and unis
+
     if (!CompileFSAndVS(segments, programData)) {
         return false;
     }
 
-    if (!this->bindAttribsAndLinkProgram(texCoordAttrs, programData)) {
+    if (!this->bindOutputsAttribsAndLinkProgram(texCoordAttrs,
+                                                usingDeclaredOutputs,
+                                                dualSourceOutputWritten,
+                                                programData)) {
         return false;
     }
 
@@ -403,10 +626,16 @@
 bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
                                  CachedData* programData) {
 
-    const char* strings[4];
-    int lengths[4];
+    static const int MAX_STRINGS = 6;
+    const char* strings[MAX_STRINGS];
+    int lengths[MAX_STRINGS];
     int stringCnt = 0;
 
+    if (segments.fHeader.size()) {
+        strings[stringCnt] = segments.fHeader.c_str();
+        lengths[stringCnt] = segments.fHeader.size();
+        ++stringCnt;
+    }
     if (segments.fVSUnis.size()) {
         strings[stringCnt] = segments.fVSUnis.c_str();
         lengths[stringCnt] = segments.fVSUnis.size();
@@ -429,12 +658,14 @@
     ++stringCnt;
 
 #if PRINT_SHADERS
+    GrPrintf(segments.fHeader.c_str());
     GrPrintf(segments.fVSUnis.c_str());
     GrPrintf(segments.fVSAttrs.c_str());
     GrPrintf(segments.fVaryings.c_str());
     GrPrintf(segments.fVSCode.c_str());
     GrPrintf("\n");
 #endif
+    GrAssert(stringCnt <= MAX_STRINGS);
     programData->fVShaderID = CompileShader(GR_GL_VERTEX_SHADER,
                                         stringCnt,
                                         strings,
@@ -446,6 +677,11 @@
 
     stringCnt = 0;
 
+    if (segments.fHeader.size()) {
+        strings[stringCnt] = segments.fHeader.c_str();
+        lengths[stringCnt] = segments.fHeader.size();
+        ++stringCnt;
+    }
     if (strlen(GrShaderPrecision()) > 1) {
         strings[stringCnt] = GrShaderPrecision();
         lengths[stringCnt] = strlen(GrShaderPrecision());
@@ -461,6 +697,11 @@
         lengths[stringCnt] = segments.fVaryings.size();
         ++stringCnt;
     }
+    if (segments.fFSOutputs.size()) {
+        strings[stringCnt] = segments.fFSOutputs.c_str();
+        lengths[stringCnt] = segments.fFSOutputs.size();
+        ++stringCnt;
+    }
 
     GrAssert(segments.fFSCode.size());
     strings[stringCnt] = segments.fFSCode.c_str();
@@ -468,12 +709,15 @@
     ++stringCnt;
 
 #if PRINT_SHADERS
+    GrPrintf(segments.fHeader.c_str());
     GrPrintf(GrShaderPrecision());
     GrPrintf(segments.fFSUnis.c_str());
     GrPrintf(segments.fVaryings.c_str());
+    GrPrintf(segments.fFSOutputs.c_str());
     GrPrintf(segments.fFSCode.c_str());
     GrPrintf("\n");
 #endif
+    GrAssert(stringCnt <= MAX_STRINGS);
     programData->fFShaderID = CompileShader(GR_GL_FRAGMENT_SHADER,
                                             stringCnt,
                                             strings,
@@ -482,6 +726,7 @@
     if (!programData->fFShaderID) {
         return false;
     }
+
     return true;
 }
 
@@ -521,8 +766,11 @@
     return shader;
 }
 
-bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
-                                            CachedData* programData) const {
+bool GrGLProgram::bindOutputsAttribsAndLinkProgram(
+                                        GrStringBuilder texCoordAttrNames[],
+                                        bool bindColorOut,
+                                        bool bindDualSrcOut,
+                                        CachedData* programData) const {
     programData->fProgramID = GR_GL(CreateProgram());
     if (!programData->fProgramID) {
         return false;
@@ -532,6 +780,15 @@
     GR_GL(AttachShader(progID, programData->fVShaderID));
     GR_GL(AttachShader(progID, programData->fFShaderID));
 
+    if (bindColorOut) {
+        GR_GL(BindFragDataLocationIndexed(programData->fProgramID,
+                                          0, 0, declared_color_output_name()));
+    }
+    if (bindDualSrcOut) {
+        GR_GL(BindFragDataLocationIndexed(programData->fProgramID,
+                                          0, 1, dual_source_output_name()));
+    }
+
     // Bind the attrib locations to same values for all shaders
     GR_GL(BindAttribLocation(progID, PositionAttributeIdx(), POS_ATTR_NAME));
     for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
@@ -542,7 +799,6 @@
         }
     }
 
-
     if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) {
         GR_GL(BindAttribLocation(progID,
                              ViewMatrixAttributeIdx(),
@@ -560,7 +816,6 @@
         }
     }
 
-
     GR_GL(BindAttribLocation(progID, ColorAttributeIdx(), COL_ATTR_NAME));
 
     GR_GL(LinkProgram(progID));
@@ -595,18 +850,18 @@
         GrAssert(kUnusedUniform != programData->fUniLocations.fViewMatrixUni);
     }
     if (kUseUniform == programData->fUniLocations.fColorUni) {
-        programData->fUniLocations.fColorUni = 
+        programData->fUniLocations.fColorUni =
                                 GR_GL(GetUniformLocation(progID, COL_UNI_NAME));
         GrAssert(kUnusedUniform != programData->fUniLocations.fColorUni);
     }
     if (kUseUniform == programData->fUniLocations.fColorFilterUni) {
-        programData->fUniLocations.fColorFilterUni = 
+        programData->fUniLocations.fColorFilterUni =
                         GR_GL(GetUniformLocation(progID, COL_FILTER_UNI_NAME));
         GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
     }
 
-    if (fProgramDesc.fUsesEdgeAA) {
-        programData->fUniLocations.fEdgesUni = 
+    if (kUseUniform == programData->fUniLocations.fEdgesUni) {
+        programData->fUniLocations.fEdgesUni =
             GR_GL(GetUniformLocation(progID, EDGES_UNI_NAME));
         GrAssert(kUnusedUniform != programData->fUniLocations.fEdgesUni);
     } else {
@@ -615,7 +870,7 @@
 
     for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
         StageUniLocations& locations = programData->fUniLocations.fStages[s];
-        if (fProgramDesc.fStages[s].fEnabled) {
+        if (fProgramDesc.fStages[s].isEnabled()) {
             if (kUseUniform == locations.fTextureMatrixUni) {
                 GrStringBuilder texMName;
                 tex_matrix_name(s, &texMName);
@@ -637,7 +892,7 @@
             if (kUseUniform == locations.fNormalizedTexelSizeUni) {
                 GrStringBuilder texelSizeName;
                 normalized_texel_size_name(s, &texelSizeName);
-                locations.fNormalizedTexelSizeUni = 
+                locations.fNormalizedTexelSizeUni =
                    GR_GL(GetUniformLocation(progID, texelSizeName.c_str()));
                 GrAssert(kUnusedUniform != locations.fNormalizedTexelSizeUni);
             }
@@ -650,6 +905,15 @@
                                              radial2ParamName.c_str()));
                 GrAssert(kUnusedUniform != locations.fRadial2Uni);
             }
+
+            if (kUseUniform == locations.fTexDomUni) {
+                GrStringBuilder texDomName;
+                tex_domain_name(s, &texDomName);
+                locations.fTexDomUni = GR_GL(GetUniformLocation(
+                                             progID,
+                                             texDomName.c_str()));
+                GrAssert(kUnusedUniform != locations.fTexDomUni);
+            }
         }
     }
     GR_GL(UseProgram(progID));
@@ -726,7 +990,7 @@
         segments->fFSUnis.appendf("uniform vec2 %s;\n", texelSizeName.c_str());
     }
 
-    segments->fVaryings.appendf("varying %s %s;\n", 
+    segments->fVaryings.appendf("varying %s %s;\n",
                                 float_vector_type(varyingDims), varyingName.c_str());
 
     if (desc.fOptFlags & ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit) {
@@ -748,9 +1012,9 @@
 
     if (ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping == desc.fCoordMapping) {
 
-        segments->fVSUnis.appendf("uniform %s float %s[6];\n", 
+        segments->fVSUnis.appendf("uniform %s float %s[6];\n",
                                   GrPrecision(), radial2ParamsName.c_str());
-        segments->fFSUnis.appendf("uniform float %s[6];\n", 
+        segments->fFSUnis.appendf("uniform float %s[6];\n",
                                   radial2ParamsName.c_str());
         locations->fRadial2Uni = kUseUniform;
 
@@ -869,6 +1133,24 @@
         modulate.printf(" * %s", fsInColor);
     }
 
+    if (desc.fOptFlags &
+        ProgramDesc::StageDesc::kCustomTextureDomain_OptFlagBit) {
+        GrStringBuilder texDomainName;
+        tex_domain_name(stageNum, &texDomainName);
+        segments->fFSUnis.appendf("uniform %s %s;\n",
+                                  float_vector_type(4),
+                                  texDomainName.c_str());
+        GrStringBuilder coordVar("clampCoord");
+        segments->fFSCode.appendf("\t%s %s = clamp(%s, %s.xy, %s.zw);\n",
+                                  float_vector_type(coordDims),
+                                  coordVar.c_str(),
+                                  sampleCoords.c_str(),
+                                  texDomainName.c_str(),
+                                  texDomainName.c_str());
+        sampleCoords = coordVar;
+        locations->fTexDomUni = kUseUniform;
+    }
+
     if (ProgramDesc::StageDesc::k2x2_FetchMode == desc.fFetchMode) {
         locations->fNormalizedTexelSizeUni = kUseUniform;
         if (complexCoord) {
@@ -889,11 +1171,6 @@
         segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear);
         segments->fFSCode.appendf("\t%s = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str());
     } else {
-        segments->fFSCode.appendf("\t%s = %s(%s, %s)%s %s;\n", fsOutColor, texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), smear, modulate.c_str());
-    }
-
-    if(fStageEffects[stageNum]) {
-        fStageEffects[stageNum]->genShaderCode(segments);
+        segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n", fsOutColor, texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), smear, modulate.c_str());
     }
 }
-
diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h
index 1611ca2..473bcb6 100644
--- a/gpu/src/GrGLProgram.h
+++ b/gpu/src/GrGLProgram.h
@@ -19,13 +19,22 @@
 
 #include "GrGLInterface.h"
 #include "GrStringBuilder.h"
-#include "GrDrawTarget.h"
+#include "GrGpu.h"
 
 #include "SkXfermode.h"
 
 class GrBinHashKeyBuilder;
-class GrGLEffect;
-struct ShaderCodeSegments;
+
+struct ShaderCodeSegments {
+    GrStringBuilder fHeader; // VS+FS, GLSL version, etc
+    GrStringBuilder fVSUnis;
+    GrStringBuilder fVSAttrs;
+    GrStringBuilder fVaryings;
+    GrStringBuilder fFSUnis;
+    GrStringBuilder fFSOutputs;
+    GrStringBuilder fVSCode;
+    GrStringBuilder fFSCode;
+};
 
 /**
  * This class manages a GPU program and records per-program information.
@@ -58,39 +67,29 @@
      */
     bool genProgram(CachedData* programData) const;
 
-    /**
-     *  Routine that is called before rendering. Sets-up all the state and
-     *  other initializations required for the Gpu Program to run.
-     */
-    bool doGLSetup(GrPrimitiveType type, CachedData* programData) const;
+     /**
+      * The shader may modify the blend coeffecients. Params are in/out
+      */
+     void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const;
 
     /**
-     *  Routine that is called after rendering. Performs state restoration.
-     *  May perform secondary render passes.
+     * Attribute indices
      */
-    void doGLPost() const;
-
-    /**
-     *  Configures the GrGLProgram based on the state of a GrDrawTarget
-     *  object.  This is the fast and light initialization. Retrieves all the
-     *  state that is required for performing the heavy init (i.e. genProgram),
-     *  or for retrieving heavy init results from cache.
-     */
-    void buildFromTarget(const GrDrawTarget* target);
-
     static int PositionAttributeIdx() { return 0; }
     static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; }
     static int ColorAttributeIdx() { return 1 + GrDrawTarget::kMaxTexCoords; }
-    static int ViewMatrixAttributeIdx() { 
-        return 2 + GrDrawTarget::kMaxTexCoords; 
+    static int ViewMatrixAttributeIdx() {
+        return 2 + GrDrawTarget::kMaxTexCoords;
     }
-    static int TextureMatrixAttributeIdx(int stage) { 
-        return 5 + GrDrawTarget::kMaxTexCoords + 3 * stage; 
+    static int TextureMatrixAttributeIdx(int stage) {
+        return 5 + GrDrawTarget::kMaxTexCoords + 3 * stage;
     }
 
 private:
 
-    //Parameters that affect code generation
+    // Parameters that affect code generation
+    // These structs should be kept compact; they are the input to an
+    // expensive hash key generator.
     struct ProgramDesc {
         ProgramDesc() {
             // since we use this as part of a key we can't have any unitialized
@@ -98,46 +97,76 @@
             memset(this, 0, sizeof(ProgramDesc));
         }
 
-        // stripped of bits that don't affect prog generation
-        GrVertexLayout fVertexLayout;
-
-        enum {
-            kNone_ColorType         = 0,
-            kAttribute_ColorType    = 1,
-            kUniform_ColorType      = 2,
-        } fColorType;
-
-        bool fEmitsPointSize;
-        bool fUsesEdgeAA;
-
-        SkXfermode::Mode fColorFilterXfermode;
-
         struct StageDesc {
             enum OptFlagBits {
-                kNoPerspective_OptFlagBit  = 0x1,
-                kIdentityMatrix_OptFlagBit = 0x2
+                kNoPerspective_OptFlagBit       = 1 << 0,
+                kIdentityMatrix_OptFlagBit      = 1 << 1,
+                kCustomTextureDomain_OptFlagBit = 1 << 2,
+                kIsEnabled_OptFlagBit           = 1 << 7
             };
-
-            unsigned fOptFlags;
-            bool fEnabled;
-
             enum Modulation {
                 kColor_Modulation,
                 kAlpha_Modulation
-            } fModulation;
-
+            };
             enum FetchMode {
                 kSingle_FetchMode,
                 k2x2_FetchMode
-            } fFetchMode;
-
+            };
             enum CoordMapping {
                 kIdentity_CoordMapping,
                 kRadialGradient_CoordMapping,
                 kSweepGradient_CoordMapping,
                 kRadial2Gradient_CoordMapping
-            } fCoordMapping;
-        } fStages[GrDrawTarget::kNumStages];
+            };
+
+            uint8_t fOptFlags;
+            uint8_t fModulation;  // casts to enum Modulation
+            uint8_t fFetchMode;  // casts to enum FetchMode
+            uint8_t fCoordMapping;  // casts to enum CoordMapping
+
+            inline bool isEnabled() const {
+                return fOptFlags & kIsEnabled_OptFlagBit;
+            }
+            inline void setEnabled(bool newValue) {
+                if (newValue) {
+                    fOptFlags |= kIsEnabled_OptFlagBit;
+                } else {
+                    fOptFlags &= ~kIsEnabled_OptFlagBit;
+                }
+            }
+        };
+
+        enum ColorType {
+            kNone_ColorType         = 0,
+            kAttribute_ColorType    = 1,
+            kUniform_ColorType      = 2,
+        };
+        // 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
+        };
+
+        // stripped of bits that don't affect prog generation
+        GrVertexLayout fVertexLayout;
+
+        StageDesc fStages[GrDrawTarget::kNumStages];
+
+        uint8_t fColorType;  // casts to enum ColorType
+        uint8_t fDualSrcOutput;  // casts to enum DualSrcOutput
+        int8_t fFirstCoverageStage;
+        SkBool8 fEmitsPointSize;
+
+        int8_t fEdgeAANumEdges;
+        uint8_t fColorFilterXfermode;  // casts to enum SkXfermode::Mode
+
+        uint8_t fPadTo32bLengthMultiple [2];
+
     } fProgramDesc;
 
     const ProgramDesc& getDesc() { return fProgramDesc; }
@@ -153,11 +182,13 @@
         GrGLint fNormalizedTexelSizeUni;
         GrGLint fSamplerUni;
         GrGLint fRadial2Uni;
+        GrGLint fTexDomUni;
         void reset() {
             fTextureMatrixUni = kUnusedUniform;
             fNormalizedTexelSizeUni = kUnusedUniform;
             fSamplerUni = kUnusedUniform;
             fRadial2Uni = kUnusedUniform;
+            fTexDomUni = kUnusedUniform;
         }
     };
 
@@ -181,35 +212,13 @@
     class CachedData : public ::GrNoncopyable {
     public:
         CachedData() {
-            GR_DEBUGCODE(fEffectUniCount = 0;)
-            fEffectUniLocationsExtended = NULL;
         }
 
         ~CachedData() {
-            GrFree(fEffectUniLocationsExtended);
         }
 
         void copyAndTakeOwnership(CachedData& other) {
             memcpy(this, &other, sizeof(*this));
-            other.fEffectUniLocationsExtended = NULL; // ownership transfer
-            GR_DEBUGCODE(other.fEffectUniCount = 0;)
-        }
-
-        void setEffectUniformCount(size_t effectUniforms) {
-            GR_DEBUGCODE(fEffectUniCount = effectUniforms;)
-            GrFree(fEffectUniLocationsExtended);
-            if (effectUniforms > kUniLocationPreAllocSize) {
-                fEffectUniLocationsExtended = (GrGLint*)GrMalloc(sizeof(GrGLint)*(effectUniforms-kUniLocationPreAllocSize));
-            } else {
-                fEffectUniLocationsExtended = NULL;
-            }
-        }
-
-        GrGLint&  effectUniLocation(size_t index) {
-            GrAssert(index < fEffectUniCount);
-            return (index < kUniLocationPreAllocSize) ? 
-                fEffectUniLocations[index] :
-                fEffectUniLocationsExtended[index - kUniLocationPreAllocSize];
         }
 
     public:
@@ -234,19 +243,15 @@
         GrScalar                    fRadial2CenterX1[GrDrawTarget::kNumStages];
         GrScalar                    fRadial2Radius0[GrDrawTarget::kNumStages];
         bool                        fRadial2PosRoot[GrDrawTarget::kNumStages];
+        GrRect                      fTextureDomain[GrDrawTarget::kNumStages];
 
     private:
         enum Constants {
             kUniLocationPreAllocSize = 8
         };
 
-        GrGLint     fEffectUniLocations[kUniLocationPreAllocSize];
-        GrGLint*    fEffectUniLocationsExtended;
-        GR_DEBUGCODE(size_t fEffectUniCount;)
     }; // CachedData
 
-    GrGLEffect* fStageEffects[GrDrawTarget::kNumStages];
-
 private:
     enum {
         kUseUniform = 2000
@@ -273,8 +278,11 @@
 
     // Creates a GL program ID, binds shader attributes to GL vertex attrs, and
     // links the program
-    bool bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[GrDrawTarget::kMaxTexCoords],
-                                   CachedData* programData) const;
+    bool bindOutputsAttribsAndLinkProgram(
+                GrStringBuilder texCoordAttrNames[GrDrawTarget::kMaxTexCoords],
+                bool bindColorOut,
+                bool bindDualSrcOut,
+                CachedData* programData) const;
 
     // Gets locations for all uniforms set to kUseUniform and initializes cache
     // to invalid values.
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index 9950afd..4fe7ccc 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -142,17 +142,6 @@
     return this->onCreateTexture(desc, srcData, rowBytes);
 }
 
-GrRenderTarget* GrGpu::createPlatformRenderTarget(intptr_t platformRenderTarget,
-                                                  int stencilBits,
-                                                  bool isMultisampled,
-                                                  int width, int height) {
-    this->handleDirtyContext();
-    return this->onCreatePlatformRenderTarget(platformRenderTarget,
-                                                  stencilBits,
-                                                  isMultisampled,
-                                                  width, height);
-}
-
 GrRenderTarget* GrGpu::createRenderTargetFrom3DApiState() {
     this->handleDirtyContext();
     return this->onCreateRenderTargetFrom3DApiState();
@@ -467,17 +456,16 @@
                                                // resolve in/out status.
 
                 GrPathRenderer* pr = NULL;
-                GrPath::Iter pathIter;
+                const GrPath* clipPath = NULL;
                 if (kRect_ClipType == clip.getElementType(c)) {
                     canRenderDirectToStencil = true;
                     fill = kEvenOdd_PathFill;
                 } else {
                     fill = clip.getPathFill(c);
-                    const GrPath& path = clip.getPath(c);
-                    pathIter.reset(path);
-                    pr = this->getClipPathRenderer(&pathIter, NonInvertedFill(fill));
+                    clipPath = &clip.getPath(c);
+                    pr = this->getClipPathRenderer(*clipPath, NonInvertedFill(fill));
                     canRenderDirectToStencil =
-                        !pr->requiresStencilPass(this, &pathIter,
+                        !pr->requiresStencilPass(this, *clipPath,
                                                  NonInvertedFill(fill));
                 }
 
@@ -513,12 +501,10 @@
                     } else {
                         if (canRenderDirectToStencil) {
                             this->setStencil(gDrawToStencil);
-                            pr->drawPath(this, 0,
-                                         &pathIter,
-                                         NonInvertedFill(fill),
+                            pr->drawPath(this, 0, *clipPath, NonInvertedFill(fill),
                                          NULL);
                         } else {
-                            pr->drawPathToStencil(this, &pathIter,
+                            pr->drawPathToStencil(this, *clipPath,
                                                   NonInvertedFill(fill),
                                                   NULL);
                         }
@@ -537,7 +523,7 @@
                         } else {
                             SET_RANDOM_COLOR
                             GrAssert(!IsFillInverted(fill));
-                            pr->drawPath(this, 0, &pathIter, fill, NULL);
+                            pr->drawPath(this, 0, *clipPath, fill, NULL);
                         }
                     } else {
                         SET_RANDOM_COLOR
@@ -561,7 +547,7 @@
     return true;
 }
 
-GrPathRenderer* GrGpu::getClipPathRenderer(GrPathIter* path,
+GrPathRenderer* GrGpu::getClipPathRenderer(const GrPath& path,
                                            GrPathFill fill) {
     if (NULL != fClientPathRenderer &&
         fClientPathRenderer->canDrawPath(this, path, fill)) {
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index e8c7afb..ff2d406 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -16,6 +16,7 @@
 
 #include "GrGpuGL.h"
 #include "GrMemory.h"
+#include "GrTypes.h"
 
 static const GrGLuint GR_MAX_GLUINT = ~0;
 static const GrGLint  GR_INVAL_GLINT = ~0;
@@ -41,9 +42,15 @@
     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::BlendCoefReferencesConstant(GrBlendCoeff coeff) {
+bool GrGpuGL::BlendCoeffReferencesConstant(GrBlendCoeff coeff) {
     static const bool gCoeffReferencesBlendConst[] = {
         false,
         false,
@@ -59,28 +66,40 @@
         true,
         true,
         true,
+
+        // extended blend coeffs
+        false,
+        false,
+        false,
+        false,
     };
     return gCoeffReferencesBlendConst[coeff];
-    GR_STATIC_ASSERT(kBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
+    GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
+
+    GR_STATIC_ASSERT(0 == kZero_BlendCoeff);
+    GR_STATIC_ASSERT(1 == kOne_BlendCoeff);
+    GR_STATIC_ASSERT(2 == kSC_BlendCoeff);
+    GR_STATIC_ASSERT(3 == kISC_BlendCoeff);
+    GR_STATIC_ASSERT(4 == kDC_BlendCoeff);
+    GR_STATIC_ASSERT(5 == kIDC_BlendCoeff);
+    GR_STATIC_ASSERT(6 == kSA_BlendCoeff);
+    GR_STATIC_ASSERT(7 == kISA_BlendCoeff);
+    GR_STATIC_ASSERT(8 == kDA_BlendCoeff);
+    GR_STATIC_ASSERT(9 == kIDA_BlendCoeff);
+    GR_STATIC_ASSERT(10 == kConstC_BlendCoeff);
+    GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff);
+    GR_STATIC_ASSERT(12 == kConstA_BlendCoeff);
+    GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff);
+
+    GR_STATIC_ASSERT(14 == kS2C_BlendCoeff);
+    GR_STATIC_ASSERT(15 == kIS2C_BlendCoeff);
+    GR_STATIC_ASSERT(16 == kS2A_BlendCoeff);
+    GR_STATIC_ASSERT(17 == kIS2A_BlendCoeff);
+
+    // assertion for gXfermodeCoeff2Blend have to be in GrGpu scope
+    GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
 }
 
-GR_STATIC_ASSERT(0 == kZero_BlendCoeff);
-GR_STATIC_ASSERT(1 == kOne_BlendCoeff);
-GR_STATIC_ASSERT(2 == kSC_BlendCoeff);
-GR_STATIC_ASSERT(3 == kISC_BlendCoeff);
-GR_STATIC_ASSERT(4 == kDC_BlendCoeff);
-GR_STATIC_ASSERT(5 == kIDC_BlendCoeff);
-GR_STATIC_ASSERT(6 == kSA_BlendCoeff);
-GR_STATIC_ASSERT(7 == kISA_BlendCoeff);
-GR_STATIC_ASSERT(8 == kDA_BlendCoeff);
-GR_STATIC_ASSERT(9 == kIDA_BlendCoeff);
-GR_STATIC_ASSERT(10 == kConstC_BlendCoeff);
-GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff);
-GR_STATIC_ASSERT(12 == kConstA_BlendCoeff);
-GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff);
-
-GR_STATIC_ASSERT(kBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
-
 ///////////////////////////////////////////////////////////////////////////////
 
 void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
@@ -201,6 +220,16 @@
         GR_GL_GetIntegerv(GR_GL_MAX_TEXTURE_UNITS, &maxTextureUnits);
         GrAssert(maxTextureUnits > kNumStages);
     }
+    if (GR_GL_SUPPORT_ES2) {
+        GR_GL_GetIntegerv(GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS,
+                          &fMaxFragmentUniformVectors);
+    } else if (GR_GL_SUPPORT_DESKTOP) {
+        GrGLint max;
+        GR_GL_GetIntegerv(GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max);
+        fMaxFragmentUniformVectors = max / 4;
+    } else {
+        fMaxFragmentUniformVectors = 16;
+    }
 
     ////////////////////////////////////////////////////////////////////////////
     // Check for supported features.
@@ -604,32 +633,6 @@
     }
 }
 
-GrRenderTarget* GrGpuGL::onCreatePlatformRenderTarget(
-                                                intptr_t platformRenderTarget,
-                                                int stencilBits,
-                                                bool isMultisampled,
-                                                int width,
-                                                int height) {
-    GrGLRenderTarget::GLRenderTargetIDs rtIDs;
-    rtIDs.fStencilRenderbufferID = 0;
-    rtIDs.fMSColorRenderbufferID = 0;
-    rtIDs.fTexFBOID              = 0;
-    rtIDs.fOwnIDs                = false;
-    GrGLIRect viewport;
-
-    // viewport is in GL coords (top >= bottom)
-    viewport.fLeft      = 0;
-    viewport.fBottom    = 0;
-    viewport.fWidth     = width;
-    viewport.fHeight    = height;
-
-    rtIDs.fRTFBOID  = (GrGLuint)platformRenderTarget;
-    rtIDs.fTexFBOID = (GrGLuint)platformRenderTarget;
-
-    return new GrGLRenderTarget(this, rtIDs, NULL, stencilBits, 
-                                isMultisampled, viewport, NULL);
-}
-
 GrRenderTarget* GrGpuGL::onCreateRenderTargetFrom3DApiState() {
 
     GrGLRenderTarget::GLRenderTargetIDs rtIDs;
@@ -1667,7 +1670,9 @@
     }
 }
 
-void GrGpuGL::flushBlend(GrPrimitiveType type) {
+void GrGpuGL::flushBlend(GrPrimitiveType type, 
+                         GrBlendCoeff srcCoeff, 
+                         GrBlendCoeff dstCoeff) {
     if (GrIsPrimTypeLines(type) && useSmoothLines()) {
         if (fHWBlendDisabled) {
             GR_GL(Enable(GR_GL_BLEND));
@@ -1691,15 +1696,15 @@
             fHWBlendDisabled = blendOff;
         }
         if (!blendOff) {
-            if (fHWDrawState.fSrcBlend != fCurrDrawState.fSrcBlend ||
-                  fHWDrawState.fDstBlend != fCurrDrawState.fDstBlend) {
-                GR_GL(BlendFunc(gXfermodeCoeff2Blend[fCurrDrawState.fSrcBlend],
-                                gXfermodeCoeff2Blend[fCurrDrawState.fDstBlend]));
-                fHWDrawState.fSrcBlend = fCurrDrawState.fSrcBlend;
-                fHWDrawState.fDstBlend = fCurrDrawState.fDstBlend;
+            if (fHWDrawState.fSrcBlend != srcCoeff ||
+                fHWDrawState.fDstBlend != dstCoeff) {
+                GR_GL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff],
+                                gXfermodeCoeff2Blend[dstCoeff]));
+                fHWDrawState.fSrcBlend = srcCoeff;
+                fHWDrawState.fDstBlend = dstCoeff;
             }
-            if ((BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) ||
-                 BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) &&
+            if ((BlendCoeffReferencesConstant(srcCoeff) ||
+                 BlendCoeffReferencesConstant(dstCoeff)) &&
                 fHWDrawState.fBlendConstant != fCurrDrawState.fBlendConstant) {
 
                 float c[] = {
@@ -1802,7 +1807,6 @@
     }
     this->flushRenderTarget(rect);
     this->flushAAState(type);
-    this->flushBlend(type);
     
     if ((fCurrDrawState.fFlagBits & kDither_StateBit) !=
         (fHWDrawState.fFlagBits & kDither_StateBit)) {
@@ -2064,3 +2068,9 @@
         }
     }
 }
+
+int GrGpuGL::getMaxEdges() const {
+    // FIXME:  This is a pessimistic estimate based on how many other things
+    // want to add uniforms.  This should be centralized somewhere.
+    return GR_CT_MIN(fMaxFragmentUniformVectors - 8, kMaxEdges);
+}
diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h
index da955cf..696b72f 100644
--- a/gpu/src/GrGpuGL.h
+++ b/gpu/src/GrGpuGL.h
@@ -81,11 +81,6 @@
     virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size,
                                                bool dynamic);
     virtual GrResource* onCreatePlatformSurface(const GrPlatformSurfaceDesc& desc);
-    virtual GrRenderTarget* onCreatePlatformRenderTarget(
-                                                 intptr_t platformRenderTarget,
-                                                 int stencilBits,
-                                                 bool isMultisampled,
-                                                 int width, int height);
     virtual GrRenderTarget* onCreateRenderTargetFrom3DApiState();
 
     virtual void onClear(const GrIRect* rect, GrColor color);
@@ -107,6 +102,7 @@
     virtual void flushScissor(const GrIRect* rect);
     void clearStencil(uint32_t value, uint32_t mask);
     virtual void clearStencilClip(const GrIRect& rect);
+    virtual int getMaxEdges() const;
 
     // binds texture unit in GL
     void setTextureUnit(int unitIdx);
@@ -120,13 +116,17 @@
     // flushes state that is common to fixed and programmable GL
     // dither
     // line smoothing
-    // blend func
     // texture binding
     // sampler state (filtering, tiling)
     // FBO binding
     // line width
     bool flushGLStateCommon(GrPrimitiveType type);
 
+    // subclass should call this to flush the blend state
+    void flushBlend(GrPrimitiveType type,
+                    GrBlendCoeff srcCoeff,
+                    GrBlendCoeff dstCoeff);
+
     // adjusts texture matrix to account for orientation, size, and npotness
     static void AdjustTextureMatrix(const GrGLTexture* texture,
                                     GrSamplerState::SampleMode mode,
@@ -138,7 +138,7 @@
     static bool TextureMatrixIsIdentity(const GrGLTexture* texture,
                                         const GrSamplerState& sampler);
 
-    static bool BlendCoefReferencesConstant(GrBlendCoeff coeff);
+    static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
 
 private:
 
@@ -160,7 +160,6 @@
     void flushRenderTarget(const GrIRect* bound);
     void flushStencil();
     void flushAAState(GrPrimitiveType type);
-    void flushBlend(GrPrimitiveType type);
 
     void resolveRenderTarget(GrGLRenderTarget* texture);
 
@@ -189,6 +188,9 @@
     // Do we have stencil wrap ops.
     bool fHasStencilWrap;
 
+    // The maximum number of fragment uniform vectors (GLES has min. 16).
+    int fMaxFragmentUniformVectors;
+
     // ES requires an extension to support RGBA8 in RenderBufferStorage
     bool fRGBA8Renderbuffer;
 
diff --git a/gpu/src/GrGpuGLFixed.cpp b/gpu/src/GrGpuGLFixed.cpp
index e197a82..65229dc 100644
--- a/gpu/src/GrGpuGLFixed.cpp
+++ b/gpu/src/GrGpuGLFixed.cpp
@@ -57,6 +57,7 @@
 
 GrGpuGLFixed::GrGpuGLFixed() {
     f4X4DownsampleFilterSupport = false;
+    fDualSourceBlendingSupport = false;
 }
 
 GrGpuGLFixed::~GrGpuGLFixed() {
@@ -136,8 +137,8 @@
     }
 
     if (GR_GL_SUPPORT_ES1) {
-        if (BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) ||
-            BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) {
+        if (BlendCoeffReferencesConstant(fCurrDrawState.fSrcBlend) ||
+            BlendCoeffReferencesConstant(fCurrDrawState.fDstBlend)) {
             unimpl("ES1 doesn't support blend constant");
             return false;
         }
@@ -147,6 +148,8 @@
         return false;
     }
 
+    this->flushBlend(type, fCurrDrawState.fSrcBlend, fCurrDrawState.fDstBlend);
+
     if (fDirtyFlags.fRenderTargetChanged) {
         flushProjectionMatrix();
     }
diff --git a/gpu/src/GrGpuGLFixed.h b/gpu/src/GrGpuGLFixed.h
index 077b6e2..487c09f 100644
--- a/gpu/src/GrGpuGLFixed.h
+++ b/gpu/src/GrGpuGLFixed.h
@@ -41,7 +41,7 @@
     const GrMatrix& getHWSamplerMatrix(int stage) const {
         return fHWDrawState.fSamplerStates[stage].getMatrix();
     }
-    const void recordHWSamplerMatrix(int stage, const GrMatrix& matrix) {
+    void recordHWSamplerMatrix(int stage, const GrMatrix& matrix) {
         fHWDrawState.fSamplerStates[stage].setMatrix(matrix);
     }
 
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index 8965b06..50be67f 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -15,7 +15,6 @@
  */
 
 #include "GrBinHashKey.h"
-#include "GrGLEffect.h"
 #include "GrGLProgram.h"
 #include "GrGpuGLShaders.h"
 #include "GrGpuVertex.h"
@@ -169,6 +168,14 @@
     GrRandom random;
     for (int t = 0; t < NUM_TESTS; ++t) {
 
+#if 0
+        GrPrintf("\nTest Program %d\n-------------\n", t);
+        static const int stop = -1;
+        if (t == stop) {
+            int breakpointhere = 9;
+        }
+#endif
+
         pdesc.fVertexLayout = 0;
         pdesc.fEmitsPointSize = random.nextF() > .5f;
         float colorType = random.nextF();
@@ -179,6 +186,24 @@
         } else {
             pdesc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType;
         }
+
+        int idx = (int)(random.nextF() * (SkXfermode::kCoeffModesCnt));
+        pdesc.fColorFilterXfermode = (SkXfermode::Mode)idx;
+
+        idx = (int)(random.nextF() * (kNumStages+1));
+        pdesc.fFirstCoverageStage = idx;
+
+        pdesc.fEdgeAANumEdges = (random.nextF() * (getMaxEdges() + 1));
+
+        if (fDualSourceBlendingSupport) {
+            pdesc.fDualSrcOutput =
+               (GrGLProgram::ProgramDesc::DualSrcOutput)
+               (int)(random.nextF() * GrGLProgram::ProgramDesc::kDualSrcOutputCnt);
+        } else {
+            pdesc.fDualSrcOutput =
+                                GrGLProgram::ProgramDesc::kNone_DualSrcOutput;
+        }
+
         for (int s = 0; s < kNumStages; ++s) {
             // enable the stage?
             if (random.nextF() > .5f) {
@@ -194,19 +219,15 @@
             if (random.nextF() > .5f) {
                 pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit;
             }
-        }
-
-        for (int s = 0; s < kNumStages; ++s) {
-            int x;
-            pdesc.fStages[s].fEnabled = VertexUsesStage(s, pdesc.fVertexLayout);
-            x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS));
-            pdesc.fStages[s].fOptFlags = STAGE_OPTS[x];
-            x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_MODULATES));
-            pdesc.fStages[s].fModulation = STAGE_MODULATES[x];
-            x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_COORD_MAPPINGS));
-            pdesc.fStages[s].fCoordMapping = STAGE_COORD_MAPPINGS[x];
-            x = (int)(random.nextF() * GR_ARRAY_COUNT(FETCH_MODES));
-            pdesc.fStages[s].fFetchMode = FETCH_MODES[x];
+            idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS));
+            pdesc.fStages[s].fOptFlags = STAGE_OPTS[idx];
+            idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_MODULATES));
+            pdesc.fStages[s].fModulation = STAGE_MODULATES[idx];
+            idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_COORD_MAPPINGS));
+            pdesc.fStages[s].fCoordMapping = STAGE_COORD_MAPPINGS[idx];
+            idx = (int)(random.nextF() * GR_ARRAY_COUNT(FETCH_MODES));
+            pdesc.fStages[s].fFetchMode = FETCH_MODES[idx];
+            pdesc.fStages[s].setEnabled(VertexUsesStage(s, pdesc.fVertexLayout));
         }
         GrGLProgram::CachedData cachedData;
         program.genProgram(&cachedData);
@@ -219,11 +240,20 @@
     }
 }
 
-
 GrGpuGLShaders::GrGpuGLShaders() {
 
     resetContext();
+    int major, minor;
+    gl_version(&major, &minor);
+
     f4X4DownsampleFilterSupport = true;
+    if (GR_GL_SUPPORT_DESKTOP) {
+        fDualSourceBlendingSupport =
+            major > 3 ||(3 == major && 3 <= minor) ||
+            has_gl_extension("GL_ARB_blend_func_extended");
+    } else {
+        fDualSourceBlendingSupport = false;
+    }
 
     fProgramData = NULL;
     fProgramCache = new ProgramCache();
@@ -283,16 +313,16 @@
 
     // ES doesn't allow you to pass true to the transpose param,
     // so do our own transpose
-    GrScalar mt[]  = {
-        m[GrMatrix::kMScaleX],
-        m[GrMatrix::kMSkewY],
-        m[GrMatrix::kMPersp0],
-        m[GrMatrix::kMSkewX],
-        m[GrMatrix::kMScaleY],
-        m[GrMatrix::kMPersp1],
-        m[GrMatrix::kMTransX],
-        m[GrMatrix::kMTransY],
-        m[GrMatrix::kMPersp2]
+    GrGLfloat mt[]  = {
+        GrScalarToFloat(m[GrMatrix::kMScaleX]),
+        GrScalarToFloat(m[GrMatrix::kMSkewY]),
+        GrScalarToFloat(m[GrMatrix::kMPersp0]),
+        GrScalarToFloat(m[GrMatrix::kMSkewX]),
+        GrScalarToFloat(m[GrMatrix::kMScaleY]),
+        GrScalarToFloat(m[GrMatrix::kMPersp1]),
+        GrScalarToFloat(m[GrMatrix::kMTransX]),
+        GrScalarToFloat(m[GrMatrix::kMTransY]),
+        GrScalarToFloat(m[GrMatrix::kMPersp2])
     };
 
     if (GrGLProgram::kSetAsAttribute ==  
@@ -309,8 +339,48 @@
     }
 }
 
+void GrGpuGLShaders::flushTextureDomain(int s) {
+    const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTexDomUni;
+    if (GrGLProgram::kUnusedUniform != uni) {
+        const GrRect &texDom =
+            fCurrDrawState.fSamplerStates[s].getTextureDomain();
+
+        if (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
+            fProgramData->fTextureDomain[s] != texDom) {
+
+            fProgramData->fTextureDomain[s] = texDom;
+
+            float values[4] = {
+                GrScalarToFloat(texDom.left()),
+                GrScalarToFloat(texDom.top()),
+                GrScalarToFloat(texDom.right()),
+                GrScalarToFloat(texDom.bottom())
+            };
+
+            GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
+            GrGLTexture::Orientation orientation = texture->orientation();
+
+            // vertical flip if necessary
+            if (GrGLTexture::kBottomUp_Orientation == orientation) {
+                values[1] = 1.0f - values[1];
+                values[3] = 1.0f - values[3];
+                // The top and bottom were just flipped, so correct the ordering
+                // of elements so that values = (l, t, r, b).
+                SkTSwap(values[1], values[3]);
+            }
+
+            values[0] *= SkScalarToFloat(texture->contentScaleX());
+            values[2] *= SkScalarToFloat(texture->contentScaleX());
+            values[1] *= SkScalarToFloat(texture->contentScaleY());
+            values[3] *= SkScalarToFloat(texture->contentScaleY());
+
+            GR_GL(Uniform4fv(uni, 1, values));
+        }
+    }
+}
+
 void GrGpuGLShaders::flushTextureMatrix(int s) {
-    const int& uni = fProgramData->fUniLocations.fStages[s].fTextureMatrixUni;
+    const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTextureMatrixUni;
     GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
     if (NULL != texture) {
         if (GrGLProgram::kUnusedUniform != uni &&
@@ -328,17 +398,18 @@
 
             // ES doesn't allow you to pass true to the transpose param,
             // so do our own transpose
-            GrScalar mt[]  = {
-                m[GrMatrix::kMScaleX],
-                m[GrMatrix::kMSkewY],
-                m[GrMatrix::kMPersp0],
-                m[GrMatrix::kMSkewX],
-                m[GrMatrix::kMScaleY],
-                m[GrMatrix::kMPersp1],
-                m[GrMatrix::kMTransX],
-                m[GrMatrix::kMTransY],
-                m[GrMatrix::kMPersp2]
+            GrGLfloat mt[]  = {
+                GrScalarToFloat(m[GrMatrix::kMScaleX]),
+                GrScalarToFloat(m[GrMatrix::kMSkewY]),
+                GrScalarToFloat(m[GrMatrix::kMPersp0]),
+                GrScalarToFloat(m[GrMatrix::kMSkewX]),
+                GrScalarToFloat(m[GrMatrix::kMScaleY]),
+                GrScalarToFloat(m[GrMatrix::kMPersp1]),
+                GrScalarToFloat(m[GrMatrix::kMTransX]),
+                GrScalarToFloat(m[GrMatrix::kMTransY]),
+                GrScalarToFloat(m[GrMatrix::kMPersp2])
             };
+
             if (GrGLProgram::kSetAsAttribute ==
                 fProgramData->fUniLocations.fStages[s].fTextureMatrixUni) {
                 int baseIdx = GrGLProgram::TextureMatrixAttributeIdx(s);
@@ -399,16 +470,17 @@
 void GrGpuGLShaders::flushEdgeAAData() {
     const int& uni = fProgramData->fUniLocations.fEdgesUni;
     if (GrGLProgram::kUnusedUniform != uni) {
-        float edges[18];
-        memcpy(edges, fCurrDrawState.fEdgeAAEdges, sizeof(edges));
+        int count = fCurrDrawState.fEdgeAANumEdges;
+        Edge edges[kMaxEdges];
         // Flip the edges in Y
         float height = fCurrDrawState.fRenderTarget->height();
-        for (int i = 0; i < 6; ++i) {
-            float b = edges[i * 3 + 1];
-            edges[i * 3 + 1] = -b;
-            edges[i * 3 + 2] += b * height;
+        for (int i = 0; i < count; ++i) {
+            edges[i] = fCurrDrawState.fEdgeAAEdges[i];
+            float b = edges[i].fY;
+            edges[i].fY = -b;
+            edges[i].fZ += b * height;
         }
-        GR_GL(Uniform3fv(uni, 6, edges));
+        GR_GL(Uniform3fv(uni, count, &edges[0].fX));
     }
 }
 
@@ -489,10 +561,11 @@
         GR_GL(UseProgram(fProgramData->fProgramID));
         fHWProgramID = fProgramData->fProgramID;
     }
-
-    if (!fCurrentProgram.doGLSetup(type, fProgramData)) {
-        return false;
-    }
+    GrBlendCoeff srcCoeff = fCurrDrawState.fSrcBlend;
+    GrBlendCoeff dstCoeff = fCurrDrawState.fDstBlend;
+    
+    fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
+    this->flushBlend(type, srcCoeff, dstCoeff);
 
     this->flushColor();
 
@@ -515,6 +588,8 @@
         this->flushRadial2(s);
 
         this->flushTexelSize(s);
+
+        this->flushTextureDomain(s);
     }
     this->flushEdgeAAData();
     resetDirtyFlags();
@@ -522,7 +597,6 @@
 }
 
 void GrGpuGLShaders::postDraw() {
-    fCurrentProgram.doGLPost();
 }
 
 void GrGpuGLShaders::setupGeometry(int* startVertex,
@@ -634,6 +708,8 @@
     // existing program in the cache.
     desc.fVertexLayout &= ~(kColor_VertexLayoutBit);
 
+    desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode;
+
 #if GR_AGGRESSIVE_SHADER_OPTS
     if (!requiresAttributeColors && (0xffffffff == fCurrDrawState.fColor)) {
         desc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType;
@@ -649,24 +725,26 @@
         desc.fColorType = GrGLProgram::ProgramDesc::kAttribute_ColorType;
     }
 
-    desc.fUsesEdgeAA = fCurrDrawState.fFlagBits & kEdgeAA_StateBit;
+    desc.fEdgeAANumEdges = fCurrDrawState.fEdgeAANumEdges;
+
+    int lastEnabledStage = -1;
 
     for (int s = 0; s < kNumStages; ++s) {
         GrGLProgram::ProgramDesc::StageDesc& stage = desc.fStages[s];
 
-        stage.fEnabled = this->isStageEnabled(s);
+        stage.fOptFlags = 0;
+        stage.setEnabled(this->isStageEnabled(s));
 
-        if (stage.fEnabled) {
+        if (stage.isEnabled()) {
+            lastEnabledStage = s;
             GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
             GrAssert(NULL != texture);
             // we matrix to invert when orientation is TopDown, so make sure
             // we aren't in that case before flagging as identity.
             if (TextureMatrixIsIdentity(texture, fCurrDrawState.fSamplerStates[s])) {
-                stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit;
+                stage.fOptFlags |= GrGLProgram::ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit;
             } else if (!getSamplerMatrix(s).hasPerspective()) {
-                stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kNoPerspective_OptFlagBit;
-            } else {
-                stage.fOptFlags = 0;
+                stage.fOptFlags |= GrGLProgram::ProgramDesc::StageDesc::kNoPerspective_OptFlagBit;
             }
             switch (fCurrDrawState.fSamplerStates[s].getSampleMode()) {
                 case GrSamplerState::kNormal_SampleMode:
@@ -701,27 +779,62 @@
                     break;
             }
 
+            if (fCurrDrawState.fSamplerStates[s].hasTextureDomain()) {
+                GrAssert(GrSamplerState::kClamp_WrapMode ==
+                    fCurrDrawState.fSamplerStates[s].getWrapX() &&
+                    GrSamplerState::kClamp_WrapMode ==
+                    fCurrDrawState.fSamplerStates[s].getWrapY());
+                stage.fOptFlags |=
+                    GrGLProgram::ProgramDesc::StageDesc::
+                    kCustomTextureDomain_OptFlagBit;
+            }
+
             if (GrPixelConfigIsAlphaOnly(texture->config())) {
                 stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kAlpha_Modulation;
             } else {
                 stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kColor_Modulation;
             }
-
-            if (fCurrDrawState.fEffects[s]) {
-                fCurrentProgram.fStageEffects[s] = GrGLEffect::Create(fCurrDrawState.fEffects[s]);
-            } else {
-                delete fCurrentProgram.fStageEffects[s];
-                fCurrentProgram.fStageEffects[s] = NULL;
-            }
         } else {
             stage.fOptFlags     = 0;
             stage.fCoordMapping = (GrGLProgram::ProgramDesc::StageDesc::CoordMapping)0;
             stage.fModulation   = (GrGLProgram::ProgramDesc::StageDesc::Modulation)0;
-            fCurrentProgram.fStageEffects[s] = NULL;
         }
     }
-    desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode;
+
+    desc.fDualSrcOutput = GrGLProgram::ProgramDesc::kNone_DualSrcOutput;
+    // use canonical value when coverage/color distinction won't affect
+    // generated code to prevent duplicate programs.
+    desc.fFirstCoverageStage = kNumStages;
+    if (fCurrDrawState.fFirstCoverageStage <= lastEnabledStage) {
+        // color filter is applied between color/coverage computation
+        if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
+            desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+        }
+
+        // We could consider cases where the final color is solid (0xff alpha)
+        // and the dst coeff can correctly be set to a non-dualsrc gl value.
+        // (e.g. solid draw, and dst coeff is kZero. It's correct to make
+        // the dst coeff be kISA. Or solid draw with kSA can be tweaked to be
+        // kOne).
+        if (fDualSourceBlendingSupport) {
+            if (kZero_BlendCoeff == fCurrDrawState.fDstBlend) {
+                // write the coverage value to second color
+                desc.fDualSrcOutput = 
+                                GrGLProgram::ProgramDesc::kCoverage_DualSrcOutput;
+                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+            } else if (kSA_BlendCoeff == fCurrDrawState.fDstBlend) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially 
+                // cover
+                desc.fDualSrcOutput = 
+                            GrGLProgram::ProgramDesc::kCoverageISA_DualSrcOutput;
+                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+            } else if (kSC_BlendCoeff == fCurrDrawState.fDstBlend) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
+                // cover
+                desc.fDualSrcOutput = 
+                        GrGLProgram::ProgramDesc::kCoverageISC_DualSrcOutput;
+                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+            }
+        }
+    }
 }
-
-
-
diff --git a/gpu/src/GrGpuGLShaders.h b/gpu/src/GrGpuGLShaders.h
index bb3024f..9392d1c 100644
--- a/gpu/src/GrGpuGLShaders.h
+++ b/gpu/src/GrGpuGLShaders.h
@@ -51,6 +51,9 @@
     // sets the texture matrix uniform for currently bound program
     void flushTextureMatrix(int stage);
 
+    // sets the texture domain uniform for currently bound program
+    void flushTextureDomain(int stage);
+
     // sets the color specified by GrDrawTarget::setColor()
     void flushColor();
 
diff --git a/gpu/src/GrPath.cpp b/gpu/src/GrPath.cpp
deleted file mode 100644
index aa89d37..0000000
--- a/gpu/src/GrPath.cpp
+++ /dev/null
@@ -1,523 +0,0 @@
-#include "GrPath.h"
-
-GrPath::GrPath() {
-    fConvexHint = kNone_ConvexHint;
-    fConservativeBounds.setLargestInverted();
-}
-
-GrPath::GrPath(const GrPath& src) : INHERITED() {
-    GrPath::Iter iter(src);
-    this->resetFromIter(&iter);
-}
-
-GrPath::GrPath(GrPathIter& iter) {
-    this->resetFromIter(&iter);
-}
-
-GrPath::~GrPath() {
-}
-
-bool GrPath::operator ==(const GrPath& path) const {
-    if (fCmds.count() != path.fCmds.count() ||
-        fPts.count() != path.fPts.count()) {
-        return false;
-    }
-
-    for (int v = 0; v < fCmds.count(); ++v) {
-        if (fCmds[v] != path.fCmds[v]) {
-            return false;
-        }
-    }
-
-    for (int p = 0; p < fPts.count(); ++p) {
-        if (fPts[p] != path.fPts[p]) {
-            return false;
-        }
-    }
-    return true;
-}
-
-void GrPath::ensureMoveTo() {
-    if (fCmds.isEmpty() || this->wasLastVerb(kClose_PathCmd)) {
-        *fCmds.append() = kMove_PathCmd;
-        fPts.append()->set(0, 0);
-        fConservativeBounds.growToInclude(0,0);
-    }
-}
-
-void GrPath::moveTo(GrScalar x, GrScalar y) {
-    if (this->wasLastVerb(kMove_PathCmd)) {
-        // overwrite prev kMove value
-        fPts[fPts.count() - 1].set(x, y);
-    } else {
-        *fCmds.append() = kMove_PathCmd;
-        fPts.append()->set(x, y);
-    }
-    fConservativeBounds.growToInclude(x,y);
-}
-
-void GrPath::lineTo(GrScalar x, GrScalar y) {
-    this->ensureMoveTo();
-    *fCmds.append() = kLine_PathCmd;
-    fPts.append()->set(x, y);
-    fConservativeBounds.growToInclude(x,y);
-}
-
-void GrPath::quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1) {
-    this->ensureMoveTo();
-    *fCmds.append() = kQuadratic_PathCmd;
-    fPts.append()->set(x0, y0);
-    fPts.append()->set(x1, y1);
-    fConservativeBounds.growToInclude(x0,y0);
-    fConservativeBounds.growToInclude(x1,y1);
-}
-
-void GrPath::cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1,
-                     GrScalar x2, GrScalar y2) {
-    this->ensureMoveTo();
-    *fCmds.append() = kCubic_PathCmd;
-    fPts.append()->set(x0, y0);
-    fPts.append()->set(x1, y1);
-    fPts.append()->set(x2, y2);
-    fConservativeBounds.growToInclude(x0,y0);
-    fConservativeBounds.growToInclude(x1,y1);
-    fConservativeBounds.growToInclude(x2,y2);
-}
-
-void GrPath::close() {
-    if (!fCmds.isEmpty() && !this->wasLastVerb(kClose_PathCmd)) {
-        // should we allow kMove followed by kClose?
-        *fCmds.append() = kClose_PathCmd;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void GrPath::offset(GrScalar tx, GrScalar ty) {
-    if (!tx && !ty) {
-        return; // nothing to do
-    }
-
-    GrPoint* iter = fPts.begin();
-    GrPoint* stop = fPts.end();
-    while (iter < stop) {
-        iter->offset(tx, ty);
-        ++iter;
-    }
-    fConservativeBounds.offset(tx, ty);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static bool check_two_vecs(const GrVec& prevVec,
-                           const GrVec& currVec,
-                           GrScalar turnDir,
-                           int* xDir,
-                           int* yDir,
-                           int* flipX,
-                           int* flipY) {
-    if (currVec.fX * *xDir < 0) {
-        ++*flipX;
-        if (*flipX > 2) {
-            return false;
-        }
-        *xDir = -*xDir;
-    }
-    if (currVec.fY * *yDir < 0) {
-        ++*flipY;
-        if (*flipY > 2) {
-            return false;
-        }
-        *yDir = -*yDir;
-    }
-    GrScalar d = prevVec.cross(currVec);
-    return (d * turnDir) >= 0;
-}
-
-static void init_from_two_vecs(const GrVec& firstVec,
-                               const GrVec& secondVec,
-                               GrScalar* turnDir,
-                               int* xDir, int* yDir) {
-    *turnDir = firstVec.cross(secondVec);
-    if (firstVec.fX > 0) {
-        *xDir = 1;
-    } else if (firstVec.fX < 0) {
-        *xDir = -1;
-    } else {
-        *xDir = 0;
-    }
-    if (firstVec.fY > 0) {
-        *yDir = 1;
-    } else if (firstVec.fY < 0) {
-        *yDir = -1;
-    } else {
-        *yDir = 0;
-    }
-}
-
-void GrPath::resetFromIter(GrPathIter* iter) {
-    fPts.reset();
-    fCmds.reset();
-    fConservativeBounds.setLargestInverted();
-
-    fConvexHint = iter->convexHint();
-
-    // first point of the subpath
-    GrPoint firstPt = { 0, 0 };
-    // first edge of the subpath
-    GrVec firstVec = { 0, 0 };
-    // vec of most recently processed edge, that wasn't degenerate
-    GrVec previousVec = { 0, 0 };
-    // most recently processed point
-    GrPoint previousPt = { 0, 0 };
-
-    // sign indicates whether we're bending left or right
-    GrScalar turnDir = 0;
-    // number of times the direction has flipped in x or y
-
-    // we track which direction we are moving in x/y and the
-    // number of times it changes.
-    int xDir = 0;
-    int yDir = 0;
-    int flipX = 0;
-    int flipY = 0;
-
-    // counts number of sub path pts that didn't add a degenerate edge.
-    int subPathPts = 0;
-    bool subPathClosed = false;
-
-    int numSubPaths = 0;
-    iter->rewind();
-    GrPathCmd cmd;
-    GrPoint pts[4];
-    do {
-        cmd = iter->next(pts);
-        // If the convexity test is ever updated to handle multiple subpaths
-        // the loop has to be adjusted to handle moving to a new subpath without
-        // closing the previous one. Currently the implicit closing vectors for a
-        // filled path would never be examined.
-        switch (cmd) {
-            case kMove_PathCmd:
-                this->moveTo(pts[0].fX, pts[0].fY);
-                subPathPts = 0;
-                subPathClosed = false;
-                break;
-            case kLine_PathCmd:
-                this->lineTo(pts[1].fX, pts[1].fY);
-                break;
-            case kQuadratic_PathCmd:
-                this->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
-                break;
-            case kCubic_PathCmd:
-                this->cubicTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
-                              pts[3].fX, pts[3].fY);
-                break;
-            case kClose_PathCmd:
-                this->close();
-                subPathClosed = true;
-                break;
-            case kEnd_PathCmd:
-                break;
-        }
-        int n = NumPathCmdPoints(cmd);
-        for (int i = 0; i < n; ++i) {
-            fConservativeBounds.growToInclude(pts[i].fX, pts[i].fY);
-        }
-        if (0 == subPathPts && n > 0) {
-            previousPt = pts[0];
-            firstPt = previousPt;
-            flipX = 0;
-            flipY = 0;
-            turnDir = 0;
-            subPathPts = 1;
-            ++numSubPaths;
-        }
-        // either we skip the first pt because it is redundant with
-        // last point of the previous subpath cmd or we just ate it
-        // in the above if.
-        int consumed = 1;
-        if (numSubPaths < 2 && kNone_ConvexHint == fConvexHint) {
-            while (consumed < n) {
-                GrAssert(pts[consumed-1] == previousPt);
-                GrVec vec = pts[consumed] - previousPt;
-//                vec.setBetween(previousPt, pts[consumed]);
-                if (vec.fX || vec.fY) {
-                    if (subPathPts >= 2) {
-                        if (0 == turnDir) {
-                            firstVec = previousVec;
-                            init_from_two_vecs(firstVec, vec,
-                                               &turnDir, &xDir, &yDir);
-                            // here we aren't checking whether the x/y dirs
-                            // change between the first and second edge. It
-                            // gets covered when the path is closed.
-                        } else {
-                            if (!check_two_vecs(previousVec, vec, turnDir,
-                                                &xDir, &yDir,
-                                                &flipX, &flipY)) {
-                                fConvexHint = kConcave_ConvexHint;
-                                break;
-                            }
-                        }
-                    }
-                    previousVec = vec;
-                    previousPt = pts[consumed];
-                    ++subPathPts;
-                }
-                ++consumed;
-            }
-            if (subPathPts > 2 && (kClose_PathCmd == cmd ||
-                        (!subPathClosed && kEnd_PathCmd == cmd ))) {
-                // if an additional vector is needed to close the loop check
-                // that it validates against the previous vector.
-                GrVec vec = firstPt - previousPt;
-//                vec.setBetween(previousPt, firstPt);
-                if (vec.fX || vec.fY) {
-                    if (!check_two_vecs(previousVec, vec, turnDir,
-                                        &xDir, &yDir, &flipX, &flipY)) {
-                        fConvexHint = kConcave_ConvexHint;
-                        break;
-                    }
-                    previousVec = vec;
-                }
-                // check that closing vector validates against the first vector.
-                if (!check_two_vecs(previousVec, firstVec, turnDir,
-                                    &xDir, &yDir, &flipX, &flipY)) {
-                    fConvexHint = kConcave_ConvexHint;
-                    break;
-                }
-            }
-        }
-    } while (cmd != kEnd_PathCmd);
-    if (kNone_ConvexHint == fConvexHint && numSubPaths < 2) {
-        fConvexHint = kConvex_ConvexHint;
-    } else {
-        bool recurse = false;
-        if (recurse) {
-            this->resetFromIter(iter);
-        }
-    }
-}
-
-void GrPath::ConvexUnitTest() {
-    GrPath testPath;
-    GrPath::Iter testIter;
-
-    GrPath pt;
-    pt.moveTo(0, 0);
-    pt.close();
-
-    testIter.reset(pt);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
-    GrPath line;
-    line.moveTo(GrIntToScalar(12), GrIntToScalar(20));
-    line.lineTo(GrIntToScalar(-12), GrIntToScalar(-20));
-    line.close();
-
-    testIter.reset(line);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
-    GrPath triLeft;
-    triLeft.moveTo(0, 0);
-    triLeft.lineTo(1, 0);
-    triLeft.lineTo(1, 1);
-    triLeft.close();
-
-    testIter.reset(triLeft);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
-    GrPath triRight;
-    triRight.moveTo(0, 0);
-    triRight.lineTo(-1, 0);
-    triRight.lineTo(1, 1);
-    triRight.close();
-
-    testIter.reset(triRight);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
-    GrPath square;
-    square.moveTo(0, 0);
-    square.lineTo(1, 0);
-    square.lineTo(1, 1);
-    square.lineTo(0, 1);
-    square.close();
-
-    testIter.reset(square);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
-    GrPath redundantSquare;
-    square.moveTo(0, 0);
-    square.lineTo(0, 0);
-    square.lineTo(0, 0);
-    square.lineTo(1, 0);
-    square.lineTo(1, 0);
-    square.lineTo(1, 0);
-    square.lineTo(1, 1);
-    square.lineTo(1, 1);
-    square.lineTo(1, 1);
-    square.lineTo(0, 1);
-    square.lineTo(0, 1);
-    square.lineTo(0, 1);
-    square.close();
-
-    testIter.reset(redundantSquare);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
-    GrPath bowTie;
-    bowTie.moveTo(0, 0);
-    bowTie.lineTo(0, 0);
-    bowTie.lineTo(0, 0);
-    bowTie.lineTo(1, 1);
-    bowTie.lineTo(1, 1);
-    bowTie.lineTo(1, 1);
-    bowTie.lineTo(1, 0);
-    bowTie.lineTo(1, 0);
-    bowTie.lineTo(1, 0);
-    bowTie.lineTo(0, 1);
-    bowTie.lineTo(0, 1);
-    bowTie.lineTo(0, 1);
-    bowTie.close();
-
-    testIter.reset(bowTie);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
-
-    GrPath spiral;
-    spiral.moveTo(0, 0);
-    spiral.lineTo(1, 0);
-    spiral.lineTo(1, 1);
-    spiral.lineTo(0, 1);
-    spiral.lineTo(0,.5);
-    spiral.lineTo(.5,.5);
-    spiral.lineTo(.5,.75);
-    spiral.close();
-
-    testIter.reset(spiral);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
-
-    GrPath dent;
-    dent.moveTo(0, 0);
-    dent.lineTo(1, 1);
-    dent.lineTo(0, 1);
-    dent.lineTo(-.5,2);
-    dent.lineTo(-2, 1);
-    dent.close();
-
-    testIter.reset(dent);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
-}
-///////////////////////////////////////////////////////////////////////////////
-
-GrPath::Iter::Iter() : fPath(NULL) {
-}
-
-GrPath::Iter::Iter(const GrPath& path) : fPath(&path) {
-    this->rewind();
-}
-
-#ifdef SK_DEBUG
-static bool containsInclusive(const GrRect& rect, const GrPoint& point) {
-    return point.fX >= rect.fLeft && point.fX <= rect.fRight &&
-            point.fY >= rect.fTop && point.fY <= rect.fBottom;
-}
-#endif
-
-GrPathCmd GrPath::Iter::next(GrPoint points[]) {
-    if (fCmdIndex == fPath->fCmds.count()) {
-        GrAssert(fPtIndex == fPath->fPts.count());
-        return kEnd_PathCmd;
-    } else {
-        GrAssert(fCmdIndex < fPath->fCmds.count());
-    }
-
-    GrPathCmd cmd = fPath->fCmds[fCmdIndex++];
-    const GrPoint* srcPts = fPath->fPts.begin() + fPtIndex;
-
-    switch (cmd) {
-        case kMove_PathCmd:
-            if (points) {
-                points[0] = srcPts[0];
-            }
-            fLastPt = srcPts[0];
-            GrAssert(fPtIndex <= fPath->fPts.count() + 1);
-            GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0]));
-            fPtIndex += 1;
-            break;
-        case kLine_PathCmd:
-            if (points) {
-                points[0] = fLastPt;
-                points[1] = srcPts[0];
-            }
-            fLastPt = srcPts[0];
-            GrAssert(fPtIndex <= fPath->fPts.count() + 1);
-            GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0]));
-            fPtIndex += 1;
-            break;
-        case kQuadratic_PathCmd:
-            if (points) {
-                points[0] = fLastPt;
-                points[1] = srcPts[0];
-                points[2] = srcPts[1];
-            }
-            fLastPt = srcPts[1];
-            GrAssert(fPtIndex <= fPath->fPts.count() + 2);
-            GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0]));
-            GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[1]));
-            fPtIndex += 2;
-            break;
-        case kCubic_PathCmd:
-            if (points) {
-                points[0] = fLastPt;
-                points[1] = srcPts[0];
-                points[2] = srcPts[1];
-                points[3] = srcPts[2];
-            }
-            fLastPt = srcPts[2];
-            GrAssert(fPtIndex <= fPath->fPts.count() + 3);
-            GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0]));
-            GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[1]));
-            GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[2]));
-            fPtIndex += 3;
-            break;
-        case kClose_PathCmd:
-            break;
-        default:
-            GrAssert(!"unknown grpath cmd");
-            break;
-    }
-    return cmd;
-}
-
-GrConvexHint GrPath::Iter::convexHint() const {
-    return fPath->getConvexHint();
-}
-
-GrPathCmd GrPath::Iter::next() {
-    return this->next(NULL);
-}
-
-void GrPath::Iter::rewind() {
-    this->reset(*fPath);
-}
-
-void GrPath::Iter::reset(const GrPath& path) {
-    fPath = &path;
-    fCmdIndex = fPtIndex = 0;
-}
-
-bool GrPath::Iter::getConservativeBounds(GrRect* rect) const {
-    if (!fPath->getConservativeBounds().isEmpty()) {
-        *rect = fPath->getConservativeBounds();
-        return true;
-    }
-    return false;
-}
-
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index 317b7d3..58bb12e 100644
--- a/gpu/src/GrPathRenderer.cpp
+++ b/gpu/src/GrPathRenderer.cpp
@@ -2,7 +2,6 @@
 
 #include "GrPoint.h"
 #include "GrDrawTarget.h"
-#include "GrPathIter.h"
 #include "GrPathUtils.h"
 #include "GrMemory.h"
 #include "GrTexture.h"
@@ -147,20 +146,24 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Helpers for drawPath
 
+static GrConvexHint getConvexHint(const SkPath& path) {
+    return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint;
+}
+
 #define STENCIL_OFF     0   // Always disable stencil (even when needed)
 
 static inline bool single_pass_path(const GrDrawTarget& target,
-                                    const GrPathIter& path,
+                                    const GrPath& path,
                                     GrPathFill fill) {
 #if STENCIL_OFF
     return true;
 #else
     if (kEvenOdd_PathFill == fill) {
-        GrConvexHint hint = path.convexHint();
+        GrConvexHint hint = getConvexHint(path);
         return hint == kConvex_ConvexHint ||
                hint == kNonOverlappingConvexPieces_ConvexHint;
     } else if (kWinding_PathFill == fill) {
-        GrConvexHint hint = path.convexHint();
+        GrConvexHint hint = getConvexHint(path);
         return hint == kConvex_ConvexHint ||
                hint == kNonOverlappingConvexPieces_ConvexHint ||
                (hint == kSameWindingConvexPieces_ConvexHint &&
@@ -172,14 +175,14 @@
 }
 
 bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
-                                                GrPathIter* path, 
+                                                const GrPath& path, 
                                                 GrPathFill fill) const {
-    return !single_pass_path(*target, *path, fill);
+    return !single_pass_path(*target, path, fill);
 }
 
 void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
                                        GrDrawTarget::StageBitfield stages,
-                                       GrPathIter* path,
+                                       const GrPath& path,
                                        GrPathFill fill,
                                        const GrPoint* translate,
                                        bool stencilOnly) {
@@ -200,13 +203,10 @@
         // TODO: deal with perspective in some better way.
         tol /= 10;
     } else {
-        GrScalar sinv = GR_Scalar1 / stretch;
-        tol = GrMul(tol, sinv);
+        tol = GrScalarDiv(tol, stretch);
     }
     GrScalar tolSqd = GrMul(tol, tol);
 
-    path->rewind();
-
     int subpathCnt;
     int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
 
@@ -226,8 +226,6 @@
 
     GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
 
-    path->rewind();
-
     // TODO: use primitve restart if available rather than multiple draws
     GrPrimitiveType             type;
     int                         passCount = 0;
@@ -248,7 +246,7 @@
         drawFace[0] = GrDrawTarget::kBoth_DrawFace;
     } else {
         type = kTriangleFan_PrimitiveType;
-        if (single_pass_path(*target, *path, fill)) {
+        if (single_pass_path(*target, path, fill)) {
             passCount = 1;
             if (stencilOnly) {
                 passes[0] = &gDirectToStencil;
@@ -329,8 +327,10 @@
     bool first = true;
     int subpath = 0;
 
+    SkPath::Iter iter(path, false);
+
     for (;;) {
-        GrPathCmd cmd = path->next(pts);
+        GrPathCmd cmd = (GrPathCmd)iter.next(pts);
         switch (cmd) {
             case kMove_PathCmd:
                 if (!first) {
@@ -431,14 +431,14 @@
 
 void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
                                      GrDrawTarget::StageBitfield stages,
-                                     GrPathIter* path,
+                                     const GrPath& path,
                                      GrPathFill fill,
                                      const GrPoint* translate) {
     this->onDrawPath(target, stages, path, fill, translate, false);
 }
 
 void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
-                                              GrPathIter* path,
+                                              const GrPath& path,
                                               GrPathFill fill,
                                               const GrPoint* translate) {
     GrAssert(kInverseEvenOdd_PathFill != fill);
diff --git a/gpu/src/GrPathUtils.cpp b/gpu/src/GrPathUtils.cpp
index 115b0f6..8a72ba8 100644
--- a/gpu/src/GrPathUtils.cpp
+++ b/gpu/src/GrPathUtils.cpp
@@ -15,8 +15,6 @@
  */
 
 #include "GrPathUtils.h"
-
-#include "GrPathIter.h"
 #include "GrPoint.h"
 
 const GrScalar GrPathUtils::gTolerance = GR_Scalar1;
@@ -33,8 +31,8 @@
         // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
         // points.
         // 2^(log4(x)) = sqrt(x);
-        d = ceilf(sqrtf(d/tol));
-        return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE);
+        int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
+        return GrMin(GrNextPow2(temp), MAX_POINTS_PER_CURVE);
     }
 }
 
@@ -67,12 +65,12 @@
                                            GrScalar tol) {
     GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
                        points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
-    d = sqrtf(d);
+    d = SkScalarSqrt(d);
     if (d < tol) {
         return 1;
     } else {
-        d = ceilf(sqrtf(d/tol));
-        return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE);
+        int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
+        return GrMin(GrNextPow2(temp), MAX_POINTS_PER_CURVE);
     }
 }
 
@@ -106,18 +104,18 @@
     return a + b;
 }
 
-int GrPathUtils::worstCasePointCount(GrPathIter* path,
-                                        int* subpaths,
-                                        GrScalar tol) {
+int GrPathUtils::worstCasePointCount(const GrPath& path, int* subpaths,
+                                     GrScalar tol) {
     int pointCount = 0;
     *subpaths = 1;
 
     bool first = true;
 
+    SkPath::Iter iter(path, true);
     GrPathCmd cmd;
 
     GrPoint pts[4];
-    while ((cmd = path->next(pts)) != kEnd_PathCmd) {
+    while ((cmd = (GrPathCmd)iter.next(pts)) != kEnd_PathCmd) {
 
         switch (cmd) {
             case kLine_PathCmd:
diff --git a/gpu/src/GrPathUtils.h b/gpu/src/GrPathUtils.h
index af05682..2cd00cb 100644
--- a/gpu/src/GrPathUtils.h
+++ b/gpu/src/GrPathUtils.h
@@ -19,15 +19,14 @@
 
 #include "GrNoncopyable.h"
 #include "GrPoint.h"
-
-class GrPathIter;
+#include "GrPath.h"
 
 /**
  *  Utilities for evaluating paths.
  */
 class GrPathUtils : public GrNoncopyable {
 public:
-    static int worstCasePointCount(GrPathIter* path,
+    static int worstCasePointCount(const GrPath&,
                                    int* subpaths,
                                    GrScalar tol);
     static uint32_t quadraticPointCount(const GrPoint points[], GrScalar tol);
diff --git a/gpu/src/GrStencil.cpp b/gpu/src/GrStencil.cpp
index a537e16..edf83fe 100644
--- a/gpu/src/GrStencil.cpp
+++ b/gpu/src/GrStencil.cpp
@@ -16,7 +16,14 @@
 
 #include "GrStencil.h"
 
-const GrStencilSettings GrStencilSettings::gDisabled = {};
+const GrStencilSettings GrStencilSettings::gDisabled = {
+    kKeep_StencilOp,     kKeep_StencilOp,
+    kKeep_StencilOp,     kKeep_StencilOp,
+    kAlways_StencilFunc, kAlways_StencilFunc,
+    0x0,                 0x0,
+    0x0,                 0x0,
+    0x0,                 0x0
+};
 GR_STATIC_ASSERT(0 == kKeep_StencilOp);
 GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
 
diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp
index 3993adb..0e3389c 100644
--- a/gpu/src/GrTesselatedPathRenderer.cpp
+++ b/gpu/src/GrTesselatedPathRenderer.cpp
@@ -18,8 +18,10 @@
 
 #include "GrMemory.h"
 #include "GrPathUtils.h"
+#include "GrPoint.h"
+#include "GrTDArray.h"
 
-#include <internal_glu.h>
+#include <sk_glu.h>
 
 struct PolygonData {
     PolygonData(GrTDArray<GrPoint>* vertices, GrTDArray<short>* indices)
@@ -83,24 +85,12 @@
 GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
 }
 
-class Edge {
-  public:
-    Edge() {}
-    Edge(float x, float y, float z) : fX(x), fY(y), fZ(z) {}
-    GrPoint intersect(const Edge& other) {
-        return GrPoint::Make(
-            (fY * other.fZ - other.fY * fZ) / (fX * other.fY - other.fX * fY),
-            (fX * other.fZ - other.fX * fZ) / (other.fX * fY - fX * other.fY));
-    }
-    float fX, fY, fZ;
-};
+typedef GrTDArray<GrDrawTarget::Edge> EdgeArray;
 
-typedef GrTDArray<Edge> EdgeArray;
-
-bool isCCW(const GrPoint* v)
+bool isCCW(const GrPoint* pts)
 {
-    GrVec v1 = v[1] - v[0];
-    GrVec v2 = v[2] - v[1];
+    GrVec v1 = pts[1] - pts[0];
+    GrVec v2 = pts[2] - pts[1];
     return v1.cross(v2) < 0;
 }
 
@@ -110,25 +100,24 @@
                                             size_t numVertices,
                                             EdgeArray* edges)
 {
+    matrix.mapPoints(vertices, numVertices);
     GrPoint p = vertices[numVertices - 1];
-    matrix.mapPoints(&p, 1);
     float sign = isCCW(vertices) ? -1.0f : 1.0f;
     for (size_t i = 0; i < numVertices; ++i) {
         GrPoint q = vertices[i];
-        matrix.mapPoints(&q, 1);
         if (p == q) continue;
         GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
         float scale = sign / tangent.length();
         float cross2 = p.fX * q.fY - q.fX * p.fY;
-        Edge edge(tangent.fX * scale,
+        GrDrawTarget::Edge edge(tangent.fX * scale,
                   tangent.fY * scale,
                   cross2 * scale + 0.5f);
         *edges->append() = edge;
         p = q;
     }
-    Edge prev_edge = *edges->back();
-    for (size_t i = 0; i < edges->count(); ++i) {
-        Edge edge = edges->at(i);
+    GrDrawTarget::Edge prev_edge = *edges->back();
+    for (int i = 0; i < edges->count(); ++i) {
+        GrDrawTarget::Edge edge = edges->at(i);
         vertices[i] = prev_edge.intersect(edge);
         inverse.mapPoints(&vertices[i], 1);
         prev_edge = edge;
@@ -138,11 +127,10 @@
 
 void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
                                         GrDrawTarget::StageBitfield stages,
-                                        GrPathIter* path,
+                                        const GrPath& path,
                                         GrPathFill fill,
                                         const GrPoint* translate) {
     GrDrawTarget::AutoStateRestore asr(target);
-    bool colorWritesWereDisabled = target->isColorWriteDisabled();
     // face culling doesn't make sense here
     GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
 
@@ -157,13 +145,10 @@
         // TODO: deal with perspective in some better way.
         tol /= 10;
     } else {
-        GrScalar sinv = GR_Scalar1 / stretch;
-        tol = GrMul(tol, sinv);
+        tol = GrScalarDiv(tol, stretch);
     }
     GrScalar tolSqd = GrMul(tol, tol);
 
-    path->rewind();
-
     int subpathCnt;
     int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
 
@@ -185,16 +170,14 @@
 
     GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
 
-    path->rewind();
-
     GrPoint pts[4];
+    SkPath::Iter iter(path, true);
 
     bool first = true;
     int subpath = 0;
 
     for (;;) {
-        GrPathCmd cmd = path->next(pts);
-        switch (cmd) {
+        switch (iter.next(pts)) {
             case kMove_PathCmd:
                 if (!first) {
                     subpathVertCount[subpath] = vert-subpathBase;
@@ -263,31 +246,32 @@
       return;
     }
 
-    if (subpathCnt == 1 && !inverted && path->convexHint() == kConvex_ConvexHint) {
+    if (subpathCnt == 1 && !inverted && path.isConvex()) {
         if (target->isAntialiasState()) {
-            target->enableState(GrDrawTarget::kEdgeAA_StateBit);
             EdgeArray edges;
             GrMatrix inverse, matrix = target->getViewMatrix();
             target->getViewInverse(&inverse);
 
             count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges);
-            GrPoint triangle[3];
-            triangle[0] = base[0];
-            Edge triangleEdges[6];
-            triangleEdges[0] = *edges.back();
-            triangleEdges[1] = edges[0];
-            for (size_t i = 1; i < count - 1; i++) {
-                triangle[1] = base[i];
-                triangle[2] = base[i + 1];
-                triangleEdges[2] = edges[i - 1];
-                triangleEdges[3] = edges[i];
-                triangleEdges[4] = edges[i];
-                triangleEdges[5] = edges[i + 1];
-                target->setVertexSourceToArray(layout, triangle, 3);
-                target->setEdgeAAData(&triangleEdges[0].fX);
-                target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
+            size_t maxEdges = target->getMaxEdges();
+            if (count <= maxEdges) {
+                // All edges fit; upload all edges and draw all verts as a fan
+                target->setVertexSourceToArray(layout, base, count);
+                target->setEdgeAAData(&edges[0], count);
+                target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
+            } else {
+                // Upload "maxEdges" edges and verts at a time, and draw as
+                // separate fans
+                for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
+                    edges[i] = edges[0];
+                    base[i] = base[0];
+                    int size = GR_CT_MIN(count - i, maxEdges);
+                    target->setVertexSourceToArray(layout, &base[i], size);
+                    target->setEdgeAAData(&edges[i], size);
+                    target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
+                }
             }
-            target->disableState(GrDrawTarget::kEdgeAA_StateBit);
+            target->setEdgeAAData(NULL, 0);
         } else {
             target->setVertexSourceToArray(layout, base, count);
             target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
@@ -305,34 +289,34 @@
         inVertices[i * 3 + 2] = 1.0;
     }
 
-    GLUtesselator* tess = internal_gluNewTess();
+    GLUtesselator* tess = Sk_gluNewTess();
     unsigned windingRule = fill_type_to_glu_winding_rule(fill);
-    internal_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
-    internal_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
-    internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
-    internal_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
-    internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
-    internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
+    Sk_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
+    Sk_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
+    Sk_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
+    Sk_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
+    Sk_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
+    Sk_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
     GrTDArray<short> indices;
     GrTDArray<GrPoint> vertices;
     PolygonData data(&vertices, &indices);
 
-    internal_gluTessBeginPolygon(tess, &data);
+    Sk_gluTessBeginPolygon(tess, &data);
     size_t i = 0;
     for (int sp = 0; sp < subpathCnt; ++sp) {
-        internal_gluTessBeginContour(tess);
+        Sk_gluTessBeginContour(tess);
         int start = i;
-        int end = start + subpathVertCount[sp];
+        size_t end = start + subpathVertCount[sp];
         for (; i < end; ++i) {
             double* inVertex = &inVertices[i * 3];
             *vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]);
-            internal_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
+            Sk_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
         }
-        internal_gluTessEndContour(tess);
+        Sk_gluTessEndContour(tess);
     }
 
-    internal_gluTessEndPolygon(tess);
-    internal_gluDeleteTess(tess);
+    Sk_gluTessEndPolygon(tess);
+    Sk_gluDeleteTess(tess);
 
     if (indices.count() > 0) {
         target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
@@ -348,25 +332,25 @@
 }
 
 bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
-                                           GrPathIter* path,
+                                           const SkPath& path,
                                            GrPathFill fill) const {
     return kHairLine_PathFill != fill;
 }
 
 void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
-                                                 GrPathIter* path,
+                                                 const SkPath& path,
                                                  GrPathFill fill,
                                                  const GrPoint* translate) {
     GrAlwaysAssert(!"multipass stencil should not be needed");
 }
 
 bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
-                                                  GrPathIter* path,
+                                                  const SkPath& path,
                                                   GrPathFill fill) {
     int subpathCnt = 0;
     int tol = GrPathUtils::gTolerance;
     GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
     return (subpathCnt == 1 &&
             !IsFillInverted(fill) &&
-            path->convexHint() == kConvex_ConvexHint);
+            path.isConvex());
 }
diff --git a/gpu/src/GrTextContext.cpp b/gpu/src/GrTextContext.cpp
index 0222042..2aacfa2 100644
--- a/gpu/src/GrTextContext.cpp
+++ b/gpu/src/GrTextContext.cpp
@@ -25,11 +25,9 @@
 #include "GrGpuVertex.h"
 #include "GrDrawTarget.h"
 
-static const int TEXT_STAGE = 1;
-
-static const GrVertexLayout BASE_VLAYOUT =
-                    GrDrawTarget::kTextFormat_VertexLayoutBit |
-                    GrDrawTarget::StageTexCoordVertexLayoutBit(TEXT_STAGE,0);
+enum {
+    kGlyphMaskStage = GrPaint::kTotalStages,
+};
 
 void GrTextContext::flushGlyphs() {
     if (fCurrVertex > 0) {
@@ -45,17 +43,17 @@
         GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
                                GrSamplerState::kRepeat_WrapMode,
                                filter);
-        fDrawTarget->setSamplerState(TEXT_STAGE, sampler);
+        fDrawTarget->setSamplerState(kGlyphMaskStage, sampler);
 
         GrAssert(GrIsALIGN4(fCurrVertex));
         int nIndices = fCurrVertex + (fCurrVertex >> 1);
         GrAssert(fCurrTexture);
-        fDrawTarget->setTexture(TEXT_STAGE, fCurrTexture);
+        fDrawTarget->setTexture(kGlyphMaskStage, fCurrTexture);
 
         if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
             if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
                 kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
-                NULL != fPaint.getTexture()) {
+                fPaint.hasTexture()) {
                 GrPrintf("LCD Text will not draw correctly.\n");
             }
             // setup blend so that we get mask * paintColor + (1-mask)*dstColor
@@ -117,18 +115,31 @@
     fOrigViewMatrix = fContext->getMatrix();
     fContext->setMatrix(fExtMatrix);
 
-    fVertexLayout = BASE_VLAYOUT;
-    if (NULL != paint.getTexture()) {
-        fVertexLayout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
-        GrMatrix inverseViewMatrix;
-        if (fOrigViewMatrix.invert(&inverseViewMatrix)) {
-            fPaint.fSampler.preConcatMatrix(inverseViewMatrix);
-        }
-    }
+    fDrawTarget = fContext->getTextTarget(fPaint);
 
     fVertices = NULL;
     fMaxVertices = 0;
-    fDrawTarget = fContext->getTextTarget(fPaint);
+
+    fVertexLayout = 
+        GrDrawTarget::kTextFormat_VertexLayoutBit |
+        GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
+
+    int stageMask = paint.getActiveStageMask();
+    if (stageMask) {
+        GrMatrix inverseViewMatrix;
+        if (fOrigViewMatrix.invert(&inverseViewMatrix)) {
+            fDrawTarget->preConcatSamplerMatrices(stageMask, 
+                                                  inverseViewMatrix);
+        }
+        for (int i = 0; i < GrPaint::kTotalStages; ++i) {
+            if ((1 << i) & stageMask) {
+                fVertexLayout |= 
+                    GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
+                GrAssert(i != kGlyphMaskStage);
+            }
+        }
+    }
+
 }
 
 GrTextContext::~GrTextContext() {
@@ -201,11 +212,11 @@
             }
             glyph->fPath = path;
         }
-        GrPath::Iter iter(*glyph->fPath);
+
         GrPoint translate;
         translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
                       GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
-        fContext->drawPath(fPaint, &iter, kWinding_PathFill,
+        fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
                            &translate);
         return;
     }
diff --git a/gpu/src/gr_files.mk b/gpu/src/gr_files.mk
index fef9784..bd6f061 100644
--- a/gpu/src/gr_files.mk
+++ b/gpu/src/gr_files.mk
@@ -19,10 +19,10 @@
     GrInOrderDrawBuffer.cpp \
     GrMatrix.cpp \
     GrMemory.cpp \
-    GrPath.cpp \
     GrPathUtils.cpp \
     GrRectanizer_fifo.cpp \
     GrResource.cpp \
+    GrTesselatedPathRenderer.cpp \
     GrTexture.cpp \
     GrTextureCache.cpp \
     GrTextContext.cpp \
diff --git a/gpu/src/gr_unittests.cpp b/gpu/src/gr_unittests.cpp
index 320dd15..338f2cc 100644
--- a/gpu/src/gr_unittests.cpp
+++ b/gpu/src/gr_unittests.cpp
@@ -68,7 +68,7 @@
     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 == i);
+            GrAssert(index == (int) i);
             index = GrTBSearch<int, int>(array, n, -array[i]);
             GrAssert(index < 0);
         }
@@ -80,10 +80,10 @@
 
 static void test_binHashKey()
 {
-    const char* testStringA = "abcdA";
-    const char* testStringB = "abcdB";
+    const char* testStringA = "abcdABCD";
+    const char* testStringB = "abcdBBCD";
     enum {
-        kDataLenUsedForKey = 5
+        kDataLenUsedForKey = 8
     };
 
     typedef GrBinHashKey<BogusEntry, kDataLenUsedForKey> KeyType;
@@ -92,7 +92,7 @@
     int passCnt = 0;
     while (keyA.doPass()) {
         ++passCnt;
-        keyA.keyData(reinterpret_cast<const uint8_t*>(testStringA), kDataLenUsedForKey);
+        keyA.keyData(reinterpret_cast<const uint32_t*>(testStringA), kDataLenUsedForKey);
     }
     GrAssert(passCnt == 1); //We expect the static allocation to suffice
     GrBinHashKey<BogusEntry, kDataLenUsedForKey-1> keyBust;
@@ -100,7 +100,7 @@
     while (keyBust.doPass()) {
         ++passCnt;
         // Exceed static storage by 1
-        keyBust.keyData(reinterpret_cast<const uint8_t*>(testStringA), kDataLenUsedForKey);
+        keyBust.keyData(reinterpret_cast<const uint32_t*>(testStringA), kDataLenUsedForKey);
     }
     GrAssert(passCnt == 2); //We expect dynamic allocation to be necessary
     GrAssert(keyA.getHash() == keyBust.getHash());
@@ -109,14 +109,14 @@
     // the same hash as with one chunk
     KeyType keyA2;
     while (keyA2.doPass()) {
-        keyA2.keyData(reinterpret_cast<const uint8_t*>(testStringA), 2);
-        keyA2.keyData(&reinterpret_cast<const uint8_t*>(testStringA)[2], kDataLenUsedForKey-2);
+        keyA2.keyData(reinterpret_cast<const uint32_t*>(testStringA), 4);
+        keyA2.keyData(&reinterpret_cast<const uint32_t*>(testStringA)[4], kDataLenUsedForKey-4);
     }
     GrAssert(keyA.getHash() == keyA2.getHash());
 
     KeyType keyB;
     while (keyB.doPass()){
-        keyB.keyData(reinterpret_cast<const uint8_t*>(testStringB), kDataLenUsedForKey);
+        keyB.keyData(reinterpret_cast<const uint32_t*>(testStringB), kDataLenUsedForKey);
     }
     GrAssert(keyA.compare(keyB) < 0);
     GrAssert(keyA.compare(keyA2) == 0);
@@ -148,13 +148,130 @@
     GrAssert(keyBust3.compare(keyBust2) == 0);
 }
 
+static void test_convex() {
+#if 0
+    GrPath testPath;
+    GrPath::Iter testIter;
+    
+    GrPath pt;
+    pt.moveTo(0, 0);
+    pt.close();
+    
+    testIter.reset(pt);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+    
+    GrPath line;
+    line.moveTo(GrIntToScalar(12), GrIntToScalar(20));
+    line.lineTo(GrIntToScalar(-12), GrIntToScalar(-20));
+    line.close();
+    
+    testIter.reset(line);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+    
+    GrPath triLeft;
+    triLeft.moveTo(0, 0);
+    triLeft.lineTo(1, 0);
+    triLeft.lineTo(1, 1);
+    triLeft.close();
+    
+    testIter.reset(triLeft);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+    
+    GrPath triRight;
+    triRight.moveTo(0, 0);
+    triRight.lineTo(-1, 0);
+    triRight.lineTo(1, 1);
+    triRight.close();
+    
+    testIter.reset(triRight);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+    
+    GrPath square;
+    square.moveTo(0, 0);
+    square.lineTo(1, 0);
+    square.lineTo(1, 1);
+    square.lineTo(0, 1);
+    square.close();
+    
+    testIter.reset(square);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+    
+    GrPath redundantSquare;
+    square.moveTo(0, 0);
+    square.lineTo(0, 0);
+    square.lineTo(0, 0);
+    square.lineTo(1, 0);
+    square.lineTo(1, 0);
+    square.lineTo(1, 0);
+    square.lineTo(1, 1);
+    square.lineTo(1, 1);
+    square.lineTo(1, 1);
+    square.lineTo(0, 1);
+    square.lineTo(0, 1);
+    square.lineTo(0, 1);
+    square.close();
+    
+    testIter.reset(redundantSquare);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+    
+    GrPath bowTie;
+    bowTie.moveTo(0, 0);
+    bowTie.lineTo(0, 0);
+    bowTie.lineTo(0, 0);
+    bowTie.lineTo(1, 1);
+    bowTie.lineTo(1, 1);
+    bowTie.lineTo(1, 1);
+    bowTie.lineTo(1, 0);
+    bowTie.lineTo(1, 0);
+    bowTie.lineTo(1, 0);
+    bowTie.lineTo(0, 1);
+    bowTie.lineTo(0, 1);
+    bowTie.lineTo(0, 1);
+    bowTie.close();
+    
+    testIter.reset(bowTie);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+    
+    GrPath spiral;
+    spiral.moveTo(0, 0);
+    spiral.lineTo(1, 0);
+    spiral.lineTo(1, 1);
+    spiral.lineTo(0, 1);
+    spiral.lineTo(0,.5);
+    spiral.lineTo(.5,.5);
+    spiral.lineTo(.5,.75);
+    spiral.close();
+    
+    testIter.reset(spiral);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+    
+    GrPath dent;
+    dent.moveTo(0, 0);
+    dent.lineTo(1, 1);
+    dent.lineTo(0, 1);
+    dent.lineTo(-.5,2);
+    dent.lineTo(-2, 1);
+    dent.close();
+    
+    testIter.reset(dent);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+#endif
+}
+
 void gr_run_unittests() {
     test_tdarray();
     test_bsearch();
     test_binHashKey();
+    test_convex();
     GrRedBlackTree<int>::UnitTest();
-    GrPath::ConvexUnitTest();
     GrDrawTarget::VertexLayoutUnitTest();
 }
-
-
diff --git a/gpu/src/mac/GrGLDefaultInterface_mac.cpp b/gpu/src/mac/GrGLDefaultInterface_mac.cpp
index b9396fa..fb5b182 100644
--- a/gpu/src/mac/GrGLDefaultInterface_mac.cpp
+++ b/gpu/src/mac/GrGLDefaultInterface_mac.cpp
@@ -155,6 +155,8 @@
         gDefaultInterface.fBlitFramebuffer = glBlitFramebufferEXT;
     #endif
 #endif
+        gDefaultInterface.fBindFragDataLocationIndexed = NULL;
+
         gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding;
 
         gDefaultInterfaceInit = true;
diff --git a/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp b/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp
new file mode 100644
index 0000000..0350c30
--- /dev/null
+++ b/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp
@@ -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.
+ */
+
+#include "GrGLInterface.h"
+
+#include "GL/osmesa.h"
+#include <GL/glext.h>
+#include <GL/glu.h>
+
+#define GR_GL_GET_PROC(F) gDefaultInterface.f ## F = (GrGL ## F ## Proc) \
+        OSMesaGetProcAddress("gl" #F);
+#define GR_GL_GET_PROC_SUFFIX(F, S) gDefaultInterface.f ## F = (GrGL ## F ## Proc) \
+        OSMesaGetProcAddress("gl" #F #S);
+
+void GrGLSetDefaultGLInterface() {
+    static GrGLInterface gDefaultInterface;
+    static bool gDefaultInterfaceInit;
+    if (!gDefaultInterfaceInit && NULL != OSMesaGetCurrentContext()) {
+        int major, minor;
+        const char* versionString = (const char*) glGetString(GL_VERSION);
+        const char* extString = (const char*) glGetString(GL_EXTENSIONS);
+        gl_version_from_string(&major, &minor, versionString);
+
+        if (major == 1 && minor < 5) {
+            // We must have array and element_array buffer objects.
+            return;
+        }
+
+        gDefaultInterface.fActiveTexture = glActiveTexture;
+        GR_GL_GET_PROC(AttachShader);
+        GR_GL_GET_PROC(BindAttribLocation);
+        GR_GL_GET_PROC(BindBuffer);
+        gDefaultInterface.fBindTexture = glBindTexture;
+        gDefaultInterface.fBlendColor = glBlendColor;
+        gDefaultInterface.fBlendFunc = glBlendFunc;
+        GR_GL_GET_PROC(BufferData);
+        GR_GL_GET_PROC(BufferSubData);
+        gDefaultInterface.fClear = glClear;
+        gDefaultInterface.fClearColor = glClearColor;
+        gDefaultInterface.fClearStencil = glClearStencil;
+        gDefaultInterface.fClientActiveTexture = glClientActiveTexture;
+        gDefaultInterface.fColorMask = glColorMask;
+        gDefaultInterface.fColorPointer = glColorPointer;
+        gDefaultInterface.fColor4ub = glColor4ub;
+        GR_GL_GET_PROC(CompileShader);
+        gDefaultInterface.fCompressedTexImage2D = glCompressedTexImage2D;
+        GR_GL_GET_PROC(CreateProgram);
+        GR_GL_GET_PROC(CreateShader);
+        gDefaultInterface.fCullFace = glCullFace;
+        GR_GL_GET_PROC(DeleteBuffers);
+        GR_GL_GET_PROC(DeleteProgram);
+        GR_GL_GET_PROC(DeleteShader);
+        gDefaultInterface.fDeleteTextures = glDeleteTextures;
+        gDefaultInterface.fDepthMask = glDepthMask;
+        gDefaultInterface.fDisable = glDisable;
+        gDefaultInterface.fDisableClientState = glDisableClientState;
+        GR_GL_GET_PROC(DisableVertexAttribArray);
+        gDefaultInterface.fDrawArrays = glDrawArrays;
+        gDefaultInterface.fDrawElements = glDrawElements;
+        gDefaultInterface.fEnable = glEnable;
+        gDefaultInterface.fEnableClientState = glEnableClientState;
+        GR_GL_GET_PROC(EnableVertexAttribArray);
+        gDefaultInterface.fFrontFace = glFrontFace;
+        GR_GL_GET_PROC(GenBuffers);
+        GR_GL_GET_PROC(GetBufferParameteriv);
+        gDefaultInterface.fGetError = glGetError;
+        gDefaultInterface.fGetIntegerv = glGetIntegerv;
+        GR_GL_GET_PROC(GetProgramInfoLog);
+        GR_GL_GET_PROC(GetProgramiv);
+        GR_GL_GET_PROC(GetShaderInfoLog);
+        GR_GL_GET_PROC(GetShaderiv);
+        gDefaultInterface.fGetString = glGetString;
+        gDefaultInterface.fGenTextures = glGenTextures;
+        GR_GL_GET_PROC(GetUniformLocation);
+        gDefaultInterface.fLineWidth = glLineWidth;
+        GR_GL_GET_PROC(LinkProgram);
+        gDefaultInterface.fLoadMatrixf = glLoadMatrixf;
+        GR_GL_GET_PROC(MapBuffer);
+        gDefaultInterface.fMatrixMode = glMatrixMode;
+        gDefaultInterface.fPointSize = glPointSize;
+        gDefaultInterface.fPixelStorei = glPixelStorei;
+        gDefaultInterface.fReadPixels = glReadPixels;
+        gDefaultInterface.fScissor = glScissor;
+        gDefaultInterface.fShadeModel = glShadeModel;
+        GR_GL_GET_PROC(ShaderSource);
+        gDefaultInterface.fStencilFunc = glStencilFunc;
+        GR_GL_GET_PROC(StencilFuncSeparate);
+        gDefaultInterface.fStencilMask = glStencilMask;
+        GR_GL_GET_PROC(StencilMaskSeparate);
+        gDefaultInterface.fStencilOp = glStencilOp;
+        GR_GL_GET_PROC(StencilOpSeparate);
+        gDefaultInterface.fTexCoordPointer = glTexCoordPointer;
+        gDefaultInterface.fTexEnvi = glTexEnvi;
+        //OSMesa on Mac's glTexImage2D takes a GLenum for internalFormat rather than a GLint.
+        gDefaultInterface.fTexImage2D = reinterpret_cast<GrGLTexImage2DProc>(glTexImage2D);
+        gDefaultInterface.fTexParameteri = glTexParameteri;
+        gDefaultInterface.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);
+        gDefaultInterface.fVertexPointer = glVertexPointer;
+        gDefaultInterface.fViewport = glViewport;
+
+        // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
+        // GL_ARB_framebuffer_object doesn't use ARB suffix.)
+        if (major >= 3 || has_gl_extension_from_string(
+                "GL_ARB_framebuffer_object", extString)) {
+            GR_GL_GET_PROC(GenFramebuffers);
+            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 (has_gl_extension_from_string("GL_EXT_framebuffer_object",
+                                                extString)) {
+            GR_GL_GET_PROC_SUFFIX(GenFramebuffers, 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 (has_gl_extension_from_string("GL_EXT_framebuffer_multisample",
+                                             extString)) {
+                GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisample, EXT);
+            }
+            if (has_gl_extension_from_string("GL_EXT_framebuffer_blit",
+                                             extString)) {
+                GR_GL_GET_PROC_SUFFIX(BlitFramebuffer, EXT);
+            }
+        } else {
+            // we must have FBOs
+            return;
+        }
+        GR_GL_GET_PROC(BindFragDataLocationIndexed);
+        gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding;
+
+        gDefaultInterfaceInit = true;
+    }
+    if (gDefaultInterfaceInit)
+        GrGLSetGLInterface(&gDefaultInterface);
+}
diff --git a/gpu/src/unix/GrGLDefaultInterface_unix.cpp b/gpu/src/unix/GrGLDefaultInterface_unix.cpp
index ba67065..3e9b975 100644
--- a/gpu/src/unix/GrGLDefaultInterface_unix.cpp
+++ b/gpu/src/unix/GrGLDefaultInterface_unix.cpp
@@ -133,6 +133,7 @@
         GR_GL_GET_PROC(VertexAttribPointer);
         gDefaultInterface.fVertexPointer = glVertexPointer;
         gDefaultInterface.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.)
diff --git a/gpu/src/win/GrGLDefaultInterface_win.cpp b/gpu/src/win/GrGLDefaultInterface_win.cpp
index 53fb26a..428abb1 100644
--- a/gpu/src/win/GrGLDefaultInterface_win.cpp
+++ b/gpu/src/win/GrGLDefaultInterface_win.cpp
@@ -139,6 +139,7 @@
             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.)
diff --git a/gyp/common.gypi b/gyp/common.gypi
deleted file mode 100644
index aadaffb..0000000
--- a/gyp/common.gypi
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright (C) 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.
-{
-  'conditions' : [
-    ['OS == "win"',
-      {
-        'target_defaults': {
-          'msvs_cygwin_shell': 0,
-          'msvs_settings': {
-            'VCCLCompilerTool': {
-              'WarningLevel': '1',
-              'WarnAsError': 'false',
-              'DebugInformationFormat': '3',
-              'AdditionalOptions': '/MP',
-            },
-          },
-          'configurations': {
-            'Debug': {
-              'msvs_settings': {
-                'VCCLCompilerTool': {
-                  'Optimization': '0',    # 0 = /Od
-                  'PreprocessorDefinitions': ['_DEBUG'],
-                  'RuntimeLibrary': '3',  # 3 = /MDd (debug DLL)
-                },
-                'VCLinkerTool': {
-                  'GenerateDebugInformation': 'true',
-                },
-              },
-            },
-            'Release': {
-              'msvs_settings': {
-                'VCCLCompilerTool': {
-                  'Optimization': '2',    # 2 = /Os
-                  'PreprocessorDefinitions': ['NDEBUG'],
-                  'RuntimeLibrary': '2',  # 2 = /MD (nondebug DLL)
-                },
-                'VCLinkerTool': {
-                  'GenerateDebugInformation': 'false',
-                },
-              },
-            },
-          },
-        },
-      },
-    ],
-    ['OS == "linux"', 
-      {
-        'target_defaults': {
-          'configurations': {
-            'Debug': {
-              'cflags': ['-g']
-            },
-            'Release': {
-              'cflags': ['-O2']
-            },
-          },
-        },
-      },
-    ],
-    ['OS == "mac"', 
-      {
-        'target_defaults': {
-          'configurations': {
-            'Debug': {
-              'cflags': ['-g']
-            },
-            'Release': {
-              'cflags': ['-O2']
-            },
-          },
-        },
-        '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/gyp_skia b/gyp/gyp_skia
deleted file mode 100755
index 72b4879..0000000
--- a/gyp/gyp_skia
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (C) 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.
-
-# This script is a wrapper which invokes gyp with the correct --depth argument,
-# and supports the automatic regeneration of build files if skia.gyp is 
-# changed (Linux-only).
-
-import glob
-import os
-import shlex
-import sys
-
-script_dir = os.path.dirname(__file__)
-
-gyp_dir = os.path.normpath(os.path.join(script_dir, os.pardir, 'third_party'))
-
-sys.path.append(os.path.join(gyp_dir, 'gyp', '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
-  AddInclude(os.path.join(script_dir, 'common.gypi'))
-
-  return result
-
-if __name__ == '__main__':
-  args = sys.argv[1:]
-
-  # 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.
-  if not gyp_file_specified:
-    args.append(os.path.join(script_dir, 'skia.gyp'))
-
-  args.extend(['-I' + i for i in additional_include_files(args)])
-
-  args.extend(['--depth'])
-  args.extend([os.path.abspath(script_dir)])
-  print 'Updating projects from gyp files...'
-  sys.stdout.flush()
-
-  # Off we go...
-  sys.exit(gyp.main(args))
diff --git a/gyp/skia.gyp b/gyp/skia.gyp
deleted file mode 100644
index 7247828..0000000
--- a/gyp/skia.gyp
+++ /dev/null
@@ -1,1589 +0,0 @@
-{
-  'target_defaults': {
-    'configurations': {
-      'Debug': {
-        'defines': [
-          'SK_DEBUG',
-          'GR_DEBUG=1',
-        ],
-      },
-      'Release': {
-        'defines': [
-          'SK_RELEASE',
-          'GR_RELEASE=1',
-        ],
-      },
-    },
-    'conditions': [
-      [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
-        'include_dirs' : [
-          '/usr/include/freetype2',
-        ],
-      }],
-      [ 'OS == "mac"', {
-        'defines': [
-          'SK_BUILD_FOR_MAC',
-        ],
-      }],
-      [ 'OS == "win"', {
-        'defines': [
-          'SK_BUILD_FOR_WIN32',
-          'SK_IGNORE_STDINT_DOT_H',
-        ],
-      }],
-      [ 'OS == "linux"', {
-        'defines': [
-          'SK_SAMPLES_FOR_X',
-        ],
-      }],
-    ],
-    'direct_dependent_settings': {
-      'conditions': [
-        [ 'OS == "mac"', {
-          'defines': [
-            'SK_BUILD_FOR_MAC',
-          ],
-        }],
-        [ 'OS == "win"', {
-          'defines': [
-            'SK_BUILD_FOR_WIN32',
-          ],
-        }],
-      ],
-    },
-  },
-  'targets': [
-    {
-      'target_name': 'skia',
-      'type': 'static_library',
-      'msvs_guid': 'B7760B5E-BFA8-486B-ACFD-49E3A6DE8E76',
-      'sources': [
-        '../src/core/ARGB32_Clamp_Bilinear_BitmapShader.h',
-        '../src/core/Sk64.cpp',
-        '../src/core/SkAdvancedTypefaceMetrics.cpp',
-        '../src/core/SkAlphaRuns.cpp',
-        '../src/core/SkAntiRun.h',
-        '../src/core/SkBitmap.cpp',
-        '../src/core/SkBitmapProcShader.cpp',
-        '../src/core/SkBitmapProcShader.h',
-        '../src/core/SkBitmapProcState.cpp',
-        '../src/core/SkBitmapProcState.h',
-        '../src/core/SkBitmapProcState_matrix.h',
-        '../src/core/SkBitmapProcState_matrixProcs.cpp',
-        '../src/core/SkBitmapProcState_sample.h',
-        '../src/core/SkBitmapSampler.cpp',
-        '../src/core/SkBitmapSampler.h',
-        '../src/core/SkBitmapSamplerTemplate.h',
-        '../src/core/SkBitmapShader16BilerpTemplate.h',
-        '../src/core/SkBitmapShaderTemplate.h',
-        '../src/core/SkBitmap_scroll.cpp',
-        '../src/core/SkBlitBWMaskTemplate.h',
-        '../src/core/SkBlitRow_D16.cpp',
-        '../src/core/SkBlitRow_D32.cpp',
-        '../src/core/SkBlitRow_D4444.cpp',
-        '../src/core/SkBlitter.cpp',
-        '../src/core/SkBlitter_4444.cpp',
-        '../src/core/SkBlitter_A1.cpp',
-        '../src/core/SkBlitter_A8.cpp',
-        '../src/core/SkBlitter_ARGB32.cpp',
-        '../src/core/SkBlitter_RGB16.cpp',
-        '../src/core/SkBlitter_Sprite.cpp',
-        '../src/core/SkBuffer.cpp',
-        '../src/core/SkCanvas.cpp',
-        '../src/core/SkChunkAlloc.cpp',
-        '../src/core/SkClampRange.cpp',
-        '../src/core/SkClipStack.cpp',
-        '../src/core/SkColor.cpp',
-        '../src/core/SkColorFilter.cpp',
-        '../src/core/SkColorTable.cpp',
-        '../src/core/SkComposeShader.cpp',
-        '../src/core/SkConcaveToTriangles.cpp',
-        '../src/core/SkConcaveToTriangles.h',
-        '../src/core/SkCordic.cpp',
-        '../src/core/SkCordic.h',
-        '../src/core/SkCoreBlitters.h',
-        '../src/core/SkCubicClipper.cpp',
-        '../src/core/SkCubicClipper.h',
-        '../src/core/SkDebug.cpp',
-        '../src/core/SkDeque.cpp',
-        '../src/core/SkDevice.cpp',
-        '../src/core/SkDither.cpp',
-        '../src/core/SkDraw.cpp',
-        '../src/core/SkDrawProcs.h',
-        '../src/core/SkEdgeBuilder.cpp',
-        '../src/core/SkEdgeClipper.cpp',
-        '../src/core/SkEdge.cpp',
-        '../src/core/SkEdge.h',
-        '../src/core/SkFP.h',
-        '../src/core/SkFilterProc.cpp',
-        '../src/core/SkFilterProc.h',
-        '../src/core/SkFlattenable.cpp',
-        '../src/core/SkFloat.cpp',
-        '../src/core/SkFloat.h',
-        '../src/core/SkFloatBits.cpp',
-        '../src/core/SkGeometry.cpp',
-        '../src/core/SkGlobals.cpp',
-        '../src/core/SkGlyphCache.cpp',
-        '../src/core/SkGlyphCache.h',
-        '../src/core/SkGraphics.cpp',
-        '../src/core/SkLineClipper.cpp',
-        '../src/core/SkMallocPixelRef.cpp',
-        '../src/core/SkMask.cpp',
-        '../src/core/SkMaskFilter.cpp',
-        '../src/core/SkMath.cpp',
-        '../src/core/SkMatrix.cpp',
-        '../src/core/SkMetaData.cpp',
-        '../src/core/SkPackBits.cpp',
-        '../src/core/SkPaint.cpp',
-        '../src/core/SkPath.cpp',
-        '../src/core/SkPathEffect.cpp',
-        '../src/core/SkPathHeap.cpp',
-        '../src/core/SkPathHeap.h',
-        '../src/core/SkPathMeasure.cpp',
-        '../src/core/SkPicture.cpp',
-        '../src/core/SkPictureFlat.cpp',
-        '../src/core/SkPictureFlat.h',
-        '../src/core/SkPicturePlayback.cpp',
-        '../src/core/SkPicturePlayback.h',
-        '../src/core/SkPictureRecord.cpp',
-        '../src/core/SkPictureRecord.h',
-        '../src/core/SkPixelRef.cpp',
-        '../src/core/SkPoint.cpp',
-        '../src/core/SkProcSpriteBlitter.cpp',
-        '../src/core/SkPtrRecorder.cpp',
-        '../src/core/SkQuadClipper.cpp',
-        '../src/core/SkQuadClipper.h',
-        '../src/core/SkRasterizer.cpp',
-        '../src/core/SkRect.cpp',
-        '../src/core/SkRefDict.cpp',
-        '../src/core/SkRegion.cpp',
-        '../src/core/SkRegionPriv.h',
-        '../src/core/SkRegion_path.cpp',
-        '../src/core/SkScalar.cpp',
-        '../src/core/SkScalerContext.cpp',
-        '../src/core/SkScan.cpp',
-        '../src/core/SkScanPriv.h',
-        '../src/core/SkScan_AntiPath.cpp',
-        '../src/core/SkScan_Antihair.cpp',
-        '../src/core/SkScan_Hairline.cpp',
-        '../src/core/SkScan_Path.cpp',
-        '../src/core/SkShader.cpp',
-        '../src/core/SkShape.cpp',
-        '../src/core/SkSpriteBlitter_ARGB32.cpp',
-        '../src/core/SkSpriteBlitter_RGB16.cpp',
-        '../src/core/SkSinTable.h',
-        '../src/core/SkSpriteBlitter.h',
-        '../src/core/SkSpriteBlitterTemplate.h',
-        '../src/core/SkStream.cpp',
-        '../src/core/SkString.cpp',
-        '../src/core/SkStroke.cpp',
-        '../src/core/SkStrokerPriv.cpp',
-        '../src/core/SkStrokerPriv.h',
-        '../src/core/SkTextFormatParams.h',
-        '../src/core/SkTSearch.cpp',
-        '../src/core/SkTSort.h',
-        '../src/core/SkTemplatesPriv.h',
-        '../src/core/SkTypeface.cpp',
-        '../src/core/SkTypefaceCache.cpp',
-        '../src/core/SkTypefaceCache.h',
-        '../src/core/SkUnPreMultiply.cpp',
-        '../src/core/SkUtils.cpp',
-        '../src/core/SkWriter32.cpp',
-        '../src/core/SkXfermode.cpp',
-
-        '../src/opts/opts_check_SSE2.cpp',
-
-        '../src/ports/SkDebug_stdio.cpp',
-        '../src/ports/SkDebug_win.cpp',
-
-        '../src/ports/SkFontHost_tables.cpp',
-        '../src/ports/SkGlobals_global.cpp',
-        '../src/ports/SkMemory_malloc.cpp',
-        '../src/ports/SkOSFile_stdio.cpp',
-        '../src/ports/SkTime_Unix.cpp',
-        '../src/ports/SkXMLParser_empty.cpp',
-        '../src/ports/sk_predefined_gamma.h',
-
-        '../include/core/Sk64.h',
-        '../include/core/SkAdvancedTypefaceMetrics.h',
-        '../include/core/SkAutoKern.h',
-        '../include/core/SkBitmap.h',
-        '../include/core/SkBlitRow.h',
-        '../include/core/SkBlitter.h',
-        '../include/core/SkBounder.h',
-        '../include/core/SkBuffer.h',
-        '../include/core/SkCanvas.h',
-        '../include/core/SkChunkAlloc.h',
-        '../include/core/SkClampRange.h',
-        '../include/core/SkClipStack.h',
-        '../include/core/SkColor.h',
-        '../include/core/SkColorFilter.h',
-        '../include/core/SkColorPriv.h',
-        '../include/core/SkColorShader.h',
-        '../include/core/SkComposeShader.h',
-        '../include/core/SkDeque.h',
-        '../include/core/SkDescriptor.h',
-        '../include/core/SkDevice.h',
-        '../include/core/SkDither.h',
-        '../include/core/SkDraw.h',
-        '../include/core/SkDrawFilter.h',
-        '../include/core/SkDrawLooper.h',
-        '../include/core/SkEndian.h',
-        '../include/core/SkFDot6.h',
-        '../include/core/SkFixed.h',
-        '../include/core/SkFlattenable.h',
-        '../include/core/SkFloatBits.h',
-        '../include/core/SkFloatingPoint.h',
-        '../include/core/SkFontHost.h',
-        '../include/core/SkGeometry.h',
-        '../include/core/SkGlobals.h',
-        '../include/core/SkGraphics.h',
-        '../include/core/SkMallocPixelRef.h',
-        '../include/core/SkMask.h',
-        '../include/core/SkMaskFilter.h',
-        '../include/core/SkMath.h',
-        '../include/core/SkMatrix.h',
-        '../include/core/SkMetaData.h',
-        '../include/core/SkOSFile.h',
-        '../include/core/SkPackBits.h',
-        '../include/core/SkPaint.h',
-        '../include/core/SkPath.h',
-        '../include/core/SkPathEffect.h',
-        '../include/core/SkPathMeasure.h',
-        '../include/core/SkPerspIter.h',
-        '../include/core/SkPicture.h',
-        '../include/core/SkPixelRef.h',
-        '../include/core/SkPoint.h',
-        '../include/core/SkPtrRecorder.h',
-        '../include/core/SkRandom.h',
-        '../include/core/SkRasterizer.h',
-        '../include/core/SkReader32.h',
-        '../include/core/SkRect.h',
-        '../include/core/SkRefCnt.h',
-        '../include/core/SkRefDict.h',
-        '../include/core/SkRegion.h',
-        '../include/core/SkScalar.h',
-        '../include/core/SkScalarCompare.h',
-        '../include/core/SkScalerContext.h',
-        '../include/core/SkScan.h',
-        '../include/core/SkShader.h',
-        '../include/core/SkStream.h',
-        '../include/core/SkString.h',
-        '../include/core/SkStroke.h',
-        '../include/core/SkTDArray.h',
-        '../include/core/SkTDStack.h',
-        '../include/core/SkTDict.h',
-        '../include/core/SkTRegistry.h',
-        '../include/core/SkTScopedPtr.h',
-        '../include/core/SkTSearch.h',
-        '../include/core/SkTemplates.h',
-        '../include/core/SkThread.h',
-        '../include/core/SkThread_platform.h',
-        '../include/core/SkTime.h',
-        '../include/core/SkTypeface.h',
-        '../include/core/SkTypes.h',
-        '../include/core/SkUnPreMultiply.h',
-        '../include/core/SkUnitMapper.h',
-        '../include/core/SkUtils.h',
-        '../include/core/SkWriter32.h',
-        '../include/core/SkXfermode.h',
-      ],
-      'include_dirs': [
-        '../include/config',
-        '../include/core',
-        '../include/ports',
-        '../include/xml',
-        '../src/core',
-      ],
-      'msvs_disabled_warnings': [4244, 4267,4345, 4390, 4554, 4800],
-      'conditions': [
-        [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
-          'cflags': [
-            '-Wno-unused',
-            '-Wno-unused-function',
-          ],
-          'sources': [
-            '../include/core/SkMMapStream.h',
-            '../src/core/SkMMapStream.cpp',
-            '../src/core/SkBlitter_ARGB32_Subpixel.cpp',
-            '../src/core/SkFontHost.cpp',
-            '../src/ports/SkThread_pthread.cpp',
-            '../src/ports/SkTime_Unix.cpp',
-            '../src/ports/SkFontHost_FreeType_Subpixel.cpp',
-            '../src/ports/SkFontHost_FreeType.cpp',
-            '../src/ports/SkFontHost_gamma_none.cpp',
-            '../src/ports/SkFontHost_linux.cpp',
-          ],
-          'link_settings': {
-            'libraries': [
-              '-lfreetype',
-              '-lpthread',
-            ],
-          },
-        }],
-        [ 'OS == "mac"', {
-          'include_dirs': [
-            '../include/utils/mac',
-          ],
-          'sources': [
-            '../include/core/SkMMapStream.h',
-            '../include/utils/mac/SkCGUtils.h',
-
-            '../src/core/SkMMapStream.cpp',
-            '../src/ports/SkFontHost_mac_coretext.cpp',
-
-            '../src/ports/SkThread_pthread.cpp',
-            '../src/ports/SkTime_Unix.cpp',
-
-            '../src/utils/mac/SkCreateCGImageRef.cpp',
-          ],
-        }],
-        [ 'OS == "win"', {
-          'include_dirs': [
-            'config/win',
-          ],
-          'sources': [
-            '../src/ports/SkFontHost_win.cpp',
-            '../src/ports/SkThread_win.cpp',
-          ],
-          'sources!': [
-            '../src/ports/SkDebug_stdio.cpp',
-          ],
-        }],
-        [ 'OS != "win"', {
-          'sources!': [
-            '../src/ports/SkDebug_win.cpp',
-          ],
-        }],
-      ],
-      'direct_dependent_settings': {
-        'include_dirs': [
-          'config',
-          '../include/config',
-          '../include/core',
-          'ext',
-        ],
-      },
-      'dependencies': [
-        'skia_opts'
-      ],
-    },
-
-    # 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': 'skia_opts',
-      'type': 'static_library',
-      'include_dirs': [
-        '../include/config',
-        '../include/core',
-        '../src/core',
-      ],
-      'conditions': [
-        [ '(OS == "linux" or OS == "freebsd" or OS == "openbsd")', {
-          'cflags': [
-            '-msse2',
-          ],
-        }],
-      ],
-      'sources': [
-        '../src/opts/SkBitmapProcState_opts_SSE2.cpp',
-        '../src/opts/SkBlitRow_opts_SSE2.cpp',
-        '../src/opts/SkUtils_opts_SSE2.cpp',
-      ],
-    },
-    {
-      'target_name': 'effects',
-      'type': 'static_library',
-      'include_dirs': [
-        '../include/config',
-        '../include/core',
-        '../include/effects',
-      ],
-      'sources': [
-        '../include/effects/Sk1DPathEffect.h',
-        '../include/effects/Sk2DPathEffect.h',
-        '../include/effects/SkAvoidXfermode.h',
-        '../include/effects/SkBlurDrawLooper.h',
-        '../include/effects/SkBlurMaskFilter.h',
-        '../include/effects/SkColorMatrix.h',
-        '../include/effects/SkColorMatrixFilter.h',
-        '../include/effects/SkCornerPathEffect.h',
-        '../include/effects/SkDashPathEffect.h',
-        '../include/effects/SkDiscretePathEffect.h',
-        '../include/effects/SkDrawExtraPathEffect.h',
-        '../include/effects/SkEmbossMaskFilter.h',
-        '../include/effects/SkGradientShader.h',
-        '../include/effects/SkGroupShape.h',
-        '../include/effects/SkKernel33MaskFilter.h',
-        '../include/effects/SkLayerDrawLooper.h',
-        '../include/effects/SkLayerRasterizer.h',
-        '../include/effects/SkPaintFlagsDrawFilter.h',
-        '../include/effects/SkPixelXorXfermode.h',
-        '../include/effects/SkPorterDuff.h',
-        '../include/effects/SkRectShape.h',
-        '../include/effects/SkTableMaskFilter.h',
-        '../include/effects/SkTransparentShader.h',
-
-        '../src/effects/Sk1DPathEffect.cpp',
-        '../src/effects/Sk2DPathEffect.cpp',
-        '../src/effects/SkAvoidXfermode.cpp',
-        '../src/effects/SkBitmapCache.cpp',
-        '../src/effects/SkBitmapCache.h',
-        '../src/effects/SkBlurDrawLooper.cpp',
-        '../src/effects/SkBlurMask.cpp',
-        '../src/effects/SkBlurMask.h',
-        '../src/effects/SkBlurMaskFilter.cpp',
-        '../src/effects/SkColorFilters.cpp',
-        '../src/effects/SkColorMatrixFilter.cpp',
-        '../src/effects/SkCornerPathEffect.cpp',
-        '../src/effects/SkDashPathEffect.cpp',
-        '../src/effects/SkDiscretePathEffect.cpp',
-        '../src/effects/SkEmbossMask.cpp',
-        '../src/effects/SkEmbossMask.h',
-        '../src/effects/SkEmbossMask_Table.h',
-        '../src/effects/SkEmbossMaskFilter.cpp',
-        '../src/effects/SkGradientShader.cpp',
-        '../src/effects/SkGroupShape.cpp',
-        '../src/effects/SkKernel33MaskFilter.cpp',
-        '../src/effects/SkLayerDrawLooper.cpp',
-        '../src/effects/SkLayerRasterizer.cpp',
-        '../src/effects/SkPaintFlagsDrawFilter.cpp',
-        '../src/effects/SkPixelXorXfermode.cpp',
-        '../src/effects/SkPorterDuff.cpp',
-        '../src/effects/SkRadialGradient_Table.h',
-        '../src/effects/SkRectShape.cpp',
-        '../src/effects/SkTableMaskFilter.cpp',
-        '../src/effects/SkTransparentShader.cpp',
-      ],
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '../include/effects',
-        ],
-      },
-    },
-    {
-      'target_name': 'images',
-      'type': 'static_library',
-      '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/SkBitmap_RLEPixels.h',
-        '../src/images/SkCreateRLEPixelRef.cpp',
-        '../src/images/SkFDStream.cpp',
-        '../src/images/SkFlipPixelRef.cpp',
-        '../src/images/SkImageDecoder.cpp',
-        '../src/images/SkImageDecoder_Factory.cpp',
-        '../src/images/SkImageDecoder_libbmp.cpp',
-        '../src/images/SkImageDecoder_libgif.cpp',
-        '../src/images/SkImageDecoder_libico.cpp',
-        '../src/images/SkImageDecoder_libjpeg.cpp',
-        '../src/images/SkImageDecoder_libpng.cpp',
-        '../src/images/SkImageDecoder_libpvjpeg.c',
-        '../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/SkJpegUtility.cpp',
-        '../src/images/SkMovie.cpp',
-        '../src/images/SkMovie_gif.cpp',
-        '../src/images/SkPageFlipper.cpp',
-        '../src/images/SkScaledBitmapSampler.cpp',
-        '../src/images/SkScaledBitmapSampler.h',
-      ],
-      'conditions': [
-        [ 'OS == "win"', {
-          'sources!': [
-            '../include/images/SkJpegUtility.h',
-
-            '../src/images/SkFDStream.cpp',
-            '../src/images/SkImageDecoder_libgif.cpp',
-            '../src/images/SkImageDecoder_libjpeg.cpp',
-            '../src/images/SkImageDecoder_libpng.cpp',
-            '../src/images/SkImageDecoder_libpvjpeg.c',
-            '../src/images/SkJpegUtility.cpp',
-            '../src/images/SkMovie_gif.cpp',
-          ],
-        }],
-        [ 'OS == "mac"', {
-          'sources!': [
-            '../include/images/SkJpegUtility.h',
-
-            '../src/images/SkImageDecoder_libgif.cpp',
-            '../src/images/SkImageDecoder_libjpeg.cpp',
-            '../src/images/SkImageDecoder_libpng.cpp',
-            '../src/images/SkImageDecoder_libpvjpeg.c',
-            '../src/images/SkJpegUtility.cpp',
-            '../src/images/SkMovie_gif.cpp',
-          ],
-        }],
-        [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
-          'sources!': [
-            '../include/images/SkJpegUtility.h',
-
-            '../src/images/SkImageDecoder_libjpeg.cpp',
-            '../src/images/SkImageDecoder_libgif.cpp',
-            '../src/images/SkImageDecoder_libpvjpeg.c',
-            '../src/images/SkJpegUtility.cpp',
-            '../src/images/SkMovie_gif.cpp',
-          ],
-        }],
-
-      ],
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '../include/images',
-        ],
-      },
-    },
-    {
-      'target_name': 'xml',
-      'type': 'static_library',
-      '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': [
-        [ 'OS == "win" or OS == "mac" or OS == "linux" or OS == "openbsd" or OS == "solaris"', {
-          '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',
-        ],
-      },
-    },
-    {
-      'target_name': 'pdf',
-      'type': 'static_library',
-      'include_dirs': [
-        '../include/config',
-        '../include/core',
-        '../include/pdf',
-        '../src/core', # needed to get SkGlyphCache.h and SkTextFormatParams.h
-      ],
-      'sources': [
-        '../include/pdf/SkPDFCatalog.h',
-        '../include/pdf/SkPDFDevice.h',
-        '../include/pdf/SkPDFDocument.h',
-        '../include/pdf/SkPDFFont.h',
-        '../include/pdf/SkPDFFormXObject.h',
-        '../include/pdf/SkPDFGraphicState.h',
-        '../include/pdf/SkPDFImage.h',
-        '../include/pdf/SkPDFPage.h',
-        '../include/pdf/SkPDFShader.h',
-        '../include/pdf/SkPDFStream.h',
-        '../include/pdf/SkPDFTypes.h',
-        '../include/pdf/SkPDFUtils.h',
-
-        '../src/pdf/SkPDFCatalog.cpp',
-        '../src/pdf/SkPDFDevice.cpp',
-        '../src/pdf/SkPDFDocument.cpp',
-        '../src/pdf/SkPDFFont.cpp',
-        '../src/pdf/SkPDFFormXObject.cpp',
-        '../src/pdf/SkPDFGraphicState.cpp',
-        '../src/pdf/SkPDFImage.cpp',
-        '../src/pdf/SkPDFPage.cpp',
-        '../src/pdf/SkPDFShader.cpp',
-        '../src/pdf/SkPDFStream.cpp',
-        '../src/pdf/SkPDFTypes.cpp',
-        '../src/pdf/SkPDFUtils.cpp',
-      ],
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '../include/pdf',
-        ],
-      },
-    },
-    {
-      'target_name': 'utils',
-      'type': 'static_library',
-      'include_dirs': [
-        '../include/config',
-        '../include/core',
-        '../include/utils',
-        '../include/views',
-        '../include/effects',
-        '../include/xml',
-      ],
-      'sources': [
-        '../include/utils/SkBoundaryPatch.h',
-        '../include/utils/SkCamera.h',
-        '../include/utils/SkCubicInterval.h',
-        '../include/utils/SkCullPoints.h',
-        '../include/utils/SkDumpCanvas.h',
-        '../include/utils/SkEGLContext.h',
-        '../include/utils/SkGLCanvas.h',
-        '../include/utils/SkInterpolator.h',
-        '../include/utils/SkLayer.h',
-        '../include/utils/SkMeshUtils.h',
-        '../include/utils/SkNinePatch.h',
-        '../include/utils/SkNWayCanvas.h',
-        '../include/utils/SkParse.h',
-        '../include/utils/SkParsePaint.h',
-        '../include/utils/SkParsePath.h',
-        '../include/utils/SkProxyCanvas.h',
-        '../include/utils/SkSfntUtils.h',
-        '../include/utils/SkTextBox.h',
-        '../include/utils/SkUnitMappers.h',
-
-        '../src/utils/SkBoundaryPatch.cpp',
-        '../src/utils/SkCamera.cpp',
-        '../src/utils/SkColorMatrix.cpp',
-        '../src/utils/SkCubicInterval.cpp',
-        '../src/utils/SkCullPoints.cpp',
-        '../src/utils/SkDumpCanvas.cpp',
-        '../src/utils/SkEGLContext_none.cpp',
-        '../src/utils/SkInterpolator.cpp',
-        '../src/utils/SkLayer.cpp',
-        '../src/utils/SkMeshUtils.cpp',
-        '../src/utils/SkNinePatch.cpp',
-        '../src/utils/SkNWayCanvas.cpp',
-        '../src/utils/SkOSFile.cpp',
-        '../src/utils/SkParse.cpp',
-        '../src/utils/SkParseColor.cpp',
-        '../src/utils/SkParsePath.cpp',
-        '../src/utils/SkProxyCanvas.cpp',
-        '../src/utils/SkSfntUtils.cpp',
-        '../src/utils/SkUnitMappers.cpp',
-      ],
-      'conditions': [
-        [ 'OS == "mac"', {
-          'sources': [
-            '../include/utils/SkCGUtils.h',
-            '../src/utils/mac/SkCreateCGImageRef.cpp',
-            '../src/utils/mac/SkEGLContext_mac.cpp',
-          ],
-        }],
-      ],
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '../include/utils',
-        ],
-      },
-    },
-    {
-      'target_name': 'views',
-      'type': 'static_library',
-      'include_dirs': [
-        '../include/config',
-        '../include/core',
-        '../include/views',
-        '../include/xml',
-        '../include/utils',
-        '../include/images',
-        '../include/animator',
-        '../include/effects',
-      ],
-      'sources': [
-        '../include/views/SkApplication.h',
-        '../include/views/SkBGViewArtist.h',
-        '../include/views/SkBorderView.h',
-        '../include/views/SkEvent.h',
-        '../include/views/SkEventSink.h',
-        '../include/views/SkImageView.h',
-        '../include/views/SkKey.h',
-        '../include/views/SkOSMenu.h',
-        '../include/views/SkOSWindow_Mac.h',
-        '../include/views/SkOSWindow_SDL.h',
-        '../include/views/SkOSWindow_Unix.h',
-        '../include/views/SkOSWindow_Win.h',
-        #'../include/views/SkOSWindow_wxwidgets.h',
-        '../include/views/SkProgressBarView.h',
-        '../include/views/SkScrollBarView.h',
-        '../include/views/SkStackViewLayout.h',
-        '../include/views/SkSystemEventTypes.h',
-        '../include/views/SkTouchGesture.h',
-        '../include/views/SkView.h',
-        '../include/views/SkViewInflate.h',
-        '../include/views/SkWidget.h',
-        '../include/views/SkWidgetViews.h',
-        '../include/views/SkWindow.h',
-
-        '../src/views/SkBGViewArtist.cpp',
-        '../src/views/SkBorderView.cpp',
-        '../src/views/SkEvent.cpp',
-        '../src/views/SkEventSink.cpp',
-        '../src/views/SkImageView.cpp',
-        '../src/views/SkListView.cpp',
-        '../src/views/SkListWidget.cpp',
-        '../src/views/SkOSMenu.cpp',
-        '../src/views/SkParsePaint.cpp',
-        '../src/views/SkProgressBarView.cpp',
-        '../src/views/SkProgressView.cpp',
-        '../src/views/SkScrollBarView.cpp',
-        '../src/views/SkStackViewLayout.cpp',
-        '../src/views/SkStaticTextView.cpp',
-        '../src/views/SkTagList.cpp',
-        '../src/views/SkTagList.h',
-        '../src/views/SkTextBox.cpp',
-        '../src/views/SkTouchGesture.cpp',
-        '../src/views/SkView.cpp',
-        '../src/views/SkViewInflate.cpp',
-        '../src/views/SkViewPriv.cpp',
-        '../src/views/SkViewPriv.h',
-        '../src/views/SkWidget.cpp',
-        '../src/views/SkWidgets.cpp',
-        '../src/views/SkWidgetViews.cpp',
-        '../src/views/SkWindow.cpp',
-      ],
-      'sources!' : [
-        '../src/views/SkListView.cpp',   #depends on missing SkListSource implementation
-        '../src/views/SkListWidget.cpp', #depends on missing SkListSource implementation
-      ],
-      'conditions': [
-        [ 'OS == "win"', {
-          'sources': [
-            '../src/utils/win/SkOSWindow_Win.cpp',
-            '../src/utils/win/skia_win.cpp',
-          ],
-        }],
-        [ 'OS == "mac"', {
-          'sources': [
-            '../include/utils/SkCGUtils.h',
-            #'../src/utils/mac/SkBitmap_Mac.cpp',
-            '../src/utils/mac/SkCreateCGImageRef.cpp',
-            '../src/utils/mac/SkEGLContext_mac.cpp',
-            '../src/utils/mac/skia_mac.cpp',
-            '../src/utils/mac/SkOSWindow_Mac.cpp',
-          ],
-          'link_settings': {
-            'libraries': [
-              '$(SDKROOT)/System/Library/Frameworks/Carbon.framework',
-              '$(SDKROOT)/System/Library/Frameworks/AGL.framework',
-            ],
-          },
-        }],
-        [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
-          'include_dirs' : [
-            '../include/utils/unix',
-          ],
-          'sources': [
-            '../src/utils/unix/keysym2ucs.c',
-            '../src/utils/unix/SkOSWindow_Unix.cpp',
-            '../unix_test_app/main.cpp',
-          ],
-        }],
-      ],
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '../include/views',
-        ],
-      },
-    },
-    {
-      'target_name': 'skgr',
-      'type': 'static_library',
-      'include_dirs': [
-        '../include/config',
-        '../include/core',
-        '../src/core',
-        '../include/gpu',
-        '../gpu/include',
-      ],
-      'sources': [
-        '../include/gpu/SkGpuCanvas.h',
-        '../include/gpu/SkGpuDevice.h',
-        '../include/gpu/SkGpuDeviceFactory.h',
-        '../include/gpu/SkGr.h',
-        '../include/gpu/SkGrTexturePixelRef.h',
-
-        '../src/gpu/GrPrintf_skia.cpp',
-        '../src/gpu/SkGpuCanvas.cpp',
-        '../src/gpu/SkGpuDevice.cpp',
-        '../src/gpu/SkGr.cpp',
-        '../src/gpu/SkGrFontScaler.cpp',
-        '../src/gpu/SkGrTexturePixelRef.cpp',
-      ],
-      'conditions': [
-          [ 'OS == "linux"', {
-          'defines': [
-              'GR_LINUX_BUILD=1',
-          ],
-          }],
-          [ 'OS == "mac"', {
-          'defines': [
-              'GR_MAC_BUILD=1',
-          ],
-          }],
-          [ 'OS == "win"', {
-          'defines': [
-              'GR_WIN32_BUILD=1',
-          ],
-          }],
-      ],
-      'direct_dependent_settings': {
-        'conditions': [
-          [ 'OS == "linux"', {
-            'defines': [
-              'GR_LINUX_BUILD=1',
-            ],
-          }],
-          [ 'OS == "mac"', {
-            'defines': [
-              'GR_MAC_BUILD=1',
-            ],
-          }],
-          [ 'OS == "win"', {
-            'defines': [
-              'GR_WIN32_BUILD=1',
-            ],
-          }],
-        ],
-        'include_dirs': [
-          '../include/gpu',
-        ],
-      },
-    },
-    {
-      'target_name': 'gr',
-      'type': 'static_library',
-      'include_dirs': [
-        '../gpu/include',
-        '../include/core',
-        '../include/config',
-      ],
-      'dependencies': [
-        'libtess',
-      ],
-      'sources': [
-        '../gpu/include/GrAllocator.h',
-        '../gpu/include/GrAllocPool.h',
-        '../gpu/include/GrAtlas.h',
-        '../gpu/include/GrClip.h',
-        '../gpu/include/GrClipIterator.h',
-        '../gpu/include/GrColor.h',
-        '../gpu/include/GrConfig.h',
-        '../gpu/include/GrContext.h',
-        '../gpu/include/GrContext_impl.h',
-        '../gpu/include/GrDrawTarget.h',
-        '../gpu/include/GrFontScaler.h',
-        '../gpu/include/GrGeometryBuffer.h',
-        '../gpu/include/GrGLConfig.h',
-        '../gpu/include/GrGLConfig_chrome.h',
-        '../gpu/include/GrGLIndexBuffer.h',
-        '../gpu/include/GrGLInterface.h',
-        '../gpu/include/GrGLIRect.h',
-        '../gpu/include/GrGLTexture.h',
-        '../gpu/include/GrGLVertexBuffer.h',
-        '../gpu/include/GrGlyph.h',
-        '../gpu/include/GrGpu.h',
-        '../gpu/include/GrGpuVertex.h',
-        '../gpu/include/GrIndexBuffer.h',
-        '../gpu/include/GrInOrderDrawBuffer.h',
-        '../gpu/include/GrInstanceCounter.h',
-        '../gpu/include/GrIPoint.h',
-        '../gpu/include/GrKey.h',
-        '../gpu/include/GrMatrix.h',
-        '../gpu/include/GrMemory.h',
-        '../gpu/include/GrMesh.h',
-        '../gpu/include/GrNoncopyable.h',
-        '../gpu/include/GrPaint.h',
-        '../gpu/include/GrPath.h',
-        '../gpu/include/GrPathIter.h',
-        '../gpu/include/GrPathRenderer.h',
-        '../gpu/include/GrPathSink.h',
-        '../gpu/include/GrPlotMgr.h',
-        '../gpu/include/GrPoint.h',
-        '../gpu/include/GrRandom.h',
-        '../gpu/include/GrRect.h',
-        '../gpu/include/GrRectanizer.h',
-        '../gpu/include/GrRefCnt.h',
-        '../gpu/include/GrResource.h',
-        '../gpu/include/GrSamplerState.h',
-        '../gpu/include/GrScalar.h',
-        '../gpu/include/GrStencil.h',
-        '../gpu/include/GrStopwatch.h',
-        '../gpu/include/GrStringBuilder.h',
-        '../gpu/include/GrTArray.h',
-        '../gpu/include/GrTBSearch.h',
-        '../gpu/include/GrTDArray.h',
-        '../gpu/include/GrTesselatedPathRenderer.h',
-        '../gpu/include/GrTextContext.h',
-        '../gpu/include/GrTextStrike.h',
-        '../gpu/include/GrTexture.h',
-        '../gpu/include/GrTextureCache.h',
-        '../gpu/include/GrTHashCache.h',
-        '../gpu/include/GrTLList.h',
-        '../gpu/include/GrTouchGesture.h',
-        '../gpu/include/GrTypes.h',
-        '../gpu/include/GrUserConfig.h',
-        '../gpu/include/GrVertexBuffer.h',
-
-        '../gpu/src/GrAllocPool.cpp',
-        '../gpu/src/GrAtlas.cpp',
-        '../gpu/src/GrBinHashKey.h',
-        '../gpu/src/GrBufferAllocPool.cpp',
-        '../gpu/src/GrBufferAllocPool.h',
-        '../gpu/src/GrClip.cpp',
-        '../gpu/src/GrContext.cpp',
-        '../gpu/src/GrCreatePathRenderer_none.cpp',
-        '../gpu/src/GrDrawTarget.cpp',
-        '../gpu/src/GrGLEffect.h',
-        '../gpu/src/GrGLDefaultInterface_none.cpp',
-        '../gpu/src/GrGLIndexBuffer.cpp',
-        '../gpu/src/GrGLInterface.cpp',
-        '../gpu/src/GrGLProgram.cpp',
-        '../gpu/src/GrGLProgram.h',
-        '../gpu/src/GrGLTexture.cpp',
-        '../gpu/src/GrGLUtil.cpp',
-        '../gpu/src/GrGLVertexBuffer.cpp',
-        '../gpu/src/GrGpu.cpp',
-        '../gpu/src/GrGpuFactory.cpp',
-        '../gpu/src/GrGpuGL.cpp',
-        '../gpu/src/GrGpuGL.h',
-        '../gpu/src/GrGpuGLFixed.cpp',
-        '../gpu/src/GrGpuGLFixed.h',
-        '../gpu/src/GrGpuGLShaders.cpp',
-        '../gpu/src/GrGpuGLShaders.h',
-        '../gpu/src/GrInOrderDrawBuffer.cpp',
-        '../gpu/src/GrMatrix.cpp',
-        '../gpu/src/GrMemory.cpp',
-        '../gpu/src/GrPath.cpp',
-        '../gpu/src/GrPathRenderer.cpp',
-        '../gpu/src/GrPathUtils.cpp',
-        '../gpu/src/GrPathUtils.h',
-        '../gpu/src/GrRectanizer.cpp',
-        '../gpu/src/GrRedBlackTree.h',
-        '../gpu/src/GrResource.cpp',
-        '../gpu/src/GrStencil.cpp',
-        '../gpu/src/GrTesselatedPathRenderer.cpp',
-        '../gpu/src/GrTextContext.cpp',
-        '../gpu/src/GrTextStrike.cpp',
-        '../gpu/src/GrTextStrike_impl.h',
-        '../gpu/src/GrTexture.cpp',
-        '../gpu/src/GrTextureCache.cpp',
-        '../gpu/src/gr_unittests.cpp',
-
-        '../gpu/src/mac/GrGLDefaultInterface_mac.cpp',
-
-        '../gpu/src/win/GrGLDefaultInterface_win.cpp',
-
-        '../gpu/src/unix/GrGLDefaultInterface_unix.cpp',
-      ],
-      'defines': [
-        'GR_IMPLEMENTATION=1',
-      ],
-      'conditions': [
-        [ 'OS == "linux"', {
-          'defines': [
-              'GR_LINUX_BUILD=1',
-          ],
-          'sources!': [
-            '../gpu/src/GrGLDefaultInterface_none.cpp',
-          ],
-          'link_settings': {
-            'libraries': [
-              '-lGL',
-              '-lX11',
-            ],
-          },
-        }],
-        [ 'OS == "mac"', {
-          'defines': [
-              'GR_MAC_BUILD=1',
-          ],
-          'link_settings': {
-            'libraries': [
-              '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework',
-            ],
-          },
-          'sources!': [
-            '../gpu/src/GrGLDefaultInterface_none.cpp',
-          ],
-          }],
-        [ 'OS == "win"', {
-          'defines': [
-            'GR_WIN32_BUILD=1',
-            'GR_GL_FUNCTION_TYPE=__stdcall',
-          ],
-          'sources!': [
-            '../gpu/src/GrGLDefaultInterface_none.cpp',
-          ],
-        }],
-        [ 'OS != "win"', {
-          'sources!': [
-            '../gpu/src/win/GrGLDefaultInterface_win.cpp',
-          ],
-        }],
-        [ 'OS != "mac"', {
-          'sources!': [
-            '../gpu/src/mac/GrGLDefaultInterface_mac.cpp',
-          ],
-        }],
-        [ 'OS != "linux"', {
-          'sources!': [
-            '../gpu/src/unix/GrGLDefaultInterface_unix.cpp',
-          ],
-        }],
-      ],
-      'direct_dependent_settings': {
-        'conditions': [
-          [ 'OS == "linux"', {
-            'defines': [
-              'GR_LINUX_BUILD=1',
-            ],
-          }],
-          [ 'OS == "mac"', {
-            'defines': [
-              'GR_MAC_BUILD=1',
-            ],
-          }],
-          [ 'OS == "win"', {
-            'defines': [
-              'GR_WIN32_BUILD=1',
-              'GR_GL_FUNCTION_TYPE=__stdcall',
-            ],
-          }],
-        ],
-        'include_dirs': [
-          '../gpu/include',
-        ],
-      },
-    },
-    {
-      'target_name': 'animator',
-      'type': 'static_library',
-      'include_dirs': [
-        '../include/config',
-        '../include/core',
-        '../include/effects',
-        '../include/animator',
-        '../include/views',
-        '../include/xml',
-        '../include/utils',
-        '../include/images',
-      ],
-      '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/SkBase64.cpp',
-        '../src/animator/SkBase64.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',
-        ],
-      },
-    },
-
-    {
-      'target_name': 'svg',
-      'type': 'static_library',
-      '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',
-        ],
-      },
-    },
-
-    {
-      'target_name': 'experimental',
-      'type': 'static_library',
-      'include_dirs': [
-        '../include/config',
-        '../include/core',
-      ],
-      'sources': [
-        '../experimental/SkMatrix44.cpp',
-        '../experimental/SkMatrix44.h',
-        '../experimental/SkSetPoly3To3.cpp',
-        '../experimental/SkSetPoly3To3_A.cpp',
-        '../experimental/SkSetPoly3To3_D.cpp',
-      ],
-      'sources!': [
-        '../experimental/SkMatrix44.cpp',  #doesn't compile
-        '../experimental/SkMatrix44.h',
-      ],
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '../experimental',
-        ],
-      },
-    },
-
-    {
-      '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',       # SampleGM.cpp pulls gm.h
-      ],
-      'sources': [
-        # gm files needed for SampleGM.cpp
-        '../gm/bitmapfilters.cpp',
-        '../gm/blurs.cpp',
-        '../gm/complexclip.cpp',
-        '../gm/filltypes.cpp',
-        '../gm/gm.h',
-        '../gm/gradients.cpp',
-        '../gm/points.cpp',
-        '../gm/poly2poly.cpp',
-        '../gm/shadertext.cpp',
-        '../gm/shadows.cpp',
-        '../gm/shapes.cpp',
-        '../gm/tilemodes.cpp',
-        '../gm/xfermodes.cpp',
-
-        '../samplecode/ClockFaceView.cpp',
-        '../samplecode/OverView.cpp',
-        '../samplecode/SampleAll.cpp',
-        '../samplecode/SampleAnimator.cpp',
-        '../samplecode/SampleApp.cpp',
-        '../samplecode/SampleArc.cpp',
-        '../samplecode/SampleAvoid.cpp',
-        '../samplecode/SampleBigGradient.cpp',
-        '../samplecode/SampleBitmapRect.cpp',
-        '../samplecode/SampleBlur.cpp',
-        '../samplecode/SampleCamera.cpp',
-        '../samplecode/SampleCircle.cpp',
-        '../samplecode/SampleCode.h',
-        '../samplecode/SampleColorFilter.cpp',
-        '../samplecode/SampleComplexClip.cpp',
-        '../samplecode/SampleCull.cpp',
-        '../samplecode/SampleDecode.cpp',
-        '../samplecode/SampleDither.cpp',
-        '../samplecode/SampleDitherBitmap.cpp',
-        '../samplecode/SampleDrawLooper.cpp',
-        '../samplecode/SampleEffects.cpp',
-        '../samplecode/SampleEmboss.cpp',
-        '../samplecode/SampleEncode.cpp',
-        '../samplecode/SampleExtractAlpha.cpp',
-        '../samplecode/SampleFillType.cpp',
-        '../samplecode/SampleFilter.cpp',
-        '../samplecode/SampleFilter2.cpp',
-        '../samplecode/SampleFontCache.cpp',
-        '../samplecode/SampleFontScalerTest.cpp',
-        '../samplecode/SampleFuzz.cpp',
-        '../samplecode/SampleGM.cpp',
-        '../samplecode/SampleGradients.cpp',
-        '../samplecode/SampleHairline.cpp',
-        '../samplecode/SampleImage.cpp',
-        '../samplecode/SampleImageDir.cpp',
-        '../samplecode/SampleLayerMask.cpp',
-        '../samplecode/SampleLayers.cpp',
-        '../samplecode/SampleLCD.cpp',
-        '../samplecode/SampleLineClipper.cpp',
-        '../samplecode/SampleLines.cpp',
-        '../samplecode/SampleMeasure.cpp',
-        '../samplecode/SampleMipMap.cpp',
-        '../samplecode/SampleMovie.cpp',
-        '../samplecode/SampleNinePatch.cpp',
-        '../samplecode/SampleOvalTest.cpp',
-        '../samplecode/SampleOverflow.cpp',
-        '../samplecode/SamplePageFlip.cpp',
-        '../samplecode/SamplePatch.cpp',
-        '../samplecode/SamplePath.cpp',
-        '../samplecode/SamplePathClip.cpp',
-        '../samplecode/SamplePathEffects.cpp',
-        '../samplecode/SamplePicture.cpp',
-        '../samplecode/SamplePoints.cpp',
-        '../samplecode/SamplePolyToPoly.cpp',
-        '../samplecode/SampleAARects.cpp',
-        '../samplecode/SampleRegion.cpp',
-        '../samplecode/SampleRepeatTile.cpp',
-        '../samplecode/SampleShaders.cpp',
-        '../samplecode/SampleShaderText.cpp',
-        '../samplecode/SampleShapes.cpp',
-        '../samplecode/SampleSkLayer.cpp',
-        '../samplecode/SampleSlides.cpp',
-        '../samplecode/SampleStrokePath.cpp',
-        '../samplecode/SampleStrokeText.cpp',
-        '../samplecode/SampleSVG.cpp',
-        '../samplecode/SampleTests.cpp',
-        '../samplecode/SampleText.cpp',
-        '../samplecode/SampleTextAlpha.cpp',
-        '../samplecode/SampleTextBox.cpp',
-        '../samplecode/SampleTextEffects.cpp',
-        '../samplecode/SampleTextOnPath.cpp',
-        '../samplecode/SampleTiling.cpp',
-        '../samplecode/SampleTinyBitmap.cpp',
-        '../samplecode/SampleTriangles.cpp',
-        '../samplecode/SampleTypeface.cpp',
-        '../samplecode/SampleUnitMapper.cpp',
-        '../samplecode/SampleVertices.cpp',
-        '../samplecode/SampleXfermodes.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',
-        'effects',
-        'images',
-        'views',
-        'utils',
-        'animator',
-        'xml',
-        'svg',
-        'experimental',
-        'gr',
-        'skgr',
-      ],
-      'conditions' : [
-       [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
-         'sources!': [
-            '../samplecode/SampleDecode.cpp',
-         ],
-        }],
-        [ 'OS == "win"', {
-          'sources!': [
-            # require UNIX functions
-            '../samplecode/SampleEncode.cpp',
-            '../samplecode/SamplePageFlip.cpp',
-          ],
-        }],
-        [ 'OS == "mac"', {
-          'sources!': [
-            '../samplecode/SampleDecode.cpp',
-          ],
-        }],
-
-      ],
-      'msvs_settings': {
-        'VCLinkerTool': {
-          'SubSystem': '2',
-          'AdditionalDependencies': [
-              'OpenGL32.lib',
-              'usp10.lib',
-              'd3d9.lib',
-          ],
-        },
-      },
-    },
-    {
-      'target_name': 'libtess',
-      'type': 'static_library',
-      'include_dirs': [
-        '../third_party/glu',
-      ],
-      'sources': [
-        '../third_party/glu/internal_glu.h',
-        '../third_party/glu/gluos.h',
-        '../third_party/glu/libtess/dict-list.h',
-        '../third_party/glu/libtess/dict.c',
-        '../third_party/glu/libtess/dict.h',
-        '../third_party/glu/libtess/geom.c',
-        '../third_party/glu/libtess/geom.h',
-        '../third_party/glu/libtess/memalloc.c',
-        '../third_party/glu/libtess/memalloc.h',
-        '../third_party/glu/libtess/mesh.c',
-        '../third_party/glu/libtess/mesh.h',
-        '../third_party/glu/libtess/normal.c',
-        '../third_party/glu/libtess/normal.h',
-        '../third_party/glu/libtess/priorityq-heap.h',
-        '../third_party/glu/libtess/priorityq-sort.h',
-        '../third_party/glu/libtess/priorityq.c',
-        '../third_party/glu/libtess/priorityq.h',
-        '../third_party/glu/libtess/render.c',
-        '../third_party/glu/libtess/render.h',
-        '../third_party/glu/libtess/sweep.c',
-        '../third_party/glu/libtess/sweep.h',
-        '../third_party/glu/libtess/tess.c',
-        '../third_party/glu/libtess/tess.h',
-        '../third_party/glu/libtess/tessmono.c',
-        '../third_party/glu/libtess/tessmono.h',
-      ],
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '../third_party/glu',
-        ],
-      },
-    },
-  ],
-}
-
-# Local Variables:
-# tab-width:2
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/include/config/SkUserConfig.h b/include/config/SkUserConfig.h
index aa2e6cf..c56d8cf 100644
--- a/include/config/SkUserConfig.h
+++ b/include/config/SkUserConfig.h
@@ -54,7 +54,7 @@
 
 /*  Somewhat independent of how SkScalar is implemented, Skia also wants to know
     if it can use floats at all. Naturally, if SK_SCALAR_IS_FLOAT is defined,
-    then so muse SK_CAN_USE_FLOAT, but if scalars are fixed, SK_CAN_USE_FLOAT
+    SK_CAN_USE_FLOAT must be too; but if scalars are fixed, SK_CAN_USE_FLOAT
     can go either way.
  */
 //#define SK_CAN_USE_FLOAT
@@ -151,4 +151,3 @@
 #endif
 
 #endif
-
diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h
index ae0b974..6e8da76 100644
--- a/include/core/SkClipStack.h
+++ b/include/core/SkClipStack.h
@@ -43,6 +43,7 @@
 
         struct Clip {
             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;
diff --git a/include/core/SkColorPriv.h b/include/core/SkColorPriv.h
index f9e02a2..6fa9df3 100644
--- a/include/core/SkColorPriv.h
+++ b/include/core/SkColorPriv.h
@@ -155,31 +155,13 @@
     #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
 
-/* we check to see if the SHIFT value has already been defined (SkUserConfig.h)
-    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
-
 #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)
diff --git a/include/core/SkComposeShader.h b/include/core/SkComposeShader.h
index 0b198f6..ea37549 100644
--- a/include/core/SkComposeShader.h
+++ b/include/core/SkComposeShader.h
@@ -27,7 +27,7 @@
     This subclass of shader returns the coposition of two other shaders, combined by
     a xfermode.
 */
-class SkComposeShader : public SkShader {
+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
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 46bcf1a..d9a4fde 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -155,31 +155,6 @@
     virtual void setMatrixClip(const SkMatrix&, const SkRegion&,
                                const SkClipStack&);
 
-    /**
-     *  Observer interface for listening to the calls to
-     *  SkDevice::setMatrixClip(...).  Users of SkDevice instances should
-     *  implement matrixClipChanged(...) to receive notifications.
-     */
-    class SkMatrixClipObserver : public SkRefCnt {
-    public:
-        virtual void matrixClipChanged(const SkMatrix&, const SkRegion&,
-                                       const SkClipStack&) = 0;
-    };
-
-    /** Assign the clip observer.  Note that an extra reference is added to the
-      * observer, and removed at SkDevice construction, or re-assignment of a
-      * different observer.
-      */
-    void setMatrixClipObserver(SkMatrixClipObserver* observer);
-
-    /** Return the device's associated SkMatrixClipObserver, or NULL.
-      * If non-null is returned, the reference count of the object is not
-      * modified.
-      */
-    SkMatrixClipObserver* getMatrixClipObserver() const {
-        return fMatrixClipObserver;
-    }
-
     /** Called when this device gains focus (i.e becomes the current device
         for drawing).
     */
@@ -306,8 +281,6 @@
     SkIPoint    fOrigin;
     SkMetaData* fMetaData;
 
-    SkMatrixClipObserver* fMatrixClipObserver;
-
     SkDeviceFactory* fCachedDeviceFactory;
 };
 
diff --git a/include/core/SkMath.h b/include/core/SkMath.h
index 3e72904..efe378a 100644
--- a/include/core/SkMath.h
+++ b/include/core/SkMath.h
@@ -115,7 +115,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#if defined(__arm__) && !defined(__thumb__)
+#if defined(__arm__)
     #define SkCLZ(x)    __builtin_clz(x)
 #endif
 
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index 7c77902..480e077 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -438,6 +438,7 @@
     */
     bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const;
 
+#ifdef SK_SCALAR_IS_FIXED
     friend bool operator==(const SkMatrix& a, const SkMatrix& b) {
         return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) == 0;
     }
@@ -445,6 +446,12 @@
     friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
         return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) != 0;
     }
+#else
+    friend bool operator==(const SkMatrix& a, const SkMatrix& b);    
+    friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
+        return !(a == b);
+    }
+#endif
 
     enum {
         // flatten/unflatten will never return a value larger than this
@@ -488,7 +495,12 @@
         kRectStaysRect_Mask = 0x10,
 
         kUnknown_Mask = 0x80,
-        
+
+        kORableMasks =  kTranslate_Mask |
+                        kScale_Mask |
+                        kAffine_Mask |
+                        kPerspective_Mask,
+
         kAllMasks = kTranslate_Mask |
                     kScale_Mask |
                     kAffine_Mask |
@@ -506,7 +518,12 @@
         SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == 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);
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index f4b325f..f336f5b 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -817,6 +817,7 @@
 
 #ifdef ANDROID
     const SkGlyph& getUnicharMetrics(SkUnichar);
+    const SkGlyph& getGlyphMetrics(uint16_t);
     const void* findImage(const SkGlyph&);
 
     uint32_t getGenerationID() const;
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 18dcd11..7120d3f 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -88,9 +88,10 @@
     /** 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()
-    */
+    /**
+     *  Toggle between inverse and normal filltypes. This reverse the return
+     *  value of isInverseFillType()
+     */
     void toggleInverseFillType() {
         fFillType ^= 2;
         GEN_ID_INC;
@@ -103,14 +104,33 @@
     };
 
     /**
-     *  Return the path's convexity, as stored in the path.
+     *  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 { return (Convexity)fConvexity; }
+    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);
 
@@ -118,9 +138,11 @@
      *  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 (i.e. all segements are colinear,
-     *  then this returns kUnknown_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.
      */
@@ -635,7 +657,7 @@
     mutable SkRect      fBounds;
     mutable uint8_t     fBoundsIsDirty;
     uint8_t             fFillType;
-    uint8_t             fConvexity;
+    mutable uint8_t     fConvexity;
 #ifdef ANDROID
     uint32_t            fGenerationID;
 #endif
diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h
index 57cc368..23ce02b 100644
--- a/include/core/SkPostConfig.h
+++ b/include/core/SkPostConfig.h
@@ -132,6 +132,25 @@
     #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
diff --git a/include/core/SkPreConfig.h b/include/core/SkPreConfig.h
index 8d47d46..6ec73ce 100644
--- a/include/core/SkPreConfig.h
+++ b/include/core/SkPreConfig.h
@@ -41,14 +41,16 @@
         #define SK_BUILD_FOR_UNIX
     #elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
         #define SK_BUILD_FOR_IOS
-    #elif defined(ANDROID_NDK)
-        #define SK_BUILD_FOR_ANDROID_NDK
-    #elif defined(ANROID)
-        #define SK_BUILD_FOR_ANDROID
     #else
         #define SK_BUILD_FOR_MAC
     #endif
 
+    #if defined(ANDROID)
+        #define SK_BUILD_FOR_ANDROID
+    #endif
+    #if defined(ANDROID_NDK)
+        #define SK_BUILD_FOR_ANDROID_NDK
+    #endif
 #endif
 
 //////////////////////////////////////////////////////////////////////
diff --git a/include/core/SkReader32.h b/include/core/SkReader32.h
index 03a34c7..654ebd5 100644
--- a/include/core/SkReader32.h
+++ b/include/core/SkReader32.h
@@ -101,7 +101,14 @@
     uint16_t readU16() { return (uint16_t)this->readInt(); }
     int32_t readS32() { return this->readInt(); }
     uint32_t readU32() { return this->readInt(); }
-    
+
+    /**
+     *  Read the length of a string written by SkWriter32::writeString()
+     *  (if len is not NULL) and return the null-ternimated address of the
+     *  string.
+     */
+    const char* readString(size_t* len = NULL);
+
 private:
     // these are always 4-byte aligned
     const char* fCurr;  // current position within buffer
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
index 550c5d1..19ee12a 100644
--- a/include/core/SkRect.h
+++ b/include/core/SkRect.h
@@ -290,7 +290,7 @@
     void sort();
 
     static const SkIRect& EmptyIRect() {
-        static const SkIRect gEmpty = {0,0,0,0};
+        static const SkIRect gEmpty = { 0, 0, 0, 0 };
         return gEmpty;
     }
 };
diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h
index ebe621b..5dbf684 100644
--- a/include/core/SkScalar.h
+++ b/include/core/SkScalar.h
@@ -66,9 +66,47 @@
         int exponent = bits << 1 >> 24;
         return exponent != 0xFF;
     }
+#ifdef SK_DEBUG
+    /** SkIntToScalar(n) returns its integer argument as an SkScalar
+     *
+     * If we're compiling in DEBUG mode, and can thus afford some extra runtime
+     * cycles, check to make sure that the parameter passed in has not already
+     * been converted to SkScalar.  (A double conversion like this is harmless
+     * for SK_SCALAR_IS_FLOAT, but for SK_SCALAR_IS_FIXED this causes trouble.)
+     *
+     * Note that we need all of these method signatures to properly handle the
+     * various types that we pass into SkIntToScalar() to date:
+     * int, size_t, U8CPU, etc., even though what we really mean is "anything
+     * but a float".
+     */
+    static inline float SkIntToScalar(signed int param) {
+        return (float)param;
+    }
+    static inline float SkIntToScalar(unsigned int param) {
+        return (float)param;
+    }
+    static inline float SkIntToScalar(signed long param) {
+        return (float)param;
+    }
+    static inline float SkIntToScalar(unsigned long param) {
+        return (float)param;
+    }
+    static inline float SkIntToScalar(float param) {
+        /* If the parameter passed into SkIntToScalar is a float,
+         * one of two things has happened:
+         * 1. the parameter was an SkScalar (which is typedef'd to float)
+         * 2. the parameter was a float instead of an int
+         *
+         * Either way, it's not good.
+         */
+        SkASSERT(!"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)
@@ -282,4 +320,3 @@
                             const SkScalar values[], int length);
 
 #endif
-
diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h
index 55dfcef..3f818a3 100644
--- a/include/core/SkScalerContext.h
+++ b/include/core/SkScalerContext.h
@@ -179,6 +179,9 @@
         kEmbolden_Flag      = 0x80,
         kSubpixelPositioning_Flag = 0x100,
         kAutohinting_Flag   = 0x200,
+        // these should only ever be set if fMaskFormat is LCD
+        kLCD_Vertical_Flag  = 0x400,    // else Horizontal
+        kLCD_BGROrder_Flag  = 0x800,    // else RGB order
     };
 private:
     enum {
diff --git a/include/core/SkWriter32.h b/include/core/SkWriter32.h
index 8e133c2..c8ebb6a 100644
--- a/include/core/SkWriter32.h
+++ b/include/core/SkWriter32.h
@@ -100,7 +100,22 @@
     }
     
     void writePad(const void* src, size_t size);
-    
+
+    /**
+     *  Writes a string to the writer, which can be retrieved with
+     *  SkReader32::readString().
+     *  The length can be specified, or if -1 is passed, it will be computed by
+     *  calling strlen(). The length must be < 0xFFFF
+     */
+    void writeString(const char* str, size_t len = (size_t)-1);
+
+    /**
+     *  Computes the size (aligned to multiple of 4) need to write the string
+     *  in a call to writeString(). If the length is not specified, it will be
+     *  computed by calling strlen().
+     */
+    static size_t WriteStringSize(const char* str, size_t len = (size_t)-1);
+
     // return the current offset (will always be a multiple of 4)
     uint32_t  size() const { return fSize; }
     void      reset();
diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h
index 4d46bb9..6ab9d6d 100644
--- a/include/core/SkXfermode.h
+++ b/include/core/SkXfermode.h
@@ -103,11 +103,15 @@
         kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
         kXor_Mode,      //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
 
-        // these modes are defined in the SVG Compositing standard
+        // all remaining modes are defined in the SVG Compositing standard
         // http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/
         kPlus_Mode,
-        kMultiply_Mode,
-        kScreen_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,
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 15def87..601da09 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -163,17 +163,26 @@
     // doesn't set the texture/sampler/matrix state
     // caller needs to null out GrPaint's texture if
     // non-textured drawing is desired.
+    // Set constantColor to true if a constant color
+    // will be used.  This is an optimization, and can 
+    // always be set to false. constantColor should 
+    // never be true if justAlpha is true.
     bool skPaint2GrPaintNoShader(const SkPaint& skPaint,
                                  bool justAlpha,
-                                 GrPaint* grPaint);
+                                 GrPaint* grPaint,
+                                 bool constantColor);
 
     // uses the SkShader to setup paint, act used to
     // hold lock on cached texture and free it when
     // destroyed.
+    // If there is no shader, constantColor will
+    // be passed to skPaint2GrPaintNoShader.  Otherwise
+    // it is ignored.
     bool skPaint2GrPaintShader(const SkPaint& skPaint,
                                SkAutoCachedTexture* act,
                                const SkMatrix& ctm,
-                               GrPaint* grPaint);
+                               GrPaint* grPaint,
+                               bool constantColor);
 
     SkDrawProcs* initDrawForText(GrTextContext*);
     bool bindDeviceAsTexture(GrPaint* paint);
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index 75099b2..65565c9 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -24,8 +24,8 @@
 #include "GrConfig.h"
 #include "GrContext.h"
 #include "GrFontScaler.h"
-#include "GrPathIter.h"
 #include "GrClipIterator.h"
+#include "GrPath.h"
 
 // skia headers
 #include "SkBitmap.h"
@@ -130,29 +130,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Classes
 
-class SkGrPathIter : public GrPathIter {
-public:
-    SkGrPathIter() { fPath = NULL; }
-    SkGrPathIter(const SkPath& path) { reset(path); }
-    virtual GrPathCmd next(GrPoint pts[]);
-    virtual GrPathCmd next();
-    virtual void rewind();
-    virtual GrConvexHint convexHint() const;
-    virtual bool getConservativeBounds(GrRect* rect) const;
-
-    void reset(const SkPath& path) {
-        fPath = &path;
-        fIter.setPath(path, false);
-    }
-private:
-
-#if !SK_SCALAR_IS_GR_SCALAR
-    SkPoint             fPoints[4];
-#endif
-    SkPath::Iter        fIter;
-    const SkPath*       fPath;
-};
-
 class SkGrClipIterator : public GrClipIterator {
 public:
     SkGrClipIterator() { fClipStack = NULL;  fCurr = NULL; }
@@ -176,9 +153,8 @@
         }
     }
 
-    virtual GrPathIter* getPathIter() {
-        fPathIter.reset(*fCurr->fPath);
-        return &fPathIter;
+    virtual const GrPath* getPath() {
+        return fCurr->fPath;
     }
 
     virtual GrPathFill getPathFill() const;
@@ -186,7 +162,6 @@
 private:
     const SkClipStack*                  fClipStack;
     SkClipStack::B2FIter                fIter;
-    SkGrPathIter                        fPathIter;
     // SkClipStack's auto advances on each get
     // so we store the current pos here.
     const SkClipStack::B2FIter::Clip*   fCurr;
@@ -218,7 +193,7 @@
         rect->fBottom = GrIntToScalar(r.fBottom);
     }
 
-    virtual GrPathIter* getPathIter() {
+    virtual const GrPath* getPath() {
         SkASSERT(0);
         return NULL;
     }
diff --git a/include/pdf/SkPDFCatalog.h b/include/pdf/SkPDFCatalog.h
new file mode 100644
index 0000000..e02ffa1
--- /dev/null
+++ b/include/pdf/SkPDFCatalog.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFCatalog_DEFINED
+#define SkPDFCatalog_DEFINED
+
+#include <sys/types.h>
+
+#include "SkPDFTypes.h"
+#include "SkRefCnt.h"
+#include "SkTDArray.h"
+
+/** \class SkPDFCatalog
+
+    The PDF catalog manages object numbers and file offsets.  It is used
+    to create the PDF cross reference table.
+*/
+class SK_API SkPDFCatalog {
+public:
+    /** Create a PDF catalog.
+     */
+    SkPDFCatalog();
+    ~SkPDFCatalog();
+
+    /** Add the passed object to the catalog.  Refs obj.
+     *  @param obj         The object to add.
+     *  @param onFirstPage Is the object on the first page.
+     *  @return The obj argument is returned.
+     */
+    SkPDFObject* addObject(SkPDFObject* obj, bool onFirstPage);
+
+    /** Inform the catalog of the object's position in the final stream.
+     *  The object should already have been added to the catalog.  Returns
+     *  the object's size.
+     *  @param obj         The object to add.
+     *  @param offset      The byte offset in the output stream of this object.
+     */
+    size_t setFileOffset(SkPDFObject* obj, size_t offset);
+
+    /** Output the object number for the passed object.
+     *  @param obj         The object of interest.
+     *  @param stream      The writable output stream to send the output to.
+     */
+    void emitObjectNumber(SkWStream* stream, SkPDFObject* obj);
+
+    /** Return the number of bytes that would be emitted for the passed
+     *  object's object number.
+     *  @param obj         The object of interest
+     */
+    size_t getObjectNumberSize(SkPDFObject* obj);
+
+    /** 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);
+
+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;
+    };
+
+    // TODO(vandebo) Make this a hash if it's a performance problem.
+    SkTDArray<struct Rec> fCatalog;
+
+    // Number of objects on the first page.
+    uint32_t fFirstPageCount;
+    // Next object number to assign (on page > 1).
+    uint32_t fNextObjNum;
+    // Next object number to assign on the first page.
+    uint32_t fNextFirstPageObjNum;
+
+    int findObjectIndex(SkPDFObject* obj) const;
+
+    int assignObjNum(SkPDFObject* obj);
+};
+
+#endif
diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
new file mode 100644
index 0000000..6e4d8db
--- /dev/null
+++ b/include/pdf/SkPDFDevice.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 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 SkPDFDevice_DEFINED
+#define SkPDFDevice_DEFINED
+
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SkTScopedPtr.h"
+
+class SkPDFArray;
+class SkPDFDevice;
+class SkPDFDict;
+class SkPDFFont;
+class SkPDFFormXObject;
+class SkPDFGraphicState;
+class SkPDFObject;
+class SkPDFShader;
+class SkPDFStream;
+
+// Private classes.
+struct ContentEntry;
+struct GraphicStateEntry;
+
+class SkPDFDeviceFactory : public SkDeviceFactory {
+public:
+    virtual SkDevice* newDevice(SkCanvas*, SkBitmap::Config, int width,
+                                int height, bool isOpaque, bool isForLayer);
+};
+
+/** \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 factory 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() { return kVector_Capability; }
+
+    virtual void clear(SkColor color);
+
+    virtual bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
+        return false;
+    }
+
+    /** 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);
+    virtual void drawPath(const SkDraw&, const SkPath& origpath,
+                          const SkPaint& paint, const SkMatrix* prePathMatrix,
+                          bool pathIsMutable);
+    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);
+    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);
+    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);
+    virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+                            const SkPaint&);
+
+    // PDF specific methods.
+
+    /** Returns a reference to the resource dictionary for this device.
+     */
+    SK_API const SkRefPtr<SkPDFDict>& getResourceDict();
+
+    /** Get the list of resources (PDF objects) used on this page.
+     *  @param resourceList A list to append the resources to.
+     */
+    SK_API void getResources(SkTDArray<SkPDFObject*>* resourceList) const;
+
+    /** Get the fonts used on this device.
+     */
+    SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
+
+    /** Returns the media box for this device.
+     */
+    SK_API SkRefPtr<SkPDFArray> getMediaBox() const;
+
+    /** Returns a SkStream with the page contents.  The caller is responsible
+        for a reference to the returned value.
+     */
+    SK_API SkStream* content() const;
+
+    SK_API const SkMatrix& initialTransform() const {
+        return fInitialTransform;
+    }
+
+protected:
+    // override
+    virtual SkDeviceFactory* onNewDeviceFactory();
+
+private:
+    friend class SkPDFDeviceFactory;
+    // 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;
+    SkRefPtr<SkPDFDict> fResourceDict;
+
+    SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
+    SkTDArray<SkPDFObject*> fXObjectResources;
+    SkTDArray<SkPDFFont*> fFontResources;
+    SkTDArray<SkPDFShader*> fShaderResources;
+
+    SkTScopedPtr<ContentEntry> fContentEntries;
+    ContentEntry* fLastContentEntry;
+
+    // For use by the DeviceFactory.
+    SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack,
+                const SkRegion& existingClipRegion);
+
+    void init();
+    void cleanUp();
+    void createFormXObjectFromDevice(SkRefPtr<SkPDFFormXObject>* xobject);
+
+    // 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,
+                                    SkRefPtr<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);
+
+    // Disable the default copy and assign implementation.
+    SkPDFDevice(const SkPDFDevice&);
+    void operator=(const SkPDFDevice&);
+};
+
+#endif
diff --git a/include/pdf/SkPDFDocument.h b/include/pdf/SkPDFDocument.h
new file mode 100644
index 0000000..0a76ea2
--- /dev/null
+++ b/include/pdf/SkPDFDocument.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFDocument_DEFINED
+#define SkPDFDocument_DEFINED
+
+#include "SkPDFCatalog.h"
+#include "SkPDFTypes.h"
+#include "SkRefCnt.h"
+#include "SkTDArray.h"
+
+class SkPDFDevice;
+class SkPDFPage;
+class SkWSteam;
+
+/** \class SkPDFDocument
+
+    A SkPDFDocument assembles pages together and generates the final PDF file.
+*/
+class SkPDFDocument {
+public:
+    /** Create a PDF document.
+     */
+    SK_API SkPDFDocument();
+    SK_API ~SkPDFDocument();
+
+    /** Output the PDF to the passed stream.
+     *  @param stream    The writable output stream to send the PDF to.
+     */
+    SK_API bool emitPDF(SkWStream* stream);
+
+    /** 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(const SkRefPtr<SkPDFDevice>& pdfDevice);
+
+    /** Get the list of pages in this document.
+     */
+    SK_API const SkTDArray<SkPDFPage*>& getPages();
+
+private:
+    SkPDFCatalog fCatalog;
+    int64_t fXRefFileOffset;
+
+    SkTDArray<SkPDFPage*> fPages;
+    SkTDArray<SkPDFDict*> fPageTree;
+    SkRefPtr<SkPDFDict> fDocCatalog;
+    SkTDArray<SkPDFObject*> fPageResources;
+    int fSecondPageFirstResourceIndex;
+
+    SkRefPtr<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/pdf/SkPDFFont.h b/include/pdf/SkPDFFont.h
new file mode 100644
index 0000000..93a72f1
--- /dev/null
+++ b/include/pdf/SkPDFFont.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 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 SkPDFFont_DEFINED
+#define SkPDFFont_DEFINED
+
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkPDFTypes.h"
+#include "SkTDArray.h"
+#include "SkThread.h"
+
+class SkPaint;
+
+/** \class SkPDFFont
+    A PDF Object class representing a font.  The font may have resources
+    attached to it in order to embed the font.  SkPDFFonts are canonicalized
+    so that resource deduplication will only include one copy of a font.
+    This class uses the same pattern as SkPDFGraphicState, a static weak
+    reference to each instantiated class.
+*/
+class SkPDFFont : public SkPDFDict {
+public:
+    SK_API virtual ~SkPDFFont();
+
+    SK_API virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+    /** Returns the typeface represented by this class. Returns NULL for the
+     *  default typeface.
+     */
+    SK_API SkTypeface* typeface();
+
+    /** Returns the font type represented in this font.  For Type0 fonts,
+     *  returns the type of the decendant font.
+     */
+    SK_API SkAdvancedTypefaceMetrics::FontType getType();
+
+    /** Return true if this font has an encoding for the passed glyph id.
+     */
+    SK_API bool hasGlyph(uint16_t glyphID);
+
+    /** Returns true if this font encoding supports glyph IDs above 255.
+     */
+    SK_API bool multiByteGlyphs();
+
+    /** Convert (in place) the input glyph IDs into the font encoding.  If the
+     *  font has more glyphs than can be encoded (like a type 1 font with more
+     *  than 255 glyphs) this method only converts up to the first out of range
+     *  glyph ID.
+     *  @param glyphIDs       The input text as glyph IDs.
+     *  @param numGlyphs      The number of input glyphs.
+     *  @return               Returns the number of glyphs consumed.
+     */
+    SK_API size_t glyphsToPDFFontEncoding(uint16_t* glyphIDs, size_t numGlyphs);
+
+    /** Get the font resource for the passed typeface and glyphID. The
+     *  reference count of the object is incremented and it is the caller's
+     *  responsibility to unreference it when done.  This is needed to
+     *  accommodate the weak reference pattern used when the returned object
+     *  is new and has no other references.
+     *  @param typeface  The typeface to find.
+     *  @param glyphID   Specify which section of a large font is of interest.
+     */
+    SK_API static SkPDFFont* getFontResource(SkTypeface* typeface,
+                                             uint16_t glyphID);
+
+private:
+    SkRefPtr<SkTypeface> fTypeface;
+    SkAdvancedTypefaceMetrics::FontType fType;
+#ifdef SK_DEBUG
+    bool fDescendant;
+#endif
+    bool fMultiByteGlyphs;
+
+    // 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;
+
+    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);
+    };
+
+    // This should be made a hash table if performance is a problem.
+    static SkTDArray<FontRec>& canonicalFonts();
+    static SkMutex& canonicalFontsMutex();
+
+    /** Construct a new font dictionary and support objects.
+     *  @param fontInfo       Information about the to create.
+     *  @param typeface       The typeface for the font.
+     *  @param glyphID        The glyph ID the caller is interested in. This
+     *                        is important only for Type1 fonts, which have
+     *                        more than 255 glyphs.
+     *  @param descendantFont If this is the descendant (true) or root
+     *                        (Type 0 font - false) font dictionary.  Only True
+     *                        Type and CID encoded fonts will use a true value.
+     *  @param fontDescriptor If the font descriptor has already have generated
+     *                        for this font, pass it in here, otherwise pass
+     *                        NULL.
+     */
+    SkPDFFont(class SkAdvancedTypefaceMetrics* fontInfo, SkTypeface* typeface,
+              uint16_t glyphID, bool descendantFont, SkPDFDict* fontDescriptor);
+
+    void populateType0Font();
+    void populateCIDFont();
+    bool populateType1Font(int16_t glyphID);
+
+    /** Populate the PDF font dictionary as Type3 font which includes glyph
+     *  descriptions with instructions for painting the glyphs. This function
+     *  doesn't use any fields from SkAdvancedTypefaceMetrics (fFontInfo). Font
+     *  information including glyph paths are queried from the platform
+     *  dependent SkGlyphCache.
+    */
+    void populateType3Font(int16_t glyphID);
+    bool addFontDescriptor(int16_t defaultWidth);
+    void populateToUnicodeTable();
+    void addWidthInfoFromRange(int16_t defaultWidth,
+        const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry);
+    /** Set fFirstGlyphID and fLastGlyphID to span at most 255 glyphs,
+     *  including the passed glyphID.
+     */
+    void adjustGlyphRangeForSingleByteEncoding(int16_t glyphID);
+
+    static bool find(uint32_t fontID, uint16_t glyphID, int* index);
+};
+
+#endif
diff --git a/include/pdf/SkPDFFormXObject.h b/include/pdf/SkPDFFormXObject.h
new file mode 100644
index 0000000..41719f0
--- /dev/null
+++ b/include/pdf/SkPDFFormXObject.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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 SkPDFObject {
+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 emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+    virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+    /** Add the value to the stream 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 stream dictionary with the given key.  Refs value.
+     *  @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);
+
+private:
+    SkRefPtr<SkPDFStream> fStream;
+    SkTDArray<SkPDFObject*> fResources;
+};
+
+#endif
diff --git a/include/pdf/SkPDFGraphicState.h b/include/pdf/SkPDFGraphicState.h
new file mode 100644
index 0000000..49809a4
--- /dev/null
+++ b/include/pdf/SkPDFGraphicState.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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) : fPaint(paint) {}
+    };
+
+    // This should be made a hash table if performance is a problem.
+    static SkTDArray<GSCanonicalEntry>& canonicalPaints();
+    static SkMutex& canonicalPaintsMutex();
+
+    SkPDFGraphicState();
+    explicit SkPDFGraphicState(const SkPaint& paint);
+
+    void populateDict();
+
+    static SkPDFObject* GetInvertFunction();
+
+    static int find(const SkPaint& paint);
+};
+
+#endif
diff --git a/include/pdf/SkPDFImage.h b/include/pdf/SkPDFImage.h
new file mode 100644
index 0000000..945ff9e
--- /dev/null
+++ b/include/pdf/SkPDFImage.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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 SkPDFObject {
+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 emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+    virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+private:
+    SkRefPtr<SkPDFStream> fStream;
+    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);
+
+    /** Add the value to the stream 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 stream dictionary with the given key.  Refs value.
+     *  @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);
+};
+
+#endif
diff --git a/include/pdf/SkPDFPage.h b/include/pdf/SkPDFPage.h
new file mode 100644
index 0000000..0e30028
--- /dev/null
+++ b/include/pdf/SkPDFPage.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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(const SkRefPtr<SkPDFDevice>& content);
+    ~SkPDFPage();
+
+    /** Before a page and its contents can be sized and emitted, it must
+     *  be finalized.  No changes to the PDFDevice will be honored after
+     *  finalizePage has been called.  This function adds the page content
+     *  to the passed catalog, so it must be called for each document
+     *  that the page is part of.
+     *  @param catalog         The catalog to add page content objects to.
+     *  @param firstPage       Indicate if this is the first page of a document.
+     *  @param resourceObjects The resource objects used on the page are added
+     *                         to this array.  This gives the caller a chance
+     *                         to deduplicate resources across pages.
+     */
+    void finalizePage(SkPDFCatalog* catalog, bool firstPage,
+                      SkTDArray<SkPDFObject*>* resourceObjects);
+
+    /** Determine the size of the page content and store to the catalog
+     *  the offsets of all nonresource-indirect objects that make up the page
+     *  content.  This must be called before emitPage(), but after finalizePage.
+     *  @param catalog    The catalog to add the object offsets to.
+     *  @param fileOffset The file offset where the page content will be
+     *                    emitted.
+     */
+    off_t getPageSize(SkPDFCatalog* catalog, off_t fileOffset);
+
+    /** Output the page content to the passed stream.
+     *  @param stream     The writable output stream to send the content to.
+     *  @param catalog    The active object catalog.
+     */
+    void emitPage(SkWStream* stream, SkPDFCatalog* catalog);
+
+    /** Generate a page tree for the passed vector of pages.  New objects are
+     *  added to the catalog.  The pageTree vector is populated with all of
+     *  the 'Pages' dictionaries as well as the 'Page' objects.  Page trees
+     *  have both parent and children links, creating reference cycles, so
+     *  it must be torn down explicitly.  The first page is not added to
+     *  the pageTree dictionary array so the caller can handle it specially.
+     *  @param pages      The ordered vector of page objects.
+     *  @param catalog    The catalog to add new objects into.
+     *  @param pageTree   An output vector with all of the internal and leaf
+     *                    nodes of the pageTree.
+     *  @param rootNode   An output parameter set to the root node.
+     */
+    static void generatePageTree(const SkTDArray<SkPDFPage*>& pages,
+                                 SkPDFCatalog* catalog,
+                                 SkTDArray<SkPDFDict*>* pageTree,
+                                 SkPDFDict** rootNode);
+
+    /** Get the fonts used on this page.
+     */
+    SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
+
+private:
+    // Multiple pages may reference the content.
+    SkRefPtr<SkPDFDevice> fDevice;
+
+    // Once the content is finalized, put it into a stream for output.
+    SkRefPtr<SkPDFStream> fContentStream;
+};
+
+#endif
diff --git a/include/pdf/SkPDFShader.h b/include/pdf/SkPDFShader.h
new file mode 100644
index 0000000..17f7f03
--- /dev/null
+++ b/include/pdf/SkPDFShader.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 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 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 SkPDFObject {
+public:
+    virtual ~SkPDFShader();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+    virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+    /** 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 SkPDFShader* getPDFShader(const SkShader& shader,
+                                     const SkMatrix& matrix,
+                                     const SkIRect& surfaceBBox);
+
+private:
+    class 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;
+    };
+
+    SkRefPtr<SkPDFDict> fContent;
+    SkTDArray<SkPDFObject*> fResources;
+    SkAutoTDelete<const State> fState;
+
+    class ShaderCanonicalEntry {
+    public:
+        SkPDFShader* fPDFShader;
+        const State* fState;
+
+        bool operator==(const ShaderCanonicalEntry& b) const {
+            return fPDFShader == b.fPDFShader || *fState == *b.fState;
+        }
+        ShaderCanonicalEntry(SkPDFShader* pdfShader, const State* state)
+            : fPDFShader(pdfShader),
+              fState(state) {
+        }
+    };
+    // This should be made a hash table if performance is a problem.
+    static SkTDArray<ShaderCanonicalEntry>& canonicalShaders();
+    static SkMutex& canonicalShadersMutex();
+
+    static SkPDFObject* rangeObject();
+
+    SkPDFShader(State* state);
+
+    void doFunctionShader();
+    void doImageShader();
+    SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain);
+};
+
+#endif
diff --git a/include/pdf/SkPDFStream.h b/include/pdf/SkPDFStream.h
new file mode 100644
index 0000000..a975ad6
--- /dev/null
+++ b/include/pdf/SkPDFStream.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 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 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.
+     *  @param stream The data part of the stream.
+     */
+    explicit SkPDFStream(SkStream* stream);
+    virtual ~SkPDFStream();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+private:
+    size_t fLength;
+    // Only one of the two streams will be valid.
+    SkRefPtr<SkStream> fPlainData;
+    SkDynamicMemoryWStream fCompressedData;
+
+    typedef SkPDFDict INHERITED;
+};
+
+#endif
diff --git a/include/pdf/SkPDFTypes.h b/include/pdf/SkPDFTypes.h
new file mode 100644
index 0000000..6b5146a
--- /dev/null
+++ b/include/pdf/SkPDFTypes.h
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFTypes_DEFINED
+#define SkPDFTypes_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+#include "SkString.h"
+#include "SkTDArray.h"
+#include "SkTypes.h"
+
+class SkPDFCatalog;
+class SkWStream;
+
+/** \class SkPDFObject
+
+    A PDF Object is the base class for primitive elements in a PDF file.  A
+    common subtype is used to ease the use of indirect object references,
+    which are common in the PDF format.
+*/
+class SkPDFObject : public SkRefCnt {
+public:
+    /** Create a PDF object.
+     */
+    SkPDFObject();
+    virtual ~SkPDFObject();
+
+    /** 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;
+
+    /** Return the size (number of bytes) of this object in the final output
+     *  file. Compound objects or objects that are computationally intensive
+     *  to output should override this method.
+     *  @param catalog  The object catalog to use.
+     *  @param indirect If true, output an object identifier with the object.
+     */
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+    /** If this object explicitly depends on other objects, add them to the
+     *  end of the list.  This only applies to higher level object, where
+     *  the depenency is explicit and introduced by the class.  i.e. an
+     *  SkPDFImage added to an SkPDFDevice, but not an SkPDFObjRef added to
+     *  an SkPDFArray.
+     *  @param resourceList  The list to append dependant resources to.
+     */
+    virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+    /** 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);
+};
+
+/** \class SkPDFObjRef
+
+    An indirect reference to a PDF object.
+*/
+class SkPDFObjRef : public SkPDFObject {
+public:
+    /** Create a reference to an existing SkPDFObject.
+     *  @param obj The object to reference.
+     */
+    explicit SkPDFObjRef(SkPDFObject* obj);
+    virtual ~SkPDFObjRef();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+private:
+    SkRefPtr<SkPDFObject> fObj;
+};
+
+/** \class SkPDFInt
+
+    An integer object in a PDF.
+*/
+class SkPDFInt : public SkPDFObject {
+public:
+    /** Create a PDF integer (usually for indirect reference purposes).
+     *  @param value An integer value between 2^31 - 1 and -2^31.
+     */
+    explicit SkPDFInt(int32_t value);
+    virtual ~SkPDFInt();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+
+private:
+    int32_t fValue;
+};
+
+/** \class SkPDFBool
+
+    An boolean value in a PDF.
+*/
+class SkPDFBool : public SkPDFObject {
+public:
+    /** Create a PDF boolean.
+     *  @param value true or false.
+     */
+    explicit SkPDFBool(bool value);
+    virtual ~SkPDFBool();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+private:
+    bool fValue;
+};
+
+/** \class SkPDFScalar
+
+    A real number object in a PDF.
+*/
+class SkPDFScalar : public SkPDFObject {
+public:
+    /** Create a PDF real number.
+     *  @param value A real value.
+     */
+    explicit SkPDFScalar(SkScalar value);
+    virtual ~SkPDFScalar();
+
+    static void Append(SkScalar value, SkWStream* stream);
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+
+private:
+    SkScalar fValue;
+};
+
+/** \class SkPDFString
+
+    A string object in a PDF.
+*/
+class SkPDFString : public SkPDFObject {
+public:
+    /** Create a PDF string. Maximum length (in bytes) is 65,535.
+     *  @param value A string value.
+     */
+    explicit SkPDFString(const char value[]);
+    explicit SkPDFString(const SkString& value);
+
+    /** Create a PDF string. Maximum length (in bytes) is 65,535.
+     *  @param value     A string value.
+     *  @param len       The length of value.
+     *  @param wideChars Indicates if the top byte in value is significant and
+     *                   should be encoded (true) or not (false).
+     */
+    SkPDFString(const uint16_t* value, size_t len, bool wideChars);
+    virtual ~SkPDFString();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+    static SkString formatString(const char* input, size_t len);
+    static SkString formatString(const uint16_t* input, size_t len,
+                                 bool wideChars);
+private:
+    static const size_t kMaxLen = 65535;
+
+    const SkString fValue;
+
+    static SkString doFormatString(const void* input, size_t len,
+                                 bool wideInput, bool wideOutput);
+};
+
+/** \class SkPDFName
+
+    A name object in a PDF.
+*/
+class SkPDFName : public SkPDFObject {
+public:
+    /** Create a PDF name object. Maximum length is 127 bytes.
+     *  @param value The name.
+     */
+    explicit SkPDFName(const char name[]);
+    explicit SkPDFName(const SkString& name);
+    virtual ~SkPDFName();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+private:
+    static const size_t kMaxLen = 127;
+
+    const SkString fValue;
+
+    static SkString formatName(const SkString& input);
+};
+
+/** \class SkPDFArray
+
+    An array object in a PDF.
+*/
+class SkPDFArray : public SkPDFObject {
+public:
+    /** Create a PDF array. Maximum length is 8191.
+     */
+    SkPDFArray();
+    virtual ~SkPDFArray();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+    /** The size of the array.
+     */
+    int size() { return fValue.count(); }
+
+    /** Preallocate space for the given number of entries.
+     *  @param length The number of array slots to preallocate.
+     */
+    void reserve(int length);
+
+    /** Returns the object at the given offset in the array.
+     *  @param index The index into the array to retrieve.
+     */
+    SkPDFObject* getAt(int index) { return fValue[index]; }
+
+    /** Set the object at the given offset in the array. Ref's value.
+     *  @param index The index into the array to set.
+     *  @param value The value to add to the array.
+     *  @return The value argument is returned.
+     */
+    SkPDFObject* setAt(int index, SkPDFObject* value);
+
+    /** Append the object to the end of the array and increments its ref count.
+     *  @param value The value to add to the array.
+     *  @return The value argument is returned.
+     */
+    SkPDFObject* append(SkPDFObject* value);
+
+private:
+    static const int kMaxLen = 8191;
+    SkTDArray<SkPDFObject*> fValue;
+};
+
+/** \class SkPDFDict
+
+    A dictionary object in a PDF.
+*/
+class SkPDFDict : public SkPDFObject {
+public:
+    /** Create a PDF dictionary. Maximum number of entries is 4095.
+     */
+    SkPDFDict();
+
+    /** Create a PDF dictionary with a Type entry.
+     *  @param type   The value of the Type entry.
+     */
+    explicit SkPDFDict(const char type[]);
+
+    virtual ~SkPDFDict();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+    /** The size of the dictionary.
+     */
+    int size() { return fValue.count(); }
+
+    /** Add the value to the dictionary with the given key.  Refs value.
+     *  @param key   The key for this dictionary entry.
+     *  @param value The value for this dictionary entry.
+     *  @return The value argument is returned.
+     */
+    SkPDFObject* insert(SkPDFName* key, SkPDFObject* value);
+
+    /** Add the value to the dictionary with the given key.  Refs value.  The
+     *  method will create the SkPDFName object.
+     *  @param key   The text of the key for this dictionary entry.
+     *  @param value The value for this dictionary entry.
+     *  @return The value argument is returned.
+     */
+    SkPDFObject* insert(const char key[], SkPDFObject* value);
+
+    /** Remove all entries from the dictionary.
+     */
+    void clear();
+
+private:
+    static const int kMaxLen = 4095;
+
+    struct Rec {
+      SkPDFName* key;
+      SkPDFObject* value;
+    };
+
+    SkTDArray<struct Rec> fValue;
+};
+
+#endif
diff --git a/include/pdf/SkPDFUtils.h b/include/pdf/SkPDFUtils.h
new file mode 100644
index 0000000..50da28c
--- /dev/null
+++ b/include/pdf/SkPDFUtils.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 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 SkPDFUtils_DEFINED
+#define SkPDFUtils_DEFINED
+
+#include "SkPath.h"
+
+class SkMatrix;
+class SkPath;
+class SkPDFArray;
+struct SkRect;
+
+#if 0
+#define PRINT_NOT_IMPL(str) fprintf(stderr, str)
+#else
+#define PRINT_NOT_IMPL(str)
+#endif
+
+#define NOT_IMPLEMENTED(condition, assert)                         \
+    do {                                                           \
+        if (condition) {                                           \
+            PRINT_NOT_IMPL("NOT_IMPLEMENTED: " #condition "\n");   \
+            SkDEBUGCODE(SkASSERT(!assert);)                        \
+        }                                                          \
+    } while(0)
+
+class SkPDFUtils {
+public:
+    static SkPDFArray* MatrixToArray(const SkMatrix& matrix);
+    static void AppendTransform(const SkMatrix& matrix, SkWStream* content);
+
+    static void MoveTo(SkScalar x, SkScalar y, SkWStream* content);
+    static void AppendLine(SkScalar x, SkScalar y, SkWStream* content);
+    static void AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
+                            SkScalar ctl2X, SkScalar ctl2Y,
+                            SkScalar dstX, SkScalar dstY, SkWStream* content);
+    static void AppendRectangle(const SkRect& rect, SkWStream* content);
+    static void EmitPath(const SkPath& path, SkWStream* content);
+    static void ClosePath(SkWStream* content);
+    static void PaintPath(SkPaint::Style style, SkPath::FillType fill,
+                          SkWStream* content);
+    static void StrokePath(SkWStream* content);
+    static void DrawFormXObject(int objectIndex, SkWStream* content);
+    static void ApplyGraphicState(int objectIndex, SkWStream* content);
+};
+
+#endif
diff --git a/include/pipe/SkGPipe.h b/include/pipe/SkGPipe.h
new file mode 100644
index 0000000..897766f
--- /dev/null
+++ b/include/pipe/SkGPipe.h
@@ -0,0 +1,96 @@
+/*
+    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 SkGPipe_DEFINED
+#define SkGPipe_DEFINED
+
+#include "SkWriter32.h"
+#include "SkFlattenable.h"
+
+class SkCanvas;
+
+class SkGPipeReader {
+public:
+    SkGPipeReader(SkCanvas* target);
+    ~SkGPipeReader();
+
+    enum Status {
+        kDone_Status,   //!< no more data expected from reader
+        kEOF_Status,    //!< need more data from reader
+        kError_Status   //!< encountered error
+    };
+
+    // data must be 4-byte aligned
+    // length must be a multiple of 4
+    Status playback(const void* data, size_t length, size_t* bytesRead = NULL);
+
+private:
+    SkCanvas*           fCanvas;
+    class SkGPipeState* fState;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkGPipeController {
+public:
+    /**
+     *  Called periodically by the writer, to get a working buffer of RAM to
+     *  write into. The actual size of the block is also returned, and must be
+     *  actual >= minRequest. If NULL is returned, then actual is ignored and
+     *  writing will stop.
+     *
+     *  The returned block must be 4-byte aligned, and actual must be a
+     *  multiple of 4.
+     *  minRequest will always be a multiple of 4.
+     */
+    virtual void* requestBlock(size_t minRequest, size_t* actual) = 0;
+
+    /**
+     *  This is called each time some atomic portion of the data has been
+     *  written to the block (most recently returned by requestBlock()).
+     *  If bytes == 0, then the writer has finished.
+     *
+     *  bytes will always be a multiple of 4.
+     */
+    virtual void notifyWritten(size_t bytes) = 0;
+};
+
+class SkGPipeWriter {
+public:
+    SkGPipeWriter();
+    ~SkGPipeWriter();
+
+    bool isRecording() const { return NULL != fCanvas; }
+
+    enum Flags {
+        kCrossProcess_Flag = 1 << 0,
+    };
+
+    SkCanvas* startRecording(SkGPipeController*, uint32_t flags = 0);
+
+    // called in destructor, but can be called sooner once you know there
+    // should be no more drawing calls made into the recording canvas.
+    void endRecording();
+
+private:
+    class SkGPipeCanvas* fCanvas;
+    SkGPipeController*   fController;
+    SkFactorySet         fFactorySet;
+    SkWriter32 fWriter;
+};
+
+#endif
diff --git a/include/utils/SkEGLContext.h b/include/utils/SkEGLContext.h
index 4b17be1..ced31a5 100644
--- a/include/utils/SkEGLContext.h
+++ b/include/utils/SkEGLContext.h
@@ -1,20 +1,53 @@
 #ifndef SkEGLContext_DEFINED
 #define SkEGLContext_DEFINED
 
-#include "SkTypes.h"
+#if defined(SK_MESA)
+    #include "GL/osmesa.h"
+#elif 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>
+#else
+
+#endif
 
 /**
  *  Create an offscreen opengl context
  */
 class SkEGLContext {
 public:
-	SkEGLContext();
-	~SkEGLContext();
+    SkEGLContext();
+    ~SkEGLContext();
 
-	bool init(int width, int height);
+    bool init(const int width, const int height);
 
 private:
-	void* fContext;
+#if defined(SK_MESA)
+    OSMesaContext context;
+    GLfloat *image;
+#elif defined(SK_BUILD_FOR_MAC)
+    AGLContext context;
+#elif defined(SK_BUILD_FOR_ANDROID)
+
+#elif defined(SK_BUILD_FOR_UNIX)
+    GLXContext context;
+    Display *display;
+    Pixmap pixmap;
+    GLXPixmap glxPixmap;
+#elif defined(SK_BUILD_FOR_WIN32)
+    HWND fWindow;
+    HDC fDeviceContext;
+    HGLRC fGlRenderContext;
+#else
+
+#endif
 };
 
 #endif
diff --git a/include/utils/android/AndroidKeyToSkKey.h b/include/utils/android/AndroidKeyToSkKey.h
new file mode 100644
index 0000000..7b0a035
--- /dev/null
+++ b/include/utils/android/AndroidKeyToSkKey.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 Skia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_TO_SKIA_KEYCODES_H
+#define _ANDROID_TO_SKIA_KEYCODES_H
+
+#include "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;
+        default:
+            return kNONE_SkKey;
+    }
+}
+
+#endif
diff --git a/include/utils/unix/XkeysToSkKeys.h b/include/utils/unix/XkeysToSkKeys.h
index 3d41a22..1852d99 100644
--- a/include/utils/unix/XkeysToSkKeys.h
+++ b/include/utils/unix/XkeysToSkKeys.h
@@ -8,6 +8,14 @@
 
 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:
diff --git a/include/views/SkApplication.h b/include/views/SkApplication.h
new file mode 100644
index 0000000..4c4a4fb
--- /dev/null
+++ b/include/views/SkApplication.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkApplication_DEFINED
+#define SkApplication_DEFINED
+
+class SkOSWindow;
+
+extern SkOSWindow* create_sk_window(void* hwnd);
+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..1bca42f
--- /dev/null
+++ b/include/views/SkBGViewArtist.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkBGViewArtist_DEFINED
+#define SkBGViewArtist_DEFINED
+
+#include "SkView.h"
+#include "SkPaint.h"
+
+class SkBGViewArtist : public SkView::Artist {
+public:
+            SkBGViewArtist(SkColor c = SK_ColorWHITE);
+    virtual ~SkBGViewArtist();
+
+    const SkPaint&  paint() const { return fPaint; }
+    SkPaint&        paint() { return fPaint; }
+
+protected:
+    // overrides
+    virtual void onDraw(SkView*, SkCanvas*);
+    virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+
+private:
+    SkPaint fPaint;
+};
+
+#endif
+
diff --git a/include/views/SkBorderView.h b/include/views/SkBorderView.h
new file mode 100644
index 0000000..94ccc1f
--- /dev/null
+++ b/include/views/SkBorderView.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkBorderView_DEFINED
+#define SkBorderView_DEFINED
+
+#include "SkView.h"
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+
+class SkBorderView : public SkWidgetView {
+public:
+    SkBorderView();
+    ~SkBorderView();
+    void setSkin(const char skin[]);
+    SkScalar getLeft() const { return fLeft; }
+    SkScalar getRight() const { return fRight; }
+    SkScalar getTop() const { return fTop; }
+    SkScalar getBottom() const { return fBottom; }
+protected:
+    //overrides
+    virtual void onInflate(const SkDOM& dom,  const SkDOM::Node* node);
+    virtual void onSizeChange();
+    virtual void onDraw(SkCanvas* canvas);
+    virtual bool onEvent(const SkEvent& evt);
+private:
+    SkAnimator fAnim;
+    SkScalar fLeft, fRight, fTop, fBottom;  //margin on each side
+    SkRect fMargin;
+
+    typedef SkWidgetView INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkEvent.h b/include/views/SkEvent.h
new file mode 100644
index 0000000..f6719d6
--- /dev/null
+++ b/include/views/SkEvent.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#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
+
+    SkEvents are used to communicate type-safe information to SkEventSinks.
+    SkEventSinks (including SkViews) each have a unique ID, which is stored
+    in an event. This ID is used to target the event once it has been "posted".
+*/
+class SkEvent {
+public:
+    /** Default construct, creating an empty event.
+    */
+    SkEvent();
+    /** Construct a new event with the specified type.
+    */
+    explicit SkEvent(const SkString& type);
+    /** Construct a new event with the specified type.
+    */
+    explicit SkEvent(const char type[]);
+    /** Construct a new event by copying the fields from the src event.
+    */
+    SkEvent(const SkEvent& src);
+    ~SkEvent();
+
+//  /** Return the event's type (will never be null) */
+//  const char* getType() const;
+    /** 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.
+        In XML, use the "type" attribute.
+    */
+    void    setType(const SkString&);
+    /** Set the event's type to the specified string.
+        In XML, use the "type" attribute.
+    */
+    void    setType(const char type[], size_t len = 0);
+
+    /** Return the event's unnamed 32bit field. Default value is 0 */
+    uint32_t getFast32() const { return f32; }
+    /** Set the event's unnamed 32bit field. In XML, use
+        the subelement <data fast32=... />
+    */
+    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; }
+
+    void tron() { SkDEBUGCODE(fDebugTrace = true;) }
+    void troff() { SkDEBUGCODE(fDebugTrace = false;) }
+    bool isDebugTrace() const
+    {
+#ifdef SK_DEBUG
+        return fDebugTrace;
+#else
+        return false;
+#endif
+    }
+
+    /** 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 the specified event to the event queue, targeting the specified eventsink, with an optional
+        delay. The event must be dynamically allocated for this. It cannot be a global or on the stack.
+        After this call, ownership is transfered to the system, so the caller must not retain
+        the event's ptr. Returns false if the event could not be posted (which means it will have been deleted).
+    */
+    static bool Post(SkEvent* evt, SkEventSinkID targetID, SkMSec delay = 0);
+    /** Post the specified event to the event queue, targeting the specified eventsink, to be delivered on/after the
+        specified millisecond time. The event must be dynamically allocated for this. It cannot be a global or on the stack.
+        After this call, ownership is transfered to the system, so the caller must not retain
+        the event's ptr. Returns false if the event could not be posted (which means it will have been deleted).
+    */
+    static bool PostTime(SkEvent* evt, SkEventSinkID targetID, SkMSec time);
+
+    /** Helper method for calling SkEvent::PostTime(this, ...), where the caller specifies a delay.
+        The real "time" will be computed automatically by sampling the clock and adding its value
+        to delay.
+    */
+    bool post(SkEventSinkID sinkID, SkMSec delay = 0)
+    {
+        return SkEvent::Post(this, sinkID, delay);
+    }
+
+    void postTime(SkEventSinkID sinkID, SkMSec time)
+    {
+        SkEvent::PostTime(this, sinkID, time);
+    }
+
+    ///////////////////////////////////////////////
+    /** Porting layer must call these functions **/
+    ///////////////////////////////////////////////
+
+    /** Global initialization function for the SkEvent system. Should be called exactly
+        once before any other event method is called, and should be called after the
+        call to SkGraphics::Init().
+    */
+    static void     Init();
+    /** Global cleanup function for the SkEvent system. Should be called exactly once after
+        all event methods have been called, and should be called before calling SkGraphics::Term().
+    */
+    static void     Term();
+
+    /** Call this to process one event from the queue. If it returns true, there are more events
+        to process.
+    */
+    static bool     ProcessEvent();
+    /** Call this whenever the requested timer has expired (requested by a call to SetQueueTimer).
+        It will post any delayed events whose time as "expired" onto the event queue.
+        It may also call SignalQueueTimer() and SignalNonEmptyQueue().
+    */
+    static void     ServiceQueueTimer();
+
+    /** Return the number of queued events. note that this value may be obsolete
+        upon return, since another thread may have called ProcessEvent() or
+        Post() after the count was made.
+     */
+    static int CountEventsOnQueue();
+
+    ////////////////////////////////////////////////////
+    /** Porting layer must implement these functions **/
+    ////////////////////////////////////////////////////
+
+    /** Called whenever an SkEvent is posted to an empty queue, so that the OS
+        can be told to later call Dequeue().
+    */
+    static void SignalNonEmptyQueue();
+    /** Called whenever the delay until the next delayed event changes. If zero is
+        passed, then there are no more queued delay events.
+    */
+    static void SignalQueueTimer(SkMSec delay);
+
+#ifndef SK_USE_WXWIDGETS
+#ifdef SK_BUILD_FOR_WIN
+    static bool WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+#elif defined(SK_BUILD_FOR_UNIXx)
+  static uint32_t HandleTimer(uint32_t, void*);
+  static bool WndProc(Display*, Window, XEvent&);
+#endif
+#else
+    // Don't know yet what this will be
+    //static bool CustomEvent();
+#endif
+
+private:
+    SkMetaData      fMeta;
+    mutable char*   fType;  // may be characters with low bit set to know that it is not a pointer
+    uint32_t        f32;
+    SkDEBUGCODE(bool fDebugTrace;)
+
+    // these are for our implementation of the event queue
+    SkEventSinkID   fTargetID;
+    SkMSec          fTime;
+    SkEvent*        fNextEvent; // either in the delay or normal event queue
+    void initialize(const char* type, size_t typeLen);
+
+    static bool Enqueue(SkEvent* evt);
+    static SkMSec EnqueueTime(SkEvent* evt, SkMSec time);
+    static SkEvent* Dequeue(SkEventSinkID* targetID);
+    static bool     QHasEvents();
+};
+
+#endif
+
diff --git a/include/views/SkEventSink.h b/include/views/SkEventSink.h
new file mode 100644
index 0000000..27a6743
--- /dev/null
+++ b/include/views/SkEventSink.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkEventSink_DEFINED
+#define SkEventSink_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkEvent.h"
+
+struct SkTagList;
+
+/** \class SkEventSink
+
+    SkEventSink is the base class for all objects that receive SkEvents.
+*/
+class SkEventSink : public SkRefCnt {
+public:
+            SkEventSink();
+    virtual ~SkEventSink();
+
+    /** Returns this eventsink's unique ID. Use this to post SkEvents to
+        this eventsink.
+    */
+    SkEventSinkID getSinkID() const { return fID; }
+
+    /** Call this to pass an event to this object for processing. Returns true if the
+        event was handled.
+    */
+    bool doEvent(const SkEvent&);
+    /** Returns true if the sink (or one of its subclasses) understands the event as a query.
+        If so, the sink may modify the event to communicate its "answer".
+    */
+    bool doQuery(SkEvent* query);
+
+    /** Add sinkID to the list of listeners, to receive events from calls to sendToListeners()
+        and postToListeners(). If sinkID already exists in the listener list, no change is made.
+    */
+    void    addListenerID(SkEventSinkID sinkID);
+    /** Copy listeners from one event sink to another, typically from parent to child.
+        @param from the event sink to copy the listeners from
+    */
+    void copyListeners(const SkEventSink& from);
+    /** Remove sinkID from the list of listeners. If sinkID does not appear in the list,
+        no change is made.
+    */
+    void    removeListenerID(SkEventSinkID);
+    /** Returns true if there are 1 or more listeners attached to this eventsink
+    */
+    bool    hasListeners() const;
+    /** Posts a copy of evt to each of the eventsinks in the lisener list.
+    */
+    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 searching for an eventsink object that matches the targetID.
+        If one is found, it calls the sink's doEvent method, returning
+        either kHandled_EventResult or kNotHandled_EventResult. If no matching
+        eventsink is found, kSinkNotFound_EventResult is returned.
+    */
+    static EventResult DoEvent(const SkEvent&, SkEventSinkID targetID);
+
+    /** Returns the matching eventsink, or null if not found
+    */
+    static SkEventSink* FindSink(SkEventSinkID);
+
+protected:
+    /** Override this to handle events in your subclass. Be sure to call the inherited version
+        for events that you don't handle.
+    */
+    virtual bool onEvent(const SkEvent&);
+    virtual bool onQuery(SkEvent*);
+
+    SkTagList*  findTagList(U8CPU tag) const;
+    void        addTagList(SkTagList*);
+    void        removeTagList(U8CPU tag);
+
+private:
+    SkEventSinkID   fID;
+    SkTagList*      fTagHead;
+
+    // for our private link-list
+    SkEventSink*    fNextSink;
+};
+
+#endif
+
diff --git a/include/views/SkImageView.h b/include/views/SkImageView.h
new file mode 100644
index 0000000..57215c9
--- /dev/null
+++ b/include/views/SkImageView.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkImageView_DEFINED
+#define SkImageView_DEFINED
+
+#include "SkView.h"
+#include "SkString.h"
+
+class SkAnimator;
+class SkBitmap;
+class SkMatrix;
+
+class SkImageView : public SkView {
+public:
+            SkImageView();
+    virtual ~SkImageView();
+
+    void    getUri(SkString*) const;
+    void    setUri(const char []);
+    void    setUri(const SkString&);
+    
+
+    enum ScaleType {
+        kMatrix_ScaleType,
+        kFitXY_ScaleType,
+        kFitStart_ScaleType,
+        kFitCenter_ScaleType,
+        kFitEnd_ScaleType
+    };
+    ScaleType   getScaleType() const { return (ScaleType)fScaleType; }
+    void        setScaleType(ScaleType);
+    
+    bool    getImageMatrix(SkMatrix*) const;
+    void    setImageMatrix(const SkMatrix*);
+
+protected:
+    // overrides
+    virtual bool    onEvent(const SkEvent&);
+    virtual void    onDraw(SkCanvas*);
+    virtual void    onInflate(const SkDOM&, const SkDOMNode*);
+    
+private:
+    SkString    fUri;
+    SkMatrix*   fMatrix;    // null or copy of caller's matrix ,,,,,
+    union {
+        SkAnimator* fAnim;
+        SkBitmap* fBitmap;
+    } fData;
+    uint8_t     fScaleType;
+    SkBool8     fDataIsAnim;    // as opposed to bitmap
+    SkBool8     fUriIsValid;
+    
+    void    onUriChange();
+    bool    getDataBounds(SkRect* bounds);
+    bool    freeData();
+    bool    ensureUriIsLoaded();
+
+    typedef SkView INHERITED;
+};
+
+#endif
diff --git a/include/views/SkKey.h b/include/views/SkKey.h
new file mode 100644
index 0000000..3fd5114
--- /dev/null
+++ b/include/views/SkKey.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#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..433a601
--- /dev/null
+++ b/include/views/SkOSMenu.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkOSMenu_DEFINED
+#define SkOSMenu_DEFINED
+
+#include "SkEvent.h"
+#include "SkTDArray.h"
+
+class SkOSMenu {
+public:
+    explicit SkOSMenu(const char title[]);
+    ~SkOSMenu();
+
+    const char* getTitle() const { return fTitle; }
+
+    void    appendItem(const char title[], const char eventType[], int32_t eventData);
+
+    // called by SkOSWindow when it receives an OS menu event
+    int         countItems() const;
+    const char* getItem(int index, uint32_t* cmdID) const;
+
+    SkEvent* createEvent(uint32_t os_cmd);
+
+private:
+    const char* fTitle;
+
+    struct Item {
+        const char* fTitle;
+        const char* fEventType;
+        uint32_t    fEventData;
+        uint32_t    fOSCmd; // internal
+    };
+    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..38a4cf8
--- /dev/null
+++ b/include/views/SkOSWindow_Android.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 Skia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkOSWindow_Android_DEFINED
+#define SkOSWindow_Android_DEFINED
+
+#include "SkWindow.h"
+#include "SkEvent.h"
+
+class SkOSWindow : public SkWindow {
+public:
+    SkOSWindow(void*) {}
+    ~SkOSWindow() {}
+    bool attachGL() { return false; }
+    void detachGL() {}
+    void presentGL() {}
+
+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..232a202
--- /dev/null
+++ b/include/views/SkOSWindow_Mac.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkOSWindow_Mac_DEFINED
+#define SkOSWindow_Mac_DEFINED
+
+#include <Carbon/Carbon.h>
+#include "SkWindow.h"
+
+class SkOSWindow : public SkWindow {
+public:
+    SkOSWindow(void* hwnd);
+
+    void*   getHWND() const { return fHWND; }
+    void*   getHVIEW() const { return fHVIEW; }
+    void    updateSize();
+
+    static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+
+    static OSStatus EventHandler(EventHandlerCallRef inHandler,
+                                 EventRef inEvent, void* userData);
+
+    void   doPaint(void* ctx);
+
+
+    bool attachGL();
+    void detachGL();
+    void presentGL();
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onEvent(const SkEvent& evt);
+    // overrides from SkWindow
+    virtual void onHandleInval(const SkIRect&);
+    // overrides from SkView
+    virtual void onAddMenu(const SkOSMenu*);
+    virtual void onSetTitle(const char[]);
+    
+
+private:
+    void*   fHWND;
+    void*   fHVIEW;
+    void*   fAGLCtx;
+
+    typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_SDL.h b/include/views/SkOSWindow_SDL.h
new file mode 100644
index 0000000..0ff24f3
--- /dev/null
+++ b/include/views/SkOSWindow_SDL.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#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..803ca13
--- /dev/null
+++ b/include/views/SkOSWindow_Unix.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkOSWindow_Unix_DEFINED
+#define SkOSWindow_Unix_DEFINED
+
+#include "SkWindow.h"
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+
+class SkBitmap;
+class SkEvent;
+
+struct SkUnixWindow {
+  Display* fDisplay;
+  Window fWin;
+  size_t fOSWin;
+  GC fGc;
+  GLXContext fGLContext;
+  bool fGLCreated;
+};
+
+class SkOSWindow : public SkWindow {
+public:
+    SkOSWindow(void*);
+    ~SkOSWindow();
+
+    void* getHWND() const { return (void*)fUnixWindow.fWin; }
+    void* getDisplay() const { return (void*)fUnixWindow.fDisplay; }
+    void* getUnixWindow() const { return (void*)&fUnixWindow; }
+    void loop();
+    void post_linuxevent();
+    bool attachGL();
+    void detachGL();
+    void presentGL();
+
+    //static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+
+    //static bool WndProc(SkUnixWindow* w,  XEvent &e);
+
+protected:
+    // overrides from SkWindow
+    virtual bool onEvent(const SkEvent&);
+    virtual void onHandleInval(const SkIRect&);
+    virtual bool onHandleChar(SkUnichar);
+    virtual bool onHandleKey(SkKey);
+    virtual bool onHandleKeyUp(SkKey);
+    virtual void onSetTitle(const char title[]);
+
+private:
+    SkUnixWindow  fUnixWindow;
+    bool fGLAttached;
+    bool fRestart;
+
+    // Needed for GL
+    XVisualInfo* fVi;
+
+    void    doPaint();
+    void    restartLoop();
+    void    mapWindowAndWait();
+
+    typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_Win.h b/include/views/SkOSWindow_Win.h
new file mode 100644
index 0000000..4b3e916
--- /dev/null
+++ b/include/views/SkOSWindow_Win.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkOSWindow_Win_DEFINED
+#define SkOSWindow_Win_DEFINED
+
+#include "SkWindow.h"
+
+class SkOSWindow : public SkWindow {
+public:
+    SkOSWindow(void* hwnd);
+    virtual ~SkOSWindow();
+
+    void*   getHWND() const { return fHWND; }
+    void    setSize(int width, int height);
+    void    updateSize();
+
+    static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+    
+    bool attachGL();
+    void detachGL();
+    void presentGL();
+
+    bool attachD3D9();
+    void detachD3D9();
+    void presentD3D9();
+
+    void* d3d9Device() { return fD3D9Device; }
+
+    bool wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+    static bool QuitOnDeactivate(HWND hWnd);
+
+    enum {
+        SK_WM_SkEvent = WM_APP + 1000,
+        SK_WM_SkTimerID = 0xFFFF    // just need a non-zero value
+    };
+
+protected:
+    virtual bool quitOnDeactivate() { return true; }
+
+    // overrides from SkWindow
+    virtual void onHandleInval(const SkIRect&);
+    // overrides from SkView
+    virtual void onAddMenu(const SkOSMenu*);
+
+    virtual void onSetTitle(const char title[]);
+
+private:
+    void*               fHWND;
+    
+    void                doPaint(void* ctx);
+
+    void*               fHGLRC;
+
+    bool                fGLAttached;
+
+    void*               fD3D9Device;
+    bool                fD3D9Attached;
+
+    HMENU               fMBar;
+
+    typedef SkWindow INHERITED; 
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_wxwidgets.h b/include/views/SkOSWindow_wxwidgets.h
new file mode 100644
index 0000000..c5dfc7c
--- /dev/null
+++ b/include/views/SkOSWindow_wxwidgets.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 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.
+ */
+
+/*
+ *  SkOSWindow_wxwidgets.h
+ *  wxwidgets
+ *
+ *  Copyright 2005 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#ifndef SkOSWindow_wxwidgets_DEFINED
+#define SkOSWindow_wxwidgets_DEFINED
+
+#include "SkWindow.h"
+#include "wx/frame.h"
+
+class SkOSWindow: public SkWindow
+{
+public:
+    SkOSWindow();
+    SkOSWindow(const wxString& title, int x, int y, int width, int height);
+    ~SkOSWindow();
+    
+    wxFrame* getWXFrame() const { return fFrame; }
+    
+    void updateSize();
+    
+protected:
+    virtual void onHandleInval(const SkIRect&);
+    virtual void onAddMenu(const SkOSMenu*);
+    
+private:
+    wxFrame* fFrame;
+    typedef SkWindow INHERITED;
+    
+};
+
+#endifpedef SkWindow INHERITED;
diff --git a/include/views/SkProgressBarView.h b/include/views/SkProgressBarView.h
new file mode 100644
index 0000000..6341fcb
--- /dev/null
+++ b/include/views/SkProgressBarView.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkProgressBarView_DEFINED
+#define SkProgressBarView_DEFINED
+
+#include "SkView.h"
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+
+class SkProgressBarView : public SkWidgetView {
+    public:
+        SkProgressBarView();
+        //SkProgressBarView(int max);
+                
+        //inflate: "sk-progress"
+    
+        void reset();   //reset progress to zero
+        void setProgress(int progress);
+        void changeProgress(int diff);
+        void setMax(int max);
+        
+        int getProgress() const { return fProgress; }
+        int getMax() const { return fMax; }
+    
+    protected:
+        //overrides
+        virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+        virtual void onSizeChange();
+        virtual void onDraw(SkCanvas* canvas);
+        virtual bool onEvent(const SkEvent& evt);
+    
+    private:
+        SkAnimator  fAnim;
+        int         fProgress;
+        int         fMax;
+        
+        typedef SkWidgetView INHERITED;
+};
+
+
+
+
+#endif
diff --git a/include/views/SkScrollBarView.h b/include/views/SkScrollBarView.h
new file mode 100644
index 0000000..b8a5209
--- /dev/null
+++ b/include/views/SkScrollBarView.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkScrollBarView_DEFINED
+#define SkScrollBarView_DEFINED
+
+#include "SkView.h"
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+
+class SkScrollBarView : public SkWidgetView {
+public:
+    SkScrollBarView();
+
+    unsigned getStart() const { return fStartPoint; }
+    unsigned getShown() const { return fShownLength; }
+    unsigned getTotal() const { return fTotalLength; }
+
+    void setStart(unsigned start);  
+    void setShown(unsigned shown);
+    void setTotal(unsigned total);
+    
+protected:
+    //overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+    virtual void onSizeChange();
+    virtual void onDraw(SkCanvas* canvas);
+    virtual bool onEvent(const SkEvent& evt);
+
+private:
+    SkAnimator  fAnim;
+    unsigned    fTotalLength, fStartPoint, fShownLength;
+    
+    void adjust();
+    
+    typedef SkWidgetView INHERITED;
+};
+#endif
+
diff --git a/include/views/SkStackViewLayout.h b/include/views/SkStackViewLayout.h
new file mode 100644
index 0000000..8000319
--- /dev/null
+++ b/include/views/SkStackViewLayout.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#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..8dfe8be
--- /dev/null
+++ b/include/views/SkSystemEventTypes.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#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/SkTouchGesture.h b/include/views/SkTouchGesture.h
new file mode 100644
index 0000000..79d4e28
--- /dev/null
+++ b/include/views/SkTouchGesture.h
@@ -0,0 +1,72 @@
+#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..d3633db
--- /dev/null
+++ b/include/views/SkView.h
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkView_DEFINED
+#define SkView_DEFINED
+
+#include "SkEventSink.h"
+#include "SkRect.h"
+#include "SkDOM.h"
+#include "SkTDict.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;
+
+    /** 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); }
+    /** 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;
+    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*);
+
+    /** Depricated helper function. Just call event->post(sinkID, delay);
+    */
+    bool    postEvent(SkEvent* evt, SkEventSinkID sinkID, SkMSec delay) { return evt->post(sinkID, delay); }
+
+    //  View hierarchy management
+
+    /** Return the view's parent, or null if it has none. This does not affect the parent's reference count. */
+    SkView*     getParent() const { return fParent; }
+    SkView*     attachChildToFront(SkView* child);
+    /** Attach the child view to this view, and increment the child's reference count. The child view is added
+        such that it will be drawn before all other child views.
+        The child view parameter is returned.
+    */
+    SkView*     attachChildToBack(SkView* child);
+    /** If the view has a parent, detach the view from its parent and decrement the view's reference count.
+        If the parent was the only owner of the view, this will cause the view to be deleted.
+    */
+    void        detachFromParent();
+    /** Attach the child view to this view, and increment the child's reference count. The child view is added
+        such that it will be drawn after all other child views.
+        The child view parameter is returned.
+    */
+    /** Detach all child views from this view. */
+    void        detachAllChildren();
+
+    /** Convert the specified point from global coordinates into view-local coordinates
+    */
+    void        globalToLocal(SkPoint* pt) const { if (pt) this->globalToLocal(pt->fX, pt->fY, pt); }
+    /** Convert the specified x,y from global coordinates into view-local coordinates, returning
+        the answer in the local parameter.
+    */
+    void        globalToLocal(SkScalar globalX, SkScalar globalY, SkPoint* local) const;
+
+    /** \class F2BIter
+    
+        Iterator that will return each of this view's children, in
+        front-to-back order (the order used for clicking). The first
+        call to next() returns the front-most child view. When
+        next() returns null, there are no more child views.
+    */
+    class F2BIter {
+    public:
+        F2BIter(const SkView* parent);
+        SkView* next();
+    private:
+        SkView* fFirstChild, *fChild;
+    };
+
+    /** \class B2FIter
+    
+        Iterator that will return each of this view's children, in
+        back-to-front order (the order they are drawn). The first
+        call to next() returns the back-most child view. When
+        next() returns null, there are no more child views.
+    */
+    class B2FIter {
+    public:
+        B2FIter(const SkView* parent);
+        SkView* next();
+    private:
+        SkView* fFirstChild, *fChild;
+    };
+
+    /** \class Artist
+    
+        Install a subclass of this in a view (calling setArtist()), and then the
+        default implementation of that view's onDraw() will invoke this object
+        automatically.
+    */
+    class Artist : public SkRefCnt {
+    public:
+        void draw(SkView*, SkCanvas*);
+        void inflate(const SkDOM&, const SkDOM::Node*);
+    protected:
+        virtual void onDraw(SkView*, SkCanvas*) = 0;
+        virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+    };
+    /** Return the artist attached to this view (or null). The artist's reference
+        count is not affected.
+    */
+    Artist* getArtist() const;
+    /** Attach the specified artist (or null) to the view, replacing any existing
+        artist. If the new artist is not null, its reference count is incremented.
+        The artist parameter is returned.
+    */
+    Artist* setArtist(Artist* artist);
+
+    /** \class Layout
+    
+        Install a subclass of this in a view (calling setLayout()), and then the
+        default implementation of that view's onLayoutChildren() will invoke
+        this object automatically.
+    */
+    class Layout : public SkRefCnt {
+    public:
+        void layoutChildren(SkView* parent);
+        void inflate(const SkDOM&, const SkDOM::Node*);
+    protected:
+        virtual void onLayoutChildren(SkView* parent) = 0;
+        virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+    };
+
+    /** Return the layout attached to this view (or null). The layout's reference
+        count is not affected.
+    */
+    Layout* getLayout() const;
+    /** Attach the specified layout (or null) to the view, replacing any existing
+        layout. If the new layout is not null, its reference count is incremented.
+        The layout parameter is returned.
+    */
+    Layout* setLayout(Layout*, bool invokeLayoutNow = true);
+    /** If a layout is attached to this view, call its layoutChildren() method
+    */
+    void    invokeLayout();
+
+    /** Call this to initialize this view based on the specified XML node
+    */
+    void    inflate(const SkDOM& dom, const SkDOM::Node* node);
+    /** After a view hierarchy is inflated, this may be called with a dictionary
+        containing pairs of <name, view*>, where the name string was the view's
+        "id" attribute when it was inflated.
+
+        This will call the virtual onPostInflate for this view, and the recursively
+        call postInflate on all of the view's children.
+    */
+    void    postInflate(const SkTDict<SkView*>& ids);
+
+    SkDEBUGCODE(void dump(bool recurse) const;)
+
+protected:
+    /** Override this to draw inside the view. Be sure to call the inherited version too */
+    virtual void    onDraw(SkCanvas*);
+    /** Override this to be notified when the view's size changes. Be sure to call the inherited version too */
+    virtual void    onSizeChange();
+    /** Override this if you want to handle an inval request from this view or one of its children.
+        Tyically this is only overridden by the by the "window". If your subclass does handle the
+        request, return true so the request will not continue to propogate to the parent.
+    */
+    virtual bool    handleInval(const SkRect*);
+    //! called once before all of the children are drawn (or clipped/translated)
+    virtual SkCanvas* beforeChildren(SkCanvas* c) { return c; }
+    //! called once after all of the children are drawn (or clipped/translated)
+    virtual void afterChildren(SkCanvas* orig) {}
+
+    //! called right before this child's onDraw is called
+    virtual void beforeChild(SkView* child, SkCanvas* canvas) {}
+    //! called right after this child's onDraw is called
+    virtual void afterChild(SkView* child, SkCanvas* canvas) {}
+
+    /** Override this if you might handle the click
+    */
+    virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+    /** Override this to decide if your children are targets for a click.
+        The default returns true, in which case your children views will be
+        candidates for onFindClickHandler. Returning false wil skip the children
+        and just call your onFindClickHandler.
+     */
+    virtual bool onSendClickToChildren(SkScalar x, SkScalar y);
+    /** Override this to track clicks, returning true as long as you want to track
+        the pen/mouse.
+    */
+    virtual bool    onClick(Click*);
+    /** Override this to initialize your subclass from the XML node. Be sure to call the inherited version too */
+    virtual void    onInflate(const SkDOM& dom, const SkDOM::Node* node);
+    /** Override this if you want to perform post initialization work based on the ID dictionary built
+        during XML parsing. Be sure to call the inherited version too.
+    */
+    virtual void    onPostInflate(const SkTDict<SkView*>&);
+
+public:
+    // default action is to inval the view
+    virtual void    onFocusChange(bool gainFocusP);
+protected:
+
+    // override these if you're acting as a layer/host
+    virtual bool    onGetFocusView(SkView**) const { return false; }
+    virtual bool    onSetFocusView(SkView*) { return false; }
+
+private:
+    SkScalar    fWidth, fHeight;
+    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();
+};
+
+#endif
+
diff --git a/include/views/SkViewInflate.h b/include/views/SkViewInflate.h
new file mode 100644
index 0000000..3ec65a6
--- /dev/null
+++ b/include/views/SkViewInflate.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#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..db85f01
--- /dev/null
+++ b/include/views/SkWidget.h
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkWidget_DEFINED
+#define SkWidget_DEFINED
+
+#include "SkView.h"
+#include "SkBitmap.h"
+#include "SkDOM.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkTDArray.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+class SkWidget : public SkView {
+public:
+    SkWidget(uint32_t flags = 0) : SkView(flags | kFocusable_Mask | kEnabled_Mask) {}
+
+    /** Call this to post the widget's event to its listeners */
+    void    postWidgetEvent();
+
+    static void Init();
+    static void Term();
+protected:
+    // override to add slots to an event before posting
+    virtual void prepareWidgetEvent(SkEvent*);
+    virtual void onEnabledChange();
+
+    // <event ...> to initialize the event from XML
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+    SkEvent fEvent;
+    typedef SkView INHERITED;
+};
+
+class SkHasLabelWidget : public SkWidget {
+public:
+    SkHasLabelWidget(uint32_t flags = 0) : SkWidget(flags) {}
+
+    size_t  getLabel(SkString* label = NULL) const;
+    size_t  getLabel(char lable[] = NULL) const;
+    void    setLabel(const SkString&);
+    void    setLabel(const char label[]);
+    void    setLabel(const char label[], size_t len);
+
+protected:
+    // called when the label changes
+    virtual void onLabelChange();
+
+    // overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    SkString    fLabel;
+    typedef SkWidget INHERITED;
+};
+
+class SkButtonWidget : public SkHasLabelWidget {
+public:
+    SkButtonWidget(uint32_t flags = 0) : SkHasLabelWidget(flags), fState(kOff_State) {}
+
+    enum State {
+        kOff_State,     //!< XML: buttonState="off"
+        kOn_State,      //!< XML: buttonState="on"
+        kUnknown_State  //!< XML: buttonState="unknown"
+    };
+    State   getButtonState() const { return fState; }
+    void    setButtonState(State);
+
+protected:
+    /** called when the label changes. default behavior is to inval the widget */
+    virtual void onButtonStateChange();
+
+    // overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    State   fState;
+    typedef SkHasLabelWidget INHERITED;
+};
+
+class SkPushButtonWidget : public SkButtonWidget {
+public:
+    SkPushButtonWidget(uint32_t flags = 0) : SkButtonWidget(flags) {}
+
+protected:
+    virtual bool onEvent(const SkEvent&);
+    virtual void onDraw(SkCanvas*);
+    virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+    virtual bool onClick(Click* click);
+
+private:
+    typedef SkButtonWidget INHERITED;
+};
+
+class SkCheckBoxWidget : public SkButtonWidget {
+public:
+    SkCheckBoxWidget(uint32_t flags = 0);
+
+protected:
+    virtual bool onEvent(const SkEvent&);
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    typedef SkButtonWidget INHERITED;
+};
+
+#include "SkTextBox.h"
+
+class SkStaticTextView : public SkView {
+public:
+            SkStaticTextView(uint32_t flags = 0);
+    virtual ~SkStaticTextView();
+
+    enum Mode {
+        kFixedSize_Mode,
+        kAutoWidth_Mode,
+        kAutoHeight_Mode,
+
+        kModeCount
+    };
+    Mode    getMode() const { return (Mode)fMode; }
+    void    setMode(Mode);
+
+    SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; }
+    void    setSpacingAlign(SkTextBox::SpacingAlign);
+
+    void    getMargin(SkPoint* margin) const;
+    void    setMargin(SkScalar dx, SkScalar dy);
+
+    size_t  getText(SkString* text = NULL) const;
+    size_t  getText(char text[] = NULL) const;
+    void    setText(const SkString&);
+    void    setText(const char text[]);
+    void    setText(const char text[], size_t len);
+
+    void    getPaint(SkPaint*) const;
+    void    setPaint(const SkPaint&);
+
+protected:
+    // overrides
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    SkPoint     fMargin;
+    SkString    fText;
+    SkPaint     fPaint;
+    uint8_t     fMode;
+    uint8_t     fSpacingAlign;
+
+    void computeSize();
+
+    typedef SkView INHERITED;
+};
+
+class SkBitmapView : public SkView {
+public:
+            SkBitmapView(uint32_t flags = 0);
+    virtual ~SkBitmapView();
+
+    bool    getBitmap(SkBitmap*) const;
+    void    setBitmap(const SkBitmap*, bool viewOwnsPixels);
+    bool    loadBitmapFromFile(const char path[]);
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+
+private:
+    SkBitmap    fBitmap;
+    typedef SkView INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class SkShader;
+class SkInterpolator;
+
+class SkWidgetView : public SkView {
+public:
+            SkWidgetView(uint32_t flags = 0);
+    virtual ~SkWidgetView();
+
+    static const char*  GetEventType();
+};
+
+class SkSliderView : public SkWidgetView {
+public:
+    SkSliderView(uint32_t flags = 0);
+
+    uint16_t    getValue() const { return fValue; }
+    uint16_t    getMax() const { return fMax; }
+
+    void    setMax(U16CPU max);
+    void    setValue(U16CPU value);
+
+protected:
+    virtual void    onDraw(SkCanvas*);
+    virtual Click*  onFindClickHandler(SkScalar x, SkScalar y);
+    virtual bool    onClick(Click*);
+
+private:
+    uint16_t fValue, fMax;
+
+    typedef SkWidgetView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class SkHasLabelView : public SkView {
+public:
+    void    getLabel(SkString*) const;
+    void    setLabel(const SkString&);
+    void    setLabel(const char label[]);
+
+protected:
+    SkString    fLabel;
+
+    // called when the label changes
+    virtual void onLabelChange();
+
+    // overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+};
+
+class SkPushButtonView : public SkHasLabelView {
+public:
+    SkPushButtonView(uint32_t flags = 0);
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+};
+
+class SkCheckBoxView : public SkHasLabelView {
+public:
+    SkCheckBoxView(uint32_t flags = 0);
+
+    enum State {
+        kOff_State,
+        kOn_State,
+        kMaybe_State
+    };
+    State   getState() const { return fState; }
+    void    setState(State);
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    State   fState;
+};
+
+class SkProgressView : public SkView {
+public:
+    SkProgressView(uint32_t flags = 0);
+    virtual ~SkProgressView();
+
+    uint16_t    getValue() const { return fValue; }
+    uint16_t    getMax() const { return fMax; }
+
+    void    setMax(U16CPU max);
+    void    setValue(U16CPU value);
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+    uint16_t    fValue, fMax;
+    SkShader*   fOnShader, *fOffShader;
+    SkInterpolator* fInterp;
+    bool fDoInterp;
+
+    typedef SkView INHERITED;
+};
+
+class SkTextView : public SkView {
+public:
+            SkTextView(uint32_t flags = 0);
+    virtual ~SkTextView();
+
+    enum AnimaDir {
+        kNeutral_AnimDir,
+        kForward_AnimDir,
+        kBackward_AnimDir,
+        kAnimDirCount
+    };
+
+    void    getText(SkString*) const;
+    void    setText(const SkString&, AnimaDir dir = kNeutral_AnimDir);
+    void    setText(const char text[], AnimaDir dir = kNeutral_AnimDir);
+    void    setText(const char text[], size_t len, AnimaDir dir = kNeutral_AnimDir);
+
+    void    getMargin(SkPoint* margin) const;
+    void    setMargin(const SkPoint&);
+
+    SkPaint&    paint() { return fPaint; }
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+    SkString fText;
+    SkPaint  fPaint;
+    SkPoint  fMargin;
+
+    class Interp;
+    Interp* fInterp;
+    bool    fDoInterp;
+    // called by the other setText methods. This guy does not check for !=
+    // before doing the assign, so the caller must check for us
+    void privSetText(const SkString&, AnimaDir dir);
+
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////
+
+class SkEvent;
+
+class SkListSource : public SkEventSink {
+public:
+    virtual int countRows() = 0;
+    virtual void getRow(int index, SkString* left, SkString* right) = 0;
+    virtual SkEvent* getEvent(int index);
+
+    static SkListSource* CreateFromDir(const char path[], const char suffix[],
+                                        const char targetPrefix[]);
+    static SkListSource* CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node);
+};
+
+class SkListView : public SkWidgetView {
+public:
+            SkListView(uint32_t flags = 0);
+    virtual ~SkListView();
+
+    SkScalar    getRowHeight() const { return fRowHeight; }
+    void        setRowHeight(SkScalar);
+
+    /** Return the index of the selected row, or -1 if none
+    */
+    int     getSelection() const { return fCurrIndex; }
+    /** Set the index of the selected row, or -1 for none
+    */
+    void    setSelection(int);
+
+    void    moveSelectionUp();
+    void    moveSelectionDown();
+
+    enum Attr {
+        kBG_Attr,
+        kNormalText_Attr,
+        kHiliteText_Attr,
+        kHiliteCell_Attr,
+        kAttrCount
+    };
+    SkPaint&    paint(Attr);
+
+    SkListSource*   getListSource() const { return fSource; }
+    SkListSource*   setListSource(SkListSource*);
+
+#if 0
+    enum Action {
+        kSelectionChange_Action,
+        kSelectionPicked_Action,
+        kActionCount
+    };
+    /** If event is not null, it is retained by the view, and a copy
+        of the event will be posted to its listeners when the specified
+        action occurs. If event is null, then no event will be posted for
+        the specified action.
+    */
+    void    setActionEvent(Action, SkEvent* event);
+#endif
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onSizeChange();
+    virtual bool onEvent(const SkEvent&);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+    SkPaint         fPaint[kAttrCount];
+    SkListSource*   fSource;
+    SkScalar        fRowHeight;
+    int             fCurrIndex;     // logical index
+    int             fScrollIndex;   // logical index of top-most visible row
+    int             fVisibleRowCount;
+    SkString*       fStrCache;
+
+    void    dirtyStrCache();
+    void    ensureStrCache(int visibleCount);
+
+    int     logicalToVisualIndex(int index) const { return index - fScrollIndex; }
+    void    invalSelection();
+    bool    getRowRect(int index, SkRect*) const;
+    void    ensureSelectionIsVisible();
+
+    typedef SkWidgetView INHERITED;
+};
+
+//////////////////////////////////////////////////////////
+
+class SkGridView : public SkWidgetView {
+public:
+            SkGridView(uint32_t flags = 0);
+    virtual ~SkGridView();
+
+    void    getCellSize(SkPoint*) const;
+    void    setCellSize(SkScalar x, SkScalar y);
+
+    /** Return the index of the selected item, or -1 if none
+    */
+    int     getSelection() const { return fCurrIndex; }
+    /** Set the index of the selected row, or -1 for none
+    */
+    void    setSelection(int);
+
+    void    moveSelectionUp();
+    void    moveSelectionDown();
+
+    enum Attr {
+        kBG_Attr,
+        kHiliteCell_Attr,
+        kAttrCount
+    };
+    SkPaint&    paint(Attr);
+
+    SkListSource*   getListSource() const { return fSource; }
+    SkListSource*   setListSource(SkListSource*);
+
+protected:
+    virtual void onDraw(SkCanvas*);
+    virtual void onSizeChange();
+    virtual bool onEvent(const SkEvent&);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+    SkView*         fScrollBar;
+    SkPaint         fPaint[kAttrCount];
+    SkListSource*   fSource;
+    int             fCurrIndex;     // logical index
+
+    SkPoint         fCellSize;
+    SkIPoint        fVisibleCount;
+
+    int     logicalToVisualIndex(int index) const { return index; }
+    void    invalSelection();
+    bool    getCellRect(int index, SkRect*) const;
+    void    ensureSelectionIsVisible();
+
+    typedef SkWidgetView INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkWidgetViews.h b/include/views/SkWidgetViews.h
new file mode 100644
index 0000000..9b3a816
--- /dev/null
+++ b/include/views/SkWidgetViews.h
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkWidgetViews_DEFINED
+#define SkWidgetViews_DEFINED
+
+#include "SkView.h"
+
+
+enum SkWidgetEnum {
+    kBorder_WidgetEnum,         //!< <sk-border>
+    kButton_WidgetEnum,         //!< <sk-button>
+    kImage_WidgetEnum,          //!< <sk-image>
+    kList_WidgetEnum,           //!< <sk-list>
+    kProgress_WidgetEnum,       //!< <sk-progress>
+    kScroll_WidgetEnum,         //!< <sk-scroll>
+    kText_WidgetEnum,           //!< <sk-text>
+    
+    kWidgetEnumCount
+};
+
+//determines which skin to use
+enum SkinEnum {
+    kBorder_SkinEnum,
+    kButton_SkinEnum,
+    kProgress_SkinEnum,
+    kScroll_SkinEnum,
+    kStaticText_SkinEnum,
+    
+    kSkinEnumCount
+};
+
+#include "SkAnimator.h"
+//used for inflates
+const char* get_skin_enum_path(SkinEnum se);
+void init_skin_anim(const char path[], SkAnimator* anim);
+void init_skin_anim(SkinEnum se, SkAnimator* anim);
+void init_skin_paint(SkinEnum se, SkPaint* paint);
+void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint);
+
+/** Given an enum value, return an instance of the specified widget.
+    If the enum is out of range, returns null
+*/
+SkView* SkWidgetFactory(SkWidgetEnum);
+/** Given the inflate/element name of a widget, return an instance of
+    the specified widget, or null if name does not match any known
+    widget type.
+*/
+SkView* SkWidgetFactory(const char name[]);
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkWidgetView : public SkView {
+public:
+    SkWidgetView();
+
+    const char* getLabel() const;
+    void        getLabel(SkString* label) const;
+
+    void        setLabel(const char[]);
+    void        setLabel(const char[], size_t len);
+    void        setLabel(const SkString&);
+
+    SkEvent&        event() { return fEvent; }
+    const SkEvent&  event() const { return fEvent; }
+
+    /** Returns true if the widget can post its event to its listeners.
+    */
+    bool    postWidgetEvent();
+    
+    /** Returns the sinkID of the widgetview that posted the event, or 0
+    */
+    static SkEventSinkID GetWidgetEventSinkID(const SkEvent&);
+
+protected:
+    /** called when the label changes. override in subclasses. default action invals the view's bounds.
+        called with the old and new labels, before the label has actually changed.
+    */
+    virtual void onLabelChange(const char oldLabel[], const char newLabel[]);
+    /** called before posting the event to our listeners. Override to add slots to the event
+        before posting. Return true to proceed with posting, or false to not post the event to any
+        listener. Note: the event passed in may not be the same as calling this->event().
+        Be sure to call your INHERITED method as well, so that all classes in the hierarchy get a shot
+        at modifying the event (and possibly returning false to abort).
+    */
+    virtual bool onPrepareWidgetEvent(SkEvent* evt);
+
+    // overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+    
+private:
+    SkString    fLabel;
+    SkEvent     fEvent;
+    
+    typedef SkView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkButtonView : public SkWidgetView {
+public:
+    // inflate: "sk-button"
+    
+protected:
+    // overrides
+    virtual bool onEvent(const SkEvent&);
+private:
+    typedef SkWidgetView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkCheckButtonView : public SkWidgetView {
+public:
+    SkCheckButtonView();
+
+    // inflate: "sk-checkbutton"
+    
+    enum CheckState {
+        kOff_CheckState,        //!< inflate: check-state="off"
+        kOn_CheckState,         //!< inflate: check-state="on"
+        kUnknown_CheckState     //!< inflate: check-state="unknown"
+    };
+    CheckState  getCheckState() const { return (CheckState)fCheckState; }
+    void        setCheckState(CheckState);
+
+    /** use this to extract the CheckState from an event (i.e. one that as posted
+        by a SkCheckButtonView). Returns true if the proper slot was present in the event,
+        and sets state to that value. If no proper slot is found, returns false and does not
+        modify state.
+    */
+    static bool GetWidgetEventCheckState(const SkEvent&, CheckState* state);
+
+protected:
+    // called when the check-state is about to change, but before it actually has
+    virtual void onCheckStateChange(CheckState oldState, CheckState newState);
+
+    // overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+    virtual bool onPrepareWidgetEvent(SkEvent* evt);
+    
+private:
+    uint8_t  fCheckState;
+    
+    typedef SkWidgetView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkTextBox.h"
+
+class SkStaticTextView : public SkView {
+public:
+            SkStaticTextView();
+    virtual ~SkStaticTextView();
+
+    enum Mode {
+        kFixedSize_Mode,
+        kAutoWidth_Mode,
+        kAutoHeight_Mode,
+
+        kModeCount
+    };
+    Mode    getMode() const { return (Mode)fMode; }
+    void    setMode(Mode);
+
+    SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; }
+    void    setSpacingAlign(SkTextBox::SpacingAlign);
+
+    void    getMargin(SkPoint* margin) const;
+    void    setMargin(SkScalar dx, SkScalar dy);
+
+    size_t  getText(SkString* text = NULL) const;
+    size_t  getText(char text[] = NULL) const;
+    void    setText(const SkString&);
+    void    setText(const char text[]);
+    void    setText(const char text[], size_t len);
+
+    void    getPaint(SkPaint*) const;
+    void    setPaint(const SkPaint&);
+
+protected:
+    // overrides
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    SkPoint     fMargin;
+    SkString    fText;
+    SkPaint     fPaint;
+    uint8_t     fMode;
+    uint8_t     fSpacingAlign;
+
+    void computeSize();
+
+    typedef SkView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkAnimator;
+class SkListSource;
+class SkScrollBarView;
+
+class SkListView : public SkWidgetView {
+public:
+            SkListView();
+    virtual ~SkListView();
+
+    bool    hasScrollBar() const { return fScrollBar != NULL; }
+    void    setHasScrollBar(bool);
+    
+    /** Return the number of visible rows
+    */
+    int     getVisibleRowCount() const { return fVisibleRowCount; }
+    /** Return the index of the selected row, or -1 if none
+    */
+    int     getSelection() const { return fCurrIndex; }
+    /** Set the index of the selected row, or -1 for none
+    */
+    void    setSelection(int);
+    /** If possible, move the selection up and return true,
+        else do nothing and return false
+        If nothing is selected, select the last item (unless there are no items).
+    */
+    bool    moveSelectionUp();
+    /** If possible, move the selection down and return true,
+        else do nothing and return false.
+        If nothing is selected, select the first item (unless there are no items).
+    */
+    bool    moveSelectionDown();
+
+    SkListSource*   getListSource() const { return fSource; }
+    SkListSource*   setListSource(SkListSource*);
+
+    /** Call this in your event handler. If the specified event is from a SkListView,
+        then it returns the index of the selected item in this list, otherwise it
+        returns -1
+    */
+    static int GetWidgetEventListIndex(const SkEvent&);
+
+protected:
+    // overrides
+    virtual void onDraw(SkCanvas*);
+    virtual void onSizeChange();
+    virtual bool onEvent(const SkEvent&);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+    virtual bool onPrepareWidgetEvent(SkEvent*);
+
+private:
+    enum DirtyFlags {
+        kAnimCount_DirtyFlag    = 0x01,
+        kAnimContent_DirtyFlag  = 0x02
+    };
+    void    dirtyCache(unsigned dirtyFlags);
+    bool    ensureCache();
+
+    int     logicalToVisualIndex(int index) const { return index - fScrollIndex; }
+    void    invalSelection();
+    SkScalar getContentWidth() const;
+    bool    getRowRect(int index, SkRect*) const;
+    void    ensureSelectionIsVisible();
+    void    ensureVisibleRowCount();
+
+    struct BindingRec;
+
+    enum Heights {
+        kNormal_Height,
+        kSelected_Height
+    };
+    SkListSource*   fSource;
+    SkScrollBarView*    fScrollBar;
+    SkAnimator*     fAnims;
+    BindingRec*     fBindings;
+    SkString        fSkinName;
+    SkScalar        fHeights[2];
+    int16_t         fScrollIndex, fCurrIndex;
+    uint16_t        fVisibleRowCount, fBindingCount;
+    SkBool8         fAnimContentDirty;
+    SkBool8         fAnimFocusDirty;
+
+    typedef SkWidgetView INHERITED;
+};
+
+class SkListSource : public SkRefCnt {
+public:
+    virtual int countFields();
+    virtual void getFieldName(int index, SkString* field);
+    /** Return the index of the named field, or -1 if not found */
+    virtual int findFieldIndex(const char field[]);
+
+    virtual int countRecords();
+    virtual void getRecord(int rowIndex, int fieldIndex, SkString* data);
+
+    virtual bool prepareWidgetEvent(SkEvent*, int rowIndex);
+    
+    static SkListSource* Factory(const char name[]);
+};
+
+#endif
diff --git a/include/views/SkWindow.h b/include/views/SkWindow.h
new file mode 100644
index 0000000..fd4ce0a
--- /dev/null
+++ b/include/views/SkWindow.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef SkWindow_DEFINED
+#define SkWindow_DEFINED
+
+#include "SkView.h"
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "SkRegion.h"
+#include "SkEvent.h"
+#include "SkKey.h"
+#include "SkTDArray.h"
+
+#ifdef SK_BUILD_FOR_WINCEx
+    #define SHOW_FPS
+#endif
+//#define USE_GX_SCREEN
+
+class SkCanvas;
+
+class SkOSMenu;
+
+class SkWindow : public SkView {
+public:
+            SkWindow();
+    virtual ~SkWindow();
+
+    const SkBitmap& getBitmap() const { return fBitmap; }
+
+    void    setConfig(SkBitmap::Config);
+    void    resize(int width, int height, SkBitmap::Config config = SkBitmap::kNo_Config);
+    void    eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
+    void    eraseRGB(U8CPU r, U8CPU g, U8CPU b);
+
+    bool    isDirty() const { return !fDirtyRgn.isEmpty(); }
+    bool    update(SkIRect* updateArea, SkCanvas* = NULL);
+    // does not call through to onHandleInval(), but does force the fDirtyRgn
+    // to be wide open. Call before update() to ensure we redraw everything.
+    void    forceInvalAll();
+    // return the bounds of the dirty/inval rgn, or [0,0,0,0] if none
+    const SkIRect& getDirtyBounds() const { return fDirtyRgn.getBounds(); }
+
+    bool    handleClick(int x, int y, Click::State);
+    bool    handleChar(SkUnichar);
+    bool    handleKey(SkKey);
+    bool    handleKeyUp(SkKey);
+    bool    handleMenu(uint32_t os_cmd);
+
+    void    addMenu(SkOSMenu*);
+    
+    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&);
+
+protected:
+    virtual bool onEvent(const SkEvent&);
+    virtual bool onDispatchClick(int x, int y, Click::State);
+    // 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 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;
+    Click*      fClick; // to track clicks
+
+    SkTDArray<SkOSMenu*>    fMenus;
+
+    SkView* fFocusView;
+    bool    fWaitingOnInval;
+    
+    SkString    fTitle;
+    SkMatrix    fMatrix;
+
+    typedef SkView INHERITED;
+};
+
+///////////////////////////////////////////////////////////
+
+#ifdef SK_USE_WXWIDGETS
+    #include "SkOSWindow_wxwidgets.h"
+#elif defined(SK_BUILD_FOR_MAC)
+    #include "SkOSWindow_Mac.h"
+#elif defined(SK_BUILD_FOR_WIN)
+    #include "SkOSWindow_Win.h"
+#elif defined(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/samplecode/ClockFaceView.cpp b/samplecode/ClockFaceView.cpp
new file mode 100644
index 0000000..c829b69
--- /dev/null
+++ b/samplecode/ClockFaceView.cpp
@@ -0,0 +1,255 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTypeface.h"
+#include "SkAvoidXfermode.h"
+
+static inline SkPMColor rgb2gray(SkPMColor c)
+{
+    unsigned r = SkGetPackedR32(c);
+    unsigned g = SkGetPackedG32(c);
+    unsigned b = SkGetPackedB32(c);
+
+    unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
+
+    return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
+}
+
+class SkGrayScaleColorFilter : public SkColorFilter {
+public:
+    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[])
+    {
+        for (int i = 0; i < count; i++)
+            result[i] = rgb2gray(src[i]);
+    }
+};
+
+class SkChannelMaskColorFilter : public SkColorFilter {
+public:
+    SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask)
+    {
+        fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
+    }
+
+    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[])
+    {
+        SkPMColor mask = fMask;
+        for (int i = 0; i < count; i++)
+            result[i] = src[i] & mask;
+    }
+
+private:
+    SkPMColor   fMask;
+};
+
+///////////////////////////////////////////////////////////
+
+#include "SkGradientShader.h"
+#include "SkLayerRasterizer.h"
+#include "SkBlurMaskFilter.h"
+
+#include "Sk2DPathEffect.h"
+
+class Dot2DPathEffect : public Sk2DPathEffect {
+public:
+    Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix,
+                    SkTDArray<SkPoint>* pts)
+    : Sk2DPathEffect(matrix), fRadius(radius), fPts(pts) {}
+
+    virtual void flatten(SkFlattenableWriteBuffer& buffer)
+    {
+        this->INHERITED::flatten(buffer);
+
+        buffer.writeScalar(fRadius);
+    }
+    virtual Factory getFactory() { return CreateProc; }
+
+protected:
+    virtual void begin(const SkIRect& uvBounds, SkPath* dst) {
+        if (fPts) {
+            fPts->reset();
+        }
+        this->INHERITED::begin(uvBounds, dst);
+    }
+//    virtual void end(SkPath* dst) {}
+	virtual void next(const SkPoint& loc, int u, int v, SkPath* dst)
+    {
+        if (fPts) {
+            *fPts->append() = loc;
+        }
+        dst->addCircle(loc.fX, loc.fY, fRadius);
+    }
+
+    Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer)
+    {
+        fRadius = buffer.readScalar();
+        fPts = NULL;
+    }
+private:
+    SkScalar fRadius;
+    SkTDArray<SkPoint>* fPts;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)
+    {
+        return new Dot2DPathEffect(buffer);
+    }
+
+    typedef Sk2DPathEffect INHERITED;
+};
+
+class InverseFillPE : public SkPathEffect {
+public:
+    InverseFillPE() {}
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
+        *dst = src;
+        dst->setFillType(SkPath::kInverseWinding_FillType);
+        return true;
+    }
+    virtual Factory getFactory() { return Factory; }
+protected:
+//    InverseFillPE(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+private:
+    static SkFlattenable* Factory(SkFlattenableReadBuffer& buffer) {
+        return new InverseFillPE;
+    }
+    typedef SkPathEffect INHERITED;
+};
+
+static SkPathEffect* makepe(float interp, SkTDArray<SkPoint>* pts) {
+    SkMatrix    lattice;
+    SkScalar    rad = 3 + SkIntToScalar(4) * (1 - interp);
+    lattice.setScale(rad*2, rad*2, 0, 0);
+    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+    return new Dot2DPathEffect(rad, lattice, pts);
+}
+
+static void r7(SkLayerRasterizer* rast, SkPaint& p, SkScalar interp) {
+    p.setPathEffect(makepe(interp, NULL))->unref();
+    rast->addLayer(p);
+#if 0
+    p.setPathEffect(new InverseFillPE())->unref();
+    p.setXfermodeMode(SkXfermode::kSrcIn_Mode);
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    p.setAlpha((1 - interp) * 255);
+    rast->addLayer(p);
+#endif
+}
+
+typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
+
+#include "SkXfermode.h"
+
+static void apply_shader(SkPaint* paint, float scale)
+{
+    SkPaint p;
+    SkLayerRasterizer*  rast = new SkLayerRasterizer;
+
+    p.setAntiAlias(true);
+    r7(rast, p, scale);
+    paint->setRasterizer(rast)->unref();
+
+    paint->setColor(SK_ColorBLUE);
+}
+
+class ClockFaceView : public SkView {
+    SkTypeface* fFace;
+    SkScalar fInterp;
+    SkScalar fDx;
+public:
+	ClockFaceView()
+    {
+        fFace = SkTypeface::CreateFromFile("/Users/reed/Downloads/p052024l.pfb");
+        fInterp = 0;
+        fDx = SK_Scalar1/64;
+    }
+
+    virtual ~ClockFaceView()
+    {
+        SkSafeUnref(fFace);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)
+    {
+        if (SampleCode::TitleQ(*evt))
+        {
+            SampleCode::TitleR(evt, "Text Effects");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void drawBG(SkCanvas* canvas)
+    {
+//        canvas->drawColor(0xFFDDDDDD);
+        canvas->drawColor(SK_ColorWHITE);
+    }
+
+    static void drawdots(SkCanvas* canvas, const SkPaint& orig) {
+        SkTDArray<SkPoint> pts;
+        SkPathEffect* pe = makepe(0, &pts);
+
+        SkScalar width = -1;
+        SkPath path, dstPath;
+        orig.getTextPath("9", 1, 0, 0, &path);
+        pe->filterPath(&dstPath, path, &width);
+
+        SkPaint p;
+        p.setAntiAlias(true);
+        p.setStrokeWidth(10);
+        p.setColor(SK_ColorRED);
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, pts.count(), pts.begin(),
+                           p);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+
+        SkScalar    x = SkIntToScalar(20);
+        SkScalar    y = SkIntToScalar(300);
+        SkPaint     paint;
+
+        paint.setAntiAlias(true);
+        paint.setTextSize(SkIntToScalar(240));
+        paint.setTypeface(SkTypeface::CreateFromName("sans-serif",
+                                                     SkTypeface::kBold));
+
+        SkString str("9");
+
+        paint.setTypeface(fFace);
+
+        apply_shader(&paint, fInterp);
+        canvas->drawText(str.c_str(), str.size(), x, y, paint);
+
+    //    drawdots(canvas, paint);
+
+        if (false) {
+            fInterp += fDx;
+            if (fInterp > 1) {
+                fInterp = 1;
+                fDx = -fDx;
+            } else if (fInterp < 0) {
+                fInterp = 0;
+                fDx = -fDx;
+            }
+            this->inval(NULL);
+        }
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ClockFaceView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/OverView.cpp b/samplecode/OverView.cpp
new file mode 100644
index 0000000..2ae2119
--- /dev/null
+++ b/samplecode/OverView.cpp
@@ -0,0 +1,94 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkView.h"
+
+static const int N = 8;
+const SkScalar W = SkIntToScalar(640);
+const SkScalar H = SkIntToScalar(480); 
+
+class OverView : public SkView {
+public:
+    OverView(int count, const SkViewFactory factories[]);
+    virtual ~OverView();
+    
+protected:
+    virtual bool onEvent(const SkEvent&);
+    virtual void onSizeChange();
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorLTGRAY);
+    }
+
+    virtual SkCanvas* beforeChildren(SkCanvas*);
+
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Overview");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual bool onSendClickToChildren(SkScalar x, SkScalar y) {
+        return false;
+    }
+
+    virtual Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        int ix = (int)(SkScalarDiv(x * N, W));
+        int iy = (int)(SkScalarDiv(y * N, H));
+        if (ix >= 0 && iy >= 0) {
+            SkEvent evt("set-curr-index");
+            evt.setFast32(iy * N + ix);
+            this->sendEventToParents(evt);
+        }
+        return NULL;
+    }
+
+private:
+    int             fCount;
+    const SkViewFactory*  fFactories;
+
+    typedef SkView INHERITED;
+};
+
+SkView* create_overview(int count, const SkViewFactory factories[]);
+SkView* create_overview(int count, const SkViewFactory factories[]) {
+    return SkNEW_ARGS(OverView, (count, factories));
+};
+
+OverView::OverView(int count, const SkViewFactory factories[]) {
+    fCount = count;
+    fFactories = factories;
+}
+
+OverView::~OverView() {
+}
+
+bool OverView::onEvent(const SkEvent& evt) {
+    return this->INHERITED::onEvent(evt);
+}
+
+void OverView::onSizeChange() {
+    this->detachAllChildren();
+    
+    SkScalar locX = 0;
+    SkScalar locY = 0;
+    for (int i = 0; i < fCount; i++) {
+        SkView* view = fFactories[i]();
+        view->setVisibleP(true);
+        this->attachChildToBack(view)->unref();
+        view->setLoc(locX, locY);
+        view->setSize(W, H);
+        locX += W;
+        if ((i % N) == N - 1) {
+            locY += H;
+            locX = 0;
+        }
+    }
+}
+
+SkCanvas* OverView::beforeChildren(SkCanvas* canvas) {
+    canvas->scale(SK_Scalar1 / N, SK_Scalar1 / N);
+    return canvas;
+}
+
diff --git a/samplecode/SampleAARects.cpp b/samplecode/SampleAARects.cpp
new file mode 100644
index 0000000..34a33b0
--- /dev/null
+++ b/samplecode/SampleAARects.cpp
@@ -0,0 +1,191 @@
+#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..abbf8f9
--- /dev/null
+++ b/samplecode/SampleAll.cpp
@@ -0,0 +1,715 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkView.h"
+#include "Sk1DPathEffect.h"
+#include "Sk2DPathEffect.h"
+#include "SkAvoidXfermode.h"
+#include "SkBlurMaskFilter.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkCornerPathEffect.h"
+#include "SkDashPathEffect.h"
+#include "SkDiscretePathEffect.h"
+#include "SkEmbossMaskFilter.h"
+#include "SkGradientShader.h"
+#include "SkImageDecoder.h"
+#include "SkLayerRasterizer.h"
+#include "SkMath.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkComposeShader.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkPicture.h"
+#include "SkRandom.h"
+#include "SkTransparentShader.h"
+#include "SkTypeface.h"
+#include "SkUnitMappers.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+#include <math.h>
+    
+static inline SkPMColor rgb2gray(SkPMColor c) {
+    unsigned r = SkGetPackedR32(c);
+    unsigned g = SkGetPackedG32(c);
+    unsigned b = SkGetPackedB32(c);
+    
+    unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
+    
+    return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
+}
+
+class SkGrayScaleColorFilter : public SkColorFilter {
+public:
+    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) {
+        for (int i = 0; i < count; i++)
+            result[i] = rgb2gray(src[i]);
+    }
+};
+
+class SkChannelMaskColorFilter : public SkColorFilter {
+public:
+    SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) {
+        fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
+    }
+
+    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) {
+        SkPMColor mask = fMask;
+        for (int i = 0; i < count; i++) {
+            result[i] = src[i] & mask;
+        }
+    }
+    
+private:
+    SkPMColor   fMask;
+};
+
+///////////////////////////////////////////////////////////
+
+static void r0(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
+                                             SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+    p.setMaskFilter(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+
+    p.setAlpha(0x11);
+    p.setStyle(SkPaint::kFill_Style);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    rast->addLayer(p);
+}
+
+static void r1(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    p.setAlpha(0x40);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*2);
+    rast->addLayer(p);
+}
+    
+static void r2(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setStyle(SkPaint::kStrokeAndFill_Style);
+    p.setStrokeWidth(SK_Scalar1*4);
+    rast->addLayer(p);
+
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*3/2);
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+}
+
+static void r3(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*3);
+    rast->addLayer(p);
+
+    p.setAlpha(0x20);
+    p.setStyle(SkPaint::kFill_Style);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    rast->addLayer(p);
+}
+
+static void r4(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setAlpha(0x60);
+    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+    p.setAlpha(0xFF);
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);
+
+    p.setXfermode(NULL);
+    rast->addLayer(p);
+}
+
+static void r5(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
+    p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
+    rast->addLayer(p);
+}
+
+static void r6(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+    
+    p.setAntiAlias(false);
+    SkLayerRasterizer* rast2 = new SkLayerRasterizer;
+    r5(rast2, p);
+    p.setRasterizer(rast2)->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+}
+
+class Dot2DPathEffect : public Sk2DPathEffect {
+public:
+    Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix)
+        : Sk2DPathEffect(matrix), fRadius(radius) {}
+
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+        this->INHERITED::flatten(buffer);
+        
+        buffer.writeScalar(fRadius);
+    }
+    virtual Factory getFactory() { return CreateProc; }
+
+protected:
+	virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) {
+        dst->addCircle(loc.fX, loc.fY, fRadius);
+    }
+    
+    Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) {
+        fRadius = buffer.readScalar();
+    }
+private:
+    SkScalar fRadius;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return new Dot2DPathEffect(buffer);
+    }
+
+    typedef Sk2DPathEffect INHERITED;
+};
+
+static void r7(SkLayerRasterizer* rast, SkPaint& p) {
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+    p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*4, lattice))->unref();
+    rast->addLayer(p);
+}
+
+static void r8(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+    
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+    p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*2, lattice))->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+
+    p.setPathEffect(NULL);
+    p.setXfermode(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+}
+
+class Line2DPathEffect : public Sk2DPathEffect {
+public:
+    Line2DPathEffect(SkScalar width, const SkMatrix& matrix)
+        : Sk2DPathEffect(matrix), fWidth(width) {}
+
+	virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
+        if (this->INHERITED::filterPath(dst, src, width)) {
+            *width = fWidth;
+            return true;
+        }
+        return false;
+    }
+    
+    virtual Factory getFactory() { return CreateProc; }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+        this->INHERITED::flatten(buffer);
+        buffer.writeScalar(fWidth);
+    }
+protected:
+	virtual void nextSpan(int u, int v, int ucount, SkPath* dst) {
+        if (ucount > 1) {
+            SkPoint	src[2], dstP[2];
+
+            src[0].set(SkIntToScalar(u) + SK_ScalarHalf,
+                       SkIntToScalar(v) + SK_ScalarHalf);
+            src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf,
+                       SkIntToScalar(v) + SK_ScalarHalf);
+            this->getMatrix().mapPoints(dstP, src, 2);
+            
+            dst->moveTo(dstP[0]);
+            dst->lineTo(dstP[1]);
+        }
+    }
+    
+    Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) {
+        fWidth = buffer.readScalar();
+    }
+    
+private:
+    SkScalar fWidth;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { return new Line2DPathEffect(buffer); }
+
+    typedef Sk2DPathEffect INHERITED;
+};
+
+static void r9(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+    
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
+    lattice.postRotate(SkIntToScalar(30), 0, 0);
+    p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+
+    p.setPathEffect(NULL);
+    p.setXfermode(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+}
+
+typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
+
+static const raster_proc gRastProcs[] = {
+    r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
+};
+
+static const struct {
+    SkColor fMul, fAdd;
+} gLightingColors[] = {
+    { 0x808080, 0x800000 }, // general case
+    { 0x707070, 0x707070 }, // no-pin case
+    { 0xFFFFFF, 0x800000 }, // just-add case
+    { 0x808080, 0x000000 }, // just-mul case
+    { 0xFFFFFF, 0x000000 }  // identity case
+};
+
+static unsigned color_dist16(uint16_t a, uint16_t b) {
+    unsigned dr = SkAbs32(SkPacked16ToR32(a) - SkPacked16ToR32(b));
+    unsigned dg = SkAbs32(SkPacked16ToG32(a) - SkPacked16ToG32(b));
+    unsigned db = SkAbs32(SkPacked16ToB32(a) - SkPacked16ToB32(b));
+    
+    return SkMax32(dr, SkMax32(dg, db));
+}
+
+static unsigned scale_dist(unsigned dist, unsigned scale) {
+    dist >>= 6;
+    dist = (dist << 2) | dist;
+    dist = (dist << 4) | dist;
+    return dist;
+
+//    return SkAlphaMul(dist, scale);
+}
+
+static void apply_shader(SkPaint* paint, int index) {    
+    raster_proc proc = gRastProcs[index];
+    if (proc) {
+        SkPaint p;
+        SkLayerRasterizer*  rast = new SkLayerRasterizer;
+
+        p.setAntiAlias(true);
+        proc(rast, p);
+        paint->setRasterizer(rast)->unref();
+    }
+
+#if 1
+    SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 };
+    paint->setMaskFilter(SkBlurMaskFilter::CreateEmboss(dir, SK_Scalar1/4, SkIntToScalar(4), SkIntToScalar(3)))->unref();    
+    paint->setColor(SK_ColorBLUE);
+#endif
+}
+
+class DemoView : public SampleView {
+public:
+    DemoView() {}
+	
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Demo");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual bool onClick(Click* click) {
+        return this->INHERITED::onClick(click);
+    }
+    
+    void makePath(SkPath& path) {
+        path.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(20),
+            SkPath::kCCW_Direction);
+        for (int index = 0; index < 10; index++) {
+            SkScalar x = SkFloatToScalar(cos(index / 10.0f * 2 * 3.1415925358f));
+            SkScalar y = SkFloatToScalar(sin(index / 10.0f * 2 * 3.1415925358f));
+            x *= index & 1 ? 7 : 14;
+            y *= index & 1 ? 7 : 14;
+            x += SkIntToScalar(20);
+            y += SkIntToScalar(20);
+            if (index == 0)
+                path.moveTo(x, y);
+            else
+                path.lineTo(x, y);
+        }
+        path.close();
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        canvas->save();
+        drawPicture(canvas, 0);
+        canvas->restore();
+
+        {
+            SkPicture picture;
+            SkCanvas* record = picture.beginRecording(320, 480);
+            drawPicture(record, 120);
+            canvas->translate(0, SkIntToScalar(120));
+
+            SkRect clip;
+            clip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160));
+            do {
+                canvas->save();
+                canvas->clipRect(clip);
+                picture.draw(canvas);
+                canvas->restore();
+                if (clip.fRight < SkIntToScalar(320))
+                    clip.offset(SkIntToScalar(160), 0);
+                else if (clip.fBottom < SkIntToScalar(480))
+                    clip.offset(-SkIntToScalar(320), SkIntToScalar(160));
+                else
+                    break;
+            } while (true);
+        }
+    }
+    
+    void drawPicture(SkCanvas* canvas, int spriteOffset) {
+	    SkMatrix matrix; matrix.reset();
+		SkPaint paint;
+		SkPath path;
+        SkPoint start = {0, 0};
+        SkPoint stop = { SkIntToScalar(40), SkIntToScalar(40) };
+		SkRect rect = {0, 0, SkIntToScalar(40), SkIntToScalar(40) };
+		SkRect rect2 = {0, 0, SkIntToScalar(65), SkIntToScalar(20) };
+		SkScalar left = 0, top = 0, x = 0, y = 0;
+		size_t index;
+		
+		char ascii[] = "ascii...";
+		size_t asciiLength = sizeof(ascii) - 1;
+		char utf8[] = "utf8" "\xe2\x80\xa6";
+		short utf16[] = {'u', 't', 'f', '1', '6', 0x2026 };
+		short utf16simple[] = {'u', 't', 'f', '1', '6', '!' };
+		
+        makePath(path);
+        SkTDArray<SkPoint>(pos);
+		pos.setCount(asciiLength);
+		for (index = 0;  index < asciiLength; index++)
+			pos[index].set(SkIntToScalar(index * 10), SkIntToScalar(index * 2));
+        SkTDArray<SkPoint>(pos2);
+		pos2.setCount(asciiLength);
+		for (index = 0;  index < asciiLength; index++)
+			pos2[index].set(SkIntToScalar(index * 10), SkIntToScalar(20));
+		
+        // shaders
+        SkPoint linearPoints[] = { { 0, 0, }, { SkIntToScalar(40), SkIntToScalar(40) } };
+        SkColor linearColors[] = { SK_ColorRED, SK_ColorBLUE };
+        SkScalar* linearPos = NULL;
+        int linearCount = 2;
+        SkShader::TileMode linearMode = SkShader::kMirror_TileMode;
+        SkUnitMapper* linearMapper = new SkDiscreteMapper(3);
+        SkAutoUnref unmapLinearMapper(linearMapper);
+        SkShader* linear = SkGradientShader::CreateLinear(linearPoints,
+            linearColors, linearPos, linearCount, linearMode, linearMapper);
+
+        SkPoint radialCenter = { SkIntToScalar(25), SkIntToScalar(25) };
+        SkScalar radialRadius = SkIntToScalar(25);
+        SkColor radialColors[] = { SK_ColorGREEN, SK_ColorGRAY, SK_ColorRED };
+        SkScalar radialPos[] = { 0, SkIntToScalar(3) / 5, SkIntToScalar(1)};
+        int radialCount = 3;
+        SkShader::TileMode radialMode = SkShader::kRepeat_TileMode;
+        SkUnitMapper* radialMapper = new SkCosineMapper();
+        SkAutoUnref unmapRadialMapper(radialMapper);
+        SkShader* radial = SkGradientShader::CreateRadial(radialCenter, 
+            radialRadius, radialColors, radialPos, radialCount,
+            radialMode, radialMapper);
+        
+        SkTransparentShader* transparentShader = new SkTransparentShader();
+        SkEmbossMaskFilter::Light light;
+        light.fDirection[0] = SK_Scalar1/2;
+        light.fDirection[1] = SK_Scalar1/2;
+        light.fDirection[2] = SK_Scalar1/3;
+        light.fAmbient		= 0x48;
+        light.fSpecular		= 0x80;
+        SkScalar radius = SkIntToScalar(12)/5;
+        SkEmbossMaskFilter* embossFilter = new SkEmbossMaskFilter(light, 
+            radius);
+            
+        SkXfermode* xfermode = SkXfermode::Create(SkXfermode::kXor_Mode);
+        SkColorFilter* lightingFilter = SkColorFilter::CreateLightingFilter(
+            0xff89bc45, 0xff112233);
+        
+        canvas->save();
+		canvas->translate(SkIntToScalar(0), SkIntToScalar(5));
+		paint.setFlags(SkPaint::kAntiAlias_Flag | SkPaint::kFilterBitmap_Flag);
+		// !!! draw through a clip
+		paint.setColor(SK_ColorLTGRAY);
+		paint.setStyle(SkPaint::kFill_Style);
+        SkRect clip = {0, 0, SkIntToScalar(320), SkIntToScalar(120)};
+        canvas->clipRect(clip);
+        paint.setShader(SkShader::CreateBitmapShader(fTx, 
+            SkShader::kMirror_TileMode, SkShader::kRepeat_TileMode))->unref();
+		canvas->drawPaint(paint);
+		canvas->save();
+        
+        // line (exercises xfermode, colorShader, colorFilter, filterShader)
+		paint.setColor(SK_ColorGREEN);
+		paint.setStrokeWidth(SkIntToScalar(10));
+		paint.setStyle(SkPaint::kStroke_Style);
+        paint.setXfermode(xfermode)->unref();
+        paint.setColorFilter(lightingFilter)->unref();
+		canvas->drawLine(start.fX, start.fY, stop.fX, stop.fY, paint); // should not be green
+		paint.setXfermode(NULL);
+        paint.setColorFilter(NULL);
+        
+        // rectangle
+		paint.setStyle(SkPaint::kFill_Style);
+		canvas->translate(SkIntToScalar(50), 0);
+		paint.setColor(SK_ColorYELLOW);
+        paint.setShader(linear)->unref();
+        paint.setPathEffect(pathEffectTest())->unref();
+		canvas->drawRect(rect, paint); 
+        paint.setPathEffect(NULL);
+        
+        // circle w/ emboss & transparent (exercises 3dshader)
+		canvas->translate(SkIntToScalar(50), 0);
+        paint.setMaskFilter(embossFilter)->unref();
+        canvas->drawOval(rect, paint);
+		canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+        paint.setShader(transparentShader)->unref();
+        canvas->drawOval(rect, paint);
+		canvas->translate(0, SkIntToScalar(-10));
+        
+        // path
+		canvas->translate(SkIntToScalar(50), 0);
+		paint.setColor(SK_ColorRED);
+		paint.setStyle(SkPaint::kStroke_Style);
+		paint.setStrokeWidth(SkIntToScalar(5));
+        paint.setShader(radial)->unref();
+        paint.setMaskFilter(NULL);
+		canvas->drawPath(path, paint);
+		
+        paint.setShader(NULL);
+        // bitmap, sprite
+		canvas->translate(SkIntToScalar(50), 0);
+		paint.setStyle(SkPaint::kFill_Style);
+		canvas->drawBitmap(fBug, left, top, &paint);
+		canvas->translate(SkIntToScalar(30), 0);
+		canvas->drawSprite(fTb, 
+			SkScalarRound(canvas->getTotalMatrix().getTranslateX()), 
+            spriteOffset + 10, &paint);
+
+		canvas->translate(-SkIntToScalar(30), SkIntToScalar(30));
+        paint.setShader(shaderTest())->unref(); // test compose shader
+		canvas->drawRect(rect2, paint); 
+        paint.setShader(NULL);
+		
+        canvas->restore();
+        // text
+		canvas->translate(0, SkIntToScalar(60));
+        canvas->save();
+		paint.setColor(SK_ColorGRAY);
+		canvas->drawPosText(ascii, asciiLength, pos.begin(), paint);
+		canvas->drawPosText(ascii, asciiLength, pos2.begin(), paint);
+
+		canvas->translate(SkIntToScalar(50), 0);
+		paint.setColor(SK_ColorCYAN);
+		canvas->drawText(utf8, sizeof(utf8) - 1, x, y, paint);
+       
+		canvas->translate(SkIntToScalar(30), 0);
+		paint.setColor(SK_ColorMAGENTA);
+		paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+        matrix.setTranslate(SkIntToScalar(10), SkIntToScalar(10));
+		canvas->drawTextOnPath((void*) utf16, sizeof(utf16), path, &matrix, paint);
+		canvas->translate(0, SkIntToScalar(20));
+		canvas->drawTextOnPath((void*) utf16simple, sizeof(utf16simple), path, &matrix, paint);
+        canvas->restore();
+        
+        canvas->translate(0, SkIntToScalar(60));
+		paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
+        canvas->restore();
+    }
+    
+    /*
+./SkColorFilter.h:25:class SkColorFilter : public SkFlattenable { -- abstract
+    static SkColorFilter* CreatXfermodeFilter() *** untested ***
+    static SkColorFilter* CreatePorterDuffFilter() *** untested ***
+    static SkColorFilter* CreateLightingFilter() -- tested
+./SkDrawLooper.h:9:class SkDrawLooper : public SkFlattenable { -- virtually abstract
+    ./SkBlurDrawLooper.h:9:class SkBlurDrawLooper : public SkDrawLooper { *** untested ***
+./SkMaskFilter.h:41:class SkMaskFilter : public SkFlattenable { -- abstract chmod +w .h
+    ./SkEmbossMaskFilter.h:27:class SkEmbossMaskFilter : public SkMaskFilter { -- tested
+./SkPathEffect.h:33:class SkPathEffect : public SkFlattenable { -- abstract
+    ./Sk1DPathEffect.h:27:class Sk1DPathEffect : public SkPathEffect { -- abstract
+        ./Sk1DPathEffect.h:48:class SkPath1DPathEffect : public Sk1DPathEffect { -- tested
+    ./Sk2DPathEffect.h:25:class Sk2DPathEffect : public SkPathEffect { *** untested ***
+    ./SkCornerPathEffect.h:28:class SkCornerPathEffect : public SkPathEffect { *** untested ***
+    ./SkDashPathEffect.h:27:class SkDashPathEffect : public SkPathEffect {
+    ./SkDiscretePathEffect.h:27:class SkDiscretePathEffect : public SkPathEffect {
+    ./SkPaint.h:760:class SkStrokePathEffect : public SkPathEffect {
+    ./SkPathEffect.h:58:class SkPairPathEffect : public SkPathEffect {
+        ./SkPathEffect.h:78:class SkComposePathEffect : public SkPairPathEffect {
+        ./SkPathEffect.h:114:class SkSumPathEffect : public SkPairPathEffect {
+./SkRasterizer.h:29:class SkRasterizer : public SkFlattenable {
+    ./SkLayerRasterizer.h:27:class SkLayerRasterizer : public SkRasterizer {
+./SkShader.h:36:class SkShader : public SkFlattenable {
+    ./SkColorFilter.h:59:class SkFilterShader : public SkShader {
+    ./SkColorShader.h:26:class SkColorShader : public SkShader {
+    ./SkShaderExtras.h:31:class SkComposeShader : public SkShader {
+    ./SkTransparentShader.h:23:class SkTransparentShader : public SkShader {
+./SkUnitMapper.h:24:class SkUnitMapper : public SkFlattenable {
+    ./SkUnitMapper.h:33:class SkDiscreteMapper : public SkUnitMapper {
+    ./SkUnitMapper.h:51:class SkFlipCosineMapper : public SkUnitMapper {
+./SkXfermode.h:32:class SkXfermode : public SkFlattenable {
+    ./SkAvoidXfermode.h:28:class SkAvoidXfermode : public SkXfermode { *** not done *** chmod +w .h .cpp
+    ./SkXfermode.h:54:class SkProcXfermode : public SkXfermode {
+    */
+    
+    /*
+./SkBlurMaskFilter.h:25:class SkBlurMaskFilter {
+    chmod +w SkBlurMaskFilter.cpp
+./SkGradientShader.h:30:class SkGradientShader {
+    */
+        // save layer, bounder, looper
+        // matrix
+        // clip /path/region
+        // bitmap proc shader ?
+
+/* untested:
+SkCornerPathEffect.h:28:class SkCornerPathEffect : public SkPathEffect {
+*/
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        fClickPt.set(x, y);
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+    SkPathEffect* pathEffectTest() {
+        static const int gXY[] = { 1, 0, 0, -1, 2, -1, 3, 0, 2, 1, 0, 1 };
+        SkScalar gPhase = 0;
+        SkPath path;
+        path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
+        for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2)
+            path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
+        path.close();
+        path.offset(SkIntToScalar(-6), 0);
+        SkPathEffect* outer = new SkPath1DPathEffect(path, SkIntToScalar(12), 
+            gPhase, SkPath1DPathEffect::kRotate_Style);
+        SkPathEffect* inner = new SkDiscretePathEffect(SkIntToScalar(2), 
+            SkIntToScalar(1)/10); // SkCornerPathEffect(SkIntToScalar(2));
+        SkPathEffect* result = new SkComposePathEffect(outer, inner);
+        outer->unref();
+        inner->unref();
+        return result;
+    }
+    
+    SkPathEffect* pathEffectTest2() { // unsure this works (has no visible effect)
+        SkPathEffect* outer = new SkStrokePathEffect(SkIntToScalar(4), 
+            SkPaint::kStroke_Style, SkPaint::kMiter_Join, SkPaint::kButt_Cap);
+        static const SkScalar intervals[] = {SkIntToScalar(1), SkIntToScalar(2),
+            SkIntToScalar(2), SkIntToScalar(1)};
+        SkPathEffect* inner = new SkDashPathEffect(intervals, 
+            sizeof(intervals) / sizeof(intervals[0]), 0);
+        SkPathEffect* result = new SkSumPathEffect(outer, inner);
+        outer->unref();
+        inner->unref();
+        return result;
+    }
+    
+    SkShader* shaderTest() {
+        SkPoint pts[] = { { 0, 0, }, { SkIntToScalar(100), 0 } };
+        SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
+        SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, 
+            2, SkShader::kClamp_TileMode);
+        pts[1].set(0, SkIntToScalar(100));
+        SkColor colors2[] = {SK_ColorBLACK,  SkColorSetARGB(0x80, 0, 0, 0)};
+        SkShader* shaderB = SkGradientShader::CreateLinear(pts, colors2, NULL, 
+            2, SkShader::kClamp_TileMode);
+        SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode);
+        SkShader* result = new SkComposeShader(shaderA, shaderB, mode);
+        shaderA->unref();
+        shaderB->unref();
+        mode->unref();
+        return result;
+    }
+
+    virtual void startTest() {
+		SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/bugcirc.gif", &fBug);
+		SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/tbcirc.gif", &fTb);
+		SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/05psp04.gif", &fTx);
+	}
+
+    void drawRaster(SkCanvas* canvas)  {
+        for (size_t index = 0; index < SK_ARRAY_COUNT(gRastProcs); index++)
+            drawOneRaster(canvas);
+    }
+    
+    void drawOneRaster(SkCanvas* canvas) {
+        canvas->save();
+
+        SkScalar    x = SkIntToScalar(20);
+        SkScalar    y = SkIntToScalar(40);
+        SkPaint     paint;
+        
+        paint.setAntiAlias(true);
+        paint.setTextSize(SkIntToScalar(48));
+        paint.setTypeface(SkTypeface::CreateFromName("sans-serif",
+                                                     SkTypeface::kBold));
+
+        SkString str("GOOGLE");
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
+            apply_shader(&paint, i);
+            
+          //  paint.setMaskFilter(NULL);
+          //  paint.setColor(SK_ColorBLACK);
+
+#if 01
+            int index = i % SK_ARRAY_COUNT(gLightingColors);
+            paint.setColorFilter(SkColorFilter::CreateLightingFilter(
+                                    gLightingColors[index].fMul,
+                                    gLightingColors[index].fAdd))->unref();
+#endif
+            
+            canvas->drawText(str.c_str(), str.size(), x, y, paint);
+            SkRect  oval = { x, y - SkIntToScalar(40), x + SkIntToScalar(40), y };
+            paint.setStyle(SkPaint::kStroke_Style);
+            canvas->drawOval(oval, paint);
+            paint.setStyle(SkPaint::kFill_Style);
+
+            y += paint.getFontSpacing();
+        }
+
+        canvas->restore();
+        
+        if (1) {
+            SkAvoidXfermode   mode(SK_ColorWHITE, 0xFF,
+                                   SkAvoidXfermode::kTargetColor_Mode);
+            SkPaint paint;
+            x += SkIntToScalar(20);
+            SkRect  r = { x, 0, x + SkIntToScalar(360), SkIntToScalar(700) };
+            paint.setXfermode(&mode);
+            paint.setColor(SK_ColorGREEN);
+            paint.setAntiAlias(true);
+            canvas->drawOval(r, paint);
+        }
+    }
+
+private:
+    SkPoint fClickPt;
+    SkBitmap fBug, fTb, fTx;
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DemoView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleAnimatedGradient.cpp b/samplecode/SampleAnimatedGradient.cpp
new file mode 100644
index 0000000..a7b2a46
--- /dev/null
+++ b/samplecode/SampleAnimatedGradient.cpp
@@ -0,0 +1,90 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+
+class GradientView : public SampleView {
+public:
+	GradientView() {
+        this->setBGColor(0xFFDDDDDD);
+	}
+	
+protected:
+	struct GradData {
+        int             fCount;
+        const SkColor*  fColors;
+        const SkScalar* fPos;
+    };
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)  {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Gradients");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3)));
+        paint.setStyle(SkPaint::kFill_Style);
+        
+        SkPoint p = SkPoint::Make(0,0);
+        SkPoint q = SkPoint::Make(100,100);
+        SkPoint pts[] = {p, q};
+        
+        SkScalar t, temp, x, y;
+        SkColor gColors[] = {
+            SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
+        };
+        t =    SampleCode::GetAnimScalar(SkIntToScalar(2), SkIntToScalar(20));
+        temp = SampleCode::GetAnimScalar(SkIntToScalar(1), SkIntToScalar(8));
+        SkScalar step = SK_ScalarPI / (10);
+        SkScalar angle = t * step;
+        x =  SkScalarSinCos(angle, &y);
+        SkScalar colorPositions[] = { 0, 0.1 + x, 0.4 + y, 0.9 - x + y, 1.0};
+        GradData data = { 5, gColors, colorPositions };
+        
+        
+        SkRect r = { 0, 0, SkIntToScalar(200), SkIntToScalar(200) };
+        SkShader* shader1 = SkGradientShader::CreateLinear(
+                           pts, data.fColors, data.fPos,data.fCount, 
+                           SkShader::kMirror_TileMode);
+        paint.setShader(shader1)->unref();
+        
+        canvas->drawRect(r, paint);
+        
+        
+        SkPoint s = SkPoint::Make(100,100);
+        SkShader* shader2 = SkGradientShader::CreateRadial(
+                           s, 100, data.fColors, data.fPos, data.fCount, 
+                           SkShader::kMirror_TileMode);
+        paint.setShader(shader2)->unref();
+        canvas->translate(250, 0);
+        canvas->drawRect(r, paint);
+        
+        SkShader* shader3 = SkGradientShader::CreateTwoPointRadial(
+                           p, 0, q, 100, data.fColors, data.fPos, data.fCount,
+                           SkShader::kMirror_TileMode);
+        paint.setShader(shader3)->unref();
+        canvas->translate(0, 250);
+        canvas->drawRect(r, paint);
+        
+        SkShader* shader4 = SkGradientShader::CreateSweep(
+                            100, 100, data.fColors, data.fPos, data.fCount);
+ 
+        paint.setShader(shader4)->unref();
+        canvas->translate(-250, 0);
+        canvas->drawRect(r, paint);
+        
+        this->inval(NULL);		
+    }
+	
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new GradientView; }
+static SkViewRegister reg(MyFactory);
\ No newline at end of file
diff --git a/samplecode/SampleAnimator.cpp b/samplecode/SampleAnimator.cpp
new file mode 100644
index 0000000..99173fc
--- /dev/null
+++ b/samplecode/SampleAnimator.cpp
@@ -0,0 +1,159 @@
+#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*);
+    
+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) {
+    if (fAnimator) {
+        canvas->drawColor(SK_ColorWHITE);
+        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
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+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..3efffe6
--- /dev/null
+++ b/samplecode/SampleApp.cpp
@@ -0,0 +1,1647 @@
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkGpuCanvas.h"
+#include "SkGraphics.h"
+#include "SkImageEncoder.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "SkTime.h"
+#include "SkWindow.h"
+
+#include "SampleCode.h"
+#include "GrContext.h"
+#include "SkTouchGesture.h"
+#include "SkTypeface.h"
+
+#define TEST_GPIPEx
+
+#ifdef  TEST_GPIPE
+#define PIPE_FILE
+#define FILE_PATH "/path/to/drawing.data"
+#endif
+
+#define USE_ARROWS_FOR_ZOOM true
+//#define DEFAULT_TO_GPU
+
+extern SkView* create_overview(int, const SkViewFactory[]);
+
+#define SK_SUPPORT_GL
+
+#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)
+
+#ifdef SK_SUPPORT_GL
+    #include "GrGLConfig.h"
+#endif
+
+///////////////
+static const char view_inval_msg[] = "view-inval-msg";
+
+static void postInvalDelay(SkEventSinkID sinkID) {
+    SkEvent* evt = new SkEvent(view_inval_msg);
+    evt->post(sinkID, 1);
+}
+
+static bool isInvalEvent(const SkEvent& evt) {
+    return evt.isType(view_inval_msg);
+}
+//////////////////
+
+SkViewRegister* SkViewRegister::gHead;
+SkViewRegister::SkViewRegister(SkViewFactory fact) : fFact(fact) {
+    static bool gOnce;
+    if (!gOnce) {
+        gHead = NULL;
+        gOnce = true;
+    }
+
+    fChain = gHead;
+    gHead = this;
+}
+
+#if defined(SK_SUPPORT_GL)
+    #define SK_USE_SHADERS
+#endif
+
+#ifdef SK_BUILD_FOR_MAC
+#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)
+};
+
+enum SkTriState {
+    kFalse_SkTriState,
+    kTrue_SkTriState,
+    kUnknown_SkTriState,
+};
+
+static SkTriState cycle_tristate(SkTriState state) {
+    static const SkTriState gCycle[] = {
+        /* kFalse_SkTriState   -> */  kUnknown_SkTriState,
+        /* kTrue_SkTriState    -> */  kFalse_SkTriState,
+        /* kUnknown_SkTriState -> */  kTrue_SkTriState,
+    };
+    return gCycle[state];
+}
+
+#include "SkDrawFilter.h"
+
+class FlagsDrawFilter : public SkDrawFilter {
+public:
+    FlagsDrawFilter(SkTriState lcd, SkTriState aa, SkTriState filter,
+                    SkTriState hinting) :
+        fLCDState(lcd), fAAState(aa), fFilterState(filter), fHintingState(hinting) {}
+
+    virtual void filter(SkPaint* paint, Type t) {
+        if (kText_Type == t && kUnknown_SkTriState != fLCDState) {
+            paint->setLCDRenderText(kTrue_SkTriState == fLCDState);
+        }
+        if (kUnknown_SkTriState != fAAState) {
+            paint->setAntiAlias(kTrue_SkTriState == fAAState);
+        }
+        if (kUnknown_SkTriState != fFilterState) {
+            paint->setFilterBitmap(kTrue_SkTriState == fFilterState);
+        }
+        if (kUnknown_SkTriState != fHintingState) {
+            paint->setHinting(kTrue_SkTriState == fHintingState ?
+                              SkPaint::kNormal_Hinting :
+                              SkPaint::kSlight_Hinting);
+        }
+    }
+
+private:
+    SkTriState  fLCDState;
+    SkTriState  fAAState;
+    SkTriState  fFilterState;
+    SkTriState  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";
+
+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::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);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* curr_view(SkWindow* wind) {
+    SkView::F2BIter iter(wind);
+    return iter.next();
+}
+
+class SampleWindow : public SkOSWindow {
+    SkTDArray<SkViewFactory> fSamples;
+public:
+    SampleWindow(void* hwnd);
+    virtual ~SampleWindow();
+
+    virtual void draw(SkCanvas* canvas);
+
+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);
+    virtual bool onClick(Click* click);
+    virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+
+#if 0
+    virtual bool handleChar(SkUnichar uni);
+    virtual bool handleEvent(const SkEvent& evt);
+    virtual bool handleKey(SkKey key);
+    virtual bool handleKeyUp(SkKey key);
+    virtual bool onHandleKeyUp(SkKey key);
+#endif
+
+private:
+    int fCurrIndex;
+
+    SkPicture* fPicture;
+    SkGpuCanvas* fGpuCanvas;
+    GrContext* fGrContext;
+    SkPath fClipPath;
+
+    SkTouchGesture fGesture;
+    int      fZoomLevel;
+    SkScalar fZoomScale;
+
+    enum CanvasType {
+        kRaster_CanvasType,
+        kPicture_CanvasType,
+        kGPU_CanvasType
+    };
+    CanvasType fCanvasType;
+
+    bool fUseClip;
+    bool fNClip;
+    bool fRepeatDrawing;
+    bool fAnimating;
+    bool fRotate;
+    bool fScale;
+    bool fRequestGrabImage;
+    bool fUsePipe;
+    bool fMeasureFPS;
+    SkMSec fMeasureFPS_Time;
+
+    // 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;
+
+    SkTriState fLCDState;
+    SkTriState fAAState;
+    SkTriState fFilterState;
+    SkTriState fHintingState;
+    unsigned   fFlipAxis;
+
+    int fScrollTestX, fScrollTestY;
+
+    bool make3DReady();
+    void changeZoomLevel(int delta);
+
+    void loadView(SkView*);
+    void updateTitle();
+    bool nextSample();
+
+    void toggleZoomer();
+    bool zoomIn();
+    bool zoomOut();
+    void updatePointer(int x, int y);
+    void showZoomer(SkCanvas* canvas);
+
+    void postAnimatingEvent() {
+        if (fAnimating) {
+            SkEvent* evt = new SkEvent(ANIMATING_EVENTTYPE);
+            evt->post(this->getSinkID(), ANIMATING_DELAY);
+        }
+    }
+
+
+    static CanvasType cycle_canvastype(CanvasType);
+
+    typedef SkOSWindow INHERITED;
+};
+
+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::toggleZoomer()
+{
+    fShowZoomer = !fShowZoomer;
+    this->inval(NULL);
+}
+
+void SampleWindow::updatePointer(int x, int y)
+{
+    fMouseX = x;
+    fMouseY = y;
+    if (fShowZoomer) {
+        this->inval(NULL);
+    }
+}
+
+bool SampleWindow::make3DReady() {
+
+#if defined(SK_SUPPORT_GL)
+    if (attachGL()) {
+        if (NULL != fGrContext) {
+        // various gr lifecycle tests
+        #if   0
+            fGrContext->freeGpuResources();
+        #elif 0
+            // this will leak resources.
+            fGrContext->contextLost();
+        #elif 0
+            GrAssert(1 == fGrContext->refcnt());
+            fGrContext->unref();
+            fGrContext = NULL;
+        #endif
+        }
+
+        if (NULL == fGrContext) {
+        #if defined(SK_USE_SHADERS)
+            fGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine, NULL);
+        #else
+            fGrContext = GrContext::Create(kOpenGL_Fixed_GrEngine, NULL);
+        #endif
+            SkDebugf("---- constructor\n");
+        }
+
+        if (NULL != fGrContext) {
+            return true;
+        } else {
+            detachGL();
+        }
+    }
+#endif
+    SkDebugf("Failed to setup 3D");
+    return false;
+}
+
+SampleWindow::CanvasType SampleWindow::cycle_canvastype(CanvasType ct) {
+    static const CanvasType gCT[] = {
+        kPicture_CanvasType,
+        kGPU_CanvasType,
+        kRaster_CanvasType
+    };
+    return gCT[ct];
+}
+
+SampleWindow::SampleWindow(void* hwnd) : INHERITED(hwnd) {
+#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;
+    fGpuCanvas = NULL;
+
+    fGrContext = NULL;
+
+#ifdef DEFAULT_TO_GPU
+    fCanvasType = kGPU_CanvasType;
+#else
+    fCanvasType = kRaster_CanvasType;
+#endif
+    fUseClip = false;
+    fNClip = false;
+    fRepeatDrawing = false;
+    fAnimating = false;
+    fRotate = false;
+    fScale = false;
+    fRequestGrabImage = false;
+    fUsePipe = false;
+    fMeasureFPS = false;
+    fLCDState = kUnknown_SkTriState;
+    fAAState = kUnknown_SkTriState;
+    fFilterState = kUnknown_SkTriState;
+    fHintingState = kUnknown_SkTriState;
+    fFlipAxis = 0;
+    fScrollTestX = fScrollTestY = 0;
+
+    fMouseX = fMouseY = 0;
+    fFatBitsScale = 8;
+    fTypeface = SkTypeface::CreateFromTypeface(NULL, SkTypeface::kBold);
+    fShowZoomer = false;
+
+    fZoomLevel = 0;
+    fZoomScale = SK_Scalar1;
+
+//    this->setConfig(SkBitmap::kRGB_565_Config);
+    this->setConfig(SkBitmap::kARGB_8888_Config);
+    this->setVisibleP(true);
+    this->setClipToBounds(false);
+
+    {
+        const SkViewRegister* reg = SkViewRegister::Head();
+        while (reg) {
+            *fSamples.append() = reg->factory();
+            reg = reg->next();
+        }
+    }
+    fCurrIndex = 0;
+    this->loadView(fSamples[fCurrIndex]());
+
+#ifdef SK_BUILD_FOR_MAC
+    testpdf();
+#endif
+}
+
+SampleWindow::~SampleWindow() {
+    delete fPicture;
+    delete fGpuCanvas;
+    if (NULL != fGrContext) {
+        fGrContext->unref();
+    }
+    fTypeface->unref();
+}
+
+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
+    gAnimTimePrev = gAnimTime;
+    gAnimTime = SkTime::GetMSecs();
+
+    SkScalar cx = SkScalarHalf(this->width());
+    SkScalar cy = SkScalarHalf(this->height());
+
+    if (fZoomLevel) {
+        SkMatrix m;
+        SkPoint center;
+        m = canvas->getTotalMatrix();//.invert(&m);
+        m.mapXY(cx, cy, &center);
+        cx = center.fX;
+        cy = center.fY;
+
+        m.setTranslate(-cx, -cy);
+        m.postScale(fZoomScale, fZoomScale);
+        m.postTranslate(cx, cy);
+
+        canvas->concat(m);
+    }
+
+    if (fFlipAxis) {
+        SkMatrix m;
+        m.setTranslate(cx, cy);
+        if (fFlipAxis & kFlipAxis_X) {
+            m.preScale(-SK_Scalar1, SK_Scalar1);
+        }
+        if (fFlipAxis & kFlipAxis_Y) {
+            m.preScale(SK_Scalar1, -SK_Scalar1);
+        }
+        m.preTranslate(-cx, -cy);
+        canvas->concat(m);
+    }
+
+    // Apply any gesture matrix
+    if (true) {
+        const SkMatrix& localM = fGesture.localM();
+        if (localM.getType() & SkMatrix::kScale_Mask) {
+            canvas->setExternalMatrix(&localM);
+        }
+        canvas->concat(localM);
+        canvas->concat(fGesture.globalM());
+
+        if (fGesture.isActive()) {
+            this->inval(NULL);
+        }
+    }
+
+    if (fNClip) {
+        this->INHERITED::draw(canvas);
+        SkBitmap orig = capture_bitmap(canvas);
+
+        const SkScalar w = this->width();
+        const SkScalar h = this->height();
+        const SkScalar cw = w / XCLIP_N;
+        const SkScalar ch = h / YCLIP_N;
+        for (int y = 0; y < YCLIP_N; y++) {
+            SkRect r;
+            r.fTop = y * ch;
+            r.fBottom = (y + 1) * ch;
+            if (y == YCLIP_N - 1) {
+                r.fBottom = h;
+            }
+            for (int x = 0; x < XCLIP_N; x++) {
+                SkAutoCanvasRestore acr(canvas, true);
+                r.fLeft = x * cw;
+                r.fRight = (x + 1) * cw;
+                if (x == XCLIP_N - 1) {
+                    r.fRight = w;
+                }
+                canvas->clipRect(r);
+                this->INHERITED::draw(canvas);
+            }
+        }
+
+        SkBitmap diff;
+        if (bitmap_diff(canvas, orig, &diff)) {
+        }
+    } else {
+        this->INHERITED::draw(canvas);
+    }
+    if (fShowZoomer && fCanvasType != kGPU_CanvasType) {
+        // In the GPU case, INHERITED::draw calls beforeChildren, which
+        // creates an SkGpuCanvas.  All further draw calls are directed
+        // at that canvas, which is deleted in afterChildren (which is
+        // also called by draw), so we cannot show the zoomer here.
+        // Instead, we call it inside afterChildren.
+        showZoomer(canvas);
+    }
+}
+
+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) {
+    if (fRepeatDrawing) {
+        this->inval(NULL);
+    }
+}
+
+#include "SkColorPriv.h"
+
+static void reverseRedAndBlue(const SkBitmap& bm) {
+    SkASSERT(bm.config() == SkBitmap::kARGB_8888_Config);
+    uint8_t* p = (uint8_t*)bm.getPixels();
+    uint8_t* stop = p + bm.getSize();
+    while (p < stop) {
+        // swap red/blue (to go from ARGB(int) to RGBA(memory) and premultiply
+        unsigned scale = SkAlpha255To256(p[3]);
+        unsigned r = p[2];
+        unsigned b = p[0];
+        p[0] = SkAlphaMul(r, scale);
+        p[1] = SkAlphaMul(p[1], scale);
+        p[2] = SkAlphaMul(b, scale);
+        p += 4;
+    }
+}
+
+SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) {
+    if (kGPU_CanvasType != fCanvasType) {
+#ifdef SK_SUPPORT_GL
+        detachGL();
+#endif
+    }
+
+    switch (fCanvasType) {
+        case kRaster_CanvasType:
+            canvas = this->INHERITED::beforeChildren(canvas);
+            break;
+        case kPicture_CanvasType:
+            fPicture = new SkPicture;
+            canvas = fPicture->beginRecording(9999, 9999);
+            break;
+        case kGPU_CanvasType: {
+            if (make3DReady()) {
+                SkDevice* device = canvas->getDevice();
+                const SkBitmap& bitmap = device->accessBitmap(true);
+
+                GrRenderTarget* renderTarget;
+                renderTarget = fGrContext->createRenderTargetFrom3DApiState();
+                fGpuCanvas = new SkGpuCanvas(fGrContext, renderTarget);
+                renderTarget->unref();
+
+                device = fGpuCanvas->createDevice(SkBitmap::kARGB_8888_Config,
+                                                  bitmap.width(), bitmap.height(),
+                                                  false, false);
+                fGpuCanvas->setDevice(device)->unref();
+
+                fGpuCanvas->concat(canvas->getTotalMatrix());
+                canvas = fGpuCanvas;
+
+            } else {
+                canvas = this->INHERITED::beforeChildren(canvas);
+            }
+            break;
+        }
+    }
+
+    if (fUseClip) {
+        canvas->drawColor(0xFFFF88FF);
+        canvas->clipPath(fClipPath);
+    }
+
+    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);
+}
+
+void SampleWindow::afterChildren(SkCanvas* orig) {
+    if (fRequestGrabImage) {
+        fRequestGrabImage = false;
+
+        SkCanvas* canvas = fGpuCanvas ? fGpuCanvas : orig;
+        SkDevice* device = canvas->getDevice();
+        SkBitmap bmp;
+        if (device->accessBitmap(false).copyTo(&bmp, SkBitmap::kARGB_8888_Config)) {
+            static int gSampleGrabCounter;
+            SkString name;
+            name.printf("sample_grab_%d", gSampleGrabCounter++);
+            SkImageEncoder::EncodeFile(name.c_str(), bmp,
+                                       SkImageEncoder::kPNG_Type, 100);
+        }
+    }
+
+    switch (fCanvasType) {
+        case kRaster_CanvasType:
+            break;
+        case kPicture_CanvasType:
+            if (true) {
+                SkPicture* pict = new SkPicture(*fPicture);
+                fPicture->unref();
+                orig->drawPicture(*pict);
+                pict->unref();
+            } else if (true) {
+                SkDynamicMemoryWStream ostream;
+                fPicture->serialize(&ostream);
+                fPicture->unref();
+
+                SkMemoryStream istream(ostream.getStream(), ostream.getOffset());
+                SkPicture pict(&istream);
+                orig->drawPicture(pict);
+            } else {
+                fPicture->draw(orig);
+                fPicture->unref();
+            }
+            fPicture = NULL;
+            break;
+#ifdef SK_SUPPORT_GL
+        case kGPU_CanvasType:
+            if (fShowZoomer) {
+                this->showZoomer(fGpuCanvas);
+            }
+            delete fGpuCanvas;
+            fGpuCanvas = NULL;
+            presentGL();
+            break;
+#endif
+    }
+
+    // Do this after presentGL and other finishing, rather than in afterChild
+    if (fMeasureFPS && fMeasureFPS_Time) {
+        fMeasureFPS_Time = SkTime::GetMSecs() - fMeasureFPS_Time;
+        this->updateTitle();
+        postInvalDelay(this->getSinkID());
+    }
+
+    //    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);
+    }        
+}
+
+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);
+    }
+
+    canvas->setDrawFilter(new FlagsDrawFilter(fLCDState, fAAState,
+                                       fFilterState, fHintingState))->unref();
+
+    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);
+    }
+    (void)SampleView::SetUsePipe(child, fUsePipe);
+}
+
+void SampleWindow::afterChild(SkView* child, SkCanvas* canvas) {
+    canvas->setDrawFilter(NULL);
+}
+
+static SkBitmap::Config gConfigCycle[] = {
+    SkBitmap::kNo_Config,           // none -> none
+    SkBitmap::kNo_Config,           // a1 -> none
+    SkBitmap::kNo_Config,           // a8 -> none
+    SkBitmap::kNo_Config,           // index8 -> none
+    SkBitmap::kARGB_4444_Config,    // 565 -> 4444
+    SkBitmap::kARGB_8888_Config,    // 4444 -> 8888
+    SkBitmap::kRGB_565_Config       // 8888 -> 565
+};
+
+static SkBitmap::Config cycle_configs(SkBitmap::Config c) {
+    return gConfigCycle[c];
+}
+
+void SampleWindow::changeZoomLevel(int delta) {
+    fZoomLevel += delta;
+    if (fZoomLevel > 0) {
+        fZoomLevel = SkMin32(fZoomLevel, MAX_ZOOM_LEVEL);
+        fZoomScale = SkIntToScalar(fZoomLevel + 1);
+    } else if (fZoomLevel < 0) {
+        fZoomLevel = SkMax32(fZoomLevel, MIN_ZOOM_LEVEL);
+        fZoomScale = SK_Scalar1 / (1 - fZoomLevel);
+    } else {
+        fZoomScale = SK_Scalar1;
+    }
+
+    this->inval(NULL);
+}
+
+bool SampleWindow::nextSample() {
+    fCurrIndex = (fCurrIndex + 1) % fSamples.count();
+    this->loadView(fSamples[fCurrIndex]());
+    return true;
+}
+
+bool SampleWindow::onEvent(const SkEvent& evt) {
+    if (evt.isType(ANIMATING_EVENTTYPE)) {
+        if (fAnimating) {
+            this->nextSample();
+            this->postAnimatingEvent();
+        }
+        return true;
+    }
+    if (evt.isType("set-curr-index")) {
+        fCurrIndex = evt.getFast32() % fSamples.count();
+        this->loadView(fSamples[fCurrIndex]());
+        return true;
+    }
+    if (isInvalEvent(evt)) {
+        this->inval(NULL);
+        return true;
+    }
+    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);
+    }
+    return this->INHERITED::onQuery(query);
+}
+
+static void cleanup_for_filename(SkString* name) {
+    char* str = name->writable_str();
+    for (size_t i = 0; i < name->size(); i++) {
+        switch (str[i]) {
+            case ':': str[i] = '-'; break;
+            case '/': str[i] = '-'; break;
+            case ' ': str[i] = '_'; break;
+            default: break;
+        }
+    }
+}
+
+bool SampleWindow::onHandleChar(SkUnichar uni) {
+    {
+        SkView* view = curr_view(this);
+        if (view) {
+            SkEvent evt(gCharEvtName);
+            evt.setFast32(uni);
+            if (view->doQuery(&evt)) {
+                return true;
+            }
+        }
+    }
+
+    int dx = 0xFF;
+    int dy = 0xFF;
+
+    switch (uni) {
+        case '5': dx =  0; dy =  0; break;
+        case '8': dx =  0; dy = -1; break;
+        case '6': dx =  1; dy =  0; break;
+        case '2': dx =  0; dy =  1; break;
+        case '4': dx = -1; dy =  0; break;
+        case '7': dx = -1; dy = -1; break;
+        case '9': dx =  1; dy = -1; break;
+        case '3': dx =  1; dy =  1; break;
+        case '1': dx = -1; dy =  1; break;
+
+        default:
+            break;
+    }
+
+    if (0xFF != dx && 0xFF != dy) {
+        if ((dx | dy) == 0) {
+            fScrollTestX = fScrollTestY = 0;
+        } else {
+            fScrollTestX += dx;
+            fScrollTestY += dy;
+        }
+        this->inval(NULL);
+        return true;
+    }
+
+    switch (uni) {
+        case 'a':
+            fAnimating = !fAnimating;
+            this->postAnimatingEvent();
+            this->updateTitle();
+            return true;
+        case 'b':
+            fAAState = cycle_tristate(fAAState);
+            this->updateTitle();
+            this->inval(NULL);
+            break;
+        case 'c':
+            fUseClip = !fUseClip;
+            this->inval(NULL);
+            this->updateTitle();
+            return true;
+        case 'd':
+            SkGraphics::SetFontCacheUsed(0);
+            return true;
+        case 'f':
+            fMeasureFPS = !fMeasureFPS;
+            this->inval(NULL);
+            break;
+        case 'g':
+            fRequestGrabImage = true;
+            this->inval(NULL);
+            break;
+        case 'h':
+            fHintingState = cycle_tristate(fHintingState);
+            this->updateTitle();
+            this->inval(NULL);
+            break;
+        case 'i':
+            this->zoomIn();
+            break;
+        case 'l':
+            fLCDState = cycle_tristate(fLCDState);
+            this->updateTitle();
+            this->inval(NULL);
+            break;
+        case 'n':
+            fFilterState = cycle_tristate(fFilterState);
+            this->updateTitle();
+            this->inval(NULL);
+            break;
+        case 'o':
+            this->zoomOut();
+            break;
+        case 'p':
+            fUsePipe = !fUsePipe;
+            this->updateTitle();
+            this->inval(NULL);
+            break;
+        case 'r':
+            fRotate = !fRotate;
+            this->inval(NULL);
+            this->updateTitle();
+            return true;
+        case 's':
+            fScale = !fScale;
+            this->inval(NULL);
+            this->updateTitle();
+            return true;
+        case 'x':
+            fFlipAxis ^= kFlipAxis_X;
+            this->updateTitle();
+            this->inval(NULL);
+            break;
+        case 'y':
+            fFlipAxis ^= kFlipAxis_Y;
+            this->updateTitle();
+            this->inval(NULL);
+            break;
+        case 'z':
+            this->toggleZoomer();
+            break;
+        default:
+            break;
+    }
+
+    return this->INHERITED::onHandleChar(uni);
+}
+
+#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:
+            fCanvasType = cycle_canvastype(fCanvasType);
+            this->updateTitle();
+            this->inval(NULL);
+            return true;
+        case kUp_SkKey:
+            if (USE_ARROWS_FOR_ZOOM) {
+                this->changeZoomLevel(1);
+            } else {
+                fNClip = !fNClip;
+                this->inval(NULL);
+            }
+            this->updateTitle();
+            return true;
+        case kDown_SkKey:
+            if (USE_ARROWS_FOR_ZOOM) {
+                this->changeZoomLevel(-1);
+            } else {
+                this->setConfig(cycle_configs(this->getBitmap().config()));
+            }
+            this->updateTitle();
+            return true;
+        case kOK_SkKey:
+            if (false) {
+                SkDebugfDumper dumper;
+                SkDumpCanvas dc(&dumper);
+                this->draw(&dc);
+            } else {
+                fRepeatDrawing = !fRepeatDrawing;
+                if (fRepeatDrawing) {
+                    this->inval(NULL);
+                }
+            }
+            return true;
+        case kBack_SkKey:
+            this->loadView(NULL);
+            return true;
+        default:
+            break;
+    }
+    return this->INHERITED::onHandleKey(key);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const char gGestureClickType[] = "GestureClickType";
+
+bool SampleWindow::onDispatchClick(int x, int y, Click::State state) {
+    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 {
+        return this->INHERITED::onDispatchClick(x, y, state);
+    }
+}
+
+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 = SkScalarToFloat(click->fCurr.fX);
+        float y = SkScalarToFloat(click->fCurr.fY);
+        switch (click->fState) {
+            case SkView::Click::kDown_State:
+                fGesture.touchBegin(click, x, y);
+                break;
+            case SkView::Click::kMoved_State:
+                fGesture.touchMoved(click, x, y);
+                this->inval(NULL);
+                break;
+            case SkView::Click::kUp_State:
+                fGesture.touchEnd(click);
+                this->inval(NULL);
+                break;
+        }
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SampleWindow::loadView(SkView* view) {
+    SkView::F2BIter iter(this);
+    SkView* prev = iter.next();
+    if (prev) {
+        prev->detachFromParent();
+    }
+
+    if (NULL == view) {
+        view = create_overview(fSamples.count(), fSamples.begin());
+    }
+    view->setVisibleP(true);
+    view->setClipToBounds(false);
+    this->attachChildToFront(view)->unref();
+    view->setSize(this->width(), this->height());
+
+    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* gCanvasTypePrefix[] = {
+    "raster: ",
+    "picture: ",
+    "opengl: "
+};
+
+static const char* trystate_str(SkTriState state,
+                                const char trueStr[], const char falseStr[]) {
+    if (kTrue_SkTriState == state) {
+        return trueStr;
+    } else if (kFalse_SkTriState == state) {
+        return falseStr;
+    }
+    return NULL;
+}
+
+void SampleWindow::updateTitle() {
+    SkString title;
+
+    SkView::F2BIter iter(this);
+    SkView* view = iter.next();
+    SkEvent evt(gTitleEvtName);
+    if (view->doQuery(&evt)) {
+        title.set(evt.findString(gTitleEvtName));
+    }
+    if (title.size() == 0) {
+        title.set("<unknown>");
+    }
+
+    title.prepend(gCanvasTypePrefix[fCanvasType]);
+
+    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> ");
+    }
+
+    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("{%d} ", fZoomLevel);
+    }
+    
+    if (fMeasureFPS) {
+        title.appendf(" %6.1f ms", fMeasureFPS_Time / (float)FPS_REPEAT_MULTIPLIER);
+    }
+    if (fUsePipe && SampleView::IsSampleView(view)) {
+        title.prepend("<P> ");
+    }
+    if (SampleView::IsSampleView(view)) {
+        title.prepend("! ");
+    }
+
+    this->setTitle(title.c_str());
+}
+
+void SampleWindow::onSizeChange() {
+    this->INHERITED::onSizeChange();
+
+    SkView::F2BIter iter(this);
+    SkView* view = iter.next();
+    view->setSize(this->width(), this->height());
+
+    // rebuild our clippath
+    {
+        const SkScalar W = this->width();
+        const SkScalar H = this->height();
+
+        fClipPath.reset();
+#if 0
+        for (SkScalar y = SK_Scalar1; y < H; y += SkIntToScalar(32)) {
+            SkRect r;
+            r.set(SK_Scalar1, y, SkIntToScalar(30), y + SkIntToScalar(30));
+            for (; r.fLeft < W; r.offset(SkIntToScalar(32), 0))
+                fClipPath.addRect(r);
+        }
+#else
+        SkRect r;
+        r.set(0, 0, W, H);
+        fClipPath.addRect(r, SkPath::kCCW_Direction);
+        r.set(W/4, H/4, W*3/4, H*3/4);
+        fClipPath.addRect(r, SkPath::kCW_Direction);
+#endif
+    }
+
+    this->updateTitle();    // to refresh our config
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const char is_sample_view_tag[] = "sample-is-sample-view";
+static const char repeat_count_tag[] = "sample-set-repeat-count";
+static const char set_use_pipe_tag[] = "sample-set-use-pipe";
+
+bool SampleView::IsSampleView(SkView* view) {
+    SkEvent evt(is_sample_view_tag);
+    return view->doQuery(&evt);
+}
+
+bool SampleView::SetRepeatDraw(SkView* view, int count) {
+    SkEvent evt(repeat_count_tag);
+    evt.setFast32(count);
+    return view->doEvent(evt);
+}
+
+bool SampleView::SetUsePipe(SkView* view, bool pred) {
+    SkEvent evt(set_use_pipe_tag);
+    evt.setFast32(pred);
+    return view->doEvent(evt);
+}
+
+bool SampleView::onEvent(const SkEvent& evt) {
+    if (evt.isType(repeat_count_tag)) {
+        fRepeatCount = evt.getFast32();
+        return true;
+    }
+    if (evt.isType(set_use_pipe_tag)) {
+        fUsePipe = !!evt.getFast32();
+        return true;
+    }
+    return this->INHERITED::onEvent(evt);
+}
+
+bool SampleView::onQuery(SkEvent* evt) {
+    if (evt->isType(is_sample_view_tag)) {
+        return true;
+    }
+    return this->INHERITED::onQuery(evt);
+}
+
+#ifdef TEST_GPIPE
+    #include "SkGPipe.h"
+
+class SimplePC : public SkGPipeController {
+public:
+    SimplePC(SkCanvas* target);
+    ~SimplePC();
+
+    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);
+    sk_free(fBlock);
+
+    if (fTotalWritten) {
+        SkDebugf("--- %d bytes %d atoms, status %d\n", fTotalWritten,
+                 fAtomsWritten, fStatus);
+    }
+}
+
+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);
+    
+#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
+    
+    fStatus = fReader.playback((const char*)fBlock + fBytesWritten, bytes);
+    SkASSERT(SkGPipeReader::kError_Status != fStatus);
+    fBytesWritten += bytes;
+    fTotalWritten += bytes;
+
+    fAtomsWritten += 1;
+}
+
+#endif
+
+
+void SampleView::onDraw(SkCanvas* canvas) {
+#ifdef TEST_GPIPE
+    SimplePC controller(canvas);
+    SkGPipeWriter writer;
+    if (fUsePipe) {
+        uint32_t flags = SkGPipeWriter::kCrossProcess_Flag;
+//        flags = 0;
+        canvas = writer.startRecording(&controller, flags);
+    }
+#endif
+
+    this->onDrawBackground(canvas);
+
+    for (int i = 0; i < fRepeatCount; i++) {
+        SkAutoCanvasRestore acr(canvas, true);
+        this->onDrawContent(canvas);
+    }
+}
+
+void SampleView::onDrawBackground(SkCanvas* canvas) {
+    canvas->drawColor(fBGColor);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> void SkTBSort(T array[], int count) {
+    for (int i = 1; i < count - 1; i++) {
+        bool didSwap = false;
+        for (int j = count - 1; j > i; --j) {
+            if (array[j] < array[j-1]) {
+                T tmp(array[j-1]);
+                array[j-1] = array[j];
+                array[j] = tmp;
+                didSwap = true;
+            }
+        }
+        if (!didSwap) {
+            break;
+        }
+    }
+
+    for (int k = 0; k < count - 1; k++) {
+        SkASSERT(!(array[k+1] < array[k]));
+    }
+}
+
+#include "SkRandom.h"
+
+static void rand_rect(SkIRect* rect, SkRandom& rand) {
+    int bits = 8;
+    int shift = 32 - bits;
+    rect->set(rand.nextU() >> shift, rand.nextU() >> shift,
+              rand.nextU() >> shift, rand.nextU() >> shift);
+    rect->sort();
+}
+
+static void dumpRect(const SkIRect& r) {
+    SkDebugf(" { %d, %d, %d, %d },\n",
+             r.fLeft, r.fTop,
+             r.fRight, r.fBottom);
+}
+
+static void test_rects(const SkIRect rect[], int count) {
+    SkRegion rgn0, rgn1;
+
+    for (int i = 0; i < count; i++) {
+        rgn0.op(rect[i], SkRegion::kUnion_Op);
+     //   dumpRect(rect[i]);
+    }
+    rgn1.setRects(rect, count);
+
+    if (rgn0 != rgn1) {
+        SkDebugf("\n");
+        for (int i = 0; i < count; i++) {
+            dumpRect(rect[i]);
+        }
+        SkDebugf("\n");
+    }
+}
+
+static void test() {
+    size_t i;
+
+    const SkIRect r0[] = {
+        { 0, 0, 1, 1 },
+        { 2, 2, 3, 3 },
+    };
+    const SkIRect r1[] = {
+        { 0, 0, 1, 3 },
+        { 1, 1, 2, 2 },
+        { 2, 0, 3, 3 },
+    };
+    const SkIRect r2[] = {
+        { 0, 0, 1, 2 },
+        { 2, 1, 3, 3 },
+        { 4, 0, 5, 1 },
+        { 6, 0, 7, 4 },
+    };
+
+    static const struct {
+        const SkIRect* fRects;
+        int            fCount;
+    } gRecs[] = {
+        { r0, SK_ARRAY_COUNT(r0) },
+        { r1, SK_ARRAY_COUNT(r1) },
+        { r2, SK_ARRAY_COUNT(r2) },
+    };
+
+    for (i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
+        test_rects(gRecs[i].fRects, gRecs[i].fCount);
+    }
+
+    SkRandom rand;
+    for (i = 0; i < 10000; i++) {
+        SkRegion rgn0, rgn1;
+
+        const int N = 8;
+        SkIRect rect[N];
+        for (int j = 0; j < N; j++) {
+            rand_rect(&rect[j], rand);
+        }
+        test_rects(rect, N);
+    }
+}
+
+SkOSWindow* create_sk_window(void* hwnd) {
+//    test();
+    return new SampleWindow(hwnd);
+}
+
+void get_preferred_size(int* x, int* y, int* width, int* height) {
+    *x = 10;
+    *y = 50;
+    *width = 640;
+    *height = 480;
+}
+
+void application_init() {
+//    setenv("ANDROID_ROOT", "../../../data", 0);
+#ifdef SK_BUILD_FOR_MAC
+    setenv("ANDROID_ROOT", "/android/device/data", 0);
+#endif
+    SkGraphics::Init();
+    SkEvent::Init();
+}
+
+void application_term() {
+    SkEvent::Term();
+    SkGraphics::Term();
+}
diff --git a/samplecode/SampleArc.cpp b/samplecode/SampleArc.cpp
new file mode 100644
index 0000000..8e3ad88
--- /dev/null
+++ b/samplecode/SampleArc.cpp
@@ -0,0 +1,184 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkComposeShader.h"
+#include "Sk1DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkLayerRasterizer.h"
+
+#include "SkParsePath.h"
+static void testparse() {
+    SkRect r;
+    r.set(0, 0, SkFloatToScalar(10), SkFloatToScalar(10.5));
+    SkPath p, p2;
+    SkString str, str2;
+
+    p.addRect(r);
+    SkParsePath::ToSVGString(p, &str);
+    SkParsePath::FromSVGString(str.c_str(), &p2);
+    SkParsePath::ToSVGString(p2, &str2);
+}
+
+class ArcsView : public SampleView {
+public:
+	ArcsView() {
+        testparse();
+        fSweep = SkIntToScalar(100);
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Arcs");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    static void drawRectWithLines(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
+        canvas->drawRect(r, p);
+        canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, p);
+        canvas->drawLine(r.fLeft, r.fBottom, r.fRight, r.fTop, p);
+        canvas->drawLine(r.fLeft, r.centerY(), r.fRight, r.centerY(), p);
+        canvas->drawLine(r.centerX(), r.fTop, r.centerX(), r.fBottom, p);
+    }
+    
+    static void draw_label(SkCanvas* canvas, const SkRect& rect,
+                            int start, int sweep) {
+        SkPaint paint;
+        
+        paint.setAntiAlias(true);
+        paint.setTextAlign(SkPaint::kCenter_Align);
+        
+        SkString    str;
+        
+        str.appendS32(start);
+        str.append(", ");
+        str.appendS32(sweep);
+        canvas->drawText(str.c_str(), str.size(), rect.centerX(),
+                         rect.fBottom + paint.getTextSize() * 5/4, paint);
+    }
+    
+    static void drawArcs(SkCanvas* canvas) {
+        SkPaint paint;
+        SkRect  r;
+        SkScalar w = SkIntToScalar(75);
+        SkScalar h = SkIntToScalar(50);
+
+        r.set(0, 0, w, h);
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        
+        canvas->save();
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(300));
+        
+        paint.setStrokeWidth(SkIntToScalar(1));
+        
+        static const int gAngles[] = {
+            0, 360,
+            0, 45,
+            0, -45,
+            720, 135,
+            -90, 269,
+            -90, 270,
+            -90, 271,
+            -180, -270,
+            225, 90
+        };
+        
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gAngles); i += 2) {
+            paint.setColor(SK_ColorBLACK);
+            drawRectWithLines(canvas, r, paint);
+
+            paint.setColor(SK_ColorRED);
+            canvas->drawArc(r, SkIntToScalar(gAngles[i]),
+                            SkIntToScalar(gAngles[i+1]), false, paint);
+            
+            draw_label(canvas, r, gAngles[i], gAngles[i+1]);
+
+            canvas->translate(w * 8 / 7, 0);
+        }
+        
+        canvas->restore();
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        fSweep = SampleCode::GetAnimScalar(SkIntToScalar(360)/24,
+                                           SkIntToScalar(360));
+//        fSweep = SkFloatToScalar(359.99f);
+
+        SkRect  r;
+        SkPaint paint;
+        
+        paint.setAntiAlias(true);
+        paint.setStrokeWidth(SkIntToScalar(2));
+        paint.setStyle(SkPaint::kStroke_Style);
+        
+        r.set(0, 0, SkIntToScalar(200), SkIntToScalar(200));
+        r.offset(SkIntToScalar(20), SkIntToScalar(20));
+        
+        if (false) {
+            const SkScalar d = SkIntToScalar(3);
+            const SkScalar rad[] = { d, d, d, d, d, d, d, d };
+            SkPath path;
+            path.addRoundRect(r, rad);
+            canvas->drawPath(path, paint);
+            return;
+        }
+
+        drawRectWithLines(canvas, r, paint);
+        
+   //     printf("----- sweep %g %X\n", SkScalarToFloat(fSweep), SkDegreesToRadians(fSweep));
+        
+        
+        paint.setStyle(SkPaint::kFill_Style);
+        paint.setColor(0x800000FF);
+        canvas->drawArc(r, 0, fSweep, true, paint);
+
+        paint.setColor(0x800FF000);
+        canvas->drawArc(r, 0, fSweep, false, paint);
+
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(SK_ColorRED);
+        canvas->drawArc(r, 0, fSweep, true, paint);
+        
+        paint.setStrokeWidth(0);
+        paint.setColor(SK_ColorBLUE);
+        canvas->drawArc(r, 0, fSweep, false, paint);
+        
+        drawArcs(canvas);
+        this->inval(NULL);
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+     //   fSweep += SK_Scalar1;
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+    virtual bool onClick(Click* click) {
+        return this->INHERITED::onClick(click);
+    }
+    
+private:
+    SkScalar fSweep;
+
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ArcsView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleAvoid.cpp b/samplecode/SampleAvoid.cpp
new file mode 100644
index 0000000..868a67c
--- /dev/null
+++ b/samplecode/SampleAvoid.cpp
@@ -0,0 +1,99 @@
+#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/SampleBigGradient.cpp b/samplecode/SampleBigGradient.cpp
new file mode 100644
index 0000000..5ebb516
--- /dev/null
+++ b/samplecode/SampleBigGradient.cpp
@@ -0,0 +1,43 @@
+#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..002b2b9
--- /dev/null
+++ b/samplecode/SampleBitmapRect.cpp
@@ -0,0 +1,92 @@
+#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"
+
+static SkBitmap make_bitmap() {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 64, 64);
+    bm.allocPixels();
+    SkCanvas canvas(bm);
+    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);
+    return bm;
+}
+
+class BitmapRectView : public SampleView {
+public:
+    SkBitmap fBitmap;
+
+	BitmapRectView() {
+        fBitmap = make_bitmap();
+        this->setBGColor(SK_ColorGRAY);
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "BitmapRect");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        const SkIRect src[] = {
+            { 0, 0, 32, 32 },
+            { 0, 0, 80, 80 },
+            { 32, 32, 96, 96 },
+            { -32, -32, 32, 32, }
+        };
+
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(SK_ColorGREEN);
+
+        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(fBitmap, 0, 0, &paint);
+            canvas->drawBitmapRect(fBitmap, &src[i], dstR, &paint);
+
+            canvas->drawRect(dstR, paint);
+            canvas->drawRect(srcR, paint);
+            
+            canvas->translate(160, 0);
+        }
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new BitmapRectView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleBlur.cpp b/samplecode/SampleBlur.cpp
new file mode 100644
index 0000000..d2ea2b0
--- /dev/null
+++ b/samplecode/SampleBlur.cpp
@@ -0,0 +1,131 @@
+#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 SkView {
+    SkBitmap    fBM;
+public:
+	BlurView() {
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Blur");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+
+    virtual void onDraw(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..d445df7
--- /dev/null
+++ b/samplecode/SampleBox.cpp
@@ -0,0 +1,48 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+
+class SimpleView : public SampleView {
+public:
+	SimpleView() {
+        this->setBGColor(0xFFDDDDDD);
+	}
+	
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)  {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Box Gradient");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+	
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3)));
+        paint.setStyle(SkPaint::kFill_Style);
+        
+        SkRect  r;
+        SkScalar x,y;
+        x = 10;
+        y = 10;
+
+        r.set(x, y, x + SkIntToScalar(100), y + SkIntToScalar(100));
+        for (int i = 0; i < 256; ++i) {
+            canvas->translate(1, 1);
+            paint.setColor(0xFF000000 + i * 0x00010000);
+            canvas->drawRect(r, paint);
+        }
+    }
+	
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new SimpleView; }
+static SkViewRegister reg(MyFactory);
\ No newline at end of file
diff --git a/samplecode/SampleCamera.cpp b/samplecode/SampleCamera.cpp
new file mode 100644
index 0000000..2db3968
--- /dev/null
+++ b/samplecode/SampleCamera.cpp
@@ -0,0 +1,99 @@
+#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..2abc28d
--- /dev/null
+++ b/samplecode/SampleCircle.cpp
@@ -0,0 +1,126 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+
+// ensure that we don't accidentally screw up the bounds when the oval is
+// fractional, and the impl computes the center and radii, and uses them to
+// reconstruct the edges of the circle.
+// see bug# 1504910
+static void test_circlebounds(SkCanvas* canvas) {
+#ifdef SK_SCALAR_IS_FLOAT
+    SkRect r = { 1.39999998f, 1, 21.3999996f, 21 };
+    SkPath p;
+    p.addOval(r);
+    SkASSERT(r == p.getBounds());
+#endif
+}
+
+class CircleView : public SampleView {
+public:
+    static const SkScalar ANIM_DX;
+    static const SkScalar ANIM_DY;
+    static const SkScalar ANIM_RAD;
+    SkScalar fDX, fDY, fRAD;
+
+    CircleView() {
+        fDX = fDY = fRAD = 0;
+        fN = 3;
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Circles");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void circle(SkCanvas* canvas, int width, bool aa) {
+        SkPaint paint;
+        
+        paint.setAntiAlias(aa);
+        if (width < 0) {
+            paint.setStyle(SkPaint::kFill_Style);
+        } else {
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(SkIntToScalar(width));
+        }
+        canvas->drawCircle(0, 0, SkIntToScalar(9) + fRAD, paint);
+    }
+    
+    void drawSix(SkCanvas* canvas, SkScalar dx, SkScalar dy) {
+        for (int width = -1; width <= 1; width++) {
+            canvas->save();
+            circle(canvas, width, false);
+            canvas->translate(0, dy);
+            circle(canvas, width, true);
+            canvas->restore();
+            canvas->translate(dx, 0);
+        }
+    }
+    
+    static void blowup(SkCanvas* canvas, const SkIRect& src, const SkRect& dst) {
+        SkDevice* device = canvas->getDevice();
+        const SkBitmap& bm = device->accessBitmap(false);
+        canvas->drawBitmapRect(bm, &src, dst, NULL);
+    }
+    
+    static void make_poly(SkPath* path, int n) {
+        if (n <= 0) {
+            return;
+        }
+        path->incReserve(n + 1);
+        path->moveTo(SK_Scalar1, 0);
+        SkScalar step = SK_ScalarPI * 2 / n;
+        SkScalar angle = 0;
+        for (int i = 1; i < n; i++) {
+            angle += step;
+            SkScalar c, s = SkScalarSinCos(angle, &c);
+            path->lineTo(c, s);
+        }
+        path->close();
+    }
+    
+    static void rotate(SkCanvas* canvas, SkScalar angle, SkScalar px, SkScalar py) {
+        canvas->translate(-px, -py);
+        canvas->rotate(angle);
+        canvas->translate(px, py);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+//        canvas->drawCircle(250, 250, 220, paint);
+        SkMatrix matrix;
+        matrix.setScale(SkIntToScalar(100), SkIntToScalar(100));
+        matrix.postTranslate(SkIntToScalar(200), SkIntToScalar(200));
+        canvas->concat(matrix);
+        for (int n = 3; n < 20; n++) {
+            SkPath path;
+            make_poly(&path, n);
+            SkAutoCanvasRestore acr(canvas, true);
+            canvas->rotate(SkIntToScalar(10) * (n - 3));
+            canvas->translate(-SK_Scalar1, 0);
+            canvas->drawPath(path, paint);
+        }
+    }
+    
+private:
+    int fN;
+    typedef SampleView INHERITED;
+};
+
+const SkScalar CircleView::ANIM_DX(SK_Scalar1 / 67);
+const SkScalar CircleView::ANIM_DY(SK_Scalar1 / 29);
+const SkScalar CircleView::ANIM_RAD(SK_Scalar1 / 19);
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new CircleView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleClamp.cpp b/samplecode/SampleClamp.cpp
new file mode 100644
index 0000000..88c1b91
--- /dev/null
+++ b/samplecode/SampleClamp.cpp
@@ -0,0 +1,61 @@
+#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/SampleCode.h b/samplecode/SampleCode.h
new file mode 100644
index 0000000..c42ee25
--- /dev/null
+++ b/samplecode/SampleCode.h
@@ -0,0 +1,81 @@
+#ifndef SampleCode_DEFINED
+#define SampleCode_DEFINED
+
+#include "SkColor.h"
+#include "SkEvent.h"
+#include "SkKey.h"
+#include "SkView.h"
+
+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 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);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+typedef SkView* (*SkViewFactory)();
+
+class SkViewRegister : SkNoncopyable {
+public:
+    explicit SkViewRegister(SkViewFactory);
+    
+    static const SkViewRegister* Head() { return gHead; }
+    
+    SkViewRegister* next() const { return fChain; }
+    SkViewFactory   factory() const { return fFact; }
+    
+private:
+    SkViewFactory   fFact;
+    SkViewRegister* fChain;
+    
+    static SkViewRegister* gHead;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SampleView : public SkView {
+public:
+    SampleView() : fRepeatCount(1), fBGColor(SK_ColorWHITE) {
+        fUsePipe = false;
+    }
+
+    void setBGColor(SkColor color) { fBGColor = color; }
+
+    static bool IsSampleView(SkView*);
+    static bool SetRepeatDraw(SkView*, int count);
+    static bool SetUsePipe(SkView*, bool);
+
+protected:
+    virtual void onDrawBackground(SkCanvas*);
+    virtual void onDrawContent(SkCanvas*) = 0;
+
+    // overrides
+    virtual bool onEvent(const SkEvent& evt);
+    virtual bool onQuery(SkEvent* evt);
+    virtual void onDraw(SkCanvas*);
+
+private:
+    int fRepeatCount;
+    SkColor fBGColor;
+
+    bool fUsePipe;
+
+    typedef SkView INHERITED;
+};
+
+#endif
+
diff --git a/samplecode/SampleColorFilter.cpp b/samplecode/SampleColorFilter.cpp
new file mode 100644
index 0000000..0e1fb11
--- /dev/null
+++ b/samplecode/SampleColorFilter.cpp
@@ -0,0 +1,210 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkColorFilter.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+static int inflate5To8(int x) {
+    return (x << 3) | (x >> 2);
+}
+
+static int trunc5(int x) {
+    return x >> 3;
+}
+
+#define SK_R16_BITS 5
+
+static int round5_slow(int x) {    
+    int orig = x & 7;
+    int fake = x >> 5;
+    int trunc = x >> 3;
+
+    int diff = fake - orig;
+
+    int bias = 0;
+    if (diff > 4) {
+        bias = -1;
+    } else if (diff < -4) {
+        bias = 1;
+    }
+    return trunc + bias;
+}
+
+static int round5_fast(int x) {
+    int result = x + 3 - (x >> 5) + (x >> 7);
+    result >>= 3;
+
+    {
+        int r2 = round5_slow(x);
+        SkASSERT(r2 == result);
+    }
+    return result;
+}
+
+static void test_5bits() {
+    int e0 = 0;
+    int e1 = 0;
+    int e2 = 0;
+    int ae0 = 0;
+    int ae1 = 0;
+    int ae2 = 0;
+    for (int i = 0; i < 256; i++) {
+        int t0 = trunc5(i);
+        int t1 = round5_fast(i);
+        int t2 = trunc5(i);
+        int v0 = inflate5To8(t0);
+        int v1 = inflate5To8(t1);
+        int v2 = inflate5To8(t2);
+        int err0 = i - v0;
+        int err1 = i - v1;
+        int err2 = i - v2;
+        SkDebugf("--- %3d : trunc=%3d (%2d) round:%3d (%2d) \n"/*new:%d (%2d)\n"*/, i,
+                 v0, err0, v1, err1, v2, err2);
+        
+
+        e0 += err0;
+        e1 += err1;
+        e2 += err2;
+        ae0 += SkAbs32(err0);
+        ae1 += SkAbs32(err1);
+        ae2 += SkAbs32(err2);
+    }
+    SkDebugf("--- trunc: %d %d  round: %d %d new: %d %d\n", e0, ae0, e1, ae1, e2, ae2);
+}
+
+static SkShader* createChecker() {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+    bm.allocPixels();
+    bm.lockPixels();
+    *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = SkPreMultiplyColor(0xFFFFFFFF);
+    *bm.getAddr32(0, 1) = *bm.getAddr32(1, 0) = SkPreMultiplyColor(0xFFCCCCCC);
+    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+                                               SkShader::kRepeat_TileMode);
+
+    SkMatrix m;
+    m.setScale(12, 12);
+    s->setLocalMatrix(m);
+    return s;
+}
+
+static SkBitmap createBitmap(int n) {
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+    
+    SkCanvas canvas(bitmap);
+    SkRect r;
+    r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n));
+    r.inset(SK_Scalar1, SK_Scalar1);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    
+    paint.setColor(SK_ColorRED);
+    canvas.drawOval(r, paint);
+
+    r.inset(SK_Scalar1*n/4, SK_Scalar1*n/4);
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    paint.setColor(0x800000FF);
+    canvas.drawOval(r, paint);
+    
+    return bitmap;
+}
+
+class ColorFilterView : public SampleView {
+    SkBitmap fBitmap;
+    SkShader* fShader;
+    enum {
+        N = 64
+    };
+public:
+    ColorFilterView() {
+        fBitmap = createBitmap(N);
+        fShader = createChecker();
+      
+//        test_5bits();
+    }
+
+    virtual ~ColorFilterView() {
+        fShader->unref();
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "ColorFilter");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawBackground(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setShader(fShader);
+        canvas->drawPaint(paint);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        if (false) {
+            SkPaint p;
+            p.setAntiAlias(true);
+            SkRect r = { 20.4f, 10, 20.6f, 20 };
+            canvas->drawRect(r, p);
+            r.set(30.9f, 10, 31.1f, 20);
+            canvas->drawRect(r, p);
+            return;
+        }
+        
+        static const SkXfermode::Mode gModes[] = {
+            SkXfermode::kClear_Mode,
+            SkXfermode::kSrc_Mode,
+            SkXfermode::kDst_Mode,
+            SkXfermode::kSrcOver_Mode,
+            SkXfermode::kDstOver_Mode,
+            SkXfermode::kSrcIn_Mode,
+            SkXfermode::kDstIn_Mode,
+            SkXfermode::kSrcOut_Mode,
+            SkXfermode::kDstOut_Mode,
+            SkXfermode::kSrcATop_Mode,
+            SkXfermode::kDstATop_Mode,
+            SkXfermode::kXor_Mode,
+            SkXfermode::kPlus_Mode,
+            SkXfermode::kMultiply_Mode,
+        };
+    
+        static const SkColor gColors[] = {
+            0xFF000000,
+            0x80000000,
+            0xFF00FF00,
+            0x8000FF00,
+            0x00000000,
+        };
+
+        float scale = 1.5f;
+        SkPaint paint;
+        canvas->translate(N / 8, N / 8);
+
+        for (size_t y = 0; y < SK_ARRAY_COUNT(gColors); y++) {
+            for (size_t x = 0; x < SK_ARRAY_COUNT(gModes); x++) {
+                SkColorFilter* cf = SkColorFilter::CreateModeFilter(gColors[y], gModes[x]);
+                SkSafeUnref(paint.setColorFilter(cf));
+                canvas->drawBitmap(fBitmap, x * N * 1.25f, y * N * scale, &paint);
+            }
+        }
+        
+    }
+    
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ColorFilterView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleComplexClip.cpp b/samplecode/SampleComplexClip.cpp
new file mode 100644
index 0000000..672d055
--- /dev/null
+++ b/samplecode/SampleComplexClip.cpp
@@ -0,0 +1,147 @@
+#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/SampleCull.cpp b/samplecode/SampleCull.cpp
new file mode 100644
index 0000000..7b4eab6
--- /dev/null
+++ b/samplecode/SampleCull.cpp
@@ -0,0 +1,189 @@
+#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..4cef07f
--- /dev/null
+++ b/samplecode/SampleDash.cpp
@@ -0,0 +1,88 @@
+#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..b192c5d
--- /dev/null
+++ b/samplecode/SampleDecode.cpp
@@ -0,0 +1,69 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+
+static const struct {
+    SkBitmap::Config    fPrefConfig;
+    bool                fDither;
+} gRec[] = {
+    { SkBitmap::kIndex8_Config,     false },
+    { SkBitmap::kARGB_8888_Config,  false },
+    { SkBitmap::kARGB_4444_Config,  false },
+    { SkBitmap::kARGB_4444_Config,  true },
+    { SkBitmap::kRGB_565_Config,    false },
+    { SkBitmap::kRGB_565_Config,    true },
+};
+
+class DecodeView : public SkView {
+public:
+    SkBitmap fBitmap[SK_ARRAY_COUNT(gRec)];
+
+	DecodeView() {
+        SkFILEStream stream("/skimages/index.png");
+        SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
+        if (codec) {
+            for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+                stream.rewind();
+                codec->setDitherImage(gRec[i].fDither);
+                codec->decode(&stream, &fBitmap[i], gRec[i].fPrefConfig,
+                              SkImageDecoder::kDecodePixels_Mode);
+            }
+        }
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "ImageDecoder");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+//        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
+        
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fBitmap); i++) {
+            canvas->drawBitmap(fBitmap[i], 0, 0);
+            canvas->translate(SkIntToScalar(fBitmap[i].width()), 0);
+        }
+    }
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DecodeView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleDither.cpp b/samplecode/SampleDither.cpp
new file mode 100644
index 0000000..3e77a5d
--- /dev/null
+++ b/samplecode/SampleDither.cpp
@@ -0,0 +1,178 @@
+#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..0d62446
--- /dev/null
+++ b/samplecode/SampleDitherBitmap.cpp
@@ -0,0 +1,141 @@
+#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..deb1fb2
--- /dev/null
+++ b/samplecode/SampleDraw.cpp
@@ -0,0 +1,373 @@
+#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/SampleDrawLooper.cpp b/samplecode/SampleDrawLooper.cpp
new file mode 100644
index 0000000..30879f7
--- /dev/null
+++ b/samplecode/SampleDrawLooper.cpp
@@ -0,0 +1,92 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkRandom.h"
+#include "SkLayerDrawLooper.h"
+#include "SkBlurMaskFilter.h"
+
+#define WIDTH   200
+#define HEIGHT  200
+
+class LooperView : public SampleView {
+public:
+
+    SkLayerDrawLooper*   fLooper;
+
+	LooperView() {
+        static const struct {
+            SkColor         fColor;
+            SkPaint::Style  fStyle;
+            SkScalar        fWidth;
+            SkScalar        fOffset;
+            int             fBlur;
+        } gParams[] = {
+            { SK_ColorWHITE, SkPaint::kStroke_Style, SkIntToScalar(1)*3/4, 0, 0 },
+            { SK_ColorRED, SkPaint::kStroke_Style, SkIntToScalar(4), 0, 0 },
+            { SK_ColorBLUE, SkPaint::kFill_Style, 0, 0, 0 },
+            { 0x88000000, SkPaint::kFill_Style, 0, SkIntToScalar(10), 3 }
+        };
+
+        fLooper = new SkLayerDrawLooper;
+
+        SkLayerDrawLooper::LayerInfo info;
+        info.fFlagsMask = SkPaint::kAntiAlias_Flag;
+        info.fPaintBits = SkLayerDrawLooper::kStyle_Bit | SkLayerDrawLooper::kMaskFilter_Bit;
+        info.fColorMode = SkXfermode::kSrc_Mode;
+        
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gParams); i++) {
+            info.fOffset.set(gParams[i].fOffset, gParams[i].fOffset);
+            SkPaint* paint = fLooper->addLayer(info);
+            paint->setAntiAlias(true);
+            paint->setColor(gParams[i].fColor);
+            paint->setStyle(gParams[i].fStyle);
+            paint->setStrokeWidth(gParams[i].fWidth);
+            if (gParams[i].fBlur > 0) {
+                SkMaskFilter* mf = SkBlurMaskFilter::Create(SkIntToScalar(gParams[i].fBlur),
+                                                            SkBlurMaskFilter::kNormal_BlurStyle);
+                paint->setMaskFilter(mf)->unref();
+            }
+        }
+        
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+    virtual ~LooperView() {
+        SkSafeUnref(fLooper);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "DrawLooper");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint  paint;
+        paint.setTextSize(SkIntToScalar(72));
+        paint.setLooper(fLooper);
+
+        canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50),
+                           SkIntToScalar(30), paint);
+
+        canvas->drawRectCoords(SkIntToScalar(150), SkIntToScalar(50),
+                               SkIntToScalar(200), SkIntToScalar(100), paint);
+
+        canvas->drawText("Looper", 6, SkIntToScalar(230), SkIntToScalar(100),
+                         paint);
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LooperView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleEffects.cpp b/samplecode/SampleEffects.cpp
new file mode 100644
index 0000000..a63c08d
--- /dev/null
+++ b/samplecode/SampleEffects.cpp
@@ -0,0 +1,130 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkView.h"
+
+#include "SkBlurMaskFilter.h"
+#include "SkColorMatrixFilter.h"
+#include "SkDiscretePathEffect.h"
+#include "SkGradientShader.h"
+
+#include "SkEdgeClipper.h"
+
+static void test_edgeclipper() {
+    SkPoint pts[] = {
+        { -8.38822452e+21f, -7.69721471e+19f },
+        { 1.57645875e+23f, 1.44634003e+21f },
+        { 1.61519691e+23f, 1.48208059e+21f },
+        { 3.13963584e+23f, 2.88057438e+21f }
+    };
+    SkRect clip = { 0, 0, 300, 200 };
+    
+    SkEdgeClipper clipper;
+    clipper.clipCubic(pts, clip);
+}
+
+///////////
+
+//#define COLOR 0xFFFF8844
+#define COLOR 0xFF888888
+
+static void paint_proc0(SkPaint* paint) {
+}
+
+static void paint_proc1(SkPaint* paint) {
+    paint->setMaskFilter(SkBlurMaskFilter::Create(2,
+                                SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+}
+
+static void paint_proc2(SkPaint* paint) {
+    SkScalar dir[3] = { 1, 1, 1};
+    paint->setMaskFilter(
+                     SkBlurMaskFilter::CreateEmboss(dir, 0.1f, 0.05f, 1))->unref();
+}
+
+static void paint_proc3(SkPaint* paint) {
+    SkColor colors[] = { SK_ColorRED, COLOR, SK_ColorBLUE };
+    SkPoint pts[] = { { 3, 0 }, { 7, 5 } };
+    paint->setShader(SkGradientShader::CreateLinear(pts, colors, NULL, SK_ARRAY_COUNT(colors),
+                                        SkShader::kMirror_TileMode))->unref();
+}
+
+static void paint_proc5(SkPaint* paint) {
+    paint_proc3(paint);
+    paint_proc2(paint);
+}
+
+typedef void (*PaintProc)(SkPaint*);
+const PaintProc gPaintProcs[] = {
+    paint_proc0,
+    paint_proc1,
+    paint_proc2,
+    paint_proc3,
+    paint_proc5,
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class EffectsView : public SampleView {
+public:
+    SkPath fPath;
+    SkPaint fPaint[SK_ARRAY_COUNT(gPaintProcs)];
+
+	EffectsView() {
+        size_t i;
+        const float pts[] = {
+            0, 0,
+            10, 0,
+            10, 5,
+            20, -5,
+            10, -15,
+            10, -10,
+            0, -10
+        };
+        fPath.moveTo(pts[0], pts[1]);
+        for (i = 2; i < SK_ARRAY_COUNT(pts); i += 2) {
+            fPath.lineTo(pts[i], pts[i+1]);
+        }
+        
+        for (i = 0; i < SK_ARRAY_COUNT(gPaintProcs); i++) {
+            fPaint[i].setAntiAlias(true);
+            fPaint[i].setColor(COLOR);
+            gPaintProcs[i](&fPaint[i]);
+        }
+
+        test_edgeclipper();
+        SkColorMatrix cm;
+        cm.setRotate(SkColorMatrix::kG_Axis, 180);
+        cm.setIdentity();
+        
+        this->setBGColor(0xFFDDDDDD);
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Effects");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        canvas->scale(3, 3);
+        canvas->translate(10, 30);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fPaint); i++) {
+            canvas->drawPath(fPath, fPaint[i]);
+            canvas->translate(32, 0);
+        }
+    }
+    
+private:
+    typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new EffectsView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleEmboss.cpp b/samplecode/SampleEmboss.cpp
new file mode 100644
index 0000000..8b3f194
--- /dev/null
+++ b/samplecode/SampleEmboss.cpp
@@ -0,0 +1,66 @@
+#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/SampleEncode.cpp b/samplecode/SampleEncode.cpp
new file mode 100644
index 0000000..f4ea195
--- /dev/null
+++ b/samplecode/SampleEncode.cpp
@@ -0,0 +1,224 @@
+#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/SampleExtractAlpha.cpp b/samplecode/SampleExtractAlpha.cpp
new file mode 100644
index 0000000..860272d
--- /dev/null
+++ b/samplecode/SampleExtractAlpha.cpp
@@ -0,0 +1,90 @@
+#include "SampleCode.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 ExtractAlphaView : public SampleView {
+    SkBitmap    fBM8;
+    SkBitmap    fBM32;
+    SkBitmap    fBM4;
+public:
+	ExtractAlphaView() {
+        fBM8 = make_bitmap();
+        fBM8.copyTo(&fBM32, SkBitmap::kARGB_8888_Config);
+        fBM8.copyTo(&fBM4, SkBitmap::kARGB_4444_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);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+
+        SkMatrix matrix;
+        matrix.setScale(3.55f, 80.f);
+        canvas->setMatrix(matrix);
+        
+        paint.setStrokeWidth(0.0588f);
+        canvas->drawLine(10, 5, 30, 4.8f, paint);
+        
+        paint.setStrokeWidth(0.06f);
+        canvas->drawLine(20, 5, 40, 4.8f, paint);
+    }
+    
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ExtractAlphaView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleFillType.cpp b/samplecode/SampleFillType.cpp
new file mode 100644
index 0000000..393c5f7
--- /dev/null
+++ b/samplecode/SampleFillType.cpp
@@ -0,0 +1,90 @@
+#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..a9089fa
--- /dev/null
+++ b/samplecode/SampleFilter.cpp
@@ -0,0 +1,138 @@
+#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..c1a16a8
--- /dev/null
+++ b/samplecode/SampleFilter2.cpp
@@ -0,0 +1,116 @@
+#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..0b8187a
--- /dev/null
+++ b/samplecode/SampleFontCache.cpp
@@ -0,0 +1,135 @@
+#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..0b0d349
--- /dev/null
+++ b/samplecode/SampleFontScalerTest.cpp
@@ -0,0 +1,117 @@
+#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);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+
+        // test handling of obscene cubic values (currently broken)
+        if (false) {
+            SkPoint pts[4];
+            pts[0].set(1.61061274e+09f, 6291456);
+            pts[1].set(-7.18397061e+15f, -1.53091184e+13f);
+            pts[2].set(-1.30077315e+16f, -2.77196141e+13f);
+            pts[3].set(-1.30077315e+16f, -2.77196162e+13f);
+
+            SkPath path;
+            path.moveTo(pts[0]);
+            path.cubicTo(pts[1], pts[2], pts[3]);
+            canvas->drawPath(path, paint);
+        }
+
+        canvas->translate(200, 20);
+        canvas->rotate(30);
+
+        paint.setAntiAlias(true);
+        paint.setLCDRenderText(true);
+        SkSafeUnref(paint.setTypeface(SkTypeface::CreateFromName("Times Roman", SkTypeface::kNormal)));
+
+//        const char* text = "abcdefghijklmnopqrstuvwxyz";
+        const char* text = "HnHnHnHnHnHnHnHnH";
+        size_t textLen = strlen(text);
+
+        SkScalar x = SkIntToScalar(10);
+        SkScalar y = SkIntToScalar(20);
+
+        {
+            SkPaint p;
+            p.setColor(SK_ColorRED);
+            SkRect r;
+            r.set(0, 0, x, y*20);
+            canvas->drawRect(r, p);
+        }
+
+        int index = 0;
+        for (int ps = 9; ps <= 24; ps++) {
+            textLen = strlen(text);
+            paint.setTextSize(SkIntToScalar(ps));
+            canvas->drawText(text, textLen, x, y, paint);
+            y += paint.getFontMetrics(NULL);
+            index += 1;
+        }
+    }
+
+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..5c41886
--- /dev/null
+++ b/samplecode/SampleFuzz.cpp
@@ -0,0 +1,363 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkBlurMaskFilter.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkXfermode.h"
+#include "SkMatrix.h"
+#include "SkColor.h"
+#include "SkRandom.h"
+
+static void set2x3(SkMatrix* m, float a, float b, float c, float d, float e, float f) {
+    m->reset();
+    m->set(0, a);
+    m->set(1, b);
+    m->set(2, c);
+    m->set(3, d);
+    m->set(4, e);
+    m->set(5, f);
+}
+
+static SkRandom gRand;
+static bool return_large;
+static bool return_undef;
+static bool quick;
+static bool scale_large;
+static int scval = 1;
+static float transval = 0;
+
+static int R(float x) {
+  return (int)floor(SkScalarToFloat(gRand.nextUScalar1()) * x);
+}
+
+static float huge() {
+    double d = 1e100;
+    float f = (float)d;
+    return f;
+}
+
+static float make_number() {
+  float v;
+  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: 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) {
+        SkIRect r = canvas->getTotalClip().getBounds();
+        do_fuzz(canvas);
+        this->inval(NULL);
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new FuzzView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleGM.cpp b/samplecode/SampleGM.cpp
new file mode 100644
index 0000000..ec5b22a
--- /dev/null
+++ b/samplecode/SampleGM.cpp
@@ -0,0 +1,114 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+
+#include "gm.h"
+
+using namespace skiagm;
+
+// need to explicitly declare this, or we get some weird infinite loop llist
+template GMRegistry* GMRegistry::gHead;
+
+class Iter {
+public:
+    Iter() {
+        fReg = GMRegistry::Head();
+    }
+	
+    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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GMView : public SampleView {
+    Iter fIter;
+    GM*  fGM;
+public:
+	GMView() {
+        fGM = fIter.next();
+        this->postNextGM();
+        
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+    virtual ~GMView() {
+        delete fGM;
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "GM");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual bool onEvent(const SkEvent& evt) {
+        if (evt.isType("next-gm")) {
+            delete fGM;
+            if (!(fGM = fIter.next())) {
+                fIter.reset();
+                fGM = fIter.next();
+            }
+            this->inval(NULL);
+            this->postNextGM();
+            return true;
+        }
+        return this->INHERITED::onEvent(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        fGM->draw(canvas);
+    }
+    
+private:
+    void postNextGM() {
+        (new SkEvent("next-gm"))->post(this->getSinkID(), 1500);
+    }
+
+    typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new GMView; }
+static SkViewRegister reg(MyFactory);
+
+///////////////////////////////////////////////////////////////////////////////
+
+using namespace skiagm;
+
+GM::GM() {}
+GM::~GM() {}
+
+void GM::draw(SkCanvas* canvas) {
+	this->onDraw(canvas);
+}
+
+
diff --git a/samplecode/SampleGradients.cpp b/samplecode/SampleGradients.cpp
new file mode 100644
index 0000000..902b0bd
--- /dev/null
+++ b/samplecode/SampleGradients.cpp
@@ -0,0 +1,176 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+
+static SkShader* setgrad(const SkRect& r, SkColor c0, SkColor c1) {
+    SkColor colors[] = { c0, c1 };
+    SkPoint pts[] = { { r.fLeft, r.fTop }, { r.fRight, r.fTop } };
+    return SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+                                          SkShader::kClamp_TileMode, NULL);
+}
+
+static void test_alphagradients(SkCanvas* canvas) {
+    SkRect r;
+    r.set(SkIntToScalar(10), SkIntToScalar(10),
+          SkIntToScalar(410), SkIntToScalar(30));
+    SkPaint p, p2;
+    p2.setStyle(SkPaint::kStroke_Style);
+    
+    p.setShader(setgrad(r, 0xFF00FF00, 0x0000FF00))->unref();
+    canvas->drawRect(r, p);
+    canvas->drawRect(r, p2);
+    
+    r.offset(0, r.height() + SkIntToScalar(4));
+    p.setShader(setgrad(r, 0xFF00FF00, 0x00000000))->unref();
+    canvas->drawRect(r, p);
+    canvas->drawRect(r, p2);
+    
+    r.offset(0, r.height() + SkIntToScalar(4));
+    p.setShader(setgrad(r, 0xFF00FF00, 0x00FF0000))->unref();
+    canvas->drawRect(r, p);
+    canvas->drawRect(r, p2);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct GradData {
+    int             fCount;
+    const SkColor*  fColors;
+    const SkScalar* fPos;
+};
+
+static const SkColor gColors[] = {
+    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
+};
+static const SkScalar gPos0[] = { 0, SK_Scalar1 };
+static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
+static const SkScalar gPos2[] = {
+    0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
+};
+
+static const GradData gGradData[] = {
+    { 2, gColors, NULL },
+    { 2, gColors, gPos0 },
+    { 2, gColors, gPos1 },
+    { 5, gColors, NULL },
+    { 5, gColors, gPos2 }
+};
+
+static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
+                            SkShader::TileMode tm, SkUnitMapper* mapper) {
+    return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
+                                          data.fCount, tm, mapper);
+}
+                                          
+static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
+                            SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center;
+    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+               SkScalarAve(pts[0].fY, pts[1].fY));
+    return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
+                                          data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
+                           SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center;
+    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+               SkScalarAve(pts[0].fY, pts[1].fY));
+    return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
+                                         data.fPos, data.fCount, mapper);
+}
+
+static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
+                           SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+    return SkGradientShader::CreateTwoPointRadial(
+                            center1, (pts[1].fX - pts[0].fX) / 7,
+                            center0, (pts[1].fX - pts[0].fX) / 2,
+                            data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* Make2RadialConcentric(const SkPoint pts[2], const GradData& data,
+                                       SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center;
+    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+               SkScalarAve(pts[0].fY, pts[1].fY));
+    return SkGradientShader::CreateTwoPointRadial(
+                            center, (pts[1].fX - pts[0].fX) / 7,
+                            center, (pts[1].fX - pts[0].fX) / 2,
+                            data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
+                     SkShader::TileMode tm, SkUnitMapper* mapper);
+static const GradMaker gGradMakers[] = {
+    MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2RadialConcentric
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GradientsView : public SampleView {
+public:
+	GradientsView() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Gradients");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPoint pts[2] = {
+            { 0, 0 },
+            { SkIntToScalar(100), SkIntToScalar(100) }
+        };
+        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
+        SkPaint paint;
+        paint.setDither(true);
+
+        canvas->save();
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(10));
+
+        for (int tm = 0; tm < SkShader::kTileModeCount; ++tm) {
+            canvas->save();
+            for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
+                canvas->save();
+                for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
+                    SkShader* shader;
+                    shader = gGradMakers[j](pts, gGradData[i], (SkShader::TileMode)tm, NULL);
+                    paint.setShader(shader)->unref();
+                    canvas->drawRect(r, paint);
+                    canvas->translate(0, SkIntToScalar(120));
+                }
+                canvas->restore();
+                canvas->translate(SkIntToScalar(120), 0);
+            }
+            canvas->restore();
+            canvas->translate(SK_ARRAY_COUNT(gGradData)*SkIntToScalar(120), 0);
+        }
+        canvas->restore();
+        
+        canvas->translate(0, SkIntToScalar(370));
+     //   test_alphagradients(canvas);
+        this->inval(NULL);
+    }
+    
+private:
+    typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new GradientsView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleHairline.cpp b/samplecode/SampleHairline.cpp
new file mode 100644
index 0000000..8368f5b
--- /dev/null
+++ b/samplecode/SampleHairline.cpp
@@ -0,0 +1,272 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkCornerPathEffect.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+#include "SkColorPriv.h"
+#include "SkImageDecoder.h"
+
+static SkRandom gRand;
+
+static void test_chromium_9005() {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 800, 600);
+    bm.allocPixels();
+
+    SkCanvas canvas(bm);
+
+    SkPoint pt0 = { SkFloatToScalar(799.33374f), SkFloatToScalar(1.2360189f) };
+    SkPoint pt1 = { SkFloatToScalar(808.49969f), SkFloatToScalar(-7.4338055f) };
+    
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    canvas.drawLine(pt0.fX, pt0.fY, pt1.fX, pt1.fY, paint);
+}
+
+static void generate_pts(SkPoint pts[], int count, int w, int h) {
+    for (int i = 0; i < count; i++) {
+        pts[i].set(gRand.nextUScalar1() * 3 * w - SkIntToScalar(w),
+                   gRand.nextUScalar1() * 3 * h - SkIntToScalar(h));
+    }
+}
+
+static bool check_zeros(const SkPMColor pixels[], int count, int skip) {
+    for (int i = 0; i < count; i++) {
+        if (*pixels) {
+            return false;
+        }
+        pixels += skip;
+    }
+    return true;
+}
+
+static bool check_bitmap_margin(const SkBitmap& bm, int margin) {
+    size_t rb = bm.rowBytes();
+    for (int i = 0; i < margin; i++) {
+        if (!check_zeros(bm.getAddr32(0, i), bm.width(), 1)) {
+            return false;
+        }
+        int bottom = bm.height() - i - 1;
+        if (!check_zeros(bm.getAddr32(0, bottom), bm.width(), 1)) {
+            return false;
+        }
+        // left column
+        if (!check_zeros(bm.getAddr32(i, 0), bm.height(), rb >> 2)) {
+            return false;
+        }
+        int right = bm.width() - margin + i;
+        if (!check_zeros(bm.getAddr32(right, 0), bm.height(), rb >> 2)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+#define WIDTH   620
+#define HEIGHT  460
+#define MARGIN  10
+
+static void line_proc(SkCanvas* canvas, const SkPaint& paint,
+                      const SkBitmap& bm) {
+    const int N = 2;
+    SkPoint pts[N];
+    for (int i = 0; i < 400; i++) {
+        generate_pts(pts, N, WIDTH, HEIGHT);
+
+        canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
+        if (!check_bitmap_margin(bm, MARGIN)) {
+            SkDebugf("---- hairline failure (%g %g) (%g %g)\n",
+                     pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
+            break;
+        }
+    }
+}
+
+static void poly_proc(SkCanvas* canvas, const SkPaint& paint,
+                      const SkBitmap& bm) {
+    const int N = 8;
+    SkPoint pts[N];
+    for (int i = 0; i < 50; i++) {
+        generate_pts(pts, N, WIDTH, HEIGHT);
+        
+        SkPath path;
+        path.moveTo(pts[0]);
+        for (int j = 1; j < N; j++) {
+            path.lineTo(pts[j]);
+        }
+        canvas->drawPath(path, paint);
+    }
+}
+
+static SkPoint ave(const SkPoint& a, const SkPoint& b) {
+    SkPoint c = a + b;
+    c.fX = SkScalarHalf(c.fX);
+    c.fY = SkScalarHalf(c.fY);
+    return c;
+}
+
+static void quad_proc(SkCanvas* canvas, const SkPaint& paint,
+                      const SkBitmap& bm) {
+    const int N = 30;
+    SkPoint pts[N];
+    for (int i = 0; i < 10; i++) {
+        generate_pts(pts, N, WIDTH, HEIGHT);
+        
+        SkPath path;
+        path.moveTo(pts[0]);
+        for (int j = 1; j < N - 2; j++) {
+            path.quadTo(pts[j], ave(pts[j], pts[j+1]));
+        }
+        path.quadTo(pts[N - 2], pts[N - 1]);
+        
+        canvas->drawPath(path, paint);
+    }
+}
+
+static void add_cubic(SkPath* path, const SkPoint& mid, const SkPoint& end) {
+    SkPoint start;
+    path->getLastPt(&start);
+    path->cubicTo(ave(start, mid), ave(mid, end), end);
+}
+
+static void cube_proc(SkCanvas* canvas, const SkPaint& paint,
+                      const SkBitmap& bm) {
+    const int N = 30;
+    SkPoint pts[N];
+    for (int i = 0; i < 10; i++) {
+        generate_pts(pts, N, WIDTH, HEIGHT);
+        
+        SkPath path;
+        path.moveTo(pts[0]);
+        for (int j = 1; j < N - 2; j++) {
+            add_cubic(&path, pts[j], ave(pts[j], pts[j+1]));
+        }
+        add_cubic(&path, pts[N - 2], pts[N - 1]);
+        
+        canvas->drawPath(path, paint);
+    }
+}
+
+typedef void (*HairProc)(SkCanvas*, const SkPaint&, const SkBitmap&);
+
+static const struct {
+    const char* fName;
+    HairProc    fProc;
+} gProcs[] = {
+    { "line",   line_proc },
+    { "poly",   poly_proc },
+    { "quad",   quad_proc },
+    { "cube",   cube_proc },
+};
+
+static int cycle_hairproc_index(int index) {
+    return (index + 1) % SK_ARRAY_COUNT(gProcs);
+}
+
+class HairlineView : public SampleView {
+    SkMSec fNow;
+    int fProcIndex;
+    bool fDoAA;
+public:
+	HairlineView() {
+        fCounter = 0;
+        fProcIndex = 0;
+        fDoAA = true;
+        fNow = 0;
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SkString str;
+            str.printf("Hair-%s", gProcs[fProcIndex].fName);
+            SampleCode::TitleR(evt, str.c_str());
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void show_bitmaps(SkCanvas* canvas, const SkBitmap& b0, const SkBitmap& b1,
+                      const SkIRect& inset) {
+        canvas->drawBitmap(b0, 0, 0, NULL);
+        canvas->drawBitmap(b1, SkIntToScalar(b0.width()), 0, NULL);
+    }
+
+    int fCounter;
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        gRand.setSeed(fNow);
+        
+        if (false) {
+            test_chromium_9005();
+        }
+        
+        SkBitmap bm, bm2;
+        bm.setConfig(SkBitmap::kARGB_8888_Config,
+                     WIDTH + MARGIN*2,
+                     HEIGHT + MARGIN*2);
+        bm.allocPixels();
+        // this will erase our margin, which we want to always stay 0
+        bm.eraseColor(0);
+
+        bm2.setConfig(SkBitmap::kARGB_8888_Config, WIDTH, HEIGHT,
+                      bm.rowBytes());
+        bm2.setPixels(bm.getAddr32(MARGIN, MARGIN));
+        
+        SkCanvas c2(bm2);
+        SkPaint paint;
+        paint.setAntiAlias(fDoAA);
+        paint.setStyle(SkPaint::kStroke_Style);
+
+        bm2.eraseColor(0);
+        gProcs[fProcIndex].fProc(&c2, paint, bm);
+        canvas->drawBitmap(bm2, SkIntToScalar(10), SkIntToScalar(10), NULL);
+
+        SkMSec now = SampleCode::GetAnimTime();
+        if (fNow != now) {
+            fNow = now;
+            fCounter += 1;
+            fDoAA = !fDoAA;
+            if (fCounter > 50) {
+                fProcIndex = cycle_hairproc_index(fProcIndex);
+                // todo: signal that we want to rebuild our TITLE
+                fCounter = 0;
+            }
+            this->inval(NULL);
+        }
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        fDoAA = !fDoAA;
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new HairlineView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleImage.cpp b/samplecode/SampleImage.cpp
new file mode 100644
index 0000000..2944299
--- /dev/null
+++ b/samplecode/SampleImage.cpp
@@ -0,0 +1,156 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkImageRef_GlobalPool.h"
+#include "SkStream.h"
+
+static const char* gNames[] = {
+    "1.bmp", "1.gif", "1.jpg", "1.png",
+    "2.bmp", "2.gif", "2.jpg", "2.png"
+};
+
+static bool SetImageRef(SkBitmap* bitmap, SkStream* stream,
+                        SkBitmap::Config pref, const char name[] = NULL)
+{
+    if (SkImageDecoder::DecodeStream(stream, bitmap, pref,
+                                 SkImageDecoder::kDecodeBounds_Mode, NULL)) {
+        SkASSERT(bitmap->config() != SkBitmap::kNo_Config);
+    
+        SkImageRef* ref = new SkImageRef_GlobalPool(stream, bitmap->config());
+        ref->setURI(name);
+        bitmap->setPixelRef(ref)->unref();
+        return true;
+    } else {
+        return false;
+    }
+}
+
+class ImageView : public SkView {
+public:
+    SkBitmap*   fBitmaps;
+    SkShader*   fShader;
+
+	ImageView() {
+        SkImageRef_GlobalPool::SetRAMBudget(32 * 1024);
+        
+        int i, N = SK_ARRAY_COUNT(gNames);
+        fBitmaps = new SkBitmap[N];
+        
+        for (i = 0; i < N; i++) {
+            SkString str("/skimages/");
+            str.append(gNames[i]);
+            SkFILEStream* stream = new SkFILEStream(str.c_str());
+            
+            SetImageRef(&fBitmaps[i], stream, SkBitmap::kNo_Config, gNames[i]);
+            if (i & 1)
+                fBitmaps[i].buildMipMap();
+            stream->unref();
+        }
+        
+        fShader = SkShader::CreateBitmapShader(fBitmaps[5],
+                                               SkShader::kRepeat_TileMode,
+                                               SkShader::kRepeat_TileMode);
+        
+        if (true) {
+            SkMatrix m;
+            
+            m.setRotate(SkIntToScalar(30));
+            fShader->setLocalMatrix(m);
+        }
+        
+#if 0
+        SkImageRef::DumpPool();
+        for (i = 0; i < N; i++) {
+            SkBitmap& bm = fBitmaps[i];
+
+            SkDebugf("<%s> addr=%p", gNames[i], bm.getPixels());
+            bool success = bm.lockPixels();
+            SkDebugf(" addr=%d", bm.getPixels());
+            if (success)
+                bm.unlockPixels();
+            SkDebugf(" addr=%p", bm.getPixels());
+            success = bm.lockPixels();
+            SkDebugf(" addr=%d", bm.getPixels());
+            if (success)
+                bm.unlockPixels();            
+            SkDebugf("\n");
+        }
+        SkImageRef::DumpPool();
+#endif
+    }
+    
+    virtual ~ImageView() {
+        delete[] fBitmaps;
+        delete fShader;
+
+        SkImageRef_GlobalPool::DumpPool();
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Image");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+//        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+        
+        SkScalar x = 0, y = 0;
+        
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++) {
+            canvas->drawBitmap(fBitmaps[i], x, y);
+            x += SkIntToScalar(fBitmaps[i].width() + 10);
+        }
+        
+        canvas->translate(0, SkIntToScalar(120));
+
+        SkPaint paint;
+        paint.setShader(fShader);
+        paint.setFilterBitmap(true);
+        SkRect r = { 0, 0, SkIntToScalar(300), SkIntToScalar(100) };
+        
+        canvas->drawRect(r, paint);
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+    virtual bool onClick(Click* click) {
+        return this->INHERITED::onClick(click);
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ImageView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleImageDir.cpp b/samplecode/SampleImageDir.cpp
new file mode 100644
index 0000000..8ef59ad
--- /dev/null
+++ b/samplecode/SampleImageDir.cpp
@@ -0,0 +1,310 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkComposeShader.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkImageRef_GlobalPool.h"
+#include "SkOSFile.h"
+#include "SkStream.h"
+
+#include "SkBlurDrawLooper.h"
+#include "SkColorMatrixFilter.h"
+
+static void drawmarshmallow(SkCanvas* canvas) {
+    SkBitmap bitmap;
+    SkPaint paint;
+    SkRect r;
+    SkMatrix m;
+
+    SkImageDecoder::DecodeFile("/Users/reed/Downloads/3elfs.jpg", &bitmap);
+    SkShader* s = SkShader::CreateBitmapShader(bitmap,
+                                               SkShader::kRepeat_TileMode,
+                                               SkShader::kRepeat_TileMode);
+    paint.setShader(s)->unref();
+    m.setTranslate(SkIntToScalar(250), SkIntToScalar(134));
+    s->setLocalMatrix(m);
+
+    r.set(SkIntToScalar(250),
+          SkIntToScalar(134),
+          SkIntToScalar(250 + 449),
+          SkIntToScalar(134 + 701));
+    paint.setFlags(2);
+
+    canvas->drawRect(r, paint);
+}
+
+static void DrawRoundRect(SkCanvas& canvas) {
+   bool ret = false;
+   SkPaint  paint;
+   SkBitmap bitmap;
+   SkMatrix matrix;
+   matrix.reset();
+
+   bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1370, 812);
+   bitmap.allocPixels();
+#if 0
+    SkCanvas canvas;
+    canvas.setBitmapDevice(bitmap);
+#endif
+
+   // set up clipper
+   SkRect skclip;
+   skclip.set(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);
+}
+
+static bool SetImageRef(SkBitmap* bitmap, SkStream* stream,
+                        SkBitmap::Config pref, const char name[] = NULL) {
+#if 0
+    // test buffer streams
+    SkStream* str = new SkBufferStream(stream, 717);
+    stream->unref();
+    stream = str;
+#endif
+
+    SkImageRef* ref = new SkImageRef_GlobalPool(stream, pref, 1);
+    ref->setURI(name);
+    if (!ref->getInfo(bitmap)) {
+        delete ref;
+        return false;
+    }
+    bitmap->setPixelRef(ref)->unref();
+    return true;
+}
+
+//#define SPECIFIC_IMAGE  "/skimages/72.jpg"
+#define SPECIFIC_IMAGE  "/Users/reed/Downloads/3elfs.jpg"
+
+#define IMAGE_DIR       "/skimages/"
+#define IMAGE_SUFFIX    ".gif"
+
+class ImageDirView : public SkView {
+public:
+    SkBitmap*   fBitmaps;
+    SkString*   fStrings;
+    int         fBitmapCount;
+    int         fCurrIndex;
+    SkScalar    fSaturation;
+    SkScalar    fAngle;
+
+	ImageDirView() {
+        SkImageRef_GlobalPool::SetRAMBudget(320 * 1024);
+        
+#ifdef SPECIFIC_IMAGE
+        fBitmaps = new SkBitmap[3];
+        fStrings = new SkString[3];
+        fBitmapCount = 3;
+        const SkBitmap::Config configs[] = {
+            SkBitmap::kARGB_8888_Config,
+            SkBitmap::kRGB_565_Config,
+            SkBitmap::kARGB_4444_Config
+        };
+        for (int i = 0; i < fBitmapCount; i++) {
+#if 1
+            SkStream* stream = new SkFILEStream(SPECIFIC_IMAGE);
+            SetImageRef(&fBitmaps[i], stream, configs[i], SPECIFIC_IMAGE);
+            stream->unref();
+#else
+            SkImageDecoder::DecodeFile(SPECIFIC_IMAGE, &fBitmaps[i]);
+#endif
+        }
+#else
+        int i, N = 0;
+        SkOSFile::Iter  iter(IMAGE_DIR, IMAGE_SUFFIX);
+        SkString    name;
+        while (iter.next(&name)) {
+            N += 1;
+        }
+        fBitmaps = new SkBitmap[N];
+        fStrings = new SkString[N];
+        iter.reset(IMAGE_DIR, IMAGE_SUFFIX);
+        for (i = 0; i < N; i++) {
+            iter.next(&name);
+            SkString path(IMAGE_DIR);
+            path.append(name);
+            SkStream* stream = new SkFILEStream(path.c_str());
+            
+            SetImageRef(&fBitmaps[i], stream, SkBitmap::kNo_Config,
+                        name.c_str());
+            stream->unref();
+            fStrings[i] = name;
+        }
+        fBitmapCount = N;
+#endif
+        fCurrIndex = 0;
+        fDX = fDY = 0;
+        
+        fSaturation = SK_Scalar1;
+        fAngle = 0;
+        
+        fScale = SK_Scalar1;
+    }
+    
+    virtual ~ImageDirView() {
+        delete[] fBitmaps;
+        delete[] fStrings;
+
+        SkImageRef_GlobalPool::DumpPool();
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SkString str("ImageDir: ");
+#ifdef SPECIFIC_IMAGE
+            str.append(SPECIFIC_IMAGE);
+#else
+            str.append(IMAGE_DIR);
+#endif
+            SampleCode::TitleR(evt, str.c_str());
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+//        canvas->drawColor(0xFFDDDDDD);
+        canvas->drawColor(SK_ColorGRAY);
+        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    SkScalar fScale;
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        if (true) {
+            canvas->scale(SkIntToScalar(2), SkIntToScalar(2));
+            drawmarshmallow(canvas);
+            return;
+        }
+        
+        if (false) {
+            SkPaint p;
+            p.setStyle(SkPaint::kStroke_Style);
+            p.setStrokeWidth(SkIntToScalar(4));
+            canvas->drawCircle(SkIntToScalar(100), SkIntToScalar(100), SkIntToScalar(50), p);
+            p.setAntiAlias(true);
+            canvas->drawCircle(SkIntToScalar(300), SkIntToScalar(100), SkIntToScalar(50), p);
+        }
+        if (false) {
+            SkScalar cx = this->width()/2;
+            SkScalar cy = this->height()/2;
+            canvas->translate(cx, cy);
+            canvas->scale(fScale, fScale);
+            canvas->translate(-cx, -cy);
+            DrawRoundRect(*canvas);
+            return;
+        }
+    
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+        
+        SkScalar x = SkIntToScalar(32), y = SkIntToScalar(32);
+        SkPaint paint;
+
+#if 0
+        for (int i = 0; i < fBitmapCount; i++) {
+            SkPaint p;
+            
+#if 1
+            const SkScalar cm[] = {
+                SkIntToScalar(2), 0, 0, 0, SkIntToScalar(-255),
+                0, SkIntToScalar(2), 0, 0, SkIntToScalar(-255),
+                0, 0, SkIntToScalar(2), 0, SkIntToScalar(-255),
+                0, 0, 0, SkIntToScalar(1), 0
+            };
+            SkColorFilter* cf = new SkColorMatrixFilter(cm);
+            p.setColorFilter(cf)->unref();
+#endif
+            
+            canvas->drawBitmap(fBitmaps[i], x, y, &p);
+            x += SkIntToScalar(fBitmaps[i].width() + 10);
+        }
+        return;
+#endif
+
+        canvas->drawBitmap(fBitmaps[fCurrIndex], x, y, &paint);
+#ifndef SPECIFIC_IMAGE
+        if (true) {
+            fCurrIndex += 1;
+            if (fCurrIndex >= fBitmapCount) {
+                fCurrIndex = 0;
+            }
+            this->inval(NULL);
+        }
+#endif
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        if (true) {
+            fCurrIndex += 1;
+            if (fCurrIndex >= fBitmapCount)
+                fCurrIndex = 0;
+            this->inval(NULL);
+        }
+        return new Click(this);
+    }
+    
+    virtual bool onClick(Click* click)  {
+        SkScalar center = this->width()/2;
+        fSaturation = SkScalarDiv(click->fCurr.fX - center, center/2);
+        center = this->height()/2;
+        fAngle = SkScalarDiv(click->fCurr.fY - center, center) * 180;
+
+        fDX += click->fCurr.fX - click->fPrev.fX;
+        fDY += click->fCurr.fY - click->fPrev.fY;
+        
+        fScale = SkScalarDiv(click->fCurr.fX, this->width());
+
+        this->inval(NULL);
+        return true;
+        return this->INHERITED::onClick(click);
+    }
+    
+private:
+    SkScalar fDX, fDY;
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ImageDirView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleLCD.cpp b/samplecode/SampleLCD.cpp
new file mode 100644
index 0000000..098958f
--- /dev/null
+++ b/samplecode/SampleLCD.cpp
@@ -0,0 +1,61 @@
+#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..9bd00ae
--- /dev/null
+++ b/samplecode/SampleLayerMask.cpp
@@ -0,0 +1,68 @@
+#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..6fc9c83
--- /dev/null
+++ b/samplecode/SampleLayers.cpp
@@ -0,0 +1,271 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCamera.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkDevice.h"
+#include "SkGradientShader.h"
+#include "SkImageDecoder.h"
+#include "SkInterpolator.h"
+#include "SkMaskFilter.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkUtils.h"
+#include "SkKey.h"
+#include "SkXfermode.h"
+#include "SkDrawFilter.h"
+
+static void make_paint(SkPaint* paint) {
+    SkColor colors[] = { 0, SK_ColorWHITE };
+    SkPoint pts[] = { { 0, 0 }, { 0, SK_Scalar1*20 } };
+    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
+    
+    paint->setShader(s)->unref();
+    paint->setXfermodeMode(SkXfermode::kDstIn_Mode);
+}
+
+static void dump_layers(const char label[], SkCanvas* canvas) {
+    SkDebugf("Dump Layers(%s)\n", label);
+
+    SkCanvas::LayerIter iter(canvas, true);
+    int index = 0;
+    while (!iter.done()) {
+        const SkBitmap& bm = iter.device()->accessBitmap(false);
+        const SkIRect& clip = iter.clip().getBounds();
+        SkDebugf("Layer[%d] bitmap [%d %d] X=%d Y=%d clip=[%d %d %d %d] alpha=%d\n", index++,
+                 bm.width(), bm.height(), iter.x(), iter.y(),
+                 clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
+                 iter.paint().getAlpha());
+        iter.next();
+    }
+}
+
+// test drawing with strips of fading gradient above and below
+static void test_fade(SkCanvas* canvas) {
+    SkAutoCanvasRestore ar(canvas, true);
+
+    SkRect r;
+    
+    SkPaint p;
+    p.setAlpha(0x88);
+
+    SkAutoCanvasRestore(canvas, false);
+
+    // create the layers
+
+    r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
+    canvas->clipRect(r);
+    
+    r.fBottom = SkIntToScalar(20);
+    canvas->saveLayer(&r, NULL, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
+
+    r.fTop = SkIntToScalar(80);
+    r.fBottom = SkIntToScalar(100);
+    canvas->saveLayer(&r, NULL, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
+    
+    // now draw the "content" 
+
+    if (true) {
+        r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
+
+        canvas->saveLayerAlpha(&r, 0x80);
+
+        SkPaint p;
+        p.setColor(SK_ColorRED);
+        p.setAntiAlias(true);
+        canvas->drawOval(r, p);
+        
+        dump_layers("inside layer alpha", canvas);
+        
+        canvas->restore();
+    } else {
+        r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
+        
+        SkPaint p;
+        p.setColor(SK_ColorRED);
+        p.setAntiAlias(true);
+        canvas->drawOval(r, p);
+    }
+    
+//    return;
+
+    dump_layers("outside layer alpha", canvas);
+
+    // now apply an effect
+
+    SkPaint paint;
+    make_paint(&paint);
+    r.set(0, 0, SkIntToScalar(100), SkIntToScalar(20));
+//    SkDebugf("--------- draw top grad\n");
+    canvas->drawRect(r, paint);
+
+    SkMatrix m;
+    SkShader* s = paint.getShader();
+    m.setScale(SK_Scalar1, -SK_Scalar1);
+    m.postTranslate(0, SkIntToScalar(100));
+    s->setLocalMatrix(m);
+    
+    r.fTop = SkIntToScalar(80);
+    r.fBottom = SkIntToScalar(100);
+//    SkDebugf("--------- draw bot grad\n");
+    canvas->drawRect(r, paint);
+}
+
+class RedFilter : public SkDrawFilter {
+public:
+    virtual bool filter(SkCanvas*, SkPaint* p, SkDrawFilter::Type) {
+        fColor = p->getColor();
+        if (fColor == SK_ColorRED) {
+            p->setColor(SK_ColorGREEN);
+        }
+        return true;
+    }
+    virtual void restore(SkCanvas*, SkPaint* p, SkDrawFilter::Type) {
+        p->setColor(fColor);
+    }
+    
+private:
+    SkColor fColor;
+};
+
+class LayersView : public SkView {
+public:
+	LayersView() {}
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Layers");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorGRAY);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        if (true) {
+            SkRect r;
+			r.set(SkIntToScalar(0), SkIntToScalar(0),
+				  SkIntToScalar(220), SkIntToScalar(120));
+            SkPaint p;
+            canvas->saveLayer(&r, &p);
+            canvas->drawColor(0xFFFF0000);
+            p.setAlpha(0);  // or 0
+            p.setXfermodeMode(SkXfermode::kSrc_Mode);
+            canvas->drawOval(r, p);
+            canvas->restore();
+            return;
+        }
+        
+        if (false) {
+            SkRect r;
+			r.set(SkIntToScalar(0), SkIntToScalar(0),
+				  SkIntToScalar(220), SkIntToScalar(120));
+            SkPaint p;
+            p.setAlpha(0x88);
+            p.setAntiAlias(true);
+            
+            if (true) {
+                canvas->saveLayer(&r, &p);
+                p.setColor(0xFFFF0000);
+                canvas->drawOval(r, p);
+                canvas->restore();
+            }
+
+            p.setColor(0xFF0000FF);
+            r.offset(SkIntToScalar(20), SkIntToScalar(50));
+            canvas->drawOval(r, p);
+        }
+
+        if (false) {
+            SkPaint p;
+            p.setAlpha(0x88);
+            p.setAntiAlias(true);
+
+            canvas->translate(SkIntToScalar(300), 0);
+
+            SkRect r;
+			r.set(SkIntToScalar(0), SkIntToScalar(0),
+				  SkIntToScalar(220), SkIntToScalar(60));
+
+            canvas->saveLayer(&r, &p, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
+//            canvas->clipRect(r, SkRegion::kDifference_Op);
+//            canvas->clipRect(r, SkRegion::kIntersect_Op);
+
+			r.set(SkIntToScalar(0), SkIntToScalar(0),
+				  SkIntToScalar(220), SkIntToScalar(120));
+            p.setColor(SK_ColorBLUE);
+            canvas->drawOval(r, p);
+            canvas->restore();
+            return;
+        }
+        
+        //canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+        test_fade(canvas);
+        return;
+
+    //    canvas->setDrawFilter(new RedFilter)->unref();
+        
+        SkRect  r;
+        SkPaint p;
+        
+        canvas->translate(SkIntToScalar(220), SkIntToScalar(20));
+        
+        p.setAntiAlias(true);
+        r.set(SkIntToScalar(20), SkIntToScalar(20),
+              SkIntToScalar(220), SkIntToScalar(120));
+        
+        p.setColor(SK_ColorBLUE);
+     //   p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(8), SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+        canvas->drawRect(r, p);
+        p.setMaskFilter(NULL);
+
+        SkRect bounds = r;
+        bounds.fBottom = bounds.centerY();
+        canvas->saveLayer(&bounds, NULL, SkCanvas::kARGB_NoClipLayer_SaveFlag);
+
+        p.setColor(SK_ColorRED);
+        canvas->drawOval(r, p);
+        
+        p.setAlpha(0x80);
+        p.setXfermodeMode(SkXfermode::kDstIn_Mode);
+        canvas->drawRect(bounds, p);
+
+        canvas->restore();
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        this->inval(NULL);
+        
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+    virtual bool onClick(Click* click) {
+        return this->INHERITED::onClick(click);
+    }
+
+	virtual bool handleKey(SkKey key) {
+        this->inval(NULL);
+        return true;
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LayersView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleLineClipper.cpp b/samplecode/SampleLineClipper.cpp
new file mode 100644
index 0000000..ac6b013
--- /dev/null
+++ b/samplecode/SampleLineClipper.cpp
@@ -0,0 +1,255 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkRandom.h"
+
+#include "SkLineClipper.h"
+#include "SkEdgeClipper.h"
+
+#define AUTO_ANIMATE    true
+
+static int test0(SkPoint pts[], SkRect* clip) {
+    pts[0].set(200000, 140);
+    pts[1].set(-740000, 483);
+    pts[2].set(1.00000102e-06f, 9.10000017e-05f);
+    clip->set(0, 0, 640, 480);
+    return 2;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawQuad(SkCanvas* canvas, const SkPoint pts[3], const SkPaint& p) {
+    SkPath path;
+    path.moveTo(pts[0]);
+    path.quadTo(pts[1], pts[2]);
+    canvas->drawPath(path, p);
+}
+
+static void drawCubic(SkCanvas* canvas, const SkPoint pts[4], const SkPaint& p) {
+    SkPath path;
+    path.moveTo(pts[0]);
+    path.cubicTo(pts[1], pts[2], pts[3]);
+    canvas->drawPath(path, p);
+}
+
+typedef void (*clipper_proc)(const SkPoint src[], const SkRect& clip,
+                            SkCanvas*, const SkPaint&, const SkPaint&);
+
+static void check_clipper(int count, const SkPoint pts[], const SkRect& clip) {
+    for (int i = 0; i < count; i++) {
+        SkASSERT(pts[i].fX >= clip.fLeft);
+        SkASSERT(pts[i].fX <= clip.fRight);
+        SkASSERT(pts[i].fY >= clip.fTop);
+        SkASSERT(pts[i].fY <= clip.fBottom);
+    }
+
+    if (count > 1) {
+        sk_assert_monotonic_y(pts, count);
+    }
+}
+
+static void line_intersector(const SkPoint src[], const SkRect& clip,
+                         SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
+    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, src, p1);
+    
+    SkPoint dst[2];
+    if (SkLineClipper::IntersectLine(src, clip, dst)) {
+        check_clipper(2, dst, clip);
+        canvas->drawPoints(SkCanvas::kLines_PointMode, 2, dst, p0);
+    }
+}
+
+static void line_clipper(const SkPoint src[], const SkRect& clip,
+                         SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
+    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, src, p1);
+    
+    SkPoint dst[SkLineClipper::kMaxPoints];
+    int count = SkLineClipper::ClipLine(src, clip, dst);
+    for (int i = 0; i < count; i++) {
+        check_clipper(2, &dst[i], clip);
+        canvas->drawPoints(SkCanvas::kLines_PointMode, 2, &dst[i], p0);
+    }
+}
+
+static void quad_clipper(const SkPoint src[], const SkRect& clip,
+                         SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
+    drawQuad(canvas, src, p1);
+    
+    SkEdgeClipper clipper;
+    if (clipper.clipQuad(src, clip)) {
+        SkPoint pts[4];
+        SkPath::Verb verb;
+        while ((verb = clipper.next(pts)) != SkPath::kDone_Verb) {
+            switch (verb) {
+                case SkPath::kLine_Verb:
+                    check_clipper(2, pts, clip);
+                    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p0);
+                    break;
+                case SkPath::kQuad_Verb:
+                    check_clipper(3, pts, clip);
+                    drawQuad(canvas, pts, p0);
+                    break;
+                default:
+                    SkASSERT(!"unexpected verb");
+            }
+        }
+    }
+}
+
+static void cubic_clipper(const SkPoint src[], const SkRect& clip,
+                       SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
+    drawCubic(canvas, src, p1);
+    
+    SkEdgeClipper clipper;
+    if (clipper.clipCubic(src, clip)) {
+        SkPoint pts[4];
+        SkPath::Verb verb;
+        while ((verb = clipper.next(pts)) != SkPath::kDone_Verb) {
+            switch (verb) {
+                case SkPath::kLine_Verb:
+                    check_clipper(2, pts, clip);
+                    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p0);
+                    break;
+                case SkPath::kCubic_Verb:
+                 //   check_clipper(4, pts, clip);
+                    drawCubic(canvas, pts, p0);
+                    break;
+                default:
+                    SkASSERT(!"unexpected verb");
+            }
+        }
+    }
+}
+
+static const clipper_proc gProcs[] = {
+    line_intersector,
+    line_clipper,
+    quad_clipper,
+    cubic_clipper
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum {
+    W = 640/3,
+    H = 480/3
+};
+
+class LineClipperView : public SkView {
+    SkMSec      fNow;
+    int         fCounter;
+    int         fProcIndex;
+    SkRect      fClip;
+    SkRandom    fRand;
+    SkPoint     fPts[4];
+
+    void randPts() {
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
+            fPts[i].set(fRand.nextUScalar1() * 640,
+                        fRand.nextUScalar1() * 480);
+        }
+        fCounter += 1;
+    }
+
+public:
+	LineClipperView() {
+        fProcIndex = 0;
+        fCounter = 0;
+        fNow = 0;
+
+        int x = (640 - W)/2;
+        int y = (480 - H)/2;
+        fClip.set(SkIntToScalar(x), SkIntToScalar(y),
+                  SkIntToScalar(x + W), SkIntToScalar(y + H));
+        this->randPts();
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "LineClipper");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    static void drawVLine(SkCanvas* canvas, SkScalar x, const SkPaint& paint) {
+        canvas->drawLine(x, -999, x, 999, paint);
+    }
+    
+    static void drawHLine(SkCanvas* canvas, SkScalar y, const SkPaint& paint) {
+        canvas->drawLine(-999, y, 999, y, paint);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+
+        SkMSec now = SampleCode::GetAnimTime();
+        if (fNow != now) {
+            fNow = now;
+            this->randPts();
+            this->inval(NULL);
+        }
+
+     //   fProcIndex = test0(fPts, &fClip);
+
+        SkPaint paint, paint1;
+        
+        drawVLine(canvas, fClip.fLeft + SK_ScalarHalf, paint);
+        drawVLine(canvas, fClip.fRight - SK_ScalarHalf, paint);
+        drawHLine(canvas, fClip.fTop + SK_ScalarHalf, paint);
+        drawHLine(canvas, fClip.fBottom - SK_ScalarHalf, paint);
+        
+        paint.setColor(SK_ColorLTGRAY);
+        canvas->drawRect(fClip, paint);
+        
+        paint.setAntiAlias(true);
+        paint.setColor(SK_ColorBLUE);
+        paint.setStyle(SkPaint::kStroke_Style);
+      //  paint.setStrokeWidth(SkIntToScalar(3));
+        paint.setStrokeCap(SkPaint::kRound_Cap);
+        
+        paint1.setAntiAlias(true);
+        paint1.setColor(SK_ColorRED);
+        paint1.setStyle(SkPaint::kStroke_Style);
+        gProcs[fProcIndex](fPts, fClip, canvas, paint, paint1);
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+     //   fProcIndex = (fProcIndex + 1) % SK_ARRAY_COUNT(gProcs);
+        if (x < 50 && y < 50) {
+            this->randPts();
+        }
+        this->inval(NULL);
+        return NULL;
+    }
+        
+    virtual bool onClick(Click* click) {
+        return false;
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LineClipperView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleLines.cpp b/samplecode/SampleLines.cpp
new file mode 100644
index 0000000..03dd30f
--- /dev/null
+++ b/samplecode/SampleLines.cpp
@@ -0,0 +1,109 @@
+#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..8078e03
--- /dev/null
+++ b/samplecode/SampleMeasure.cpp
@@ -0,0 +1,115 @@
+#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..3d95156
--- /dev/null
+++ b/samplecode/SampleMipMap.cpp
@@ -0,0 +1,143 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+static SkBitmap createBitmap(int n) {
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+    
+    SkCanvas canvas(bitmap);
+    SkRect r;
+    r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n));
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    
+    paint.setColor(SK_ColorRED);
+    canvas.drawOval(r, paint);
+    paint.setColor(SK_ColorBLUE);
+    paint.setStrokeWidth(SkIntToScalar(n)/15);
+    paint.setStyle(SkPaint::kStroke_Style);
+    canvas.drawLine(0, 0, r.fRight, r.fBottom, paint);
+    canvas.drawLine(0, r.fBottom, r.fRight, 0, paint);
+    
+    return bitmap;
+}
+
+class MipMapView : public SampleView {
+    SkBitmap fBitmap;
+    enum {
+        N = 64
+    };
+public:
+    MipMapView() {
+        fBitmap = createBitmap(N);
+        
+        fWidth = N;
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "MipMaps");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawN(SkCanvas* canvas, const SkBitmap& bitmap) {
+        SkAutoCanvasRestore acr(canvas, true);
+        for (int i = N; i > 1; i >>= 1) {
+            canvas->drawBitmap(bitmap, 0, 0, NULL);
+            canvas->translate(SkIntToScalar(N + 8), 0);
+            canvas->scale(SK_ScalarHalf, SK_ScalarHalf);
+        }
+    }
+    
+    void drawN2(SkCanvas* canvas, const SkBitmap& bitmap) {
+        SkBitmap bg;
+        bg.setConfig(SkBitmap::kARGB_8888_Config, N, N);
+        bg.allocPixels();
+        
+        SkAutoCanvasRestore acr(canvas, true);
+        for (int i = 0; i < 6; i++) {
+            bg.eraseColor(0);
+            SkCanvas c(bg);
+            c.scale(SK_Scalar1 / (1 << i), SK_Scalar1 / (1 << i));
+            c.drawBitmap(bitmap, 0, 0, NULL);
+
+            canvas->save();
+            canvas->scale(SkIntToScalar(1 << i), SkIntToScalar(1 << i));
+            canvas->drawBitmap(bg, 0, 0, NULL);
+            canvas->restore();
+            canvas->translate(SkIntToScalar(N + 8), 0);
+        }
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+        
+        canvas->scale(1.00000001f, 0.9999999f);
+
+        drawN2(canvas, fBitmap);
+
+        canvas->translate(0, SkIntToScalar(N + 8));
+        SkBitmap bitmap(fBitmap);
+        bitmap.buildMipMap();
+        drawN2(canvas, bitmap);
+
+        SkScalar time = SampleCode::GetAnimScalar(SkIntToScalar(1)/4,
+                                                  SkIntToScalar(2));
+        if (time >= SK_Scalar1) {
+            time = SkIntToScalar(2) - time;
+        }
+        fWidth = 8 + SkScalarRound(N * time);
+
+        SkRect dst;
+        dst.set(0, 0, SkIntToScalar(fWidth), SkIntToScalar(fWidth));
+
+        SkPaint paint;
+        paint.setFilterBitmap(true);
+        paint.setAntiAlias(true);
+
+        canvas->translate(0, SkIntToScalar(N + 8));
+        canvas->drawBitmapRect(fBitmap, NULL, dst, NULL);
+        canvas->translate(SkIntToScalar(N + 8), 0);
+        canvas->drawBitmapRect(fBitmap, NULL, dst, &paint);
+        canvas->translate(-SkIntToScalar(N + 8), SkIntToScalar(N + 8));
+        canvas->drawBitmapRect(bitmap, NULL, dst, NULL);
+        canvas->translate(SkIntToScalar(N + 8), 0);
+        canvas->drawBitmapRect(bitmap, NULL, dst, &paint);
+        
+        SkShader* s = SkShader::CreateBitmapShader(bitmap,
+                                                   SkShader::kRepeat_TileMode,
+                                                   SkShader::kRepeat_TileMode);
+        paint.setShader(s)->unref();
+        SkMatrix m;
+        m.setScale(SkIntToScalar(fWidth) / N,
+                   SkIntToScalar(fWidth) / N);
+        s->setLocalMatrix(m);
+        SkRect r;
+        r.set(0, 0, SkIntToScalar(4*N), SkIntToScalar(5*N/2));
+        r.offset(SkIntToScalar(N + 12), -SkIntToScalar(N + 4));
+        canvas->drawRect(r, paint);
+        
+        this->inval(NULL);
+    }
+    
+private:
+    int fWidth;
+
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new MipMapView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleMovie.cpp b/samplecode/SampleMovie.cpp
new file mode 100644
index 0000000..af34198
--- /dev/null
+++ b/samplecode/SampleMovie.cpp
@@ -0,0 +1,61 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkMovie.h"
+#include "SkTime.h"
+#include <new>
+
+class AnimGifView : public SkView {
+    SkMovie*    fMovie;
+public:
+	AnimGifView() {
+        fMovie = SkMovie::DecodeFile("/skimages/dollarblk.gif");
+    }
+
+    virtual ~AnimGifView() {
+        SkSafeUnref(fMovie);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Animated Gif");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+
+        if (fMovie) {
+            if (fMovie->duration()) {
+                fMovie->setTime(SkTime::GetMSecs() % fMovie->duration());
+            } else {
+                fMovie->setTime(0);
+            }
+            canvas->drawBitmap(fMovie->bitmap(), SkIntToScalar(20),
+                               SkIntToScalar(20));
+            this->inval(NULL);
+        }
+    }
+
+private:
+    SkRect      fClip;
+    SkIPoint*   fPoints;
+    SkPath      fPath;
+    int         fPtCount;
+
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new AnimGifView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleNinePatch.cpp b/samplecode/SampleNinePatch.cpp
new file mode 100644
index 0000000..e158287
--- /dev/null
+++ b/samplecode/SampleNinePatch.cpp
@@ -0,0 +1,114 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkNinePatch.h"
+#include "SkPaint.h"
+#include "SkUnPreMultiply.h"
+
+class NinePatchView : public SampleView {
+public:
+    SkBitmap fBM;
+
+	NinePatchView() {
+        SkImageDecoder::DecodeFile("/skimages/btn_default_normal_disable.9.png", &fBM);
+        
+        // trim off the edge guide-lines
+        SkBitmap tmp;
+        SkIRect r;
+        r.set(1, 1, fBM.width() - 1, fBM.height() - 1);
+        fBM.extractSubset(&tmp, r);
+        fBM.swap(tmp);
+        
+        fX = SkIntToScalar(fBM.width());
+        fY = 0;
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "NinePatch");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual void onDrawBackground(SkCanvas* canvas) {
+        SkPaint p;
+        p.setDither(true);
+        p.setColor(0xFF909090);
+        canvas->drawPaint(p);
+    }
+
+    static void test_rects(SkCanvas* canvas, const SkBitmap& bm, const SkPaint* paint) {
+        static const SkIRect src[] = {
+            { 0, 0, 18, 34 },
+            { 18, 0, 19, 34 },
+            { 19, 0, 36, 34 },
+            { 0, 34, 18, 35 },
+            { 18, 34, 19, 35 },
+            { 19, 34, 36, 35 },
+            { 0, 35, 18, 72 },
+            { 18, 35, 19, 72 },
+            { 19, 35, 36, 72 },
+        };
+        static const SkRect dst[] = {
+            { 0, 0, 18, 34 },
+            { 18, 0, 283, 34 },
+            { 283, 0, 300, 34 },
+            { 0, 34, 18, 163 },
+            { 18, 34, 283, 163 },
+            { 283, 34, 300, 163 },
+            { 0, 163, 18, 200 },
+            { 18, 163, 283, 200 },
+            { 283, 163, 300, 200 },
+        };
+        for (size_t i = 0; i < SK_ARRAY_COUNT(src); i++) {
+            canvas->drawBitmapRect(bm, &src[i], dst[i], paint);
+        }
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        canvas->drawBitmap(fBM, 0, 0);
+        
+        SkIRect margins;
+        SkRect  dst;
+        int d = 25;
+        
+        margins.set(d, d, d, d);
+        margins.fLeft   = fBM.width()/2 - 1;
+        margins.fTop    = fBM.height()/2 - 1;
+        margins.fRight  = fBM.width() - margins.fLeft - 1;
+        margins.fBottom = fBM.height() - margins.fTop - 1;
+
+   //     canvas->translate(fX/5, fY/5);
+        canvas->translate(0, 76);
+
+        dst.set(0, 0, SkIntToScalar(200), SkIntToScalar(200));
+        
+        SkPaint paint;
+        paint.setAntiAlias(false);
+        paint.setDither(true);
+        paint.setFilterBitmap(false);
+    //    SkNinePatch::DrawNine(canvas, dst, fBM, margins, &paint);
+        test_rects(canvas, fBM, &paint);
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        fX = x / 1.5f;
+        fY = y / 1.5f;
+        fX = x; fY = y;
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+private:
+    SkScalar fX, fY;
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new NinePatchView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleOvalTest.cpp b/samplecode/SampleOvalTest.cpp
new file mode 100644
index 0000000..f625529
--- /dev/null
+++ b/samplecode/SampleOvalTest.cpp
@@ -0,0 +1,110 @@
+#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..d3ecff7
--- /dev/null
+++ b/samplecode/SampleOverflow.cpp
@@ -0,0 +1,100 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+
+static void DrawRoundRect() {
+#ifdef SK_SCALAR_IS_FIXED
+    bool ret = false;
+    SkPaint  paint;
+    SkBitmap bitmap;
+    SkCanvas canvas;
+    SkMatrix matrix;
+    matrix.reset();
+    
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1370, 812);
+    bitmap.allocPixels();
+    canvas.setBitmapDevice(bitmap);
+    
+    // set up clipper
+    SkRect skclip;
+    skclip.set(SkIntToFixed(284), SkIntToFixed(40), SkIntToFixed(1370), SkIntToFixed(708));
+    
+    ret = canvas.clipRect(skclip);
+    SkASSERT(ret);
+    
+    matrix.set(SkMatrix::kMTransX, SkFloatToFixed(-1153.28));
+    matrix.set(SkMatrix::kMTransY, SkFloatToFixed(1180.50));
+    
+    matrix.set(SkMatrix::kMScaleX, SkFloatToFixed(0.177171));
+    matrix.set(SkMatrix::kMScaleY, SkFloatToFixed(0.177043));
+    
+    matrix.set(SkMatrix::kMSkewX, SkFloatToFixed(0.126968));
+    matrix.set(SkMatrix::kMSkewY, SkFloatToFixed(-0.126876));
+    
+    matrix.set(SkMatrix::kMPersp0, SkFloatToFixed(0.0));
+    matrix.set(SkMatrix::kMPersp1, SkFloatToFixed(0.0));
+    
+    ret = canvas.concat(matrix);
+    
+    paint.setAntiAlias(true);
+    paint.setColor(0xb2202020);
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(SkFloatToFixed(68.13));
+    
+    SkRect r;
+    r.set(SkFloatToFixed(-313.714417), SkFloatToFixed(-4.826389), SkFloatToFixed(18014.447266), SkFloatToFixed(1858.154541));
+    canvas.drawRoundRect(r, SkFloatToFixed(91.756363), SkFloatToFixed(91.756363), paint);
+#endif
+}
+
+static bool HitTestPath(const SkPath& path, SkScalar x, SkScalar y) {
+    SkRegion    rgn, clip;
+    
+    int ix = SkScalarFloor(x);
+    int iy = SkScalarFloor(y);
+
+    clip.setRect(ix, iy, ix + 1, iy + 1);
+    
+    bool contains = rgn.setPath(path, clip);
+    return contains;
+}
+
+static void TestOverflowHitTest() {
+    SkPath path;
+    
+#ifdef SK_SCALAR_IS_FLOATx
+    path.addCircle(0, 0, 70000, SkPath::kCCW_Direction);
+    SkASSERT(HitTestPath(path, 40000, 40000));
+#endif
+}
+
+class OverflowView : public SampleView {
+public:
+	OverflowView() {}
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Circles");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        DrawRoundRect();
+        TestOverflowHitTest();
+    }
+    
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new OverflowView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePageFlip.cpp b/samplecode/SamplePageFlip.cpp
new file mode 100644
index 0000000..7c5bf48
--- /dev/null
+++ b/samplecode/SamplePageFlip.cpp
@@ -0,0 +1,167 @@
+#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   200
+#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();
+
+    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());
+
+//            SkDebugf("----- dirty [%d %d %d %d]\n", dirty.getBounds().fLeft, dirty.getBounds().fTop, dirty.getBounds().width(), dirty.getBounds().height());
+            canvas.clipRegion(update.dirty());
+            
+            canvas.drawColor(0, SkXfermode::kClear_Mode);            
+            canvas.drawOval(oval, paint);
+        }
+        bounce(&x, &dx, WIDTH-OVALW);
+        bounce(&y, &dy, HEIGHT-OVALH);
+        
+#if 1
+        for (int i = 0; i < 1000; i++) {
+            for (int j = 0; j < 10000; j++) {
+                SkFixedMul(j, 10);
+            }
+        }
+#endif
+    }
+    return NULL;
+}
+
+static const SkBitmap::Config gConfigs[] = {
+    SkBitmap::kARGB_8888_Config,
+#if 1
+    SkBitmap::kRGB_565_Config,
+    SkBitmap::kARGB_4444_Config,
+    SkBitmap::kA8_Config
+#endif
+};
+
+class PageFlipView : public SampleView {
+public:
+    
+    enum { N = SK_ARRAY_COUNT(gConfigs) };
+    
+    pthread_t   fThreads[N];
+    SkBitmap    fBitmaps[N];
+
+	PageFlipView() {
+        gDone = false;
+        for (int i = 0; i < N; i++) {
+            int             status;
+            pthread_attr_t  attr;
+            
+            status = pthread_attr_init(&attr);
+            SkASSERT(0 == status);
+
+            fBitmaps[i].setConfig(gConfigs[i], WIDTH, HEIGHT);
+            SkFlipPixelRef* pr = new SkFlipPixelRef(gConfigs[i], WIDTH, HEIGHT);
+            fBitmaps[i].setPixelRef(pr)->unref();
+            fBitmaps[i].eraseColor(0);
+
+            status = pthread_create(&fThreads[i], &attr,  draw_proc, &fBitmaps[i]);
+            SkASSERT(0 == status);
+        }
+        this->setBGColor(0xFFDDDDDD);
+    }
+    
+    virtual ~PageFlipView() {
+        gDone = true;
+        for (int i = 0; i < N; i++) {
+            void* ret;
+            int status = pthread_join(fThreads[i], &ret);
+            SkASSERT(0 == status);
+        }
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "PageFlip");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkScalar x = SkIntToScalar(10);
+        SkScalar y = SkIntToScalar(10);
+        for (int i = 0; i < N; i++) {
+            canvas->drawBitmap(fBitmaps[i], x, y);
+            x += SkIntToScalar(fBitmaps[i].width() + 20);
+        }
+        this->inval(NULL);
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+    virtual bool onClick(Click* click) {
+        return this->INHERITED::onClick(click);
+    }
+    
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PageFlipView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePatch.cpp b/samplecode/SamplePatch.cpp
new file mode 100644
index 0000000..ea365c7
--- /dev/null
+++ b/samplecode/SamplePatch.cpp
@@ -0,0 +1,342 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkOSFile.h"
+#include "SkStream.h"
+
+#include "SkGeometry.h" // private include :(
+
+static void drawtriangle(SkCanvas* canvas, const SkPaint& paint,
+                         const SkPoint pts[3]) {
+    SkPath path;
+
+    path.moveTo(pts[0]);
+    path.lineTo(pts[1]);
+    path.lineTo(pts[2]);
+
+    canvas->drawPath(path, paint);
+}
+
+static SkShader* make_shader0(SkIPoint* size) {
+    SkBitmap    bm;
+
+//    SkImageDecoder::DecodeFile("/skimages/progressivejpg.jpg", &bm);
+    SkImageDecoder::DecodeFile("/skimages/logo.png", &bm);
+    size->set(bm.width(), bm.height());
+    return SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
+                                        SkShader::kClamp_TileMode);
+}
+
+static SkShader* make_shader1(const SkIPoint& size) {
+    SkPoint pts[] = { { 0, 0, },
+                      { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } };
+    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
+    return SkGradientShader::CreateLinear(pts, colors, NULL,
+                    SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Patch {
+public:
+    Patch() { sk_bzero(fPts, sizeof(fPts)); }
+    ~Patch() {}
+
+    void setPatch(const SkPoint pts[12]) {
+        memcpy(fPts, pts, 12 * sizeof(SkPoint));
+        fPts[12] = pts[0];  // the last shall be first
+    }
+    void setBounds(int w, int h) { fW = w; fH = h; }
+
+    void draw(SkCanvas*, const SkPaint&, int segsU, int segsV,
+              bool doTextures, bool doColors);
+
+private:
+    SkPoint fPts[13];
+    int     fW, fH;
+};
+
+static void eval_patch_edge(const SkPoint cubic[], SkPoint samples[], int segs) {
+    SkScalar t = 0;
+    SkScalar dt = SK_Scalar1 / segs;
+
+    samples[0] = cubic[0];
+    for (int i = 1; i < segs; i++) {
+        t += dt;
+        SkEvalCubicAt(cubic, t, &samples[i], NULL, NULL);
+    }
+}
+
+static void eval_sheet(const SkPoint edge[], int nu, int nv, int iu, int iv,
+                       SkPoint* pt) {
+    const int TL = 0;
+    const int TR = nu;
+    const int BR = TR + nv;
+    const int BL = BR + nu;
+
+    SkScalar u = SkIntToScalar(iu) / nu;
+    SkScalar v = SkIntToScalar(iv) / nv;
+
+    SkScalar uv = SkScalarMul(u, v);
+    SkScalar Uv = SkScalarMul(SK_Scalar1 - u, v);
+    SkScalar uV = SkScalarMul(u, SK_Scalar1 - v);
+    SkScalar UV = SkScalarMul(SK_Scalar1 - u, SK_Scalar1 - v);
+
+    SkScalar x0 = SkScalarMul(UV, edge[TL].fX) + SkScalarMul(uV, edge[TR].fX) +
+                  SkScalarMul(Uv, edge[BL].fX) + SkScalarMul(uv, edge[BR].fX);
+    SkScalar y0 = SkScalarMul(UV, edge[TL].fY) + SkScalarMul(uV, edge[TR].fY) +
+                  SkScalarMul(Uv, edge[BL].fY) + SkScalarMul(uv, edge[BR].fY);
+
+    SkScalar x =    SkScalarMul(SK_Scalar1 - v, edge[TL+iu].fX) +
+                    SkScalarMul(u, edge[TR+iv].fX) +
+                    SkScalarMul(v, edge[BR+nu-iu].fX) +
+                    SkScalarMul(SK_Scalar1 - u, edge[BL+nv-iv].fX) - x0;
+    SkScalar y =    SkScalarMul(SK_Scalar1 - v, edge[TL+iu].fY) +
+                    SkScalarMul(u, edge[TR+iv].fY) +
+                    SkScalarMul(v, edge[BR+nu-iu].fY) +
+                    SkScalarMul(SK_Scalar1 - u, edge[BL+nv-iv].fY) - y0;
+    pt->set(x, y);
+}
+
+static int ScalarTo255(SkScalar v) {
+    int scale = SkScalarToFixed(v) >> 8;
+    if (scale < 0) {
+        scale = 0;
+    } else if (scale > 255) {
+        scale = 255;
+    }
+    return scale;
+}
+
+static SkColor make_color(SkScalar s, SkScalar t) {
+    int cs = ScalarTo255(s);
+    int ct = ScalarTo255(t);
+    return SkColorSetARGB(0xFF, cs, 0, 0) + SkColorSetARGB(0, 0, ct, 0);
+}
+
+void Patch::draw(SkCanvas* canvas, const SkPaint& paint, int nu, int nv,
+                 bool doTextures, bool doColors) {
+    if (nu < 1 || nv < 1) {
+        return;
+    }
+
+    int i, npts = (nu + nv) * 2;
+    SkAutoSTMalloc<16, SkPoint> storage(npts + 1);
+    SkPoint* edge0 = storage.get();
+    SkPoint* edge1 = edge0 + nu;
+    SkPoint* edge2 = edge1 + nv;
+    SkPoint* edge3 = edge2 + nu;
+
+    // evaluate the edge points
+    eval_patch_edge(fPts + 0, edge0, nu);
+    eval_patch_edge(fPts + 3, edge1, nv);
+    eval_patch_edge(fPts + 6, edge2, nu);
+    eval_patch_edge(fPts + 9, edge3, nv);
+    edge3[nv] = edge0[0];   // the last shall be first
+
+    for (i = 0; i < npts; i++) {
+//        canvas->drawLine(edge0[i].fX, edge0[i].fY, edge0[i+1].fX, edge0[i+1].fY, paint);
+    }
+
+    int row, vertCount = (nu + 1) * (nv + 1);
+    SkAutoTMalloc<SkPoint>  vertStorage(vertCount);
+    SkPoint* verts = vertStorage.get();
+
+    // first row
+    memcpy(verts, edge0, (nu + 1) * sizeof(SkPoint));
+    // rows
+    SkPoint* r = verts;
+    for (row = 1; row < nv; row++) {
+        r += nu + 1;
+        r[0] = edge3[nv - row];
+        for (int col = 1; col < nu; col++) {
+            eval_sheet(edge0, nu, nv, col, row, &r[col]);
+        }
+        r[nu] = edge1[row];
+    }
+    // last row
+    SkPoint* last = verts + nv * (nu + 1);
+    for (i = 0; i <= nu; i++) {
+        last[i] = edge2[nu - i];
+    }
+
+//    canvas->drawPoints(verts, vertCount, paint);
+
+    int stripCount = (nu + 1) * 2;
+    SkAutoTMalloc<SkPoint>  stripStorage(stripCount * 2);
+    SkAutoTMalloc<SkColor>  colorStorage(stripCount);
+    SkPoint* strip = stripStorage.get();
+    SkPoint* tex = strip + stripCount;
+    SkColor* colors = colorStorage.get();
+    SkScalar t = 0;
+    const SkScalar ds = SK_Scalar1 * fW / nu;
+    const SkScalar dt = SK_Scalar1 * fH / nv;
+    r = verts;
+    for (row = 0; row < nv; row++) {
+        SkPoint* upper = r;
+        SkPoint* lower = r + nu + 1;
+        r = lower;
+        SkScalar s = 0;
+        for (i = 0; i <= nu; i++)  {
+            strip[i*2 + 0] = *upper++;
+            strip[i*2 + 1] = *lower++;
+            tex[i*2 + 0].set(s, t);
+            tex[i*2 + 1].set(s, t + dt);
+            colors[i*2 + 0] = make_color(s/fW, t/fH);
+            colors[i*2 + 1] = make_color(s/fW, (t + dt)/fH);
+            s += ds;
+        }
+        t += dt;
+        canvas->drawVertices(SkCanvas::kTriangleStrip_VertexMode, stripCount,
+                             strip, doTextures ? tex : NULL,
+                             doColors ? colors : NULL, NULL,
+                             NULL, 0, paint);
+    }
+}
+
+static void drawpatches(SkCanvas* canvas, const SkPaint& paint, int nu, int nv,
+                        Patch* patch) {
+
+    SkAutoCanvasRestore ar(canvas, true);
+
+    patch->draw(canvas, paint, 10, 10, false, false);
+    canvas->translate(SkIntToScalar(180), 0);
+    patch->draw(canvas, paint, 10, 10, true, false);
+    canvas->translate(SkIntToScalar(180), 0);
+    patch->draw(canvas, paint, 10, 10, false, true);
+    canvas->translate(SkIntToScalar(180), 0);
+    patch->draw(canvas, paint, 10, 10, true, true);
+}
+
+class PatchView : public SampleView {
+    SkShader*   fShader0;
+    SkShader*   fShader1;
+    SkIPoint    fSize0, fSize1;
+    SkPoint     fPts[12];
+
+public:
+	PatchView() {
+        fShader0 = make_shader0(&fSize0);
+        fSize1 = fSize0;
+        if (fSize0.fX == 0 || fSize0.fY == 0) {
+            fSize1.set(2, 2);
+        }
+        fShader1 = make_shader1(fSize1);
+
+        const SkScalar S = SkIntToScalar(50);
+        const SkScalar T = SkIntToScalar(40);
+        fPts[0].set(S*0, T);
+        fPts[1].set(S*1, T);
+        fPts[2].set(S*2, T);
+        fPts[3].set(S*3, T);
+        fPts[4].set(S*3, T*2);
+        fPts[5].set(S*3, T*3);
+        fPts[6].set(S*3, T*4);
+        fPts[7].set(S*2, T*4);
+        fPts[8].set(S*1, T*4);
+        fPts[9].set(S*0, T*4);
+        fPts[10].set(S*0, T*3);
+        fPts[11].set(S*0, T*2);
+
+        this->setBGColor(SK_ColorGRAY);
+    }
+
+    virtual ~PatchView() {
+        SkSafeUnref(fShader0);
+        SkSafeUnref(fShader1);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)  {
+        if (SampleCode::TitleQ(*evt))
+        {
+            SkString str("Patch");
+            SampleCode::TitleR(evt, str.c_str());
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setDither(true);
+        paint.setFilterBitmap(true);
+
+        canvas->translate(SkIntToScalar(20), 0);
+
+        Patch   patch;
+
+        paint.setShader(fShader0);
+        if (fSize0.fX == 0) {
+            fSize0.fX = 1;
+        }
+        if (fSize0.fY == 0) {
+            fSize0.fY = 1;
+        }
+        patch.setBounds(fSize0.fX, fSize0.fY);
+
+        patch.setPatch(fPts);
+        drawpatches(canvas, paint, 10, 10, &patch);
+
+        paint.setShader(NULL);
+        paint.setAntiAlias(true);
+        paint.setStrokeWidth(SkIntToScalar(5));
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, SK_ARRAY_COUNT(fPts), fPts, paint);
+
+        canvas->translate(0, SkIntToScalar(300));
+
+        paint.setAntiAlias(false);
+        paint.setShader(fShader1);
+        patch.setBounds(fSize1.fX, fSize1.fY);
+        drawpatches(canvas, paint, 10, 10, &patch);
+    }
+
+    class PtClick : public Click {
+    public:
+        int fIndex;
+        PtClick(SkView* view, int index) : Click(view), fIndex(index) {}
+    };
+
+    static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) {
+        return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(5);
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
+            if (hittest(fPts[i], x, y)) {
+                return new PtClick(this, i);
+            }
+        }
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+
+    virtual bool onClick(Click* click) {
+        fPts[((PtClick*)click)->fIndex].set(click->fCurr.fX, click->fCurr.fY);
+        this->inval(NULL);
+        return true;
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PatchView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePath.cpp b/samplecode/SamplePath.cpp
new file mode 100644
index 0000000..cd45ed9
--- /dev/null
+++ b/samplecode/SamplePath.cpp
@@ -0,0 +1,200 @@
+
+#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",
+               r.fLeft, r.fTop, r.fRight, r.fBottom,
+               ir.fLeft, ir.fTop, ir.fRight, ir.fBottom);
+    }
+    
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 300, 200);
+    bitmap.allocPixels();
+
+    SkCanvas canvas(bitmap);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    canvas.drawPath(path, paint);
+}
+
+class PathView : public SampleView {
+public:
+    int fDStroke, fStroke, fMinStroke, fMaxStroke;
+    SkPath fPath[6];
+    bool fShowHairline;
+    
+	PathView() {
+        test_cubic();
+        test_cubic2();
+
+        fShowHairline = false;
+        
+        fDStroke = 1;
+        fStroke = 10;
+        fMinStroke = 10;
+        fMaxStroke = 180;
+
+        const int V = 85;
+        
+        fPath[0].moveTo(SkIntToScalar(40), SkIntToScalar(70));
+        fPath[0].lineTo(SkIntToScalar(70), SkIntToScalar(70) + SK_Scalar1/1);
+        fPath[0].lineTo(SkIntToScalar(110), SkIntToScalar(70));
+        
+        fPath[1].moveTo(SkIntToScalar(40), SkIntToScalar(70));
+        fPath[1].lineTo(SkIntToScalar(70), SkIntToScalar(70) - SK_Scalar1/1);
+        fPath[1].lineTo(SkIntToScalar(110), SkIntToScalar(70));
+        
+        fPath[2].moveTo(SkIntToScalar(V), SkIntToScalar(V));
+        fPath[2].lineTo(SkIntToScalar(50), SkIntToScalar(V));
+        fPath[2].lineTo(SkIntToScalar(50), SkIntToScalar(50));
+        
+        fPath[3].moveTo(SkIntToScalar(50), SkIntToScalar(50));
+        fPath[3].lineTo(SkIntToScalar(50), SkIntToScalar(V));
+        fPath[3].lineTo(SkIntToScalar(V), SkIntToScalar(V));
+        
+        fPath[4].moveTo(SkIntToScalar(50), SkIntToScalar(50));
+        fPath[4].lineTo(SkIntToScalar(50), SkIntToScalar(V));
+        fPath[4].lineTo(SkIntToScalar(52), SkIntToScalar(50));
+        
+        fPath[5].moveTo(SkIntToScalar(52), SkIntToScalar(50));
+        fPath[5].lineTo(SkIntToScalar(50), SkIntToScalar(V));
+        fPath[5].lineTo(SkIntToScalar(50), SkIntToScalar(50));
+        
+        this->setBGColor(0xFFDDDDDD);
+    }
+    
+    void nextStroke() {
+        fStroke += fDStroke;
+        if (fStroke > fMaxStroke || fStroke < fMinStroke)
+            fDStroke = -fDStroke;
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Paths");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawPath(SkCanvas* canvas, const SkPath& path, SkPaint::Join j) {
+        SkPaint paint;
+        
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeJoin(j);
+        paint.setStrokeWidth(SkIntToScalar(fStroke));
+
+        if (fShowHairline) {
+            SkPath  fill;
+            
+            paint.getFillPath(path, &fill);            
+            paint.setStrokeWidth(0);
+            canvas->drawPath(fill, paint);
+        } else {
+            canvas->drawPath(path, paint);
+        }
+        
+        paint.setColor(SK_ColorRED);
+        paint.setStrokeWidth(0);
+        canvas->drawPath(path, paint);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {        
+        canvas->translate(SkIntToScalar(50), SkIntToScalar(50));
+
+        static const SkPaint::Join gJoins[] = {
+            SkPaint::kBevel_Join,
+            SkPaint::kMiter_Join,
+            SkPaint::kRound_Join
+        };
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gJoins); i++) {
+            canvas->save();
+            for (size_t j = 0; j < SK_ARRAY_COUNT(fPath); j++) {
+                this->drawPath(canvas, fPath[j], gJoins[i]);
+                canvas->translate(SkIntToScalar(200), 0);
+            }
+            canvas->restore();
+            
+            canvas->translate(0, SkIntToScalar(200));
+        }
+        
+        this->nextStroke();
+        this->inval(NULL);
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        fShowHairline = !fShowHairline;
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PathView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePathClip.cpp b/samplecode/SamplePathClip.cpp
new file mode 100644
index 0000000..8139171
--- /dev/null
+++ b/samplecode/SamplePathClip.cpp
@@ -0,0 +1,84 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+class PathClipView : public SampleView {
+public:
+    SkRect fOval;
+    SkPoint fCenter;
+
+	PathClipView() {
+        fOval.set(0, 0, SkIntToScalar(200), SkIntToScalar(50));
+        fCenter.set(SkIntToScalar(250), SkIntToScalar(250));
+        
+//        test_ats();
+    }
+    
+    virtual ~PathClipView() {}
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "PathClip");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkRect oval = fOval;
+        oval.offset(fCenter.fX - oval.centerX(), fCenter.fY - oval.centerY());
+        
+        SkPaint p;
+        p.setAntiAlias(true);
+        
+        p.setStyle(SkPaint::kStroke_Style);
+        canvas->drawOval(oval, p);
+
+        SkRect r;
+        r.set(SkIntToScalar(200), SkIntToScalar(200),
+              SkIntToScalar(300), SkIntToScalar(300));
+        canvas->clipRect(r);
+        
+        p.setStyle(SkPaint::kFill_Style);
+        p.setColor(SK_ColorRED);
+        canvas->drawRect(r, p);
+     
+        p.setColor(0x800000FF);
+        r.set(SkIntToScalar(150), SkIntToScalar(10),
+              SkIntToScalar(250), SkIntToScalar(400));
+        canvas->drawOval(oval, p);
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        return new Click(this);
+    }
+        
+    virtual bool onClick(Click* click) {
+        fCenter.set(click->fCurr.fX, click->fCurr.fY);
+        this->inval(NULL);
+        return NULL;
+    }
+    
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PathClipView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePathEffects.cpp b/samplecode/SamplePathEffects.cpp
new file mode 100644
index 0000000..75566b0
--- /dev/null
+++ b/samplecode/SamplePathEffects.cpp
@@ -0,0 +1,184 @@
+#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..845b7a8
--- /dev/null
+++ b/samplecode/SamplePathFill.cpp
@@ -0,0 +1,140 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkRandom.h"
+#include "SkBlurDrawLooper.h"
+#include "SkGradientShader.h"
+
+typedef SkScalar (*MakePathProc)(SkPath*);
+
+static SkScalar make_frame(SkPath* path) {
+    SkRect r = { 10, 10, 630, 470 };
+    path->addRoundRect(r, 15, 15);
+
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(5);
+    paint.getFillPath(*path, path);
+    return 15;
+}
+
+static SkScalar make_triangle(SkPath* path) {
+    static const int gCoord[] = {
+        10, 20, 15, 5, 30, 30
+    };
+    path->moveTo(SkIntToScalar(gCoord[0]), SkIntToScalar(gCoord[1]));
+    path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3]));
+    path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5]));
+    path->close();
+    path->offset(10, 0);
+    return SkIntToScalar(30);
+}
+
+static SkScalar make_rect(SkPath* path) {
+    SkRect r = { 10, 10, 30, 30 };
+    path->addRect(r);
+    path->offset(10, 0);
+    return SkIntToScalar(30);
+}
+
+static SkScalar make_oval(SkPath* path) {
+    SkRect r = { 10, 10, 30, 30 };
+    path->addOval(r);
+    path->offset(10, 0);
+    return SkIntToScalar(30);
+}
+
+static SkScalar make_sawtooth(SkPath* path) {
+    SkScalar x = SkIntToScalar(20);
+    SkScalar y = SkIntToScalar(20);
+    const SkScalar x0 = x;
+    const SkScalar dx = SK_Scalar1 * 5;
+    const SkScalar dy = SK_Scalar1 * 10;
+    
+    path->moveTo(x, y);
+    for (int i = 0; i < 32; i++) {
+        x += dx;
+        path->lineTo(x, y - dy);
+        x += dx;
+        path->lineTo(x, y + dy);
+    }
+    path->lineTo(x, y + 2 * dy);
+    path->lineTo(x0, y + 2 * dy);
+    path->close();
+    return SkIntToScalar(30);
+}
+
+static SkScalar make_star(SkPath* path, int n) {
+    const SkScalar c = SkIntToScalar(45);
+    const SkScalar r = SkIntToScalar(20);
+
+    SkScalar rad = -SK_ScalarPI / 2;
+    const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n;
+
+    path->moveTo(c, c - r);
+    for (int i = 1; i < n; i++) {
+        rad += drad;
+        SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV);
+        path->lineTo(c + SkScalarMul(cosV, r), c + SkScalarMul(sinV, r));
+    }
+    path->close();
+    return r * 2 * 6 / 5;
+}
+
+static SkScalar make_star_5(SkPath* path) { return make_star(path, 5); }
+static SkScalar make_star_13(SkPath* path) { return make_star(path, 13); }
+
+static const MakePathProc gProcs[] = {
+    make_frame,
+    make_triangle,
+    make_rect,
+    make_oval,
+    make_sawtooth,
+    make_star_5,
+    make_star_13
+};
+
+#define N   SK_ARRAY_COUNT(gProcs)
+
+class PathFillView : public SampleView {
+    SkPath  fPath[N];
+    SkScalar fDY[N];
+
+public:
+    PathFillView() {
+        for (size_t i = 0; i < N; i++) {
+            fDY[i] = gProcs[i](&fPath[i]);
+        }
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "PathFill");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+
+        for (size_t i = 0; i < N; i++) {
+            canvas->drawPath(fPath[i], paint);
+            canvas->translate(0, fDY[i]);
+        }
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PathFillView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePicture.cpp b/samplecode/SamplePicture.cpp
new file mode 100644
index 0000000..d7b6b22
--- /dev/null
+++ b/samplecode/SamplePicture.cpp
@@ -0,0 +1,254 @@
+#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 "SkShape.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+
+class SignalShape : public SkShape {
+public:
+    SignalShape() : fSignal(0) {}
+
+    SkShape* setSignal(int n) {
+        fSignal = n;
+        return this;
+    }
+
+protected:
+    virtual void onDraw(SkCanvas* canvas) {
+    //    SkDebugf("---- sc %d\n", canvas->getSaveCount() - 1);
+    }
+
+private:
+    int fSignal;
+};
+
+static SkPMColor SignalProc(SkPMColor src, SkPMColor dst) {
+    return dst;
+}
+
+/*  Picture playback will skip blocks of draw calls that follow a clip() call
+    that returns empty, and jump down to the corresponding restore() call.
+
+    This is a great preformance win for drawing very large/tall pictures with
+    a small visible window (think scrolling a long document). These tests make
+    sure that (a) we are performing the culling, and (b) we don't get confused
+    by nested save() calls, nor by calls to restoreToCount().
+ */
+static void test_saveRestoreCulling() {
+    SkPaint signalPaint;
+    SignalShape signalShape;
+
+    SkPicture pic;
+    SkRect r = SkRect::MakeWH(0, 0);
+    int n;
+    SkCanvas* canvas = pic.beginRecording(100, 100);
+    int startN = canvas->getSaveCount();
+    SkDebugf("---- start sc %d\n", startN);
+    canvas->drawShape(signalShape.setSignal(1));
+    canvas->save();
+    canvas->drawShape(signalShape.setSignal(2));
+    n = canvas->save();
+    canvas->drawShape(signalShape.setSignal(3));
+    canvas->save();
+    canvas->clipRect(r);
+    canvas->drawShape(signalShape.setSignal(4));
+    canvas->restoreToCount(n);
+    canvas->drawShape(signalShape.setSignal(5));
+    canvas->restore();
+    canvas->drawShape(signalShape.setSignal(6));
+    SkASSERT(canvas->getSaveCount() == startN);
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+    bm.allocPixels();
+    SkCanvas c(bm);
+    c.drawPicture(pic);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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();
+
+        test_saveRestoreCulling();
+    }
+    
+    virtual ~PictureView() {
+        fPicture->unref();
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Picture");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void drawSomething(SkCanvas* canvas) {
+        SkPaint paint;
+
+        canvas->save();
+        canvas->scale(0.5f, 0.5f);
+        canvas->drawBitmap(fBitmap, 0, 0, NULL);
+        canvas->restore();
+
+        const char beforeStr[] = "before circle";
+        const char afterStr[] = "after circle";
+
+        paint.setAntiAlias(true);
+    
+        paint.setColor(SK_ColorRED);
+        canvas->drawData(beforeStr, sizeof(beforeStr));
+        canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50),
+                           SkIntToScalar(40), paint);
+        canvas->drawData(afterStr, sizeof(afterStr));
+        paint.setColor(SK_ColorBLACK);
+        paint.setTextSize(SkIntToScalar(40));
+        canvas->drawText("Picture", 7, SkIntToScalar(50), SkIntToScalar(62),
+                         paint);
+        
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        drawSomething(canvas);
+
+        SkPicture* pict = new SkPicture;
+        SkAutoUnref aur(pict);
+
+        drawSomething(pict->beginRecording(100, 100));
+        pict->endRecording();
+    
+        canvas->save();
+        canvas->translate(SkIntToScalar(300), SkIntToScalar(50));
+        canvas->scale(-SK_Scalar1, -SK_Scalar1);
+        canvas->translate(-SkIntToScalar(100), -SkIntToScalar(50));
+        canvas->drawPicture(*pict);
+        canvas->restore();
+
+        canvas->save();
+        canvas->translate(SkIntToScalar(200), SkIntToScalar(150));
+        canvas->scale(SK_Scalar1, -SK_Scalar1);
+        canvas->translate(0, -SkIntToScalar(50));
+        canvas->drawPicture(*pict);
+        canvas->restore();
+        
+        canvas->save();
+        canvas->translate(SkIntToScalar(100), SkIntToScalar(100));
+        canvas->scale(-SK_Scalar1, SK_Scalar1);
+        canvas->translate(-SkIntToScalar(100), 0);
+        canvas->drawPicture(*pict);
+        canvas->restore();
+
+        if (false) {
+            SkDebugfDumper dumper;
+            SkDumpCanvas dumpCanvas(&dumper);
+            dumpCanvas.drawPicture(*pict);
+        }
+        
+        // test that we can re-record a subpicture, and see the results
+        
+        SkRandom rand(SampleCode::GetAnimTime());
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(250));
+        drawCircle(fSubPicture->beginRecording(50, 50), 25,
+                   rand.nextU() | 0xFF000000);
+        canvas->drawPicture(*fPicture);
+        delayInval(500);
+    }
+    
+private:
+    #define INVAL_ALL_TYPE  "inval-all"
+    
+    void delayInval(SkMSec delay) {
+        (new SkEvent(INVAL_ALL_TYPE))->post(this->getSinkID(), 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..a2804b4
--- /dev/null
+++ b/samplecode/SamplePoints.cpp
@@ -0,0 +1,78 @@
+#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..aea0cb4
--- /dev/null
+++ b/samplecode/SamplePolyToPoly.cpp
@@ -0,0 +1,161 @@
+#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, m1;
+                m0.setPolyToPoly(src, dst, 3);
+              //  SkSetPoly3To3(&m1, src, dst);
+              //  m0.dump();
+              //  m1.dump();
+            }
+        }
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)  {
+        if (SampleCode::TitleQ(*evt)) {
+            SkString str("PolyToPolyView");
+            SampleCode::TitleR(evt, str.c_str());
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    static void doDraw(SkCanvas* canvas, SkPaint* paint, const int isrc[],
+                       const int idst[], int count) {
+        SkMatrix matrix;
+        SkPoint src[4], dst[4];
+
+        for (int i = 0; i < count; i++) {
+            src[i].set(SkIntToScalar(isrc[2*i+0]), SkIntToScalar(isrc[2*i+1]));
+            dst[i].set(SkIntToScalar(idst[2*i+0]), SkIntToScalar(idst[2*i+1]));
+        }
+
+        canvas->save();
+        matrix.setPolyToPoly(src, dst, count);
+        canvas->concat(matrix);
+
+        paint->setColor(SK_ColorGRAY);
+        paint->setStyle(SkPaint::kStroke_Style);
+        const SkScalar D = SkIntToScalar(64);
+        canvas->drawRectCoords(0, 0, D, D, *paint);
+        canvas->drawLine(0, 0, D, D, *paint);
+        canvas->drawLine(0, D, D, 0, *paint);
+
+        SkPaint::FontMetrics fm;
+        paint->getFontMetrics(&fm);
+        paint->setColor(SK_ColorRED);
+        paint->setStyle(SkPaint::kFill_Style);
+        SkScalar x = D/2;
+        float y = D/2 - (fm.fAscent + fm.fDescent)/2;
+        SkString str;
+        str.appendS32(count);
+        canvas->drawText(str.c_str(), str.size(), x, y, *paint);
+
+        canvas->restore();
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStrokeWidth(SkIntToScalar(4));
+        paint.setTextSize(SkIntToScalar(40));
+        paint.setTextAlign(SkPaint::kCenter_Align);
+
+        canvas->save();
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+        // translate (1 point)
+        const int src1[] = { 0, 0 };
+        const int dst1[] = { 5, 5 };
+        doDraw(canvas, &paint, src1, dst1, 1);
+        canvas->restore();
+
+        canvas->save();
+        canvas->translate(SkIntToScalar(160), SkIntToScalar(10));
+        // rotate/uniform-scale (2 points)
+        const int src2[] = { 32, 32, 64, 32 };
+        const int dst2[] = { 32, 32, 64, 48 };
+        doDraw(canvas, &paint, src2, dst2, 2);
+        canvas->restore();
+
+        canvas->save();
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(110));
+        // rotate/skew (3 points)
+        const int src3[] = { 0, 0, 64, 0, 0, 64 };
+        const int dst3[] = { 0, 0, 96, 0, 24, 64 };
+        doDraw(canvas, &paint, src3, dst3, 3);
+        canvas->restore();
+
+        canvas->save();
+        canvas->translate(SkIntToScalar(160), SkIntToScalar(110));
+        // perspective (4 points)
+        const int src4[] = { 0, 0, 64, 0, 64, 64, 0, 64 };
+        const int dst4[] = { 0, 0, 96, 0, 64, 96, 0, 64 };
+        doDraw(canvas, &paint, src4, dst4, 4);
+        canvas->restore();
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PolyToPolyView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleRegion.cpp b/samplecode/SampleRegion.cpp
new file mode 100644
index 0000000..822bd6f
--- /dev/null
+++ b/samplecode/SampleRegion.cpp
@@ -0,0 +1,273 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkImageDecoder.h"
+
+#ifdef SK_BUILD_FOR_WIN
+// windows doesn't have roundf
+inline float roundf(float x) { return (x-floor(x))>0.5 ? ceil(x) : floor(x); }
+#endif
+
+#ifdef SK_DEBUG
+static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom,
+                     size_t count, int32_t runs[]) {
+    SkIRect r;
+    r.set(left, top, right, bottom);
+    
+    rgn->debugSetRuns(runs, count);
+    SkASSERT(rgn->getBounds() == r);
+}
+
+static void test_union_bug_1505668(SkRegion* ra, SkRegion* rb, SkRegion* rc) {
+    static int32_t dataA[] = {
+        0x00000001, 0x000001dd,
+        0x00000001, 0x0000000c, 0x0000000d, 0x00000025,
+        0x7fffffff, 0x000001de, 0x00000001, 0x00000025,
+        0x7fffffff, 0x000004b3, 0x00000001, 0x00000026,
+        0x7fffffff, 0x000004b4, 0x0000000c, 0x00000026,
+        0x7fffffff, 0x00000579, 0x00000000, 0x0000013a,
+        0x7fffffff, 0x000005d8, 0x00000000, 0x0000013b,
+        0x7fffffff, 0x7fffffff
+    };
+    make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA);
+
+    static int32_t dataB[] = {
+        0x000000b6, 0x000000c4,
+        0x000000a1, 0x000000f0, 0x7fffffff, 0x000000d6,
+        0x7fffffff, 0x000000e4, 0x00000070, 0x00000079,
+        0x000000a1, 0x000000b0, 0x7fffffff, 0x000000e6,
+        0x7fffffff, 0x000000f4, 0x00000070, 0x00000079,
+        0x000000a1, 0x000000b0, 0x7fffffff, 0x000000f6,
+        0x7fffffff, 0x00000104, 0x000000a1, 0x000000b0,
+        0x7fffffff, 0x7fffffff
+    };
+    make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB);
+    
+    rc->op(*ra, *rb, SkRegion::kUnion_Op);
+}
+#endif
+
+static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
+    dst->fLeft = (int)::roundf(src.fLeft * scale);
+    dst->fTop = (int)::roundf(src.fTop * scale);
+    dst->fRight = (int)::roundf(src.fRight * scale);
+    dst->fBottom = (int)::roundf(src.fBottom * scale);
+}
+
+static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
+    SkRegion tmp;
+    SkRegion::Iterator iter(src);
+
+    for (; !iter.done(); iter.next()) {
+        SkIRect r;
+        scale_rect(&r, iter.rect(), scale);
+        tmp.op(r, SkRegion::kUnion_Op);
+    }
+    dst->swap(tmp);
+}
+
+static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
+                      const SkPaint& paint) {
+    SkRegion scaled;
+    scale_rgn(&scaled, rgn, 0.5f);
+    
+    SkRegion::Iterator  iter(rgn);
+
+    for (; !iter.done(); iter.next())
+    {
+        SkRect    r;
+        r.set(iter.rect());
+        canvas->drawRect(r, paint);
+    }
+}
+
+class RegionView : public SampleView {
+public:
+	RegionView() {
+        fBase.set(100, 100, 150, 150);
+        fRect = fBase;
+        fRect.inset(5, 5);
+        fRect.offset(25, 25);
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+    void build_rgn(SkRegion* rgn, SkRegion::Op op) {
+        rgn->setRect(fBase);
+        SkIRect r = fBase;
+        r.offset(75, 20);
+        rgn->op(r, SkRegion::kUnion_Op);
+        rgn->op(fRect, op);
+    }
+
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Regions");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawOrig(SkCanvas* canvas, bool bg) {
+        SkRect      r;
+        SkPaint     paint;
+        
+        paint.setStyle(SkPaint::kStroke_Style);
+        if (bg)
+            paint.setColor(0xFFBBBBBB);
+        
+        r.set(fBase);
+        canvas->drawRect(r, paint);
+        r.set(fRect);
+        canvas->drawRect(r, paint);
+    }
+    
+    void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
+        SkRegion    rgn;
+
+        this->build_rgn(&rgn, op);
+        
+        {
+            SkRegion tmp, tmp2(rgn);
+            
+            tmp = tmp2;
+            tmp.translate(5, -3);
+            
+            {
+                char    buffer[1000];
+                size_t  size = tmp.flatten(NULL);
+                SkASSERT(size <= sizeof(buffer));
+                size_t  size2 = tmp.flatten(buffer);
+                SkASSERT(size == size2);
+                
+                SkRegion    tmp3;
+                size2 = tmp3.unflatten(buffer);
+                SkASSERT(size == size2);
+                
+                SkASSERT(tmp3 == tmp);
+            }
+
+            rgn.translate(20, 30, &tmp);
+            SkASSERT(rgn.isEmpty() || tmp != rgn);
+            tmp.translate(-20, -30);
+            SkASSERT(tmp == rgn);
+        }
+
+        this->drawOrig(canvas, true);
+
+        SkPaint paint;
+        paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
+        paint_rgn(canvas, rgn, paint);
+
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(color);
+        paint_rgn(canvas, rgn, paint);
+    }
+    
+    void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
+        SkRegion    rgn;
+        SkPath      path;
+
+        this->build_rgn(&rgn, op);
+        rgn.getBoundaryPath(&path);
+
+        this->drawOrig(canvas, true);
+
+        SkPaint paint;
+
+        paint.setStyle(SkPaint::kFill_Style);
+        paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
+        canvas->drawPath(path, paint);
+        paint.setColor(color);
+        paint.setStyle(SkPaint::kStroke_Style);
+        canvas->drawPath(path, paint);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+#ifdef SK_DEBUG
+        if (true) {
+            SkRegion a, b, c;
+            test_union_bug_1505668(&a, &b, &c);
+            
+            if (false) {    // draw the result of the test
+                SkPaint paint;
+                
+                canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+                paint.setColor(SK_ColorRED);
+                paint_rgn(canvas, a, paint);
+                paint.setColor(0x800000FF);
+                paint_rgn(canvas, b, paint);
+                paint.setColor(SK_ColorBLACK);
+                paint.setStyle(SkPaint::kStroke_Style);
+             //   paint_rgn(canvas, c, paint);
+                return;
+            }
+        }
+#endif
+
+        static const struct {
+            SkColor         fColor;
+            const char*     fName;
+            SkRegion::Op    fOp;
+        } gOps[] = {
+            { SK_ColorBLACK,    "Difference",   SkRegion::kDifference_Op    },
+            { SK_ColorRED,      "Intersect",    SkRegion::kIntersect_Op     },
+            { 0xFF008800,       "Union",        SkRegion::kUnion_Op         },
+            { SK_ColorBLUE,     "XOR",          SkRegion::kXOR_Op           }
+        };
+
+        SkPaint textPaint;
+        textPaint.setAntiAlias(true);
+        textPaint.setTextSize(SK_Scalar1*24);
+
+        this->drawOrig(canvas, false);
+        canvas->save();
+            canvas->translate(SkIntToScalar(200), 0);
+            this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK);
+        canvas->restore();
+        
+        canvas->translate(0, SkIntToScalar(200));
+
+        for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
+            canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint);
+
+            this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
+
+            canvas->save();
+            canvas->translate(0, SkIntToScalar(200));
+            this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
+            canvas->restore();
+            
+            canvas->translate(SkIntToScalar(200), 0);
+        }
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        return fRect.contains(SkScalarRound(x), SkScalarRound(y)) ? new Click(this) : NULL;
+    }
+    
+    virtual bool onClick(Click* click) {
+        fRect.offset(click->fICurr.fX - click->fIPrev.fX,
+                     click->fICurr.fY - click->fIPrev.fY);
+        this->inval(NULL);
+        return true;
+    }
+    
+private:
+    SkIRect    fBase, fRect;
+    
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new RegionView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleRepeatTile.cpp b/samplecode/SampleRepeatTile.cpp
new file mode 100644
index 0000000..9867074
--- /dev/null
+++ b/samplecode/SampleRepeatTile.cpp
@@ -0,0 +1,86 @@
+#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..2748b55
--- /dev/null
+++ b/samplecode/SampleShaderText.cpp
@@ -0,0 +1,195 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkUnitMappers.h"
+
+static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
+    bm->setConfig(config, w, h);
+    bm->allocPixels();
+    bm->eraseColor(0);
+
+    SkCanvas    canvas(*bm);
+    SkScalar s = SkIntToScalar(w < h ? w : h);
+    SkPoint     pts[] = { { 0, 0 }, { s, s } };
+    SkColor     colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
+    SkScalar    pos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
+    SkPaint     paint;
+
+    SkUnitMapper*   um = NULL;
+
+    um = new SkCosineMapper;
+
+    SkAutoUnref au(um);
+
+    paint.setDither(true);
+    paint.setShader(SkGradientShader::CreateLinear(pts, colors, pos,
+                SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode, um))->unref();
+    canvas.drawPaint(paint);
+}
+
+SkShader* MakeBitmapShader(SkShader::TileMode tx, SkShader::TileMode ty,
+                           int w, int h) {
+    static SkBitmap bmp;
+    if (bmp.isNull()) {
+        makebm(&bmp, SkBitmap::kARGB_8888_Config, w/2, h/4);
+    }
+    return SkShader::CreateBitmapShader(bmp, tx, ty);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct GradData {
+    int             fCount;
+    const SkColor*  fColors;
+    const SkScalar* fPos;
+};
+
+static const SkColor gColors[] = {
+    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
+};
+
+static const GradData gGradData[] = {
+    { 2, gColors, NULL },
+    { 5, gColors, NULL },
+};
+
+static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
+                            SkShader::TileMode tm, SkUnitMapper* mapper) {
+    return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
+                                          data.fCount, tm, mapper);
+}
+
+static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
+                            SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center;
+    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+               SkScalarAve(pts[0].fY, pts[1].fY));
+    return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
+                                          data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
+                           SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center;
+    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+               SkScalarAve(pts[0].fY, pts[1].fY));
+    return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
+                                         data.fPos, data.fCount, mapper);
+}
+
+static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
+                           SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+                SkScalarAve(pts[0].fY, pts[1].fY));
+    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+    return SkGradientShader::CreateTwoPointRadial(
+                            center1, (pts[1].fX - pts[0].fX) / 7,
+                            center0, (pts[1].fX - pts[0].fX) / 2,
+                            data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
+                     SkShader::TileMode tm, SkUnitMapper* mapper);
+static const GradMaker gGradMakers[] = {
+    MakeLinear, MakeRadial, MakeSweep, Make2Radial
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ShaderTextView : public SampleView {
+public:
+	ShaderTextView() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Shader Text");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        const char text[] = "Shaded Text";
+        const int textLen = SK_ARRAY_COUNT(text) - 1;
+        static int pointSize = 48;
+
+        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));
+
+        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();
+            canvas->translate(SkIntToScalar((s / testsPerCol) * colWidth),
+                              SkIntToScalar((s % testsPerCol) * rowHeight));
+            paint.setShader(shaders[s])->unref();
+            canvas->drawText(text, textLen, 0, textBase, 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..c1bb0fd
--- /dev/null
+++ b/samplecode/SampleShaders.cpp
@@ -0,0 +1,134 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkComposeShader.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTransparentShader.h"
+#include "SkTypeface.h"
+
+static SkShader* make_bitmapfade(const SkBitmap& bm)
+{
+    SkPoint pts[2];
+    SkColor colors[2];
+
+    pts[0].set(0, 0);
+    pts[1].set(0, SkIntToScalar(bm.height()));
+    colors[0] = SK_ColorBLACK;
+    colors[1] = SkColorSetARGB(0, 0, 0, 0);
+    SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
+
+    SkShader* shaderB = SkShader::CreateBitmapShader(bm,
+                        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+
+    SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode);
+
+    SkShader* shader = new SkComposeShader(shaderB, shaderA, mode);
+    shaderA->unref();
+    shaderB->unref();
+    mode->unref();
+
+    return shader;
+}
+
+class ShaderView : public SampleView {
+public:
+    SkShader*   fShader;
+    SkBitmap    fBitmap;
+
+	ShaderView() {
+        SkImageDecoder::DecodeFile("/skimages/logo.gif", &fBitmap);
+
+        SkPoint pts[2];
+        SkColor colors[2];
+
+        pts[0].set(0, 0);
+        pts[1].set(SkIntToScalar(100), 0);
+        colors[0] = SK_ColorRED;
+        colors[1] = SK_ColorBLUE;
+        SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
+
+        pts[0].set(0, 0);
+        pts[1].set(0, SkIntToScalar(100));
+        colors[0] = SK_ColorBLACK;
+        colors[1] = SkColorSetARGB(0x80, 0, 0, 0);
+        SkShader* shaderB = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
+
+        SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode);
+
+        fShader = new SkComposeShader(shaderA, shaderB, mode);
+        shaderA->unref();
+        shaderB->unref();
+        mode->unref();
+    }
+    virtual ~ShaderView() {
+        SkSafeUnref(fShader);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Shaders");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        canvas->drawBitmap(fBitmap, 0, 0);
+
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(120));
+
+        SkPaint paint;
+        SkRect  r;
+
+        paint.setColor(SK_ColorGREEN);
+        canvas->drawRectCoords(0, 0, SkIntToScalar(100), SkIntToScalar(100), paint);
+        paint.setShader(fShader);
+        canvas->drawRectCoords(0, 0, SkIntToScalar(100), SkIntToScalar(100), paint);
+
+        canvas->translate(SkIntToScalar(110), 0);
+
+        int w = fBitmap.width();
+        int h = fBitmap.height();
+        w = 120;
+        h = 80;
+        r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
+
+        paint.setShader(NULL);
+        canvas->drawRect(r, paint);
+        paint.setShader(make_bitmapfade(fBitmap))->unref();
+        canvas->drawRect(r, paint);
+
+        paint.setShader(new SkTransparentShader)->unref();
+        canvas->drawRect(r, paint);
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+
+    virtual bool onClick(Click* click) {
+        return this->INHERITED::onClick(click);
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ShaderView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleShapes.cpp b/samplecode/SampleShapes.cpp
new file mode 100644
index 0000000..dc10f1a
--- /dev/null
+++ b/samplecode/SampleShapes.cpp
@@ -0,0 +1,160 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "SkView.h"
+
+#define DO_AA   true
+
+#include "SkRectShape.h"
+#include "SkGroupShape.h"
+
+static SkRect make_rect(int l, int t, int r, int b) {
+    SkRect rect;
+    rect.set(SkIntToScalar(l), SkIntToScalar(t),
+             SkIntToScalar(r), SkIntToScalar(b));
+    return rect;
+}
+
+static SkShape* make_shape0(bool red) {
+    SkRectShape* s = new SkRectShape;
+    s->setRect(make_rect(10, 10, 90, 90));
+    if (red) {
+        s->paint().setColor(SK_ColorRED);
+    }
+    s->paint().setAntiAlias(DO_AA);
+    return s;
+}
+
+static SkShape* make_shape1() {
+    SkRectShape* s = new SkRectShape;
+    s->setOval(make_rect(10, 10, 90, 90));
+    s->paint().setColor(SK_ColorBLUE);
+    s->paint().setAntiAlias(DO_AA);
+    return s;
+}
+
+static SkShape* make_shape2() {
+    SkRectShape* s = new SkRectShape;
+    s->setRRect(make_rect(10, 10, 90, 90),
+                SkIntToScalar(20), SkIntToScalar(20));
+    s->paint().setColor(SK_ColorGREEN);
+    s->paint().setAntiAlias(DO_AA);
+    return s;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ShapesView : public SampleView {
+    SkGroupShape fGroup;
+    SkMatrixRef*    fMatrixRefs[4];
+public:
+	ShapesView() {
+        SkMatrix m;
+        fGroup.appendShape(make_shape0(false))->unref();
+        m.setRotate(SkIntToScalar(30), SkIntToScalar(50), SkIntToScalar(50));
+        m.postTranslate(0, SkIntToScalar(120));
+        fGroup.appendShape(make_shape0(true), m)->unref();
+
+        m.setTranslate(SkIntToScalar(120), 0);
+        fGroup.appendShape(make_shape1(), m)->unref();
+        m.postTranslate(0, SkIntToScalar(120));
+        fGroup.appendShape(make_shape2(), m)->unref();
+        
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
+            SkSafeRef(fMatrixRefs[i] = fGroup.getShapeMatrixRef(i));
+        }
+
+        this->setBGColor(0xFFDDDDDD);
+    }
+    
+    virtual ~ShapesView() {
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
+            SkSafeUnref(fMatrixRefs[i]);
+        }
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Shapes");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawpicture(SkCanvas* canvas, SkPicture& pict) {
+#if 0
+        SkDynamicMemoryWStream ostream;
+        pict.serialize(&ostream);
+
+        SkMemoryStream istream(ostream.getStream(), ostream.getOffset());
+        SkPicture* newPict = new SkPicture(&istream);
+        canvas->drawPicture(*newPict);
+        newPict->unref();
+#else
+        canvas->drawPicture(pict);
+#endif
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkScalar angle = SampleCode::GetAnimScalar(SkIntToScalar(180),
+                                                   SkIntToScalar(360));
+
+        SkMatrix saveM = *fMatrixRefs[3];
+        SkScalar c = SkIntToScalar(50);
+        fMatrixRefs[3]->preRotate(angle, c, c);
+        
+        const SkScalar dx = 350;
+        const SkScalar dy = 500;
+        const int N = 1;
+        for (int v = -N; v <= N; v++) {
+            for (int h = -N; h <= N; h++) {
+                SkAutoCanvasRestore acr(canvas, true);
+                canvas->translate(h * dx, v * dy);
+        
+        SkMatrix matrix;
+     
+        SkGroupShape* gs = new SkGroupShape;
+        SkAutoUnref aur(gs);
+        gs->appendShape(&fGroup);
+        matrix.setScale(-SK_Scalar1, SK_Scalar1);
+        matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240));
+        gs->appendShape(&fGroup, matrix);
+        matrix.setTranslate(SkIntToScalar(240), 0);
+        matrix.preScale(SK_Scalar1*2, SK_Scalar1*2);
+        gs->appendShape(&fGroup, matrix);
+        
+#if 0
+        canvas->drawShape(gs);
+#else
+        SkPicture* pict = new SkPicture;
+        SkCanvas* cv = pict->beginRecording(1000, 1000);
+        cv->scale(SK_ScalarHalf, SK_ScalarHalf);
+        cv->drawShape(gs);
+        cv->translate(SkIntToScalar(680), SkIntToScalar(480));
+        cv->scale(-SK_Scalar1, SK_Scalar1);
+        cv->drawShape(gs);
+        pict->endRecording();
+        
+        drawpicture(canvas, *pict);
+        pict->unref();
+#endif
+
+        }}
+
+        *fMatrixRefs[3] = saveM;
+        this->inval(NULL);
+}
+    
+private:
+    typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ShapesView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleSkLayer.cpp b/samplecode/SampleSkLayer.cpp
new file mode 100644
index 0000000..11976e9
--- /dev/null
+++ b/samplecode/SampleSkLayer.cpp
@@ -0,0 +1,239 @@
+#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..3b7d05b
--- /dev/null
+++ b/samplecode/SampleSlides.cpp
@@ -0,0 +1,804 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+
+#define BG_COLOR    0xFFDDDDDD
+
+typedef void (*SlideProc)(SkCanvas*);
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "Sk1DPathEffect.h"
+#include "Sk2DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkDashPathEffect.h"
+#include "SkDiscretePathEffect.h"
+
+static void compose_pe(SkPaint* paint) {
+    SkPathEffect* pe = paint->getPathEffect();
+    SkPathEffect* corner = new SkCornerPathEffect(25);
+    SkPathEffect* compose;
+    if (pe) {
+        compose = new SkComposePathEffect(pe, corner);
+        corner->unref();
+    } else {
+        compose = corner;
+    }
+    paint->setPathEffect(compose)->unref();
+}
+
+static void hair_pe(SkPaint* paint) {
+    paint->setStrokeWidth(0);
+}
+
+static void hair2_pe(SkPaint* paint) {
+    paint->setStrokeWidth(0);
+    compose_pe(paint);
+}
+
+static void stroke_pe(SkPaint* paint) {
+    paint->setStrokeWidth(12);
+    compose_pe(paint);
+}
+
+static void dash_pe(SkPaint* paint) {
+    SkScalar inter[] = { 20, 10, 10, 10 };
+    paint->setStrokeWidth(12);
+    paint->setPathEffect(new SkDashPathEffect(inter, SK_ARRAY_COUNT(inter),
+                                              0))->unref();
+    compose_pe(paint);
+}
+
+static const int gXY[] = {
+4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
+};
+
+static void scale(SkPath* path, SkScalar scale) {
+    SkMatrix m;
+    m.setScale(scale, scale);
+    path->transform(m);
+}
+
+static void one_d_pe(SkPaint* paint) {
+    SkPath  path;
+    path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
+    for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2)
+        path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
+    path.close();
+    path.offset(SkIntToScalar(-6), 0);
+    scale(&path, 1.5);
+    
+    paint->setPathEffect(new SkPath1DPathEffect(path, SkIntToScalar(21), 0,
+                                SkPath1DPathEffect::kRotate_Style))->unref();
+    compose_pe(paint);
+}
+
+typedef void (*PE_Proc)(SkPaint*);
+static const PE_Proc gPE[] = { hair_pe, hair2_pe, stroke_pe, dash_pe, one_d_pe };
+
+static void fill_pe(SkPaint* paint) {
+    paint->setStyle(SkPaint::kFill_Style);
+    paint->setPathEffect(NULL);
+}
+
+static void discrete_pe(SkPaint* paint) {
+    paint->setPathEffect(new SkDiscretePathEffect(10, 4))->unref();
+}
+
+class TilePathEffect : public Sk2DPathEffect {
+    static SkMatrix make_mat() {
+        SkMatrix m;
+        m.setScale(12, 12);
+        return m;
+    }
+public:
+    TilePathEffect() : Sk2DPathEffect(make_mat()) {}
+
+protected:
+    virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) {
+        dst->addCircle(loc.fX, loc.fY, 5);
+    }
+};
+
+static void tile_pe(SkPaint* paint) {
+    paint->setPathEffect(new TilePathEffect)->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"
+#include "SkNinePatch.h"
+
+static SkShader* make_shader0(SkIPoint* size) {
+    SkBitmap    bm;
+    
+    SkImageDecoder::DecodeFile("/skimages/logo.gif", &bm);
+    size->set(bm.width(), bm.height());
+    return SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
+                                        SkShader::kClamp_TileMode);
+}
+
+static SkShader* make_shader1(const SkIPoint& size) {
+    SkPoint pts[] = { { 0, 0 },
+                      { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } };
+    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
+    return SkGradientShader::CreateLinear(pts, colors, NULL,
+                                          SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL);
+}
+
+class Rec {
+public:
+    SkCanvas::VertexMode    fMode;
+    int                     fCount;
+    SkPoint*                fVerts;
+    SkPoint*                fTexs;
+    
+    Rec() : fCount(0), fVerts(NULL), fTexs(NULL) {}
+    ~Rec() { delete[] fVerts; delete[] fTexs; }
+};
+
+void make_tris(Rec* rec) {
+    int n = 10;
+    SkRandom    rand;
+    
+    rec->fMode = SkCanvas::kTriangles_VertexMode;
+    rec->fCount = n * 3;
+    rec->fVerts = new SkPoint[rec->fCount];
+    
+    for (int i = 0; i < n; i++) {
+        SkPoint* v = &rec->fVerts[i*3];
+        for (int j = 0; j < 3; j++) {
+            v[j].set(rand.nextUScalar1() * 250, rand.nextUScalar1() * 250);
+        }
+    }
+}
+
+void make_fan(Rec* rec, int texWidth, int texHeight) {
+    const SkScalar tx = SkIntToScalar(texWidth);
+    const SkScalar ty = SkIntToScalar(texHeight);
+    const int n = 24;
+    
+    rec->fMode = SkCanvas::kTriangleFan_VertexMode;
+    rec->fCount = n + 2;
+    rec->fVerts = new SkPoint[rec->fCount];
+    rec->fTexs  = new SkPoint[rec->fCount];
+    
+    SkPoint* v = rec->fVerts;
+    SkPoint* t = rec->fTexs;
+    
+    v[0].set(0, 0);
+    t[0].set(0, 0);
+    for (int i = 0; i < n; i++) {
+        SkScalar cos;
+        SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
+        v[i+1].set(cos, sin);
+        t[i+1].set(i*tx/n, ty);
+    }
+    v[n+1] = v[1];
+    t[n+1].set(tx, ty);
+    
+    SkMatrix m;
+    m.setScale(SkIntToScalar(100), SkIntToScalar(100));
+    m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
+    m.mapPoints(v, rec->fCount);
+}
+
+void make_strip(Rec* rec, int texWidth, int texHeight) {
+    const SkScalar tx = SkIntToScalar(texWidth);
+    const SkScalar ty = SkIntToScalar(texHeight);
+    const int n = 24;
+    
+    rec->fMode = SkCanvas::kTriangleStrip_VertexMode;
+    rec->fCount = 2 * (n + 1);
+    rec->fVerts = new SkPoint[rec->fCount];
+    rec->fTexs  = new SkPoint[rec->fCount];
+    
+    SkPoint* v = rec->fVerts;
+    SkPoint* t = rec->fTexs;
+    
+    for (int i = 0; i < n; i++) {
+        SkScalar cos;
+        SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
+        v[i*2 + 0].set(cos/2, sin/2);
+        v[i*2 + 1].set(cos, sin);
+        
+        t[i*2 + 0].set(tx * i / n, ty);
+        t[i*2 + 1].set(tx * i / n, 0);
+    }
+    v[2*n + 0] = v[0];
+    v[2*n + 1] = v[1];
+    
+    t[2*n + 0].set(tx, ty);
+    t[2*n + 1].set(tx, 0);
+    
+    SkMatrix m;
+    m.setScale(SkIntToScalar(100), SkIntToScalar(100));
+    m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
+    m.mapPoints(v, rec->fCount);
+}
+
+static void mesh_slide(SkCanvas* canvas) {
+    Rec fRecs[3];
+    SkIPoint    size;
+    
+    SkShader* fShader0 = make_shader0(&size);
+    SkShader* fShader1 = make_shader1(size);
+
+    SkAutoUnref aur0(fShader0);
+    SkAutoUnref aur1(fShader1);
+
+    make_strip(&fRecs[0], size.fX, size.fY);
+    make_fan(&fRecs[1], size.fX, size.fY);
+    make_tris(&fRecs[2]);
+
+    SkPaint paint;
+    paint.setDither(true);
+    paint.setFilterBitmap(true);
+    
+    for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
+        canvas->save();
+        
+        paint.setShader(NULL);
+        canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+                             fRecs[i].fVerts, fRecs[i].fTexs,
+                             NULL, NULL, NULL, 0, paint);
+        
+        canvas->translate(SkIntToScalar(210), 0);
+        
+        paint.setShader(fShader0);
+        canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+                             fRecs[i].fVerts, fRecs[i].fTexs,
+                             NULL, NULL, NULL, 0, paint);
+        
+        canvas->translate(SkIntToScalar(210), 0);
+        
+        paint.setShader(fShader1);
+        canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+                             fRecs[i].fVerts, fRecs[i].fTexs,
+                             NULL, NULL, NULL, 0, paint);
+        canvas->restore();
+        
+        canvas->translate(0, SkIntToScalar(250));
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGradientShader.h"
+#include "SkLayerRasterizer.h"
+#include "SkBlurMaskFilter.h"
+
+static void r0(SkLayerRasterizer* rast, SkPaint& p)
+{
+    p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
+                                             SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+    
+    p.setMaskFilter(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+    
+    p.setAlpha(0x11);
+    p.setStyle(SkPaint::kFill_Style);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    rast->addLayer(p);
+}
+
+static void r1(SkLayerRasterizer* rast, SkPaint& p)
+{
+    rast->addLayer(p);
+    
+    p.setAlpha(0x40);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*2);
+    rast->addLayer(p);
+}
+
+static void r2(SkLayerRasterizer* rast, SkPaint& p)
+{
+    p.setStyle(SkPaint::kStrokeAndFill_Style);
+    p.setStrokeWidth(SK_Scalar1*4);
+    rast->addLayer(p);
+    
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*3/2);
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+}
+
+static void r3(SkLayerRasterizer* rast, SkPaint& p)
+{
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*3);
+    rast->addLayer(p);
+    
+    p.setAlpha(0x20);
+    p.setStyle(SkPaint::kFill_Style);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    rast->addLayer(p);
+}
+
+static void r4(SkLayerRasterizer* rast, SkPaint& p)
+{
+    p.setAlpha(0x60);
+    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+    
+    p.setAlpha(0xFF);
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);
+    
+    p.setXfermode(NULL);
+    rast->addLayer(p);
+}
+
+#include "SkDiscretePathEffect.h"
+
+static void r5(SkLayerRasterizer* rast, SkPaint& p)
+{
+    rast->addLayer(p);
+    
+    p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
+    p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
+    rast->addLayer(p);
+}
+
+static void r6(SkLayerRasterizer* rast, SkPaint& p)
+{
+    rast->addLayer(p);
+    
+    p.setAntiAlias(false);
+    SkLayerRasterizer* rast2 = new SkLayerRasterizer;
+    r5(rast2, p);
+    p.setRasterizer(rast2)->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+}
+
+#include "Sk2DPathEffect.h"
+
+class Dot2DPathEffect : public Sk2DPathEffect {
+public:
+    Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix)
+    : Sk2DPathEffect(matrix), fRadius(radius) {}
+    
+    virtual void flatten(SkFlattenableWriteBuffer& buffer)
+    {
+        this->INHERITED::flatten(buffer);
+        
+        buffer.writeScalar(fRadius);
+    }
+    virtual Factory getFactory() { return CreateProc; }
+    
+protected:
+	virtual void next(const SkPoint& loc, int u, int v, SkPath* dst)
+    {
+        dst->addCircle(loc.fX, loc.fY, fRadius);
+    }
+    
+    Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer)
+    {
+        fRadius = buffer.readScalar();
+    }
+private:
+    SkScalar fRadius;
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)
+    {
+        return new Dot2DPathEffect(buffer);
+    }
+    
+    typedef Sk2DPathEffect INHERITED;
+};
+
+static void r7(SkLayerRasterizer* rast, SkPaint& p)
+{
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+    p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*4, lattice))->unref();
+    rast->addLayer(p);
+}
+
+static void r8(SkLayerRasterizer* rast, SkPaint& p)
+{
+    rast->addLayer(p);
+    
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+    p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*2, lattice))->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+    
+    p.setPathEffect(NULL);
+    p.setXfermode(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+}
+
+class Line2DPathEffect : public Sk2DPathEffect {
+public:
+    Line2DPathEffect(SkScalar width, const SkMatrix& matrix)
+    : Sk2DPathEffect(matrix), fWidth(width) {}
+    
+	virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
+    {
+        if (this->INHERITED::filterPath(dst, src, width))
+        {
+            *width = fWidth;
+            return true;
+        }
+        return false;
+    }
+    
+    virtual Factory getFactory() { return CreateProc; }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer)
+    {
+        this->INHERITED::flatten(buffer);
+        buffer.writeScalar(fWidth);
+    }
+protected:
+	virtual void nextSpan(int u, int v, int ucount, SkPath* dst)
+    {
+        if (ucount > 1)
+        {
+            SkPoint	src[2], dstP[2];
+            
+            src[0].set(SkIntToScalar(u) + SK_ScalarHalf,
+                       SkIntToScalar(v) + SK_ScalarHalf);
+            src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf,
+                       SkIntToScalar(v) + SK_ScalarHalf);
+            this->getMatrix().mapPoints(dstP, src, 2);
+            
+            dst->moveTo(dstP[0]);
+            dst->lineTo(dstP[1]);
+        }
+    }
+    
+    Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer)
+    {
+        fWidth = buffer.readScalar();
+    }
+    
+private:
+    SkScalar fWidth;
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)
+    {
+        return new Line2DPathEffect(buffer);
+    }
+    
+    typedef Sk2DPathEffect INHERITED;
+};
+
+static void r9(SkLayerRasterizer* rast, SkPaint& p)
+{
+    rast->addLayer(p);
+    
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
+    lattice.postRotate(SkIntToScalar(30), 0, 0);
+    p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+    
+    p.setPathEffect(NULL);
+    p.setXfermode(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+}
+
+typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
+
+static const raster_proc gRastProcs[] = {
+    r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
+};
+
+static void apply_shader(SkPaint* paint, int index) {    
+    raster_proc proc = gRastProcs[index];
+    SkPaint p;
+    SkLayerRasterizer*  rast = new SkLayerRasterizer;
+    
+    p.setAntiAlias(true);
+    proc(rast, p);
+    paint->setRasterizer(rast)->unref();
+    paint->setColor(SK_ColorBLUE);
+}
+
+#include "SkTypeface.h"
+
+static void texteffect_slide(SkCanvas* canvas) {
+    const char* str = "Google";
+    size_t len = strlen(str);
+    SkScalar x = 20;
+    SkScalar y = 80;
+    SkPaint paint;
+    paint.setTypeface(SkTypeface::CreateFromName("Georgia", SkTypeface::kItalic));
+    paint.setTextSize(75);
+    paint.setAntiAlias(true);
+    paint.setColor(SK_ColorBLUE);
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
+        apply_shader(&paint, i);
+        canvas->drawText(str, len, x, y, paint);
+        y += 80;
+        if (i == 4) {
+            x += 320;
+            y = 80;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageEncoder.h"
+
+static const SlideProc gProc[] = {
+    patheffect_slide,
+    gradient_slide,
+    textonpath_slide,
+    mesh_slide,
+    texteffect_slide
+};
+
+class SlideView : public SampleView {
+    int fIndex;
+public:
+    SlideView() {
+        fIndex = 0;
+        
+        SkBitmap bm;
+        bm.setConfig(SkBitmap::kARGB_8888_Config, 1024, 768);
+        bm.allocPixels();
+        SkCanvas canvas(bm);
+        SkScalar s = SkIntToScalar(1024) / 640;
+        canvas.scale(s, s);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gProc); i++) {
+            canvas.save();
+            canvas.drawColor(BG_COLOR);
+            gProc[i](&canvas);
+            canvas.restore();
+            SkString str;
+            str.printf("/skimages/slide_%d.png", i);
+            SkImageEncoder::EncodeFile(str.c_str(), bm, SkImageEncoder::kPNG_Type, 100);
+        }
+        this->setBGColor(BG_COLOR);
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Slides");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        gProc[fIndex](canvas);
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        fIndex = (fIndex + 1) % SK_ARRAY_COUNT(gProc);
+        this->inval(NULL);
+        return NULL;
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new SlideView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleSpiral.cpp b/samplecode/SampleSpiral.cpp
new file mode 100644
index 0000000..1a41440
--- /dev/null
+++ b/samplecode/SampleSpiral.cpp
@@ -0,0 +1,56 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+
+class SpiralView : public SampleView {
+public:
+	SpiralView() {
+        this->setBGColor(0xFFDDDDDD);
+	}
+	
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)  {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Spiral");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+	
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3)));
+        paint.setStyle(SkPaint::kFill_Style);
+        
+        SkRect r;
+        SkScalar l,t,x,y;
+        l = SampleCode::GetAnimScalar(SkIntToScalar(10),
+                                      SkIntToScalar(400));
+        t = SampleCode::GetAnimScalar(SkIntToScalar(5),
+                                      SkIntToScalar(200));
+        
+        canvas->translate(320,240);
+        for (int i = 0; i < 35; i++) {
+            paint.setColor(0xFFF00FF0 - i * 0x04000000);
+            SkScalar step = SK_ScalarPI / (55 - i);
+            SkScalar angle = t * step;
+            x = (20 + SkIntToScalar(i) * 5) * SkScalarSinCos(angle, &y);
+            y *= (20 + SkIntToScalar(i) * 5);
+            r.set(x, y, x + SkIntToScalar(10), y + SkIntToScalar(10));
+            canvas->drawRect(r, paint);
+        }
+
+        this->inval(NULL);
+    }
+	
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new SpiralView; }
+static SkViewRegister reg(MyFactory);
\ No newline at end of file
diff --git a/samplecode/SampleStrokePath.cpp b/samplecode/SampleStrokePath.cpp
new file mode 100644
index 0000000..ae630ef
--- /dev/null
+++ b/samplecode/SampleStrokePath.cpp
@@ -0,0 +1,217 @@
+#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..20c9e2c
--- /dev/null
+++ b/samplecode/SampleStrokeRect.cpp
@@ -0,0 +1,69 @@
+#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..bcb9e4f
--- /dev/null
+++ b/samplecode/SampleStrokeText.cpp
@@ -0,0 +1,140 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+static void lettersToBitmap(SkBitmap* dst, const char chars[],
+                            const SkPaint& original, SkBitmap::Config config) {
+    SkPath path;
+    SkScalar x = 0;
+    SkScalar width;
+    SkPath p;
+    for (size_t i = 0; i < strlen(chars); i++) {
+        original.getTextPath(&chars[i], 1, x, 0, &p);
+        path.addPath(p);
+        original.getTextWidths(&chars[i], 1, &width);
+        x += width;
+    }
+    SkRect bounds = path.getBounds();
+    SkScalar sw = -original.getStrokeWidth();
+    bounds.inset(sw, sw);
+    path.offset(-bounds.fLeft, -bounds.fTop);
+    bounds.offset(-bounds.fLeft, -bounds.fTop);
+    
+    int w = SkScalarRound(bounds.width());
+    int h = SkScalarRound(bounds.height());
+    SkPaint paint(original);
+    SkBitmap src;
+    src.setConfig(config, w, h);
+    src.allocPixels();
+    src.eraseColor(0);
+    {
+        SkCanvas canvas(src);
+        paint.setAntiAlias(true);
+        paint.setColor(SK_ColorBLACK);
+        paint.setStyle(SkPaint::kFill_Style);
+        canvas.drawPath(path, paint);
+    }
+    
+    dst->setConfig(config, w, h);
+    dst->allocPixels();
+    dst->eraseColor(SK_ColorWHITE);
+    {
+        SkCanvas canvas(*dst);
+        paint.setXfermodeMode(SkXfermode::kDstATop_Mode);
+        canvas.drawBitmap(src, 0, 0, &paint);
+        paint.setColor(original.getColor());
+        paint.setStyle(SkPaint::kStroke_Style);
+        canvas.drawPath(path, paint);
+    }
+}
+
+static void lettersToBitmap2(SkBitmap* dst, const char chars[],
+                            const SkPaint& original, SkBitmap::Config config) {
+    SkPath path;
+    SkScalar x = 0;
+    SkScalar width;
+    SkPath p;
+    for (size_t i = 0; i < strlen(chars); i++) {
+        original.getTextPath(&chars[i], 1, x, 0, &p);
+        path.addPath(p);
+        original.getTextWidths(&chars[i], 1, &width);
+        x += width;
+    }
+    SkRect bounds = path.getBounds();
+    SkScalar sw = -original.getStrokeWidth();
+    bounds.inset(sw, sw);
+    path.offset(-bounds.fLeft, -bounds.fTop);
+    bounds.offset(-bounds.fLeft, -bounds.fTop);
+    
+    int w = SkScalarRound(bounds.width());
+    int h = SkScalarRound(bounds.height());
+    SkPaint paint(original);
+
+    paint.setAntiAlias(true);
+    paint.setXfermodeMode(SkXfermode::kDstATop_Mode);
+    paint.setColor(original.getColor());
+    paint.setStyle(SkPaint::kStroke_Style);
+    
+    dst->setConfig(config, w, h);
+    dst->allocPixels();
+    dst->eraseColor(SK_ColorWHITE);
+
+    SkCanvas canvas(*dst);
+    canvas.drawPath(path, paint);
+}
+
+class StrokeTextView : public SampleView {
+    bool fAA;
+public:
+	StrokeTextView() : fAA(false) {
+        this->setBGColor(0xFFCC8844);
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "StrokeText");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkBitmap bm;
+        SkPaint paint;
+        
+        paint.setStrokeWidth(SkIntToScalar(6));
+        paint.setTextSize(SkIntToScalar(80));
+//        paint.setTypeface(Typeface.DEFAULT_BOLD);
+        
+        lettersToBitmap(&bm, "Test Case", paint, SkBitmap::kARGB_4444_Config);
+        
+        canvas->drawBitmap(bm, 0, 0);
+    }
+    
+private:
+    
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new StrokeTextView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTests.cpp b/samplecode/SampleTests.cpp
new file mode 100644
index 0000000..4ce8640
--- /dev/null
+++ b/samplecode/SampleTests.cpp
@@ -0,0 +1,111 @@
+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..2676530
--- /dev/null
+++ b/samplecode/SampleText.cpp
@@ -0,0 +1,396 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+
+static const int gKernel[3][3] = {
+//    { -1, -2, -1 }, { -2, 12, -2 }, { -1, -2, -1 }
+    { 1, 2, 1 }, { 2, 64-12, 2 }, { 1, 2, 1 }
+};
+static const int gShift = 6;
+
+class ReduceNoise : public SkKernel33ProcMaskFilter {
+public:
+    ReduceNoise(int percent256) : SkKernel33ProcMaskFilter(percent256) {}
+    virtual uint8_t computeValue(uint8_t* const* srcRows)
+    {
+        int c = srcRows[1][1];
+        int min = 255, max = 0;
+        for (int i = 0; i < 3; i++)
+            for (int j = 0; j < 3; j++)
+                if (i != 1 || j != 1)
+                {
+                    int v = srcRows[i][j];
+                    if (max < v)
+                        max = v;
+                    if  (min > v)
+                        min = v;
+                }
+        if (c > max) c = max;
+    //    if (c < min) c = min;
+        return c;
+    }
+    virtual Factory getFactory() { return Create; }
+private:
+    ReduceNoise(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(rb) {}
+    static SkFlattenable* Create(SkFlattenableReadBuffer& rb) {
+        return new ReduceNoise(rb);
+    }
+};
+
+class Darken : public SkKernel33ProcMaskFilter {
+public:
+    Darken(int percent256) : SkKernel33ProcMaskFilter(percent256) {}
+    virtual uint8_t computeValue(uint8_t* const* srcRows)
+    {
+        int c = srcRows[1][1];
+        float f = c / 255.f;
+
+        if (c >= 0) {
+            f = sqrtf(f);
+        } else {
+            f *= f;
+        }
+        SkASSERT(f >= 0 && f <= 1);
+        return (int)(f * 255);
+    }
+    virtual Factory getFactory() { return Create; }
+private:
+    Darken(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(rb) {}
+    static SkFlattenable* Create(SkFlattenableReadBuffer& rb) {
+        return new Darken(rb);
+    }
+};
+
+static SkMaskFilter* makemf() { return new Darken(0x30); }
+
+static void test_breakText() {
+    SkPaint paint;
+    const char* text = "sdfkljAKLDFJKEWkldfjlk#$%&sdfs.dsj";
+    size_t length = strlen(text);
+    SkScalar width = paint.measureText(text, length);
+
+    SkScalar mm = 0;
+    SkScalar nn = 0;
+    for (SkScalar w = 0; w <= width; w += SK_Scalar1) {
+        SkScalar m;
+        size_t n = paint.breakText(text, length, w, &m,
+                                    SkPaint::kBackward_TextBufferDirection);
+
+        SkASSERT(n <= length);
+        SkASSERT(m <= width);
+
+        if (n == 0) {
+            SkASSERT(m == 0);
+        } else {
+            // now assert that we're monotonic
+            if (n == nn) {
+                SkASSERT(m == mm);
+            } else {
+                SkASSERT(n > nn);
+                SkASSERT(m > mm);
+            }
+        }
+        nn = SkIntToScalar(n);
+        mm = m;
+    }
+
+    SkDEBUGCODE(size_t length2 =) paint.breakText(text, length, width, &mm);
+    SkASSERT(length2 == length);
+    SkASSERT(mm == width);
+}
+
+static SkRandom gRand;
+
+class SkPowerMode : public SkXfermode {
+public:
+    SkPowerMode(SkScalar exponent) { this->init(exponent); }
+
+    virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+
+    typedef SkFlattenable* (*Factory)(SkFlattenableReadBuffer&);
+
+    // overrides for SkFlattenable
+    virtual Factory getFactory() { return Create; }
+    virtual void flatten(SkFlattenableWriteBuffer& b) {
+    //    this->INHERITED::flatten(b);  How can we know if this is legal????
+        b.write32(SkScalarToFixed(fExp));
+    }
+
+private:
+    SkScalar fExp;          // user's value
+    uint8_t fTable[256];    // cache
+
+    void init(SkScalar exponent);
+    SkPowerMode(SkFlattenableReadBuffer& b) : SkXfermode(b) {
+        // read the exponent
+        this->init(SkFixedToScalar(b.readS32()));
+    }
+    static SkFlattenable* Create(SkFlattenableReadBuffer& b) {
+        return SkNEW_ARGS(SkPowerMode, (b));
+    }
+
+    typedef SkXfermode INHERITED;
+};
+
+void SkPowerMode::init(SkScalar e) {
+    fExp = e;
+    float ee = SkScalarToFloat(e);
+
+    printf("------ %g\n", ee);
+    for (int i = 0; i < 256; i++) {
+        float x = i / 255.f;
+     //   printf(" %d %g", i, x);
+        x = powf(x, ee);
+     //   printf(" %g", x);
+        int xx = SkScalarRound(SkFloatToScalar(x * 255));
+     //   printf(" %d\n", xx);
+        fTable[i] = SkToU8(xx);
+    }
+}
+
+void SkPowerMode::xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                         const SkAlpha aa[]) {
+    for (int i = 0; i < count; i++) {
+        SkPMColor c = src[i];
+        int r = SkGetPackedR32(c);
+        int g = SkGetPackedG32(c);
+        int b = SkGetPackedB32(c);
+        r = fTable[r];
+        g = fTable[g];
+        b = fTable[b];
+        dst[i] = SkPack888ToRGB16(r, g, b);
+    }
+}
+
+static const struct {
+    const char* fName;
+    uint32_t    fFlags;
+    bool        fFlushCache;
+} gHints[] = {
+    { "Linear", SkPaint::kLinearText_Flag,     false },
+    { "Normal",   0,                           true },
+    { "Subpixel", SkPaint::kSubpixelText_Flag, true }
+};
+
+static int count_char_points(const SkPaint& paint, char c) {
+    SkPath  path;
+
+    paint.getTextPath(&c, 1, 0, 0, &path);
+    return path.getPoints(NULL, 0);
+}
+
+static int gOld, gNew, gCount;
+
+static void dump(int c, int oldc, int newc) {
+    if (oldc != newc) {
+        gOld += oldc;
+        gNew += newc;
+        gCount += 1;
+        printf("char %c: old = %3d, new = %3d, reduction %g%%\n", c, oldc, newc, 100. * (oldc - newc) / oldc);
+    }
+}
+
+static void tab(int n) {
+//    printf("[%d] ", n); return;
+    SkASSERT(n >= 0);
+    for (int i = 0; i < n; i++)
+        printf("    ");
+}
+
+static void draw_rgn(const SkRegion& rgn, SkCanvas* canvas, const SkPaint& paint) {
+    SkRect    r;
+    SkRegion::Iterator  iter(rgn);
+
+    for (; !iter.done(); iter.next()) {
+        r.set(iter.rect());
+        canvas->drawRect(r, paint);
+    }
+}
+
+static void test_break(SkCanvas* canvas, const char text[], size_t length,
+                        SkScalar x, SkScalar y, const SkPaint& paint,
+                        SkScalar clickX) {
+    SkPaint linePaint;
+
+    linePaint.setAntiAlias(true);
+
+    SkScalar measured;
+
+    if (paint.breakText(text, length, clickX - x, &measured,
+                        SkPaint::kForward_TextBufferDirection)) {
+        linePaint.setColor(SK_ColorRED);
+        canvas->drawLine(x, y, x + measured, y, linePaint);
+    }
+
+    x += paint.measureText(text, length);
+    if (paint.breakText(text, length, x - clickX, &measured,
+                        SkPaint::kBackward_TextBufferDirection)) {
+        linePaint.setColor(SK_ColorBLUE);
+        canvas->drawLine(x - measured, y, x, y, linePaint);
+    }
+}
+
+static void DrawTheText(SkCanvas* canvas, const char text[], size_t length,
+                        SkScalar x, SkScalar y, const SkPaint& paint,
+                        SkScalar clickX, SkMaskFilter* mf) {
+    SkPaint p(paint);
+
+#if 0
+    canvas->drawText(text, length, x, y, paint);
+#else
+    {
+        SkPoint pts[1000];
+        SkScalar xpos = x;
+        SkASSERT(length <= SK_ARRAY_COUNT(pts));
+        for (size_t i = 0; i < length; i++) {
+            pts[i].set(xpos, y), xpos += paint.getTextSize();
+        }
+        canvas->drawPosText(text, length, pts, paint);
+    }
+#endif
+
+    p.setSubpixelText(true);
+    x += SkIntToScalar(180);
+    canvas->drawText(text, length, x, y, p);
+
+#ifdef SK_DEBUG
+    if (true) {
+    //    p.setMaskFilter(mf);
+        p.setSubpixelText(false);
+        p.setLinearText(true);
+        x += SkIntToScalar(180);
+        canvas->drawText(text, length, x, y, p);
+    }
+#endif
+}
+
+class TextSpeedView : public SampleView {
+public:
+	TextSpeedView() {
+        fMF = makemf();
+
+        fHints = 0;
+        fClickX = 0;
+
+        test_breakText();
+    }
+
+    virtual ~TextSpeedView() {
+        SkSafeUnref(fMF);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Text");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    static void make_textstrip(SkBitmap* bm) {
+        bm->setConfig(SkBitmap::kRGB_565_Config, 200, 18);
+        bm->allocPixels();
+        bm->eraseColor(SK_ColorWHITE);
+
+        SkCanvas    canvas(*bm);
+        SkPaint     paint;
+        const char* s = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit";
+
+        paint.setFlags(paint.getFlags() | SkPaint::kAntiAlias_Flag
+                                        | SkPaint::kDevKernText_Flag);
+        paint.setTextSize(SkIntToScalar(14));
+        canvas.drawText(s, strlen(s), SkIntToScalar(8), SkIntToScalar(14), paint);
+    }
+
+    static void fill_pts(SkPoint pts[], size_t n, SkRandom* rand) {
+        for (size_t i = 0; i < n; i++)
+            pts[i].set(rand->nextUScalar1() * 640, rand->nextUScalar1() * 480);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkAutoCanvasRestore restore(canvas, false);
+        {
+            SkRect r;
+            r.set(0, 0, SkIntToScalar(1000), SkIntToScalar(20));
+       //     canvas->saveLayer(&r, NULL, SkCanvas::kHasAlphaLayer_SaveFlag);
+        }
+
+        SkPaint paint;
+//        const uint16_t glyphs[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 };
+        int         index = fHints % SK_ARRAY_COUNT(gHints);
+        index = 1;
+//        const char* style = gHints[index].fName;
+
+//        canvas->translate(0, SkIntToScalar(50));
+
+  //      canvas->drawText(style, strlen(style), SkIntToScalar(20), SkIntToScalar(20), paint);
+
+        SkSafeUnref(paint.setTypeface(SkTypeface::CreateFromFile("/skimages/samplefont.ttf")));
+        paint.setAntiAlias(true);
+        paint.setFlags(paint.getFlags() | gHints[index].fFlags);
+
+        SkRect clip;
+        clip.set(SkIntToScalar(25), SkIntToScalar(34), SkIntToScalar(88), SkIntToScalar(155));
+
+        const char* text = "Hamburgefons";
+        size_t length = strlen(text);
+
+        SkScalar y = SkIntToScalar(0);
+        for (int i = 9; i <= 24; i++) {
+            paint.setTextSize(SkIntToScalar(i) /*+ (gRand.nextU() & 0xFFFF)*/);
+            for (SkScalar dx = 0; dx <= SkIntToScalar(3)/4;
+                                            dx += SkIntToScalar(1) /* /4 */) {
+                y += paint.getFontSpacing();
+                DrawTheText(canvas, text, length, SkIntToScalar(20) + dx, y,
+                            paint, fClickX, fMF);
+            }
+        }
+        if (gHints[index].fFlushCache) {
+//                SkGraphics::SetFontCacheUsed(0);
+        }
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        fClickX = x;
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+
+    virtual bool onClick(Click* click) {
+        return this->INHERITED::onClick(click);
+    }
+
+private:
+    int fHints;
+    SkScalar fClickX;
+    SkMaskFilter* fMF;
+
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TextSpeedView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTextAlpha.cpp b/samplecode/SampleTextAlpha.cpp
new file mode 100644
index 0000000..ccfed68
--- /dev/null
+++ b/samplecode/SampleTextAlpha.cpp
@@ -0,0 +1,114 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkOSFile.h"
+#include "SkStream.h"
+
+static void check_for_nonwhite(const SkBitmap& bm, int alpha) {
+    if (bm.config() != SkBitmap::kRGB_565_Config) {
+        return;
+    }
+    
+    for (int y = 0; y < bm.height(); y++) {
+        for (int x = 0; x < bm.width(); x++) {
+            uint16_t c = *bm.getAddr16(x, y);
+            if (c != 0xFFFF) {
+                SkDebugf("------ nonwhite alpha=%x [%d %d] %x\n", alpha, x, y, c);
+                return;
+            }
+        }
+    }
+}
+
+class TextAlphaView : public SampleView {
+public:    
+	TextAlphaView() {
+        fByte = 0xFF;
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)  {
+        if (SampleCode::TitleQ(*evt)) {
+            SkString str("TextAlpha");
+            SampleCode::TitleR(evt, str.c_str());
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        SkPaint paint;
+        SkScalar    x = SkIntToScalar(10);
+        SkScalar    y = SkIntToScalar(20);
+        
+        paint.setFlags(0x105);
+        
+        paint.setARGB(fByte, 0xFF, 0xFF, 0xFF);
+        
+        paint.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
+                                        SkBlurMaskFilter::kNormal_BlurStyle));
+        paint.getMaskFilter()->unref();
+        
+        SkRandom rand;
+        
+        for (int ps = 6; ps <= 35; ps++) {
+            paint.setColor(rand.nextU() | (0xFF << 24));
+            paint.setTextSize(SkIntToScalar(ps));
+            paint.setTextSize(SkIntToScalar(24));
+            canvas->drawText(str, strlen(str), x, y, paint);
+            y += paint.getFontMetrics(NULL);
+        }
+        //check_for_nonwhite(canvas->getDevice()->accessBitmap(), fByte);
+        //SkDebugf("------ byte %x\n", fByte);
+
+        if (false) {
+            fByte += 1;
+            fByte &= 0xFF;
+            this->inval(NULL);
+        }
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        return new Click(this);
+    }
+    
+    virtual bool onClick(Click* click) {
+        int y = click->fICurr.fY;
+        if (y < 0) {
+            y = 0;
+        } else if (y > 255) {
+            y = 255;
+        }
+        fByte = y;
+        this->inval(NULL);
+        return true;
+    }
+    
+private:
+    int fByte;
+
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TextAlphaView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTextBox.cpp b/samplecode/SampleTextBox.cpp
new file mode 100644
index 0000000..37a6be0
--- /dev/null
+++ b/samplecode/SampleTextBox.cpp
@@ -0,0 +1,91 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkTextBox.h"
+#include "SkOSFile.h"
+#include "SkStream.h"
+#include "SkKey.h"
+
+#ifdef SK_BUILD_FOR_WIN
+extern SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT&);
+#endif
+
+static const char gText[] =
+	"When in the Course of human events it becomes necessary for one people "
+	"to dissolve the political bands which have connected them with another "
+	"and to assume among the powers of the earth, the separate and equal "
+	"station to which the Laws of Nature and of Nature's God entitle them, "
+	"a decent respect to the opinions of mankind requires that they should "
+	"declare the causes which impel them to the separation.";
+
+class TextBoxView : public SampleView {
+public:
+	TextBoxView() {
+#ifdef SK_BUILD_FOR_WIN
+		LOGFONT lf;
+		sk_bzero(&lf, sizeof(lf));
+		lf.lfHeight = 9;
+		SkTypeface* tf0 = SkCreateTypefaceFromLOGFONT(lf);
+		lf.lfHeight = 12;
+		SkTypeface* tf1 = SkCreateTypefaceFromLOGFONT(lf);
+		// we assert that different sizes should not affect which face we get
+		SkASSERT(tf0 == tf1);
+		tf0->unref();
+		tf1->unref();
+#endif
+	}
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)  {
+        if (SampleCode::TitleQ(*evt)) {
+            SkString str("TextBox");
+            SampleCode::TitleR(evt, str.c_str());
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+		SkScalar margin = 20;
+        SkTextBox tbox;
+		tbox.setMode(SkTextBox::kLineBreak_Mode);
+		tbox.setBox(margin, margin,
+					this->width() - margin, this->height() - margin);
+		tbox.setSpacing(SkIntToScalar(3)/3, 0);
+
+		SkPaint paint;
+		paint.setAntiAlias(true);
+        paint.setLCDRenderText(true);
+		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());
+		}
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TextBoxView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTextEffects.cpp b/samplecode/SampleTextEffects.cpp
new file mode 100644
index 0000000..f256b2e
--- /dev/null
+++ b/samplecode/SampleTextEffects.cpp
@@ -0,0 +1,397 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTypeface.h"
+#include "SkAvoidXfermode.h"
+
+static inline SkPMColor rgb2gray(SkPMColor c) {
+    unsigned r = SkGetPackedR32(c);
+    unsigned g = SkGetPackedG32(c);
+    unsigned b = SkGetPackedB32(c);
+
+    unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
+
+    return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
+}
+
+class SkGrayScaleColorFilter : public SkColorFilter {
+public:
+    virtual void filterSpan(const SkPMColor src[], int count,
+                            SkPMColor result[]) {
+        for (int i = 0; i < count; i++) {
+            result[i] = rgb2gray(src[i]);
+        }
+    }
+};
+
+class SkChannelMaskColorFilter : public SkColorFilter {
+public:
+    SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) {
+        fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
+    }
+
+    virtual void filterSpan(const SkPMColor src[], int count,
+                            SkPMColor result[]) {
+        SkPMColor mask = fMask;
+        for (int i = 0; i < count; i++) {
+            result[i] = src[i] & mask;
+        }
+    }
+
+private:
+    SkPMColor   fMask;
+};
+
+///////////////////////////////////////////////////////////
+
+#include "SkGradientShader.h"
+#include "SkLayerRasterizer.h"
+#include "SkBlurMaskFilter.h"
+
+static void r0(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
+                                             SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+    p.setMaskFilter(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+
+    p.setAlpha(0x11);
+    p.setStyle(SkPaint::kFill_Style);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    rast->addLayer(p);
+}
+
+static void r1(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    p.setAlpha(0x40);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*2);
+    rast->addLayer(p);
+}
+
+static void r2(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setStyle(SkPaint::kStrokeAndFill_Style);
+    p.setStrokeWidth(SK_Scalar1*4);
+    rast->addLayer(p);
+
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*3/2);
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+}
+
+static void r3(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*3);
+    rast->addLayer(p);
+
+    p.setAlpha(0x20);
+    p.setStyle(SkPaint::kFill_Style);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    rast->addLayer(p);
+}
+
+static void r4(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setAlpha(0x60);
+    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+    p.setAlpha(0xFF);
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);
+
+    p.setXfermode(NULL);
+    rast->addLayer(p);
+}
+
+#include "SkDiscretePathEffect.h"
+
+static void r5(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
+    p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
+    rast->addLayer(p);
+}
+
+static void r6(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    p.setAntiAlias(false);
+    SkLayerRasterizer* rast2 = new SkLayerRasterizer;
+    r5(rast2, p);
+    p.setRasterizer(rast2)->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+}
+
+#include "Sk2DPathEffect.h"
+
+class Dot2DPathEffect : public Sk2DPathEffect {
+public:
+    Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix)
+        : Sk2DPathEffect(matrix), fRadius(radius) {}
+
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+        this->INHERITED::flatten(buffer);
+
+        buffer.writeScalar(fRadius);
+    }
+    virtual Factory getFactory() { return CreateProc; }
+
+protected:
+	virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) {
+        dst->addCircle(loc.fX, loc.fY, fRadius);
+    }
+
+    Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) {
+        fRadius = buffer.readScalar();
+    }
+private:
+    SkScalar fRadius;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return new Dot2DPathEffect(buffer);
+    }
+
+    typedef Sk2DPathEffect INHERITED;
+};
+
+static void r7(SkLayerRasterizer* rast, SkPaint& p) {
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+    p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*4, lattice))->unref();
+    rast->addLayer(p);
+}
+
+static void r8(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+    p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*2, lattice))->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+
+    p.setPathEffect(NULL);
+    p.setXfermode(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+}
+
+class Line2DPathEffect : public Sk2DPathEffect {
+public:
+    Line2DPathEffect(SkScalar width, const SkMatrix& matrix)
+        : Sk2DPathEffect(matrix), fWidth(width) {}
+
+	virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
+        if (this->INHERITED::filterPath(dst, src, width)) {
+            *width = fWidth;
+            return true;
+        }
+        return false;
+    }
+
+    virtual Factory getFactory() { return CreateProc; }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+        this->INHERITED::flatten(buffer);
+        buffer.writeScalar(fWidth);
+    }
+
+protected:
+	virtual void nextSpan(int u, int v, int ucount, SkPath* dst) {
+        if (ucount > 1) {
+            SkPoint	src[2], dstP[2];
+
+            src[0].set(SkIntToScalar(u) + SK_ScalarHalf,
+                       SkIntToScalar(v) + SK_ScalarHalf);
+            src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf,
+                       SkIntToScalar(v) + SK_ScalarHalf);
+            this->getMatrix().mapPoints(dstP, src, 2);
+
+            dst->moveTo(dstP[0]);
+            dst->lineTo(dstP[1]);
+        }
+    }
+
+    Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) {
+        fWidth = buffer.readScalar();
+    }
+
+private:
+    SkScalar fWidth;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return new Line2DPathEffect(buffer);
+    }
+
+    typedef Sk2DPathEffect INHERITED;
+};
+
+static void r9(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
+    lattice.postRotate(SkIntToScalar(30), 0, 0);
+    p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+
+    p.setPathEffect(NULL);
+    p.setXfermode(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+}
+
+typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
+
+static const raster_proc gRastProcs[] = {
+    r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
+};
+
+static const struct {
+    SkColor fMul, fAdd;
+} gLightingColors[] = {
+    { 0x808080, 0x800000 }, // general case
+    { 0x707070, 0x707070 }, // no-pin case
+    { 0xFFFFFF, 0x800000 }, // just-add case
+    { 0x808080, 0x000000 }, // just-mul case
+    { 0xFFFFFF, 0x000000 }  // identity case
+};
+
+#include "SkXfermode.h"
+
+static unsigned color_dist16(uint16_t a, uint16_t b) {
+    unsigned dr = SkAbs32(SkPacked16ToR32(a) - SkPacked16ToR32(b));
+    unsigned dg = SkAbs32(SkPacked16ToG32(a) - SkPacked16ToG32(b));
+    unsigned db = SkAbs32(SkPacked16ToB32(a) - SkPacked16ToB32(b));
+
+    return SkMax32(dr, SkMax32(dg, db));
+}
+
+static unsigned scale_dist(unsigned dist, unsigned scale) {
+    dist >>= 6;
+    dist = (dist << 2) | dist;
+    dist = (dist << 4) | dist;
+    return dist;
+
+//    return SkAlphaMul(dist, scale);
+}
+
+static void apply_shader(SkPaint* paint, int index) {
+    raster_proc proc = gRastProcs[index];
+    if (proc)
+    {
+        SkPaint p;
+        SkLayerRasterizer*  rast = new SkLayerRasterizer;
+
+        p.setAntiAlias(true);
+        proc(rast, p);
+        paint->setRasterizer(rast)->unref();
+    }
+
+#if 0
+    SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 };
+    paint->setMaskFilter(SkBlurMaskFilter::CreateEmboss(dir, SK_Scalar1/4, SkIntToScalar(4), SkIntToScalar(3)))->unref();
+#endif
+    paint->setColor(SK_ColorBLUE);
+}
+
+static int gRastIndex;
+
+class TextEffectView : public SampleView {
+    SkTypeface* fFace;
+public:
+	TextEffectView() {
+        fFace = SkTypeface::CreateFromFile("/Users/reed/Downloads/p052024l.pfb");
+    }
+
+    virtual ~TextEffectView() {
+        SkSafeUnref(fFace);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Text Effects");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        canvas->save();
+//        canvas->scale(SK_Scalar1*2, SK_Scalar1*2, 0, 0);
+
+        SkPaint     paint;
+
+        paint.setAntiAlias(true);
+        paint.setTextSize(SkIntToScalar(56));
+        paint.setTypeface(SkTypeface::CreateFromName("sans-serif",
+                                                     SkTypeface::kBold));
+
+        SkScalar    x = SkIntToScalar(20);
+        SkScalar    y = paint.getTextSize();
+
+        SkString str("TextEffects");
+
+        paint.setTypeface(fFace);
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
+            apply_shader(&paint, i);
+
+          //  paint.setMaskFilter(NULL);
+          //  paint.setColor(SK_ColorBLACK);
+
+#if 1
+            int index = i % SK_ARRAY_COUNT(gLightingColors);
+            paint.setColorFilter(SkColorFilter::CreateLightingFilter(
+                                    gLightingColors[index].fMul,
+                                    gLightingColors[index].fAdd))->unref();
+#endif
+
+            canvas->drawText(str.c_str(), str.size(), x, y, paint);
+
+            y += paint.getFontSpacing();
+        }
+
+        canvas->restore();
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        gRastIndex = (gRastIndex + 1) % SK_ARRAY_COUNT(gRastProcs);
+        this->inval(NULL);
+
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+
+    virtual bool onClick(Click* click) {
+        return this->INHERITED::onClick(click);
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TextEffectView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTextOnPath.cpp b/samplecode/SampleTextOnPath.cpp
new file mode 100644
index 0000000..96e8c9a
--- /dev/null
+++ b/samplecode/SampleTextOnPath.cpp
@@ -0,0 +1,284 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPackBits.h"
+#include "SkPath.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTypeface.h"
+#include "SkAvoidXfermode.h"
+
+#define REPEAT_COUNT    0
+
+static const char gText[] = "Hamburgefons";
+
+static bool gDevKern;
+
+static void rand_text(char text[], SkRandom& rand, size_t count) {
+    for (size_t i = 0; i < count; i++) {
+        text[i] = rand.nextU() & 0x7F;
+    }
+}
+
+static SkScalar sum_widths(const SkScalar widths[], int count) {
+    SkScalar w = 0;
+    for (int i = 0; i < count; i++) {
+        w += widths[i];
+    }
+    return w;
+}
+
+static void test_measure(const SkPaint& paint) {
+    char        text[256];
+    SkScalar    widths[256];
+    SkRect      rects[256];
+    SkRect      bounds;
+    int         count = 256;
+    
+    SkRandom rand;
+    
+    for (int i = 0; i < 100; i++) {
+        rand_text(text, rand, 256);
+        paint.getTextWidths(text, count, widths, NULL);
+        SkDEBUGCODE(SkScalar tw0 = sum_widths(widths, count);)
+        paint.getTextWidths(text, count, widths, rects);
+        SkDEBUGCODE(SkScalar tw1 = sum_widths(widths, count);)
+        SkASSERT(tw0 == tw1);
+
+        SkDEBUGCODE(SkScalar w0 = paint.measureText(text, count, NULL);)
+        SkDEBUGCODE(SkScalar w1 = paint.measureText(text, count, &bounds);)
+        SkASSERT(w0 == w1);
+        SkASSERT(w0 == tw0);
+        
+        SkRect r = rects[0];
+        SkScalar x = 0;
+        for (int j = 1; j < count; j++) {
+            x += widths[j-1];
+            rects[j].offset(x, 0);
+            r.join(rects[j]);
+        }
+        SkASSERT(r == bounds);
+        
+        if (r != bounds) {
+            printf("flags=%x i=%d [%g %g %g %g] [%g %g %g %g]\n",
+                   paint.getFlags(), i,
+                   SkScalarToFloat(r.fLeft),
+                   SkScalarToFloat(r.fTop),
+                   SkScalarToFloat(r.fRight),
+                   SkScalarToFloat(r.fBottom),
+                   SkScalarToFloat(bounds.fLeft),
+                   SkScalarToFloat(bounds.fTop),
+                   SkScalarToFloat(bounds.fRight),
+                   SkScalarToFloat(bounds.fBottom));
+        }
+    }
+}
+
+static void test_measure() {
+    SkPaint paint;
+    
+    for (int i = 0; i <= SkPaint::kAllFlags; i++) {
+        paint.setFlags(i);
+        test_measure(paint);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void test_textBounds(SkCanvas* canvas) {
+//    canvas->scale(SK_Scalar1/2, SK_Scalar1/2);
+    
+//    canvas->rotate(SkIntToScalar(30));
+
+    gDevKern = !gDevKern;
+
+    SkScalar x = SkIntToScalar(50);
+    SkScalar y = SkIntToScalar(150);
+    SkScalar w[100];
+    SkRect   r[100], bounds;
+    
+    SkPaint paint;
+    paint.setTextSize(SkIntToScalar(64));
+    paint.setAntiAlias(true);
+    paint.setDevKernText(gDevKern);
+    
+    (void)paint.measureText(gText, strlen(gText), &bounds, NULL);
+    paint.setColor(SK_ColorGREEN);
+    bounds.offset(x, y);
+    canvas->drawRect(bounds, paint);
+
+    int count = paint.getTextWidths(gText, strlen(gText), w, r);
+
+    paint.setColor(SK_ColorRED);
+    for (int i = 0; i < count; i++) {
+        r[i].offset(x, y);
+        canvas->drawRect(r[i], paint);
+        x += w[i];
+    }
+    x = SkIntToScalar(50);
+    paint.setColor(gDevKern ? SK_ColorDKGRAY : SK_ColorBLACK);
+    canvas->drawText(gText, strlen(gText), x, y, paint);
+}
+
+static void create_src(SkBitmap* bitmap, SkBitmap::Config config) {
+    bitmap->setConfig(config, 100, 100);
+    bitmap->allocPixels();
+    bitmap->eraseColor(0);
+    
+    SkCanvas    canvas(*bitmap);
+    SkPaint     paint;
+
+    paint.setAntiAlias(true);
+    canvas.drawCircle(SkIntToScalar(50), SkIntToScalar(50),
+                      SkIntToScalar(50), paint);
+}
+
+static void blur(SkBitmap* dst, const SkBitmap& src, SkScalar radius) {
+    *dst = src;
+}
+
+static void test_bitmap_blur(SkCanvas* canvas) {
+    SkBitmap    src, dst;
+    
+    create_src(&src, SkBitmap::kARGB_8888_Config);
+    blur(&dst, src, SkIntToScalar(4));
+    
+    SkPaint paint;
+    
+    paint.setColor(SK_ColorRED);
+
+    canvas->drawBitmap(dst, SkIntToScalar(30), SkIntToScalar(60), &paint);
+}
+
+static SkScalar getpathlen(const SkPath& path) {
+    SkPathMeasure   meas(path, false);
+    return meas.getLength();
+}
+
+static void test_textpathmatrix(SkCanvas* canvas) {
+    SkPaint paint;
+    SkPath  path;
+    SkMatrix matrix;
+    
+    path.moveTo(SkIntToScalar(200), SkIntToScalar(300));
+    path.quadTo(SkIntToScalar(400), SkIntToScalar(100),
+                SkIntToScalar(600), SkIntToScalar(300));
+
+    paint.setAntiAlias(true);
+    
+    paint.setStyle(SkPaint::kStroke_Style);
+ //   canvas->drawPath(path, paint);
+    paint.setStyle(SkPaint::kFill_Style);
+    paint.setTextSize(SkIntToScalar(48));
+    paint.setTextAlign(SkPaint::kRight_Align);
+    
+    const char* text = "Reflection";
+    size_t      len = strlen(text);
+    SkScalar    pathLen = getpathlen(path);
+
+    canvas->drawTextOnPath(text, len, path, NULL, paint);
+    
+    paint.setColor(SK_ColorRED);
+    matrix.setScale(-SK_Scalar1, SK_Scalar1);
+    matrix.postTranslate(pathLen, 0);
+    canvas->drawTextOnPath(text, len, path, &matrix, paint);
+    
+    paint.setColor(SK_ColorBLUE);
+    matrix.setScale(SK_Scalar1, -SK_Scalar1);
+    canvas->drawTextOnPath(text, len, path, &matrix, paint);
+    
+    paint.setColor(SK_ColorGREEN);
+    matrix.setScale(-SK_Scalar1, -SK_Scalar1);
+    matrix.postTranslate(pathLen, 0);
+    canvas->drawTextOnPath(text, len, path, &matrix, paint);
+}
+
+class TextOnPathView : public SampleView {
+public:
+    SkPath      fPath;
+    SkScalar    fHOffset;
+
+	TextOnPathView() {
+        SkRect r;
+        r.set(SkIntToScalar(100), SkIntToScalar(100),
+              SkIntToScalar(300), SkIntToScalar(300));
+        fPath.addOval(r);
+        fPath.offset(SkIntToScalar(200), 0);
+
+        fHOffset = SkIntToScalar(50);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Text On Path");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        
+        paint.setAntiAlias(true);
+        paint.setTextSize(SkIntToScalar(50));
+
+        for (int j = 0; j < REPEAT_COUNT; j++) {
+            SkScalar x = fHOffset;
+
+            paint.setColor(SK_ColorBLACK);
+            canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath,
+                                     x, paint.getTextSize()/2, paint);
+
+            paint.setColor(SK_ColorRED);
+            canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath,
+                                     x + SkIntToScalar(50), 0, paint);
+
+            paint.setColor(SK_ColorBLUE);
+            canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath,
+                         x + SkIntToScalar(100), -paint.getTextSize()/2, paint);
+        }
+        
+        paint.setColor(SK_ColorGREEN);
+        paint.setStyle(SkPaint::kStroke_Style);
+//        canvas->drawPath(fPath, paint);
+        
+        canvas->translate(0, SkIntToScalar(100));
+        test_textpathmatrix(canvas);
+        
+        if (REPEAT_COUNT > 1)
+            this->inval(NULL);
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        fHints += 1;
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+    virtual bool onClick(Click* click) {
+        return this->INHERITED::onClick(click);
+    }
+    
+private:
+    int fHints;
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() {
+    return new TextOnPathView;
+}
+
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTextureDomain.cpp b/samplecode/SampleTextureDomain.cpp
new file mode 100755
index 0000000..be000f9
--- /dev/null
+++ b/samplecode/SampleTextureDomain.cpp
@@ -0,0 +1,80 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkDevice.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 Domian");
+            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->createDevice(
+                SkBitmap::kARGB_8888_Config, 5, 5, true, 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);
+    }
+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..4752ed1
--- /dev/null
+++ b/samplecode/SampleTiling.cpp
@@ -0,0 +1,162 @@
+#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) {
+        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:
+    // 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..0841474
--- /dev/null
+++ b/samplecode/SampleTinyBitmap.cpp
@@ -0,0 +1,76 @@
+#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..be9da8f
--- /dev/null
+++ b/samplecode/SampleTriangles.cpp
@@ -0,0 +1,118 @@
+#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..63f1d5a
--- /dev/null
+++ b/samplecode/SampleTypeface.cpp
@@ -0,0 +1,128 @@
+#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..b20aece
--- /dev/null
+++ b/samplecode/SampleUnitMapper.cpp
@@ -0,0 +1,157 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkUnitMappers.h"
+#include "SkCubicInterval.h"
+
+#include "SkWidgetViews.h"
+
+static SkStaticTextView* make_textview(SkView* parent,
+                                       const SkRect& bounds,
+                                       const SkPaint& paint) {
+    SkStaticTextView* view = new SkStaticTextView;
+    view->setMode(SkStaticTextView::kFixedSize_Mode);
+    view->setPaint(paint);
+    view->setVisibleP(true);
+    view->setSize(bounds.width(), bounds.height());
+    view->setLoc(bounds.fLeft, bounds.fTop);
+    parent->attachChildToFront(view)->unref();
+    return view;
+}
+
+static void set_scalar(SkStaticTextView* view, SkScalar value) {
+    SkString str;
+    str.appendScalar(value);
+    view->setText(str);
+}
+
+class UnitMapperView : public SampleView {
+    SkPoint fPts[4];
+    SkMatrix fMatrix;
+    SkStaticTextView* fViews[4];
+
+    void setViews() {
+        set_scalar(fViews[0], fPts[1].fX);
+        set_scalar(fViews[1], fPts[1].fY);
+        set_scalar(fViews[2], fPts[2].fX);
+        set_scalar(fViews[3], fPts[2].fY);
+    }
+
+public:
+    UnitMapperView() {
+        fPts[0].set(0, 0);
+        fPts[1].set(SK_Scalar1 / 3, SK_Scalar1 / 3);
+        fPts[2].set(SK_Scalar1 * 2 / 3, SK_Scalar1 * 2 / 3);
+        fPts[3].set(SK_Scalar1, SK_Scalar1);
+
+        fMatrix.setScale(SK_Scalar1 * 200, -SK_Scalar1 * 200);
+        fMatrix.postTranslate(SkIntToScalar(100), SkIntToScalar(300));
+
+        SkRect r = {
+            SkIntToScalar(350), SkIntToScalar(100),
+            SkIntToScalar(500), SkIntToScalar(130)
+        };
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setTextSize(SkIntToScalar(25));
+        for (int i = 0; i < 4; i++) {
+            fViews[i] = make_textview(this, r, paint);
+            r.offset(0, r.height());
+        }
+        this->setViews();
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "UnitMapper");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xFF8888FF);
+
+        SkRect r = { 0, 0, SK_Scalar1, SK_Scalar1 };
+        
+        canvas->concat(fMatrix);
+        canvas->drawRect(r, paint);
+
+        paint.setColor(SK_ColorBLACK);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(0);
+        paint.setStrokeCap(SkPaint::kRound_Cap);
+        
+        SkPath path;
+        path.moveTo(fPts[0]);
+        path.cubicTo(fPts[1], fPts[2], fPts[3]);
+        canvas->drawPath(path, paint);
+
+        paint.setColor(SK_ColorRED);
+        paint.setStrokeWidth(0);
+        canvas->drawLine(0, 0, SK_Scalar1, SK_Scalar1, paint);
+
+        paint.setColor(SK_ColorBLUE);
+        paint.setStrokeWidth(SK_Scalar1 / 60);
+        for (int i = 0; i < 50; i++) {
+            SkScalar x = i * SK_Scalar1 / 49;
+            canvas->drawPoint(x, SkEvalCubicInterval(&fPts[1], x), paint);
+        }
+
+        paint.setStrokeWidth(SK_Scalar1 / 20);
+        paint.setColor(SK_ColorGREEN);
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, &fPts[1], paint);
+    }
+
+    SkPoint invertPt(SkScalar x, SkScalar y) {
+        SkPoint pt;
+        SkMatrix m;
+        fMatrix.invert(&m);
+        m.mapXY(x, y, &pt);
+        return pt;
+    }
+
+    int hittest(SkScalar x, SkScalar y) {
+        SkPoint target = { x, y };
+        SkPoint pts[2] = { fPts[1], fPts[2] };
+        fMatrix.mapPoints(pts, 2);
+        for (int i = 0; i < 2; i++) {
+            if (SkPoint::Distance(pts[i], target) < SkIntToScalar(4)) {
+                return i + 1;
+            }
+        }
+        return -1;
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        fDragIndex = hittest(x, y);
+        return fDragIndex >= 0 ? new Click(this) : NULL;
+    }
+    
+    virtual bool onClick(Click* click) {
+        if (fDragIndex >= 0) {
+            fPts[fDragIndex] = invertPt(click->fCurr.fX, click->fCurr.fY);
+            this->setViews();
+            this->inval(NULL);
+            return true;
+        }
+        return false;
+    }
+    
+private:
+    int fDragIndex;
+
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new UnitMapperView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleVertices.cpp b/samplecode/SampleVertices.cpp
new file mode 100644
index 0000000..74e757f
--- /dev/null
+++ b/samplecode/SampleVertices.cpp
@@ -0,0 +1,230 @@
+#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 "SkNinePatch.h"
+
+static SkShader* make_shader0(SkIPoint* size) {
+    SkBitmap    bm;
+    size->set(2, 2);
+    bm.setConfig(SkBitmap::kARGB_8888_Config, size->fX, size->fY);
+    SkPMColor color0 = SkPreMultiplyARGB(0x80, 0x80, 0xff, 0x80);
+    SkPMColor color1 = SkPreMultiplyARGB(0x40, 0xff, 0x00, 0xff);
+    bm.allocPixels();
+    bm.eraseColor(color0);
+    bm.lockPixels();
+    uint32_t* pixels = (uint32_t*) bm.getPixels();
+    pixels[0] = pixels[2] = color0;
+    pixels[1] = pixels[3] = color1;
+    bm.unlockPixels();
+
+    return SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+                                            SkShader::kRepeat_TileMode);
+}
+
+static SkShader* make_shader1(const SkIPoint& size) {
+    SkPoint pts[] = { { 0, 0 },
+                      { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } };
+    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
+    return SkGradientShader::CreateLinear(pts, colors, NULL,
+                    SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL);
+}
+
+class VerticesView : public SampleView {
+    SkShader*   fShader0;
+    SkShader*   fShader1;
+
+public:
+	VerticesView() {
+        SkIPoint    size;
+
+        fShader0 = make_shader0(&size);
+        fShader1 = make_shader1(size);
+
+        make_strip(&fRecs[0], size.fX, size.fY);
+        make_fan(&fRecs[1], size.fX, size.fY);
+        make_tris(&fRecs[2]);
+
+        fScale = SK_Scalar1;
+
+        this->setBGColor(SK_ColorGRAY);
+    }
+
+    virtual ~VerticesView() {
+        SkSafeUnref(fShader0);
+        SkSafeUnref(fShader1);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)  {
+        if (SampleCode::TitleQ(*evt))
+        {
+            SkString str("Vertices");
+            SampleCode::TitleR(evt, str.c_str());
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    SkScalar fScale;
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setDither(true);
+        paint.setFilterBitmap(true);
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
+            canvas->save();
+
+            paint.setShader(NULL);
+            canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+                                 fRecs[i].fVerts, fRecs[i].fTexs,
+                                 NULL, NULL, NULL, 0, paint);
+
+            canvas->translate(SkIntToScalar(250), 0);
+
+            paint.setShader(fShader0);
+            canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+                                 fRecs[i].fVerts, fRecs[i].fTexs,
+                                 NULL, NULL, NULL, 0, paint);
+
+            canvas->translate(SkIntToScalar(250), 0);
+
+            paint.setShader(fShader1);
+            canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+                                 fRecs[i].fVerts, fRecs[i].fTexs,
+                                 NULL, NULL, NULL, 0, paint);
+            canvas->restore();
+
+            canvas->translate(0, SkIntToScalar(250));
+        }
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        return new Click(this);
+    }
+
+    virtual bool onClick(Click* click) {
+    //    fCurrX = click->fICurr.fX;
+    //    fCurrY = click->fICurr.fY;
+        this->inval(NULL);
+        return true;
+    }
+
+private:
+    struct Rec {
+        SkCanvas::VertexMode    fMode;
+        int                     fCount;
+        SkPoint*                fVerts;
+        SkPoint*                fTexs;
+
+        Rec() : fCount(0), fVerts(NULL), fTexs(NULL) {}
+        ~Rec() { delete[] fVerts; delete[] fTexs; }
+    };
+
+    void make_tris(Rec* rec) {
+        int n = 10;
+        SkRandom    rand;
+
+        rec->fMode = SkCanvas::kTriangles_VertexMode;
+        rec->fCount = n * 3;
+        rec->fVerts = new SkPoint[rec->fCount];
+
+        for (int i = 0; i < n; i++) {
+            SkPoint* v = &rec->fVerts[i*3];
+            for (int j = 0; j < 3; j++) {
+                v[j].set(rand.nextUScalar1() * 250, rand.nextUScalar1() * 250);
+            }
+        }
+    }
+
+    void make_fan(Rec* rec, int texWidth, int texHeight) {
+        const SkScalar tx = SkIntToScalar(texWidth);
+        const SkScalar ty = SkIntToScalar(texHeight);
+        const int n = 24;
+
+        rec->fMode = SkCanvas::kTriangleFan_VertexMode;
+        rec->fCount = n + 2;
+        rec->fVerts = new SkPoint[rec->fCount];
+        rec->fTexs  = new SkPoint[rec->fCount];
+
+        SkPoint* v = rec->fVerts;
+        SkPoint* t = rec->fTexs;
+
+        v[0].set(0, 0);
+        t[0].set(0, 0);
+        for (int i = 0; i < n; i++) {
+            SkScalar cos;
+            SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
+            v[i+1].set(cos, sin);
+            t[i+1].set(i*tx/n, ty);
+        }
+        v[n+1] = v[1];
+        t[n+1].set(tx, ty);
+
+        SkMatrix m;
+        m.setScale(SkIntToScalar(100), SkIntToScalar(100));
+        m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
+        m.mapPoints(v, rec->fCount);
+    }
+
+    void make_strip(Rec* rec, int texWidth, int texHeight) {
+        const SkScalar tx = SkIntToScalar(texWidth);
+        const SkScalar ty = SkIntToScalar(texHeight);
+        const int n = 24;
+
+        rec->fMode = SkCanvas::kTriangleStrip_VertexMode;
+        rec->fCount = 2 * (n + 1);
+        rec->fVerts = new SkPoint[rec->fCount];
+        rec->fTexs  = new SkPoint[rec->fCount];
+
+        SkPoint* v = rec->fVerts;
+        SkPoint* t = rec->fTexs;
+
+        for (int i = 0; i < n; i++) {
+            SkScalar cos;
+            SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
+            v[i*2 + 0].set(cos/2, sin/2);
+            v[i*2 + 1].set(cos, sin);
+
+            t[i*2 + 0].set(tx * i / n, ty);
+            t[i*2 + 1].set(tx * i / n, 0);
+        }
+        v[2*n + 0] = v[0];
+        v[2*n + 1] = v[1];
+
+        t[2*n + 0].set(tx, ty);
+        t[2*n + 1].set(tx, 0);
+
+        SkMatrix m;
+        m.setScale(SkIntToScalar(100), SkIntToScalar(100));
+        m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
+        m.mapPoints(v, rec->fCount);
+    }
+
+    Rec fRecs[3];
+
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new VerticesView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleWarp.cpp b/samplecode/SampleWarp.cpp
new file mode 100644
index 0000000..bf4ef0d
--- /dev/null
+++ b/samplecode/SampleWarp.cpp
@@ -0,0 +1,467 @@
+#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/SampleXfermodes.cpp b/samplecode/SampleXfermodes.cpp
new file mode 100644
index 0000000..0a3c4c7
--- /dev/null
+++ b/samplecode/SampleXfermodes.cpp
@@ -0,0 +1,250 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkCornerPathEffect.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+#include "SkColorPriv.h"
+#include "SkImageDecoder.h"
+
+static void setNamedTypeface(SkPaint* paint, const char name[]) {
+    SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
+    paint->setTypeface(face);
+    SkSafeUnref(face);
+}
+
+#if 0
+static int newscale(U8CPU a, U8CPU b, int shift) {
+    unsigned prod = a * b + (1 << (shift - 1));
+    return (prod + (prod >> shift)) >> shift;
+}
+
+static void test_srcover565(SkCanvas* canvas) {
+    const int width = 32;
+    SkBitmap bm1, bm2, bm3;
+    bm1.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm1.allocPixels(NULL);
+    bm2.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm2.allocPixels(NULL);
+    bm3.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm3.allocPixels(NULL);
+
+    int rgb = 0x18;
+    int r = rgb >> 3;
+    int g = rgb >> 2;
+    uint16_t dst = SkPackRGB16(r, g, r);
+    for (int alpha = 0; alpha <= 255; alpha++) {
+        SkPMColor pm = SkPreMultiplyARGB(alpha, rgb, rgb, rgb);
+        uint16_t newdst = SkSrcOver32To16(pm, dst);
+        sk_memset16(bm1.getAddr16(0, alpha), newdst, bm1.width());
+
+        int ia = 255 - alpha;
+        int iscale = SkAlpha255To256(ia);
+        int dr = (SkGetPackedR32(pm) + (r * iscale >> 5)) >> 3;
+        int dg = (SkGetPackedG32(pm) + (g * iscale >> 6)) >> 2;
+
+        sk_memset16(bm2.getAddr16(0, alpha), SkPackRGB16(dr, dg, dr), bm2.width());
+
+        int dr2 = (SkMulDiv255Round(alpha, rgb) + newscale(r, ia, 5)) >> 3;
+        int dg2 = (SkMulDiv255Round(alpha, rgb) + newscale(g, ia, 6)) >> 2;
+
+        sk_memset16(bm3.getAddr16(0, alpha), SkPackRGB16(dr2, dg2, dr2), bm3.width());
+
+//        if (mr != dr || mg != dg)
+        {
+//            SkDebugf("[%d] macro [%d %d] inline [%d %d] new [%d %d]\n", alpha, mr, mg, dr, dg, dr2, dg2);
+        }
+    }
+
+    SkScalar dx = SkIntToScalar(width+4);
+
+    canvas->drawBitmap(bm1, 0, 0, NULL); canvas->translate(dx, 0);
+    canvas->drawBitmap(bm2, 0, 0, NULL); canvas->translate(dx, 0);
+    canvas->drawBitmap(bm3, 0, 0, NULL); canvas->translate(dx, 0);
+
+    SkRect rect = { 0, 0, SkIntToScalar(bm1.width()), SkIntToScalar(bm1.height()) };
+    SkPaint p;
+    p.setARGB(0xFF, rgb, rgb, rgb);
+    canvas->drawRect(rect, p);
+}
+#endif
+
+static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) {
+    src->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    src->allocPixels();
+    src->eraseColor(0);
+
+    SkCanvas c(*src);
+    SkPaint p;
+    SkRect r;
+    SkScalar ww = SkIntToScalar(w);
+    SkScalar hh = SkIntToScalar(h);
+
+    p.setAntiAlias(true);
+    p.setColor(0xFFFFCC44);
+    r.set(0, 0, ww*3/4, hh*3/4);
+    c.drawOval(r, p);
+
+    dst->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    dst->allocPixels();
+    dst->eraseColor(0);
+    c.setBitmapDevice(*dst);
+
+    p.setColor(0xFF66AAFF);
+    r.set(ww/3, hh/3, ww*19/20, hh*19/20);
+    c.drawRect(r, p);
+}
+
+static uint16_t gBG[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
+
+class XfermodesView : public SampleView {
+    SkBitmap    fBG;
+    SkBitmap    fSrcB, fDstB;
+
+    void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
+                   SkScalar x, SkScalar y) {
+        SkPaint p;
+
+        canvas->drawBitmap(fSrcB, x, y, &p);
+        p.setAlpha(alpha);
+        p.setXfermode(mode);
+        canvas->drawBitmap(fDstB, x, y, &p);
+    }
+
+public:
+    const static int W = 64;
+    const static int H = 64;
+	XfermodesView() {
+        const int W = 64;
+        const int H = 64;
+
+        fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
+        fBG.setPixels(gBG);
+        fBG.setIsOpaque(true);
+
+        make_bitmaps(W, H, &fSrcB, &fDstB);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Xfermodes");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
+
+        const struct {
+            SkXfermode::Mode  fMode;
+            const char*         fLabel;
+        } gModes[] = {
+            { SkXfermode::kClear_Mode,    "Clear"     },
+            { SkXfermode::kSrc_Mode,      "Src"       },
+            { SkXfermode::kDst_Mode,      "Dst"       },
+            { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
+            { SkXfermode::kDstOver_Mode,  "DstOver"   },
+            { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
+            { SkXfermode::kDstIn_Mode,    "DstIn"     },
+            { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
+            { SkXfermode::kDstOut_Mode,   "DstOut"    },
+            { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
+            { SkXfermode::kDstATop_Mode,  "DstATop"   },
+            { SkXfermode::kXor_Mode,      "Xor"       },
+
+            { SkXfermode::kPlus_Mode,         "Plus"          },
+            { SkXfermode::kMultiply_Mode,     "Multiply"      },
+            { SkXfermode::kScreen_Mode,       "Screen"        },
+            { SkXfermode::kOverlay_Mode,      "Overlay"       },
+            { SkXfermode::kDarken_Mode,       "Darken"        },
+            { SkXfermode::kLighten_Mode,      "Lighten"       },
+            { SkXfermode::kColorDodge_Mode,   "ColorDodge"    },
+            { SkXfermode::kColorBurn_Mode,    "ColorBurn"     },
+            { SkXfermode::kHardLight_Mode,    "HardLight"     },
+            { SkXfermode::kSoftLight_Mode,    "SoftLight"     },
+            { SkXfermode::kDifference_Mode,   "Difference"    },
+            { SkXfermode::kExclusion_Mode,    "Exclusion"     },
+        };
+
+        const SkScalar w = SkIntToScalar(W);
+        const SkScalar h = SkIntToScalar(H);
+        SkShader* s = SkShader::CreateBitmapShader(fBG,
+                                                   SkShader::kRepeat_TileMode,
+                                                   SkShader::kRepeat_TileMode);
+        SkMatrix m;
+        m.setScale(SkIntToScalar(6), SkIntToScalar(6));
+        s->setLocalMatrix(m);
+
+        SkPaint labelP;
+        labelP.setAntiAlias(true);
+        labelP.setLCDRenderText(true);
+        labelP.setTextAlign(SkPaint::kCenter_Align);
+        setNamedTypeface(&labelP, "Menlo Regular");
+//        labelP.setTextSize(SkIntToScalar(11));
+
+        const int W = 5;
+
+        SkScalar x0 = 0;
+        for (int twice = 0; twice < 2; twice++) {
+            SkScalar x = x0, y = 0;
+            for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
+                SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
+                SkAutoUnref aur(mode);
+                SkRect r;
+                r.set(x, y, x+w, y+h);
+
+                SkPaint p;
+                p.setStyle(SkPaint::kFill_Style);
+                p.setShader(s);
+                canvas->drawRect(r, p);
+
+                canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
+         //       canvas->save();
+                draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop);
+                canvas->restore();
+
+                r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+                p.setStyle(SkPaint::kStroke_Style);
+                p.setShader(NULL);
+                canvas->drawRect(r, p);
+
+#if 1
+                canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel),
+                                 x + w/2, y - labelP.getTextSize()/2, labelP);
+#endif
+                x += w + SkIntToScalar(10);
+                if ((i % W) == W - 1) {
+                    x = x0;
+                    y += h + SkIntToScalar(30);
+                }
+            }
+            x0 += SkIntToScalar(400);
+        }
+        s->unref();
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new XfermodesView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleXfermodesBlur.cpp b/samplecode/SampleXfermodesBlur.cpp
new file mode 100644
index 0000000..166e4e5
--- /dev/null
+++ b/samplecode/SampleXfermodesBlur.cpp
@@ -0,0 +1,182 @@
+#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"
+
+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);
+    }
+
+public:
+    const static int W = 64;
+    const static int H = 64;
+	XfermodesBlurView() {
+        const int W = 64;
+        const int H = 64;
+
+        fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
+        fBG.setPixels(gBG);
+        fBG.setIsOpaque(true);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "XfermodesBlur");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
+
+        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/samplecode_files.mk b/samplecode/samplecode_files.mk
new file mode 100644
index 0000000..4c660e5
--- /dev/null
+++ b/samplecode/samplecode_files.mk
@@ -0,0 +1,69 @@
+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 \
+    SampleText.cpp \
+    SampleTextBox.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..c124ad8
--- /dev/null
+++ b/samplecode/vertexdump.cpp
@@ -0,0 +1,88 @@
+#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/src/animator/SkDisplayApply.cpp b/src/animator/SkDisplayApply.cpp
index b9e65f7..e456f26 100644
--- a/src/animator/SkDisplayApply.cpp
+++ b/src/animator/SkDisplayApply.cpp
@@ -294,7 +294,7 @@
     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)
+    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();
@@ -356,7 +356,7 @@
     if (old < 0)
         goto append;
     else if (fContainsScope) {
-        if ((*parentList)[old] != this || restore == true) {
+        if ((*parentList)[old] != this || restore) {
 append:
             if (parentGroup)
                 parentGroup->markCopySize(old);
@@ -479,7 +479,7 @@
     } else {
         SkScriptValue scriptValue;
         bool success = target->getProperty(info->propertyIndex(), &scriptValue);
-        SkASSERT(success = true);
+        SkASSERT(success == true);
         last[0] = scriptValue.fOperand;
         scriptValue.fOperand = fActive->fSaveRestore[activeIndex][0];
         target->setProperty(info->propertyIndex(), scriptValue);
@@ -624,8 +624,8 @@
         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) {
+        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);
diff --git a/src/animator/SkDisplayXMLParser.cpp b/src/animator/SkDisplayXMLParser.cpp
index a94b848..db6c838 100644
--- a/src/animator/SkDisplayXMLParser.cpp
+++ b/src/animator/SkDisplayXMLParser.cpp
@@ -304,8 +304,8 @@
         }
         return info;
 next:
-        if (type == SkType_Drawable || type == SkType_Displayable && 
-            container->fDisplayable->isDrawable()) {
+        if (type == SkType_Drawable || (type == SkType_Displayable && 
+            container->fDisplayable->isDrawable())) {
 rectNext:
             if (fParents.count() > 1) {
                 Parent* parent = fParents.end() - 2;
diff --git a/src/animator/SkDump.cpp b/src/animator/SkDump.cpp
index eac956c..426a1e6 100644
--- a/src/animator/SkDump.cpp
+++ b/src/animator/SkDump.cpp
@@ -58,7 +58,7 @@
         maker.fEvents.dump(maker);
     if ((hasAttr |= (name.size() > 0)) == true)
         maker.dump(name.c_str());
-    if (displayList > 0 || displayList != 0 && hasAttr == false)
+    if (displayList > 0 || (displayList != 0 && hasAttr == false))
         maker.fDisplayList.dump(&maker);
     return true;
 }
diff --git a/src/animator/SkScript.cpp b/src/animator/SkScript.cpp
index f81147d..96b73cd 100644
--- a/src/animator/SkScript.cpp
+++ b/src/animator/SkScript.cpp
@@ -305,8 +305,8 @@
             } while (true);
             signed char topPrecedence = gPrecedence[compare];
             SkASSERT(topPrecedence != -1);
-            if (topPrecedence > precedence || topPrecedence == precedence && 
-                    gOpAttributes[op].fLeftType == kNoType) {
+            if (topPrecedence > precedence || (topPrecedence == precedence && 
+                    gOpAttributes[op].fLeftType == kNoType)) {
                 break;
             }
             if (processOp() == false)
@@ -1207,7 +1207,7 @@
             } break;
         case kElse:
 flipSuppress:
-            if (fSuppressStack.top().fElse == true)
+            if (fSuppressStack.top().fElse)
                 fSuppressStack.pop();
             fSuppressStack.top().fElse = true;
             fSuppressStack.top().fSuppress ^= true;
diff --git a/src/animator/SkScriptDecompile.cpp b/src/animator/SkScriptDecompile.cpp
index d582d33..98db1fb 100644
--- a/src/animator/SkScriptDecompile.cpp
+++ b/src/animator/SkScriptDecompile.cpp
@@ -114,7 +114,7 @@
 // check to see that there are no missing or duplicate entries
 void SkScriptEngine2::ValidateDecompileTable() {
     SkScriptEngine2::TypeOp op = SkScriptEngine2::kNop;
-    int index;
+    size_t index;
     for (index = 0; index < gOpNamesSize; index++) {
         SkASSERT(gOpNames[index].fOp == op);
         op = (SkScriptEngine2::TypeOp) (op + 1);
@@ -132,9 +132,9 @@
     SkASSERT(length > 0);
     const unsigned char* opCode = start;
     do {
-        SkASSERT(opCode - start < length);
+        SkASSERT((size_t)(opCode - start) < length);
         SkScriptEngine2::TypeOp op = (SkScriptEngine2::TypeOp) *opCode++;
-        SkASSERT(op < gOpNamesSize);
+        SkASSERT((size_t)op < gOpNamesSize);
         SkDebugf("%d: %s", opCode - start - 1, gOpNames[op].fName);
         switch (op) {
         case SkScriptEngine2::kCallback: {
@@ -184,7 +184,7 @@
             SkOperand2::OpType type;
             memcpy(&type, opCode, sizeof(type));
             opCode += sizeof(type);
-            int index = 0;
+            size_t index = 0;
             if (type == 0)
                 SkDebugf(" type: %s", gOperandNames[index].fName);
             else {
@@ -211,6 +211,8 @@
             goto done;
         case SkScriptEngine2::kNop:
                 SkASSERT(0);
+        default:
+            break;
     }
     SkDebugf("\n");
     } while (true);
diff --git a/src/animator/SkScriptRuntime.cpp b/src/animator/SkScriptRuntime.cpp
index 6d8c208..1284198 100644
--- a/src/animator/SkScriptRuntime.cpp
+++ b/src/animator/SkScriptRuntime.cpp
@@ -304,6 +304,8 @@
 			goto done;
 		case SkScriptEngine2::kNop:
 				SkASSERT(0);
+    default:
+        break;
 	}
 	} while (true);
 done:
diff --git a/src/animator/SkScriptTokenizer.cpp b/src/animator/SkScriptTokenizer.cpp
index efd1872..edcc2af 100644
--- a/src/animator/SkScriptTokenizer.cpp
+++ b/src/animator/SkScriptTokenizer.cpp
@@ -8,34 +8,34 @@
 #include "SkOpArray.h"
 
 const SkScriptEngine2::OperatorAttributes SkScriptEngine2::gOpAttributes[] = {
-{ SkOperand2::kNoType },
+{ SkOperand2::kNoType, SkOperand2::kNoType, kNoBias, kResultIsNotBoolean },
 { SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), 
-    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsString },    // kAdd
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kBitAnd
-{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kBitNot
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kBitOr
+    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 }, // kDivide
+    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 },     // kFlipOps
+{ 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 }, // kLogicalAnd    (really, ToBool)
-{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kLogicalNot
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kLogicalOr
-{ SkOperand2::kNoType, SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kMinus
+{ 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 }, // kModulo
+    SkOperand2::OpType(SkOperand2::kS32 |SkOperand2:: kScalar), kNoBias, kResultIsNotBoolean }, // kModulo
 { SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), 
-    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kMultiply
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kShiftLeft
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kShiftRight
+    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 }, // kSubtract
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias } // kXor
+    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kSubtract
+{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean } // kXor
 };
 
 #define kBracketPrecedence 16
@@ -308,8 +308,8 @@
         } while (true);
         signed char topPrecedence = gPrecedence[compare];
         SkASSERT(topPrecedence != -1);
-        if (topPrecedence > precedence || topPrecedence == precedence && 
-            gOpAttributes[op].fLeftType == SkOperand2::kNoType) {
+        if (topPrecedence > precedence || (topPrecedence == precedence && 
+            gOpAttributes[op].fLeftType == SkOperand2::kNoType)) {
             break;
         }
         processOp();
@@ -1051,7 +1051,8 @@
     fOpStack.pop(&op);
     op = (Op) (op & ~kArtificialOp);
     const OperatorAttributes* attributes = &gOpAttributes[op];
-    SkScriptValue2 value1 = { 0 };
+    SkScriptValue2 value1;
+    memset(&value1, 0, sizeof(SkScriptValue2));
     SkScriptValue2 value2;
     fValueStack.pop(&value2);
     value2.fIsWritten = SkScriptValue2::kUnwritten;
@@ -1230,7 +1231,7 @@
 
 SkScalar SkScriptEngine2::IntToScalar(int32_t s32) {
     SkScalar scalar;
-    if (s32 == SK_NaN32)
+    if (s32 == (int32_t) SK_NaN32)
         scalar = SK_ScalarNaN;
     else if (SkAbs32(s32) == SK_MaxS32)
         scalar = SkSign32(s32) * SK_ScalarMax;
@@ -1261,21 +1262,21 @@
 
 #ifdef SK_DEBUG
 
-#define testInt(expression) { #expression, SkOperand2::kS32, expression }
+#define testInt(expression) { #expression, SkOperand2::kS32, expression, 0, NULL }
 #ifdef SK_SCALAR_IS_FLOAT
-#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (float) expression }
-#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, fmodf(exp1, exp2) }
+#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (float) expression, NULL }
+#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, fmodf(exp1, exp2), NULL }
 #else
 #ifdef SK_CAN_USE_FLOAT
-#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (int) ((expression) * 65536.0f) }
-#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, (int) (fmod(exp1, exp2)  * 65536.0f) }
+#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (int) ((expression) * 65536.0f), NULL }
+#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, (int) (fmod(exp1, exp2)  * 65536.0f), NULL }
 #endif
 #endif
-#define testTrue(expression) { #expression, SkOperand2::kS32, 1 }
-#define testFalse(expression) { #expression, SkOperand2::kS32, 0 }
+#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),
+    testInt(1||(0&&3)),
 #ifdef SK_CAN_USE_FLOAT
     testScalar(- -5.5- -1.5),
     testScalar(1.0+5), 
@@ -1307,12 +1308,12 @@
     {    "'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 },
-    {    "123|\"456\"", SkOperand2::kS32, 123|456 },
-    {    "'123'|456", SkOperand2::kS32, 123|456 },
-    {    "'2'<11", SkOperand2::kS32, 1 },
-    {    "2<'11'", SkOperand2::kS32, 1 },
-    {    "'2'<'11'", SkOperand2::kS32, 0 },
+    {    "'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),
@@ -1461,15 +1462,15 @@
     // 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),
+    testInt((1&&0)||3),
+    testInt((1&&0)||0),
+    testInt(1||(0&&3)),
+    testInt(0||(0&&3)),
+    testInt(0||(1&&3)),
     testInt(0&&1?2:3)
 #ifdef SK_CAN_USE_FLOAT
-    , {    "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2 }
+    , {    "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2, NULL }
 #endif
 };
 
diff --git a/src/core/SkAdvancedTypefaceMetrics.cpp b/src/core/SkAdvancedTypefaceMetrics.cpp
index 7e8a030..05d5c28 100644
--- a/src/core/SkAdvancedTypefaceMetrics.cpp
+++ b/src/core/SkAdvancedTypefaceMetrics.cpp
@@ -22,6 +22,10 @@
 #include FT_FREETYPE_H
 #endif
 
+#ifdef SK_BUILD_FOR_MAC
+#include <Carbon/Carbon.h>
+#endif
+
 namespace skia_advanced_typeface_metrics_utils {
 
 template <typename Data>
@@ -142,6 +146,11 @@
         FT_Face face,
         int num_glyphs,
         bool (*getAdvance)(FT_Face face, int gId, int16_t* data));
+#elif defined(SK_BUILD_FOR_MAC)
+template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
+        CTFontRef ctFont,
+        int num_glyphs,
+        bool (*getAdvance)(CTFontRef ctFont, int gId, int16_t* data));
 #endif
 template void resetRange(
         SkAdvancedTypefaceMetrics::WidthRange* range,
diff --git a/src/core/SkAlphaRuns.cpp b/src/core/SkAlphaRuns.cpp
index 4125b58..a5fc3c9 100644
--- a/src/core/SkAlphaRuns.cpp
+++ b/src/core/SkAlphaRuns.cpp
@@ -16,10 +16,14 @@
 */
 
 #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;
@@ -75,13 +79,17 @@
     }
 }
 
-void SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha,
-                      U8CPU maxValue) {
+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);
 
-    int16_t*    runs = fRuns;
-    uint8_t*     alpha = fAlpha;
+    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);
@@ -97,6 +105,7 @@
         runs += x + 1;
         alpha += x + 1;
         x = 0;
+        lastAlpha += x; // we don't want the +1
         SkDEBUGCODE(this->validate();)
     }
 
@@ -114,13 +123,18 @@
             middleCount -= n;
         } while (middleCount > 0);
         SkDEBUGCODE(this->validate();)
+        lastAlpha = alpha;
     }
 
     if (stopAlpha) {
         SkAlphaRuns::Break(runs, alpha, x, 1);
-        alpha[x] = SkToU8(alpha[x] + stopAlpha);
+        alpha += x;
+        alpha[0] = SkToU8(alpha[0] + stopAlpha);
         SkDEBUGCODE(this->validate();)
+        lastAlpha = alpha;
     }
+
+    return lastAlpha - fAlpha;  // new offsetX
 }
 
 #ifdef SK_DEBUG
diff --git a/src/core/SkAntiRun.h b/src/core/SkAntiRun.h
index 89e5481..669e5d2 100644
--- a/src/core/SkAntiRun.h
+++ b/src/core/SkAntiRun.h
@@ -31,7 +31,15 @@
     }
 
     void    reset(int width);
-    void    add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue);
+    
+    /**
+     *  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;)
 
diff --git a/src/core/SkBitmapProcState_matrix.h b/src/core/SkBitmapProcState_matrix.h
index 9ae8b17..553bd89 100644
--- a/src/core/SkBitmapProcState_matrix.h
+++ b/src/core/SkBitmapProcState_matrix.h
@@ -1,3 +1,19 @@
+/*
+    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.
+ */
+
 
 #define SCALE_NOFILTER_NAME     MAKENAME(_nofilter_scale)
 #define SCALE_FILTER_NAME       MAKENAME(_filter_scale)
diff --git a/src/core/SkBitmapProcState_shaderproc.h b/src/core/SkBitmapProcState_shaderproc.h
index 15831b6..9a9e8c4 100644
--- a/src/core/SkBitmapProcState_shaderproc.h
+++ b/src/core/SkBitmapProcState_shaderproc.h
@@ -1,3 +1,20 @@
+/*
+    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.
+ */
+
+
 #define SCALE_FILTER_NAME       MAKENAME(_filter_DX_shaderproc)
 
 static void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y,
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index 5ffa5b1..4ad4d41 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -188,6 +188,11 @@
            ((a.fPath == NULL && b.fPath == NULL) || *a.fPath == *b.fPath);
 }
 
+bool operator!=(const SkClipStack::B2FIter::Clip& a,
+               const SkClipStack::B2FIter::Clip& b) {
+    return !(a == b);
+}
+
 SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) {
     this->reset(stack);
 }
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 4a76936..aee5a5e 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -25,13 +25,13 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkDevice::SkDevice(SkCanvas* canvas) : fCanvas(canvas), fMetaData(NULL), fMatrixClipObserver(NULL) {
+SkDevice::SkDevice(SkCanvas* canvas) : fCanvas(canvas), fMetaData(NULL) {
     fOrigin.setZero();
     fCachedDeviceFactory = NULL;
 }
 
 SkDevice::SkDevice(SkCanvas* canvas, const SkBitmap& bitmap, bool isForLayer)
-        : fCanvas(canvas), fBitmap(bitmap), fMetaData(NULL), fMatrixClipObserver(NULL) {
+        : fCanvas(canvas), fBitmap(bitmap), fMetaData(NULL) {
     fOrigin.setZero();
     // auto-allocate if we're for offscreen drawing
     if (isForLayer) {
@@ -48,7 +48,6 @@
 SkDevice::~SkDevice() {
     delete fMetaData;
     SkSafeUnref(fCachedDeviceFactory);
-    SkSafeUnref(fMatrixClipObserver);
 }
 
 SkDeviceFactory* SkDevice::onNewDeviceFactory() {
@@ -108,13 +107,6 @@
 
 void SkDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& region,
                              const SkClipStack& clipStack) {
-    if (fMatrixClipObserver) {
-        fMatrixClipObserver->matrixClipChanged(matrix, region, clipStack);
-    }
-}
-
-void SkDevice::setMatrixClipObserver(SkMatrixClipObserver* observer) {
-    SkRefCnt_SafeAssign(fMatrixClipObserver, observer);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 983c8d2..06bca1e 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -119,6 +119,21 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+#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;
@@ -202,9 +217,27 @@
 }
 
 bool SkMatrix::preScale(SkScalar sx, SkScalar sy) {
+#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) {
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 14c0397..a607424 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -364,6 +364,16 @@
     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;
@@ -1293,17 +1303,36 @@
     }
 
     rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
-    rec->fFlags = SkToU8(flags);
-    rec->setHinting(computeHinting(paint));
+
+    if (SkMask::kLCD16_Format == rec->fMaskFormat) {
+        SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder();
+        SkFontHost::LCDOrientation orient = SkFontHost::GetSubpixelOrientation();
+        if (SkFontHost::kNONE_LCDOrder == order) {
+            // 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()) {
-        rec->fFlags |= SkScalerContext::kEmbeddedBitmapText_Flag;
+        flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
     }
     if (paint.isSubpixelText()) {
-        rec->fFlags |= SkScalerContext::kSubpixelPositioning_Flag;
+        flags |= SkScalerContext::kSubpixelPositioning_Flag;
     }
     if (paint.isAutohinted()) {
-        rec->fFlags |= SkScalerContext::kAutohinting_Flag;
+        flags |= SkScalerContext::kAutohinting_Flag;
     }
+    rec->fFlags = SkToU16(flags);
+
+    // setHinting modifies fFlags, so do this last
+    rec->setHinting(computeHinting(paint));
 
     /*  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,
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 68eb35d..ca57237 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -254,6 +254,12 @@
 //////////////////////////////////////////////////////////////////////////////
 //  Construction methods
 
+#define DIRTY_AFTER_EDIT                \
+    do {                                \
+        fBoundsIsDirty = true;          \
+        fConvexity = kUnknown_Convexity;\
+    } while (0)
+
 void SkPath::incReserve(U16CPU inc) {
     SkDEBUGCODE(this->validate();)
 
@@ -278,7 +284,7 @@
     pt->set(x, y);
 
     GEN_ID_INC;
-    fBoundsIsDirty = true;
+    DIRTY_AFTER_EDIT;
 }
 
 void SkPath::rMoveTo(SkScalar x, SkScalar y) {
@@ -298,7 +304,7 @@
     *fVerbs.append() = kLine_Verb;
 
     GEN_ID_INC;
-    fBoundsIsDirty = true;
+    DIRTY_AFTER_EDIT;
 }
 
 void SkPath::rLineTo(SkScalar x, SkScalar y) {
@@ -321,7 +327,7 @@
     *fVerbs.append() = kQuad_Verb;
 
     GEN_ID_INC;
-    fBoundsIsDirty = true;
+    DIRTY_AFTER_EDIT;
 }
 
 void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
@@ -345,7 +351,7 @@
     *fVerbs.append() = kCubic_Verb;
 
     GEN_ID_INC;
-    fBoundsIsDirty = true;
+    DIRTY_AFTER_EDIT;
 }
 
 void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
@@ -1293,7 +1299,7 @@
     buffer.read(fVerbs.begin(), fVerbs.count());
 
     GEN_ID_INC;
-    fBoundsIsDirty = true;
+    DIRTY_AFTER_EDIT;
 
     SkDEBUGCODE(this->validate();)
 }
@@ -1410,7 +1416,7 @@
 
 // only valid for a single contour
 struct Convexicator {
-    Convexicator() : fPtCount(0), fConvexity(SkPath::kUnknown_Convexity) {
+    Convexicator() : fPtCount(0), fConvexity(SkPath::kConvex_Convexity) {
         fSign = 0;
         // warnings
         fCurrPt.set(0, 0);
@@ -1472,9 +1478,7 @@
         if (0 == fSign) {
             fSign = sign;
         } else if (sign) {
-            if (fSign == sign) {
-                fConvexity = SkPath::kConvex_Convexity;
-            } else {
+            if (fSign != sign) {
                 fConvexity = SkPath::kConcave_Convexity;
             }
         }
diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
index 5c5ea0b..105df3c 100644
--- a/src/core/SkPictureFlat.cpp
+++ b/src/core/SkPictureFlat.cpp
@@ -28,9 +28,9 @@
 }
 
 SkFlatMatrix* SkFlatMatrix::Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index) {
-    int32_t size = sizeof(SkMatrix);
+    size_t size = matrix.flatten(NULL);
     SkFlatMatrix* result = (SkFlatMatrix*) INHERITED::Alloc(heap, size, index);
-    memcpy(&result->fMatrixData, &matrix, sizeof(SkMatrix));
+    matrix.flatten(&result->fMatrixData);
     return result;
 }
 
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index dae7b8a..697b399 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -147,7 +147,7 @@
     static SkFlatMatrix* Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index);
 
     void unflatten(SkMatrix* result) const {
-        memcpy(result, fMatrixData, sizeof(SkMatrix));
+        result->unflatten(fMatrixData);
     }
 
 #ifdef SK_DEBUG_DUMP
diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
index 5457b59..398f786 100644
--- a/src/core/SkScan_AntiPath.cpp
+++ b/src/core/SkScan_AntiPath.cpp
@@ -53,7 +53,7 @@
     int         fWidth, fLeft, fSuperLeft;
 
     SkDEBUGCODE(int fCurrX;)
-    SkDEBUGCODE(int fCurrY;)
+    int fCurrY;
 };
 
 BaseSuperBlitter::BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
@@ -69,7 +69,8 @@
     fSuperLeft = left << SHIFT;
     fWidth = right - left;
     fCurrIY = -1;
-    SkDEBUGCODE(fCurrX = -1; fCurrY = -1;)
+    fCurrY = -1;
+    SkDEBUGCODE(fCurrX = -1;)
 }
 
 class SuperBlitter : public BaseSuperBlitter {
@@ -89,6 +90,7 @@
 
 private:
     SkAlphaRuns fRuns;
+    int         fOffsetX;
 };
 
 SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
@@ -100,6 +102,8 @@
     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() {
@@ -108,6 +112,7 @@
         //  SkDEBUGCODE(fRuns.dump();)
             fRealBlitter->blitAntiH(fLeft, fCurrIY, fRuns.fAlpha, fRuns.fRuns);
             fRuns.reset(fWidth);
+            fOffsetX = 0;
         }
         fCurrIY = -1;
         SkDEBUGCODE(fCurrX = -1;)
@@ -134,11 +139,14 @@
     }
 
 #ifdef SK_DEBUG
-    SkASSERT(y >= fCurrY);
     SkASSERT(y != fCurrY || x >= fCurrX);
-    fCurrY = y;
 #endif
-
+    SkASSERT(y >= fCurrY);
+    if (fCurrY != y) {
+        fOffsetX = 0;
+        fCurrY = y;
+    }
+    
     if (iy != fCurrIY) {  // new scanline
         this->flush();
         fCurrIY = iy;
@@ -148,36 +156,29 @@
     // hit 256 as a summed max, but 255.
 //  int maxValue = (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT);
 
-#if 0
-    SkAntiRun<SHIFT>    arun;
-    arun.set(x, x + width);
-    fRuns.add(x >> SHIFT, arun.getStartAlpha(), arun.getMiddleCount(),
-              arun.getStopAlpha(), maxValue);
-#else
-    {
-        int start = x;
-        int stop = x + width;
+    int start = x;
+    int stop = x + width;
 
-        SkASSERT(start >= 0 && stop > start);
-        int fb = start & SUPER_Mask;
-        int fe = stop & SUPER_Mask;
-        int n = (stop >> SHIFT) - (start >> SHIFT) - 1;
+    SkASSERT(start >= 0 && stop > start);
+    int fb = start & SUPER_Mask;
+    int fe = stop & SUPER_Mask;
+    int n = (stop >> SHIFT) - (start >> SHIFT) - 1;
 
-        if (n < 0) {
-            fb = fe - fb;
-            n = 0;
-            fe = 0;
+    if (n < 0) {
+        fb = fe - fb;
+        n = 0;
+        fe = 0;
+    } else {
+        if (fb == 0) {
+            n += 1;
         } else {
-            if (fb == 0) {
-                n += 1;
-            } else {
-                fb = (1 << SHIFT) - fb;
-            }
+            fb = (1 << SHIFT) - fb;
         }
-        fRuns.add(x >> SHIFT, coverage_to_alpha(fb), n, coverage_to_alpha(fe),
-                  (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT));
     }
-#endif
+
+    fOffsetX = fRuns.add(x >> SHIFT, coverage_to_alpha(fb), n, coverage_to_alpha(fe),
+                         (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT),
+                         fOffsetX);
 
 #ifdef SK_DEBUG
     fRuns.assertValid(y & MASK, (1 << (8 - SHIFT)));
diff --git a/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp
index b84c576..52f2a32 100644
--- a/src/core/SkScan_Antihair.cpp
+++ b/src/core/SkScan_Antihair.cpp
@@ -653,6 +653,8 @@
     }
 }
 
+#endif // SK_SCALAR_IS_FLOAT
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #define SkAlphaMulRound(a, b)   SkMulDiv255Round(a, b)
@@ -811,7 +813,3 @@
         innerstrokedot8(L, T, R, B, blitter);
     }
 }
-
-#endif
-
-
diff --git a/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
index 141f751..9a60b8e 100644
--- a/src/core/SkWriter32.cpp
+++ b/src/core/SkWriter32.cpp
@@ -185,3 +185,72 @@
     return true;
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkReader32.h"
+
+const char* SkReader32::readString(size_t* outLen) {
+    // we need to read at least 1-4 bytes
+    SkASSERT(this->isAvailable(4));
+    const uint8_t* base = (const uint8_t*)this->peek();
+    const uint8_t* ptr = base;
+
+    size_t len = *ptr++;
+    if (0xFF == len) {
+        len = (ptr[0] << 8) | ptr[1];
+        ptr += 2;
+        SkASSERT(len < 0xFFFF);
+    }
+    
+    // skip what we've read, and 0..3 pad bytes
+    // add 1 for the terminating 0 that writeString() included
+    size_t alignedSize = SkAlign4(len + (ptr - base) + 1);
+    this->skip(alignedSize);
+
+    if (outLen) {
+        *outLen = len;
+    }
+    return (const char*)ptr;
+}
+
+void SkWriter32::writeString(const char str[], size_t len) {
+    if ((long)len < 0) {
+        SkASSERT(str);
+        len = strlen(str);
+    }
+    size_t lenBytes = 1;
+    if (len >= 0xFF) {
+        lenBytes = 3;
+        SkASSERT(len < 0xFFFF);
+    }
+    // add 1 since we also write a terminating 0
+    size_t alignedLen = SkAlign4(lenBytes + len + 1);
+    uint8_t* ptr = (uint8_t*)this->reserve(alignedLen);
+    if (1 == lenBytes) {
+        *ptr++ = SkToU8(len);
+    } else {
+        *ptr++ = 0xFF;
+        *ptr++ = SkToU8(len >> 8);
+        *ptr++ = len & 0xFF;
+    }
+    memcpy(ptr, str, len);
+    ptr[len] = 0;
+    // we may have left 0,1,2,3 bytes uninitialized, since we reserved align4
+    // number of bytes. That's ok, since the reader will know to skip those
+}
+
+size_t SkWriter32::WriteStringSize(const char* str, size_t len) {
+    if ((long)len < 0) {
+        SkASSERT(str);
+        len = strlen(str);
+    }
+    size_t lenBytes = 1;
+    if (len >= 0xFF) {
+        lenBytes = 3;
+        SkASSERT(len < 0xFFFF);
+    }
+    // add 1 since we also write a terminating 0
+    return SkAlign4(lenBytes + len + 1);
+}
+
+
diff --git a/src/effects/Sk2DPathEffect.cpp b/src/effects/Sk2DPathEffect.cpp
index 603deb7..1dfc24d 100644
--- a/src/effects/Sk2DPathEffect.cpp
+++ b/src/effects/Sk2DPathEffect.cpp
@@ -82,12 +82,19 @@
 
 void Sk2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer)
 {
-    buffer.writeMul4(&fMatrix, sizeof(fMatrix));
+    char storage[SkMatrix::kMaxFlattenSize];
+    uint32_t size = fMatrix.flatten(storage);
+    buffer.write32(size);
+    buffer.write(storage, size);
 }
 
 Sk2DPathEffect::Sk2DPathEffect(SkFlattenableReadBuffer& buffer)
 {
-    buffer.read(&fMatrix, sizeof(fMatrix));
+    char storage[SkMatrix::kMaxFlattenSize];
+    uint32_t size = buffer.readS32();
+    SkASSERT(size <= sizeof(storage));
+    buffer.read(storage, size);
+    fMatrix.unflatten(storage);
     fMatrix.invert(&fInverse);
 }
 
diff --git a/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp
index 3b21d49..5d0fdcf 100644
--- a/src/effects/SkLayerDrawLooper.cpp
+++ b/src/effects/SkLayerDrawLooper.cpp
@@ -189,6 +189,7 @@
     
     Rec* rec = fRecs;
     for (int i = 0; i < fCount; i++) {
+        buffer.writeInt(rec->fInfo.fFlagsMask);
         buffer.writeInt(rec->fInfo.fPaintBits);
         buffer.writeInt(rec->fInfo.fColorMode);
         buffer.writeScalar(rec->fInfo.fOffset.fX);
@@ -208,6 +209,7 @@
 
     for (int i = 0; i < count; i++) {
         LayerInfo info;
+        info.fFlagsMask = buffer.readInt();
         info.fPaintBits = buffer.readInt();
         info.fColorMode = (SkXfermode::Mode)buffer.readInt();
         info.fOffset.fX = buffer.readScalar();
diff --git a/src/effects/SkTransparentShader.cpp b/src/effects/SkTransparentShader.cpp
index c6caba3..6e68b5e 100644
--- a/src/effects/SkTransparentShader.cpp
+++ b/src/effects/SkTransparentShader.cpp
@@ -53,7 +53,10 @@
     switch (fDevice->getConfig()) {
         case SkBitmap::kARGB_8888_Config:
             if (scale == 256) {
-                memcpy(span, fDevice->getAddr32(x, y), count * sizeof(SkPMColor));
+                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) {
@@ -125,6 +128,9 @@
 void SkTransparentShader::shadeSpan16(int x, int y, uint16_t span[], int count) {
     SkASSERT(fDevice->getConfig() == SkBitmap::kRGB_565_Config);
 
-    memcpy(span, fDevice->getAddr16(x, y), count << 1);
+    uint16_t* src = fDevice->getAddr16(x, y);
+    if (src != span) {
+        memcpy(span, src, count << 1);
+    }
 }
 
diff --git a/src/gpu/GrPrintf_skia.cpp b/src/gpu/GrPrintf_skia.cpp
index fa8b6a7..6da8822 100644
--- a/src/gpu/GrPrintf_skia.cpp
+++ b/src/gpu/GrPrintf_skia.cpp
@@ -23,7 +23,7 @@
 #include "SkTypes.h"
 
 void GrPrintf(const char format[], ...) {
-    const size_t MAX_BUFFER_SIZE = 512;
+    const size_t MAX_BUFFER_SIZE = 2048;
 
     char buffer[MAX_BUFFER_SIZE + 1];
     va_list args;
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 5b4f529..7166a07 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -40,6 +40,13 @@
     #define CHECK_SHOULD_DRAW(draw) this->prepareRenderTarget(draw)
 #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
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkGpuDevice::SkAutoCachedTexture::
@@ -130,9 +137,9 @@
             SkASSERT(NULL != fTexture->asRenderTarget());
         }
 #else
-        const GrGpu::TextureDesc desc = {
-            GrGpu::kRenderTarget_TextureFlag,
-            GrGpu::kNone_AALevel,
+        const GrTextureDesc desc = {
+            kRenderTarget_GrTextureFlagBit,
+            kNone_GrAALevel,
             this->width(),
             this->height(),
             SkGr::Bitmap2PixelConfig(bm)
@@ -316,7 +323,7 @@
 
 bool SkGpuDevice::bindDeviceAsTexture(GrPaint* paint) {
     if (NULL != fTexture) {
-        paint->setTexture(fTexture);
+        paint->setTexture(kBitmapTextureIdx, fTexture);
         return true;
     }
     return false;
@@ -342,7 +349,8 @@
 
 bool SkGpuDevice::skPaint2GrPaintNoShader(const SkPaint& skPaint,
                                           bool justAlpha,
-                                          GrPaint* grPaint) {
+                                          GrPaint* grPaint,
+                                          bool constantColor) {
 
     grPaint->fDither    = skPaint.isDither();
     grPaint->fAntiAlias = skPaint.isAntiAlias();
@@ -365,33 +373,44 @@
     if (justAlpha) {
         uint8_t alpha = skPaint.getAlpha();
         grPaint->fColor = GrColorPackRGBA(alpha, alpha, alpha, alpha);
+        // justAlpha is currently set to true only if there is a texture,
+        // so constantColor should not also be true.
+        GrAssert(!constantColor);
     } else {
         grPaint->fColor = SkGr::SkColor2GrColor(skPaint.getColor());
-        grPaint->setTexture(NULL);
+        grPaint->setTexture(kShaderTextureIdx, NULL);
     }
     SkColorFilter* colorFilter = skPaint.getColorFilter();
     SkColor color;
     SkXfermode::Mode filterMode;
     if (colorFilter != NULL && colorFilter->asColorMode(&color, &filterMode)) {
-        grPaint->fColorFilterColor = SkGr::SkColor2GrColor(color);
-        grPaint->fColorFilterXfermode = filterMode;
-    } else {
-        grPaint->resetColorFilter();
+        if (!constantColor) {
+            grPaint->fColorFilterColor = SkGr::SkColor2GrColor(color);
+            grPaint->fColorFilterXfermode = filterMode;
+            return true;
+        }
+        SkColor filtered = colorFilter->filterColor(skPaint.getColor());
+        grPaint->fColor = SkGr::SkColor2GrColor(filtered);
     }
+    grPaint->resetColorFilter();
     return true;
 }
 
 bool SkGpuDevice::skPaint2GrPaintShader(const SkPaint& skPaint,
                                         SkAutoCachedTexture* act,
                                         const SkMatrix& ctm,
-                                        GrPaint* grPaint) {
+                                        GrPaint* grPaint,
+                                        bool constantColor) {
 
     SkASSERT(NULL != act);
 
     SkShader* shader = skPaint.getShader();
     if (NULL == shader) {
-        return this->skPaint2GrPaintNoShader(skPaint, false, grPaint);
-    } else if (!this->skPaint2GrPaintNoShader(skPaint, true, grPaint)) {
+        return this->skPaint2GrPaintNoShader(skPaint,
+                                             false,
+                                             grPaint,
+                                             constantColor);
+    } else if (!this->skPaint2GrPaintNoShader(skPaint, true, grPaint, false)) {
         return false;
     }
 
@@ -411,26 +430,27 @@
         SkDebugf("shader->asABitmap() == kNone_BitmapType\n");
         return false;
     }
-    grPaint->fSampler.setSampleMode(sampleMode);
+    GrSamplerState* sampler = grPaint->getTextureSampler(kShaderTextureIdx);
+    sampler->setSampleMode(sampleMode);
     if (skPaint.isFilterBitmap()) {
-        grPaint->fSampler.setFilter(GrSamplerState::kBilinear_Filter);
+        sampler->setFilter(GrSamplerState::kBilinear_Filter);
     } else {
-        grPaint->fSampler.setFilter(GrSamplerState::kNearest_Filter);
+        sampler->setFilter(GrSamplerState::kNearest_Filter);
     }
-    grPaint->fSampler.setWrapX(sk_tile_mode_to_grwrap(tileModes[0]));
-    grPaint->fSampler.setWrapY(sk_tile_mode_to_grwrap(tileModes[1]));
+    sampler->setWrapX(sk_tile_mode_to_grwrap(tileModes[0]));
+    sampler->setWrapY(sk_tile_mode_to_grwrap(tileModes[1]));
     if (GrSamplerState::kRadial2_SampleMode == sampleMode) {
-        grPaint->fSampler.setRadial2Params(twoPointParams[0],
-                                           twoPointParams[1],
-                                           twoPointParams[2] < 0);
+        sampler->setRadial2Params(twoPointParams[0],
+                                  twoPointParams[1],
+                                  twoPointParams[2] < 0);
     }
 
-    GrTexture* texture = act->set(this, bitmap, grPaint->fSampler);
+    GrTexture* texture = act->set(this, bitmap, *sampler);
     if (NULL == texture) {
         SkDebugf("Couldn't convert bitmap to texture.\n");
         return false;
     }
-    grPaint->setTexture(texture);
+    grPaint->setTexture(kShaderTextureIdx, texture);
 
     // since our texture coords will be in local space, we wack the texture
     // matrix to map them back into 0...1 before we load it
@@ -449,7 +469,7 @@
         GrScalar s = GrFixedToScalar(GR_Fixed1 / bitmap.width());
         matrix.postScale(s, s);
     }
-    grPaint->fSampler.setMatrix(matrix);
+    sampler->setMatrix(matrix);
 
     return true;
 }
@@ -595,7 +615,11 @@
 
     GrPaint grPaint;
     SkAutoCachedTexture act;
-    if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+    if (!this->skPaint2GrPaintShader(paint,
+                                     &act,
+                                     *draw.fMatrix,
+                                     &grPaint,
+                                     true)) {
         return;
     }
 
@@ -626,7 +650,11 @@
 
     GrPaint grPaint;
     SkAutoCachedTexture act;
-    if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+    if (!this->skPaint2GrPaintShader(paint,
+                                     &act,
+                                     *draw.fMatrix,
+                                     &grPaint,
+                                     true)) {
         return;
     }
 
@@ -665,6 +693,10 @@
     if (paint.getMaskFilter()) {
         usePath = true;
     }
+    // until we aa rotated rects...
+    if (!usePath && paint.isAntiAlias() && !draw.fMatrix->rectStaysRect()) {
+        usePath = true;
+    }
 
     if (usePath) {
         SkPath path;
@@ -675,7 +707,11 @@
 
     GrPaint grPaint;
     SkAutoCachedTexture act;
-    if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix,  &grPaint)) {
+    if (!this->skPaint2GrPaintShader(paint,
+                                     &act,
+                                     *draw.fMatrix,
+                                     &grPaint,
+                                     true)) {
         return;
     }
     fContext->drawRect(grPaint, rect, doStroke ? width : -1);
@@ -713,6 +749,9 @@
     // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
     // the current clip (and identity matrix) and grpaint settings
 
+    // used to compute inverse view, if necessary
+    GrMatrix ivm = context->getMatrix();
+
     GrAutoMatrix avm(context, GrMatrix::I());
 
     const GrTextureDesc desc = {
@@ -729,18 +768,29 @@
         return false;
     }
 
-    grp->setTexture(texture);
+    if (grp->hasTextureOrMask() && ivm.invert(&ivm)) {
+        grp->preConcatActiveSamplerMatrices(ivm);
+    }
+
+    static const int MASK_IDX = GrPaint::kMaxMasks - 1;
+    // we assume the last mask index is available for use
+    GrAssert(NULL == grp->getMask(MASK_IDX));
+    grp->setMask(MASK_IDX, texture);
     texture->unref();
-    grp->fSampler.setClampNoFilter();
+    grp->getMaskSampler(MASK_IDX)->setClampNoFilter();
 
     GrRect d;
     d.setLTRB(GrIntToScalar(dstM.fBounds.fLeft),
               GrIntToScalar(dstM.fBounds.fTop),
               GrIntToScalar(dstM.fBounds.fRight),
               GrIntToScalar(dstM.fBounds.fBottom));
-    GrRect s;
-    s.setLTRB(0, 0, GR_Scalar1, GR_Scalar1);
-    context->drawRectToRect(*grp, d, s);
+
+    GrMatrix m;
+    m.setTranslate(-dstM.fBounds.fLeft, -dstM.fBounds.fTop);
+    m.postIDiv(dstM.fBounds.width(), dstM.fBounds.height());
+    grp->getMaskSampler(MASK_IDX)->setMatrix(m);
+    
+    context->drawRect(*grp, d);
     return true;
 }
 
@@ -751,7 +801,11 @@
 
     GrPaint grPaint;
     SkAutoCachedTexture act;
-    if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+    if (!this->skPaint2GrPaintShader(paint,
+                                     &act,
+                                     *draw.fMatrix,
+                                     &grPaint,
+                                     true)) {
         return;
     }
 
@@ -832,8 +886,7 @@
         }
     }
 
-    SkGrPathIter iter(*pathPtr);
-    fContext->drawPath(grPaint, &iter, fill);
+    fContext->drawPath(grPaint, *pathPtr, fill);
 }
 
 void SkGpuDevice::drawBitmap(const SkDraw& draw,
@@ -851,15 +904,15 @@
     }
 
     GrPaint grPaint;
-    if (!this->skPaint2GrPaintNoShader(paint, true, &grPaint)) {
+    if (!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
         return;
     }
+    GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx);
     if (paint.isFilterBitmap()) {
-        grPaint.fSampler.setFilter(GrSamplerState::kBilinear_Filter);
+        sampler->setFilter(GrSamplerState::kBilinear_Filter);
     } else {
-        grPaint.fSampler.setFilter(GrSamplerState::kNearest_Filter);
+        sampler->setFilter(GrSamplerState::kNearest_Filter);
     }
-    
 
     const int maxTextureDim = fContext->getMaxTextureDimension();
     if (bitmap.getTexture() || (bitmap.width() <= maxTextureDim &&
@@ -941,27 +994,54 @@
         return;
     }
 
-    grPaint->fSampler.setWrapX(GrSamplerState::kClamp_WrapMode);
-    grPaint->fSampler.setWrapY(GrSamplerState::kClamp_WrapMode);
-    grPaint->fSampler.setSampleMode(GrSamplerState::kNormal_SampleMode);
-    grPaint->fSampler.setMatrix(GrMatrix::I());
+    GrSamplerState* sampler = grPaint->getTextureSampler(kBitmapTextureIdx);
+
+    sampler->setWrapX(GrSamplerState::kClamp_WrapMode);
+    sampler->setWrapY(GrSamplerState::kClamp_WrapMode);
+    sampler->setSampleMode(GrSamplerState::kNormal_SampleMode);
+    sampler->setMatrix(GrMatrix::I());
 
     GrTexture* texture;
-    SkAutoCachedTexture act(this, bitmap, grPaint->fSampler, &texture);
+    SkAutoCachedTexture act(this, bitmap, *sampler, &texture);
     if (NULL == texture) {
         return;
     }
 
-    grPaint->setTexture(texture);
+    grPaint->setTexture(kShaderTextureIdx, texture);
 
     GrRect dstRect = SkRect::MakeWH(GrIntToScalar(srcRect.width()),
                                     GrIntToScalar(srcRect.height()));
     GrRect paintRect;
-    paintRect.setLTRB(GrFixedToScalar((srcRect.fLeft << 16)   / bitmap.width()),
-                      GrFixedToScalar((srcRect.fTop << 16)    / bitmap.height()),
-                      GrFixedToScalar((srcRect.fRight << 16)  / bitmap.width()),
+    paintRect.setLTRB(GrFixedToScalar((srcRect.fLeft << 16) / bitmap.width()),
+                      GrFixedToScalar((srcRect.fTop << 16) / bitmap.height()),
+                      GrFixedToScalar((srcRect.fRight << 16) / bitmap.width()),
                       GrFixedToScalar((srcRect.fBottom << 16) / bitmap.height()));
 
+    if (GrSamplerState::kNearest_Filter != sampler->getFilter() &&
+        (srcRect.width() < bitmap.width() || 
+         srcRect.height() < bitmap.height())) {
+        // If drawing a subrect of the bitmap and filtering is enabled,
+        // use a constrained texture domain to avoid color bleeding
+        GrScalar left, top, right, bottom;
+        if (srcRect.width() > 1) {
+            GrScalar border = GR_ScalarHalf / bitmap.width();
+            left = paintRect.left() + border;
+            right = paintRect.right() - border;
+        } else {
+            left = right = GrScalarHalf(paintRect.left() + paintRect.right());
+        }
+        if (srcRect.height() > 1) {
+            GrScalar border = GR_ScalarHalf / bitmap.height();
+            top = paintRect.top() + border;
+            bottom = paintRect.bottom() - border;
+        } else {
+            top = bottom = GrScalarHalf(paintRect.top() + paintRect.bottom());
+        }
+        GrRect textureDomain;
+        textureDomain.setLTRB(left, top, right, bottom);
+        sampler->setTextureDomain(textureDomain);
+    }
+
     fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
 }
 
@@ -975,17 +1055,19 @@
     }
 
     GrPaint grPaint;
-    if(!this->skPaint2GrPaintNoShader(paint, true, &grPaint)) {
+    if(!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
         return;
     }
 
     GrAutoMatrix avm(fContext, GrMatrix::I());
 
-    GrTexture* texture;
-    grPaint.fSampler.setClampNoFilter();
-    SkAutoCachedTexture act(this, bitmap, grPaint.fSampler, &texture);
+    GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx);
 
-    grPaint.setTexture(texture);
+    GrTexture* texture;
+    sampler->setClampNoFilter();
+    SkAutoCachedTexture act(this, bitmap, *sampler, &texture);
+
+    grPaint.setTexture(kBitmapTextureIdx, texture);
 
     fContext->drawRectToRect(grPaint,
                              GrRect::MakeXYWH(GrIntToScalar(left),
@@ -1001,11 +1083,11 @@
 
     GrPaint grPaint;
     if (!((SkGpuDevice*)dev)->bindDeviceAsTexture(&grPaint) ||
-        !this->skPaint2GrPaintNoShader(paint, true, &grPaint)) {
+        !this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
         return;
     }
 
-    SkASSERT(NULL != grPaint.getTexture());
+    SkASSERT(NULL != grPaint.getTexture(0));
 
     const SkBitmap& bm = dev->accessBitmap(false);
     int w = bm.width();
@@ -1013,7 +1095,7 @@
 
     GrAutoMatrix avm(fContext, GrMatrix::I());
 
-    grPaint.fSampler.setClampNoFilter();
+    grPaint.getTextureSampler(kBitmapTextureIdx)->setClampNoFilter();
 
     fContext->drawRectToRect(grPaint,
                              GrRect::MakeXYWH(GrIntToScalar(x),
@@ -1044,13 +1126,17 @@
     SkAutoCachedTexture act;
     // we ignore the shader if texs is null.
     if (NULL == texs) {
-        if (!this->skPaint2GrPaintNoShader(paint, false, &grPaint)) {
+        if (!this->skPaint2GrPaintNoShader(paint,
+                                           false,
+                                           &grPaint, 
+                                           NULL == colors)) {
             return;
         }
     } else {
         if (!this->skPaint2GrPaintShader(paint, &act,
                                          *draw.fMatrix,
-                                         &grPaint)) {
+                                         &grPaint,
+                                         NULL == colors)) {
             return;
         }
     }
@@ -1167,7 +1253,11 @@
         GrPaint grPaint;
         SkAutoCachedTexture act;
 
-        if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+        if (!this->skPaint2GrPaintShader(paint,
+                                         &act,
+                                         *draw.fMatrix,
+                                         &grPaint,
+                                         true)) {
             return;
         }
         GrTextContext context(fContext, grPaint, draw.fExtMatrix);
@@ -1191,7 +1281,11 @@
 
         GrPaint grPaint;
         SkAutoCachedTexture act;
-        if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+        if (!this->skPaint2GrPaintShader(paint,
+                                         &act,
+                                         *draw.fMatrix,
+                                         &grPaint,
+                                         true)) {
             return;
         }
 
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index f65cf1e..600c336 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -112,42 +112,6 @@
                                      bitmap->rowBytes());
 }
 
-////////////////////////////////////////////////////////////////////////////////
-
-
-GrPathCmd SkGrPathIter::next(GrPoint pts[]) {
-    GrAssert(NULL != pts);
-#if SK_SCALAR_IS_GR_SCALAR
-    return sk_path_verb_to_gr_path_command(fIter.next((SkPoint*)pts));
-#else
-    Command cmd = sk_path_verb_to_gr_path_command(fIter.next(fPoints));
-    int n = NumCommandPoints(cmd);
-    for (int i = 0; i < n; ++i) {
-        pts[i].fX = SkScalarToGrScalar(fPoints[i].fX);
-        pts[i].fY = SkScalarToGrScalar(fPoints[i].fY);
-    }
-    return cmd;
-#endif
-}
-
-GrPathCmd SkGrPathIter::next() {
-    return sk_path_verb_to_gr_path_command(fIter.next(NULL));
-}
-
-void SkGrPathIter::rewind() {
-    fIter.setPath(*fPath, false);
-}
-
-GrConvexHint SkGrPathIter::convexHint() const {
-    return fPath->isConvex() ? kConvex_ConvexHint :
-                               kNone_ConvexHint;
-}
-
-bool SkGrPathIter::getConservativeBounds(GrRect* rect) const {
-    *rect = fPath->getBounds();
-    return true;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkGrClipIterator::reset(const SkClipStack& clipStack) {
diff --git a/src/gpu/SkGrFontScaler.cpp b/src/gpu/SkGrFontScaler.cpp
index e58f035..eb260fb 100644
--- a/src/gpu/SkGrFontScaler.cpp
+++ b/src/gpu/SkGrFontScaler.cpp
@@ -163,13 +163,13 @@
     return true;
 }
 
+// we should just return const SkPath* (NULL means false)
 bool SkGrFontScaler::getGlyphPath(uint16_t glyphID, GrPath* path) {
 
     const SkGlyph& glyph = fStrike->getGlyphIDMetrics(glyphID);
     const SkPath* skPath = fStrike->findPath(glyph);
     if (skPath) {
-        SkGrPathIter iter(*skPath);
-        path->resetFromIter(&iter);
+        *path = *skPath;
         return true;
     }
     return false;
diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp
index 6fee747..4d79213 100644
--- a/src/images/SkImageDecoder_libwebp.cpp
+++ b/src/images/SkImageDecoder_libwebp.cpp
@@ -246,7 +246,7 @@
           VP8kClip[(y) + (goff) - YUV_RANGE_MIN],    \
           VP8kClip[(y) + (boff) - YUV_RANGE_MIN])
 
-static void block_put(const VP8Io* io) {
+static int block_put(const VP8Io* io) {
     WEBPImage *p = (WEBPImage*) io->opaque;
     SkBitmap* decodedBitmap = p->image;
 
@@ -282,7 +282,7 @@
             break;
         default:
             // Unsupported config
-            return;
+            return 0;
     }
 
     for (j = 0; j < mb_h;) {
@@ -434,7 +434,7 @@
         v += io->uv_stride;
     }
 
-    return;
+    return 1;
 }
 
 static int block_setup(VP8Io* io) {
diff --git a/src/pdf/SkPDFCatalog.cpp b/src/pdf/SkPDFCatalog.cpp
new file mode 100644
index 0000000..afa9d9a
--- /dev/null
+++ b/src/pdf/SkPDFCatalog.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFCatalog.h"
+#include "SkPDFTypes.h"
+#include "SkStream.h"
+#include "SkTypes.h"
+
+SkPDFCatalog::SkPDFCatalog()
+    : fFirstPageCount(0),
+      fNextObjNum(1),
+      fNextFirstPageObjNum(0) {
+}
+
+SkPDFCatalog::~SkPDFCatalog() {}
+
+SkPDFObject* SkPDFCatalog::addObject(SkPDFObject* obj, bool onFirstPage) {
+    SkASSERT(findObjectIndex(obj) == -1);
+    SkASSERT(fNextFirstPageObjNum == 0);
+    if (onFirstPage)
+        fFirstPageCount++;
+
+    struct Rec newEntry(obj, onFirstPage);
+    fCatalog.append(1, &newEntry);
+    return obj;
+}
+
+size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, size_t offset) {
+    int objIndex = assignObjNum(obj) - 1;
+    SkASSERT(fCatalog[objIndex].fObjNumAssigned);
+    SkASSERT(fCatalog[objIndex].fFileOffset == 0);
+    fCatalog[objIndex].fFileOffset = offset;
+
+    return 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;
+    }
+    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;
+}
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
new file mode 100644
index 0000000..c6ddf39
--- /dev/null
+++ b/src/pdf/SkPDFDevice.cpp
@@ -0,0 +1,1488 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "SkPDFDevice.h"
+
+#include "SkColor.h"
+#include "SkClipStack.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, SkScalar* width) {
+    if (paint.getTextAlign() == SkPaint::kLeft_Align && width == NULL)
+        return;
+
+    SkMatrix ident;
+    ident.reset();
+    SkAutoGlyphCache autoCache(paint, &ident);
+    SkGlyphCache* cache = autoCache.getCache();
+
+    const char* start = (char*)glyphs;
+    const char* stop = (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 (width)
+        *width = SkFixedToScalar(xAdv);
+    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 SkIPoint& 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 interator on the "stack" argument
+// and then skips over the leading entries as specified in prefix.  It requires
+// and asserts that "prefix" will be a prefix to "stack."
+static void skip_clip_stack_prefix(const SkClipStack& prefix,
+                                   const SkClipStack& stack,
+                                   SkClipStack::B2FIter* iter) {
+    SkClipStack::B2FIter prefixIter(prefix);
+    iter->reset(stack);
+
+    const SkClipStack::B2FIter::Clip* prefixEntry;
+    const SkClipStack::B2FIter::Clip* iterEntry;
+
+    int count = 0;
+    for (prefixEntry = prefixIter.next(); prefixEntry;
+            prefixEntry = prefixIter.next(), count++) {
+        iterEntry = iter->next();
+        SkASSERT(iterEntry);
+        // Because of SkClipStack does internal intersection, the last clip
+        // entry may differ.
+        if(*prefixEntry != *iterEntry) {
+            SkASSERT(prefixEntry->fOp == SkRegion::kIntersect_Op);
+            SkASSERT(iterEntry->fOp == SkRegion::kIntersect_Op);
+            SkASSERT((iterEntry->fRect == NULL) ==
+                    (prefixEntry->fRect == NULL));
+            SkASSERT((iterEntry->fPath == NULL) ==
+                    (prefixEntry->fPath == NULL));
+            // We need to back up the iterator by one but don't have that
+            // function, so reset and go forward by one less.
+            iter->reset(stack);
+            for (int i = 0; i < count; i++) {
+                iter->next();
+            }
+            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, contentStream);
+        clipFill = clipPath->getFillType();
+    } else if (clipRect) {
+        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 SkIPoint& 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::B2FIter iter;
+    skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
+
+    // If the clip stack does anything other than intersect or if it uses
+    // an inverse fill type, we have to fall back to the clip region.
+    bool needRegion = false;
+    const SkClipStack::B2FIter::Clip* clipEntry;
+    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::B2FIter::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;
+        }
+    }
+}
+
+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.get());
+        }
+    }
+
+    ContentEntry* entry() { return fContentEntry; }
+private:
+    SkPDFDevice* fDevice;
+    ContentEntry* fContentEntry;
+    SkXfermode::Mode fXfermode;
+    SkRefPtr<SkPDFFormXObject> fDstFormXObject;
+
+    void init(const SkClipStack* clipStack, const SkRegion& clipRegion,
+              const SkMatrix& matrix, const SkPaint& paint, bool hasText) {
+        if (paint.getXfermode()) {
+            paint.getXfermode()->asMode(&fXfermode);
+        }
+        fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion,
+                                                   matrix, paint, hasText,
+                                                   &fDstFormXObject);
+    }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkPDFDeviceFactory::newDevice(SkCanvas* c, SkBitmap::Config config,
+                                        int width, int height, bool isOpaque,
+                                        bool isForLayer) {
+    SkMatrix initialTransform;
+    initialTransform.reset();
+    SkISize size = SkISize::Make(width, height);
+    if (isForLayer) {
+        return SkNEW_ARGS(SkPDFDevice, (size, c->getTotalClipStack(),
+                                        c->getTotalClip()));
+    } else {
+        return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform));
+    }
+}
+
+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(contentSize.fWidth, contentSize.fHeight);
+        initialTransform->invert(&inverse);
+        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;
+}
+
+SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
+                         const SkMatrix& initialTransform)
+    : SkDevice(NULL, makeContentBitmap(contentSize, &initialTransform), false),
+      fPageSize(pageSize),
+      fContentSize(contentSize),
+      fLastContentEntry(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, pageSize.fHeight);
+    fInitialTransform.preScale(1, -1);
+    fInitialTransform.preConcat(initialTransform);
+
+    SkIRect existingClip = SkIRect::MakeWH(this->width(), this->height());
+    fExistingClipStack.clipDevRect(existingClip);
+    fExistingClipRegion.setRect(existingClip);
+
+    this->init();
+}
+
+SkPDFDevice::SkPDFDevice(const SkISize& layerSize,
+                         const SkClipStack& existingClipStack,
+                         const SkRegion& existingClipRegion)
+    : SkDevice(NULL, makeContentBitmap(layerSize, NULL), false),
+      fPageSize(layerSize),
+      fContentSize(layerSize),
+      fExistingClipStack(existingClipStack),
+      fExistingClipRegion(existingClipRegion),
+      fLastContentEntry(NULL) {
+    fInitialTransform.reset();
+    this->init();
+}
+
+SkPDFDevice::~SkPDFDevice() {
+    this->cleanUp();
+}
+
+void SkPDFDevice::init() {
+    fResourceDict = NULL;
+    fContentEntries.reset();
+    fLastContentEntry = NULL;
+}
+
+SkDeviceFactory* SkPDFDevice::onNewDeviceFactory() {
+    return SkNEW(SkPDFDeviceFactory);
+}
+
+void SkPDFDevice::cleanUp() {
+    fGraphicStateResources.unrefAll();
+    fXObjectResources.unrefAll();
+    fFontResources.unrefAll();
+    fShaderResources.unrefAll();
+}
+
+void SkPDFDevice::clear(SkColor color) {
+    this->cleanUp();
+    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;
+    inverse.reset();
+    totalTransform.invert(&inverse);
+    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;
+    }
+
+    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;
+    }
+
+    ScopedContentEntry content(this, d, paint);
+    if (!content.entry()) {
+        return;
+    }
+    SkPDFUtils::EmitPath(*pathPtr, &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 =
+        (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);
+    }
+
+    SkScalar width;
+    SkScalar* widthPtr = NULL;
+    if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
+        widthPtr = &width;
+
+    SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
+    align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y,
+               widthPtr);
+    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);
+        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");
+
+    // Draw underline and/or strikethrough if the paint has them.
+    // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
+    // because the raster versions don't.  Use paint instead of textPaint
+    // because we may have changed strokeWidth to do fakeBold text.
+    if (paint.isUnderlineText() || paint.isStrikeThruText()) {
+        SkScalar textSize = paint.getTextSize();
+        SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
+
+        if (paint.isUnderlineText()) {
+            SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
+            SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
+            drawRect(d, r, paint);
+        }
+        if (paint.isStrikeThruText()) {
+            SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
+            SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
+            drawRect(d, r, paint);
+        }
+    }
+}
+
+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 = (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 = (uint16_t*)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;
+        }
+        SkScalar x = pos[i * scalarsPerPos];
+        SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
+        align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
+        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;
+    }
+    NOT_IMPLEMENTED("drawTextOnPath", true);
+}
+
+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);
+}
+
+const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
+    if (fResourceDict.get() == NULL) {
+        fResourceDict = new SkPDFDict;
+        fResourceDict->unref();  // SkRefPtr and new both took a reference.
+
+        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->append(new SkPDFName(procs[i]))->unref();
+        fResourceDict->insert("ProcSet", procSets.get());
+    }
+    return fResourceDict;
+}
+
+void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) 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();
+        fGraphicStateResources[i]->getResources(resourceList);
+    }
+    for (int i = 0; i < fXObjectResources.count(); i++) {
+        resourceList->push(fXObjectResources[i]);
+        fXObjectResources[i]->ref();
+        fXObjectResources[i]->getResources(resourceList);
+    }
+    for (int i = 0; i < fFontResources.count(); i++) {
+        resourceList->push(fFontResources[i]);
+        fFontResources[i]->ref();
+        fFontResources[i]->getResources(resourceList);
+    }
+    for (int i = 0; i < fShaderResources.count(); i++) {
+        resourceList->push(fShaderResources[i]);
+        fShaderResources[i]->ref();
+        fShaderResources[i]->getResources(resourceList);
+    }
+}
+
+const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const {
+    return fFontResources;
+}
+
+SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
+    SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
+    zero->unref();  // SkRefPtr and new both took a reference.
+
+    SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
+    mediaBox->unref();  // SkRefPtr and new both took a reference.
+    mediaBox->reserve(4);
+    mediaBox->append(zero.get());
+    mediaBox->append(zero.get());
+    mediaBox->append(new SkPDFInt(fPageSize.fWidth))->unref();
+    mediaBox->append(new SkPDFInt(fPageSize.fHeight))->unref();
+    return mediaBox;
+}
+
+SkStream* SkPDFDevice::content() const {
+    SkDynamicMemoryWStream data;
+    if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
+        SkPDFUtils::AppendTransform(fInitialTransform, &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(this->width(), this->height());
+        emit_clip(NULL, &r, &data);
+    }
+
+    GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, &data);
+    for (ContentEntry* entry = fContentEntries.get();
+            entry != NULL;
+            entry = entry->fNext.get()) {
+        SkIPoint translation = this->getOrigin();
+        translation.negate();
+        gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion,
+                           translation);
+        gsState.updateMatrix(entry->fState.fMatrix);
+        gsState.updateDrawingState(entry->fState);
+        data.write(entry->fContent.getStream(), entry->fContent.getOffset());
+    }
+    gsState.drainStack();
+
+    SkMemoryStream* result = new SkMemoryStream;
+    result->setMemoryOwned(data.detach(), data.getOffset());
+    return result;
+}
+
+void SkPDFDevice::createFormXObjectFromDevice(
+        SkRefPtr<SkPDFFormXObject>* xobject) {
+    *xobject = new SkPDFFormXObject(this);
+    (*xobject)->unref();  // SkRefPtr and new both took a reference.
+    cleanUp();  // Reset this device to have no content.
+    init();
+}
+
+void SkPDFDevice::clearClipFromContent(const SkClipStack* clipStack,
+                                       const SkRegion& clipRegion) {
+    if (clipRegion.isEmpty() || isContentEmpty()) {
+        return;
+    }
+    SkRefPtr<SkPDFFormXObject> curContent;
+    createFormXObjectFromDevice(&curContent);
+
+    // Redraw what we already had, but with the clip as a mask.
+    drawFormXObjectWithClip(curContent.get(), 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);
+    SkRefPtr<SkPDFFormXObject> maskFormXObject;
+    createFormXObjectFromDevice(&maskFormXObject);
+    SkRefPtr<SkPDFGraphicState> sMaskGS =
+        SkPDFGraphicState::getSMaskGraphicState(maskFormXObject.get(),
+                                                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,
+                                             SkRefPtr<SkPDFFormXObject>* dst) {
+    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);
+            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 {
+            createFormXObjectFromDevice(dst);
+        }
+    }
+    // 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;
+    if (fLastContentEntry && fLastContentEntry->fContent.getOffset() == 0) {
+        entry = fLastContentEntry;
+    } else {
+        newEntry.reset(new ContentEntry);
+        entry = newEntry.get();
+    }
+
+    populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint,
+                                       hasText, &entry->fState);
+    if (fLastContentEntry && xfermode != SkXfermode::kDstOver_Mode &&
+            entry->fState.compareInitialState(fLastContentEntry->fState)) {
+        return fLastContentEntry;
+    }
+
+    if (!fLastContentEntry) {
+        fContentEntries.reset(entry);
+        fLastContentEntry = entry;
+    } else if (xfermode == SkXfermode::kDstOver_Mode) {
+        entry->fNext.reset(fContentEntries.release());
+        fContentEntries.reset(entry);
+    } else {
+        fLastContentEntry->fNext.reset(entry);
+        fLastContentEntry = 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;
+    }
+    SkASSERT(dst);
+    SkASSERT(!fContentEntries->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 = fContentEntries->fState.fClipStack;
+    SkRegion clipRegion = fContentEntries->fState.fClipRegion;
+
+    SkRefPtr<SkPDFFormXObject> srcFormXObject;
+    if (!isContentEmpty()) {
+        createFormXObjectFromDevice(&srcFormXObject);
+    }
+
+    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->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() {
+    if (!fContentEntries.get() || fContentEntries->fContent.getOffset() == 0) {
+        SkASSERT(!fContentEntries.get() || !fContentEntries->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;
+
+    // PDF treats a shader as a color, so we only set one or the other.
+    SkRefPtr<SkPDFShader> 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();
+        pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds);
+        SkSafeUnref(pdfShader.get());  // getShader and SkRefPtr both took a ref
+
+        // A color shader is treated as an invalid shader so we don't have
+        // to set a shader just for a color.
+        if (pdfShader.get() == NULL) {
+            entry->fColor = 0;
+            color = 0;
+
+            // Check for a color shader.
+            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;
+            }
+        }
+    }
+
+    if (pdfShader) {
+        // 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 {
+        entry->fShaderIndex = -1;
+        entry->fColor = SkColorSetA(paint.getColor(), 0xFF);
+        color = paint.getColor();
+    }
+
+    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(1, -1);
+    scaled.postTranslate(0, 1);
+    // 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);
+}
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
new file mode 100644
index 0000000..95370b4
--- /dev/null
+++ b/src/pdf/SkPDFDocument.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "SkPDFDevice.h"
+#include "SkPDFDocument.h"
+#include "SkPDFPage.h"
+#include "SkStream.h"
+
+// Add the resources, starting at firstIndex to the catalog, removing any dupes.
+// A hash table would be really nice here.
+void addResourcesToCatalog(int firstIndex, bool firstPage,
+                          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);
+        }
+    }
+}
+
+SkPDFDocument::SkPDFDocument() : fXRefFileOffset(0) {
+    fDocCatalog = new SkPDFDict("Catalog");
+    fDocCatalog->unref();  // SkRefPtr and new both took a reference.
+    fCatalog.addObject(fDocCatalog.get(), 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();
+}
+
+bool SkPDFDocument::emitPDF(SkWStream* stream) {
+    if (fPages.isEmpty())
+        return false;
+
+    // We haven't emitted the document before if fPageTree is empty.
+    if (fPageTree.count() == 0) {
+        SkPDFDict* pageTreeRoot;
+        SkPDFPage::generatePageTree(fPages, &fCatalog, &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 first_page = true;
+        for (int i = 0; i < fPages.count(); i++) {
+            int resourceCount = fPageResources.count();
+            fPages[i]->finalizePage(&fCatalog, first_page, &fPageResources);
+            addResourcesToCatalog(resourceCount, first_page, &fPageResources,
+                                 &fCatalog);
+            if (i == 0) {
+                first_page = false;
+                fSecondPageFirstResourceIndex = fPageResources.count();
+            }
+        }
+
+        // Figure out the size of things and inform the catalog of file offsets.
+        off_t fileOffset = headerSize();
+        fileOffset += fCatalog.setFileOffset(fDocCatalog.get(), fileOffset);
+        fileOffset += fCatalog.setFileOffset(fPages[0], fileOffset);
+        fileOffset += fPages[0]->getPageSize(&fCatalog, fileOffset);
+        for (int i = 0; i < fSecondPageFirstResourceIndex; i++)
+            fileOffset += fCatalog.setFileOffset(fPageResources[i], fileOffset);
+        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, fileOffset);
+
+        for (int i = fSecondPageFirstResourceIndex;
+                 i < fPageResources.count();
+                 i++)
+            fileOffset += fCatalog.setFileOffset(fPageResources[i], fileOffset);
+
+        fXRefFileOffset = fileOffset;
+    }
+
+    emitHeader(stream);
+    fDocCatalog->emitObject(stream, &fCatalog, true);
+    fPages[0]->emitObject(stream, &fCatalog, true);
+    fPages[0]->emitPage(stream, &fCatalog);
+    for (int i = 0; i < fSecondPageFirstResourceIndex; i++)
+        fPageResources[i]->emitObject(stream, &fCatalog, 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, true);
+
+    for (int i = 1; i < fPages.count(); i++)
+        fPages[i]->emitPage(stream, &fCatalog);
+
+    for (int i = fSecondPageFirstResourceIndex; i < fPageResources.count(); i++)
+        fPageResources[i]->emitObject(stream, &fCatalog, true);
+
+    int64_t objCount = fCatalog.emitXrefTable(stream, fPages.count() > 1);
+    emitFooter(stream, objCount);
+    return true;
+}
+
+bool SkPDFDocument::appendPage(const SkRefPtr<SkPDFDevice>& pdfDevice) {
+    if (fPageTree.count() != 0)
+        return false;
+
+    SkPDFPage* page = new SkPDFPage(pdfDevice);
+    fPages.push(page);  // Reference from new passed to fPages.
+    // The rest of the pages will be added to the catalog along with the rest
+    // of the page tree.  But the first page has to be marked as such, so we
+    // handle it here.
+    if (fPages.count() == 1)
+        fCatalog.addObject(page, true);
+    return true;
+}
+
+const SkTDArray<SkPDFPage*>& SkPDFDocument::getPages() {
+    return fPages;
+}
+
+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 (fTrailerDict.get() == NULL) {
+        fTrailerDict = new SkPDFDict();
+        fTrailerDict->unref();  // SkRefPtr and new both took a reference.
+
+        // TODO(vandebo) Linearized format will take a Prev entry too.
+        // TODO(vandebo) PDF/A requires an ID entry.
+        fTrailerDict->insert("Size", new SkPDFInt(objCount))->unref();
+        fTrailerDict->insert("Root",
+                             new SkPDFObjRef(fDocCatalog.get()))->unref();
+    }
+
+    stream->writeText("trailer\n");
+    fTrailerDict->emitObject(stream, &fCatalog, 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 100755
index 0000000..277ed12
--- /dev/null
+++ b/src/pdf/SkPDFFont.cpp
@@ -0,0 +1,976 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <ctype.h>
+
+#include "SkFontHost.h"
+#include "SkGlyphCache.h"
+#include "SkPaint.h"
+#include "SkPDFDevice.h"
+#include "SkPDFFont.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"
+
+namespace {
+
+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;
+    if (buf[1] == 3)
+        return true;
+    if (*len < 6)
+        return false;
+
+    *size = buf[2] | (buf[3] << 8) | (buf[4] << 16) | (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;
+    if (c <= '9') return c - '0';
+    if (c <= 'F') return c - 'A' + 10;
+    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;
+    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 bufSize = 4096;
+        uint8_t buf[bufSize];
+        size_t amount;
+        while ((amount = srcStream->read(buf, bufSize)) > 0)
+            dynamicStream.write(buf, amount);
+        amount = 0;
+        dynamicStream.write(&amount, 1);  // NULL terminator.
+        // getStream makes another copy, but we couldn't do any better.
+        src = (const uint8_t*)dynamicStream.getStream();
+        srcLen = dynamicStream.getOffset() - 1;
+    }
+
+    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->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fLeft,
+                                                    emSize)))->unref();
+    bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fBottom,
+                                                    emSize)))->unref();
+    bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fRight,
+                                                    emSize)))->unref();
+    bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fTop,
+                                                    emSize)))->unref();
+    return bbox;
+}
+
+SkPDFArray* appendWidth(const int16_t& width, uint16_t emSize,
+                        SkPDFArray* array) {
+    array->append(new SkPDFScalar(scaleFromFontUnits(width, emSize)))->unref();
+    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->append(new SkPDFInt(advanceInfo->fStartId))->unref();
+                result->append(advanceArray.get());
+                break;
+            }
+            case SkAdvancedTypefaceMetrics::WidthRange::kRun: {
+                SkASSERT(advanceInfo->fAdvance.count() == 1);
+                result->append(new SkPDFInt(advanceInfo->fStartId))->unref();
+                result->append(new SkPDFInt(advanceInfo->fEndId))->unref();
+                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_bfchar_table(uint16_t* glyph_id, SkUnichar* unicode,
+                                     size_t count,
+                                     SkDynamicMemoryWStream* cmap) {
+    cmap->writeDecAsText(count);
+    cmap->writeText(" beginbfchar\n");
+    for (size_t i = 0; i < count; ++i) {
+        cmap->writeText("<");
+        cmap->writeHexAsText(glyph_id[i], 4);
+        cmap->writeText("> <");
+        cmap->writeHexAsText(unicode[i], 4);
+        cmap->writeText(">\n");
+    }
+    cmap->writeText("endbfchar\n");
+}
+
+static void append_cmap_footer(SkDynamicMemoryWStream* cmap) {
+    const char* kFooter =
+        "endcmap\n"
+        "CMapName currentdict /CMap defineresource pop\n"
+        "end\n"
+        "end";
+    cmap->writeText(kFooter);
+}
+
+// Generate <bfchar> table according to PDF spec 1.4 and Adobe Technote 5014.
+static void append_cmap_bfchar_sections(
+                const SkTDArray<SkUnichar>& glyphUnicode,
+                SkDynamicMemoryWStream* cmap) {
+    // PDF spec defines that every bf* list can have at most 100 entries.
+    const size_t kMaxEntries = 100;
+    uint16_t glyphId[kMaxEntries];
+    SkUnichar unicode[kMaxEntries];
+    size_t index = 0;
+    for (int i = 0; i < glyphUnicode.count(); i++) {
+        if (glyphUnicode[i]) {
+            glyphId[index] = i;
+            unicode[index] = glyphUnicode[i];
+            ++index;
+        }
+        if (index == kMaxEntries) {
+            append_cmap_bfchar_table(glyphId, unicode, index, cmap);
+            index = 0;
+        }
+    }
+
+    if (index) {
+        append_cmap_bfchar_table(glyphId, unicode, index, cmap);
+    }
+}
+
+/* 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().removeShuffle(index);
+#ifdef SK_DEBUG
+        SkASSERT(!fDescendant);
+    } else {
+        SkASSERT(fDescendant);
+#endif
+    }
+    fResources.unrefAll();
+}
+
+void SkPDFFont::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();
+        fResources[i]->getResources(resourceList);
+    }
+}
+
+SkTypeface* SkPDFFont::typeface() {
+    return fTypeface.get();
+}
+
+SkAdvancedTypefaceMetrics::FontType SkPDFFont::getType() {
+    return fType;
+}
+
+bool SkPDFFont::hasGlyph(uint16_t id) {
+    return (id >= fFirstGlyphID && id <= fLastGlyphID) || id == 0;
+}
+
+bool SkPDFFont::multiByteGlyphs() {
+    return fMultiByteGlyphs;
+}
+
+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 (fMultiByteGlyphs) {
+        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 index;
+    if (find(fontID, glyphID, &index)) {
+        canonicalFonts()[index].fFont->ref();
+        return canonicalFonts()[index].fFont;
+    }
+
+    SkRefPtr<SkAdvancedTypefaceMetrics> fontInfo;
+    SkPDFDict* fontDescriptor = NULL;
+    if (index >= 0) {
+        SkPDFFont* relatedFont = canonicalFonts()[index].fFont;
+        SkASSERT(relatedFont->fFontInfo.get());
+        fontInfo = relatedFont->fFontInfo;
+        fontDescriptor = relatedFont->fDescriptor.get();
+    } else {
+        SkAdvancedTypefaceMetrics::PerGlyphInfo info;
+        info = SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo;
+        info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
+                  info, SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo);
+        info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
+                  info, SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo);
+        fontInfo = SkFontHost::GetAdvancedTypefaceMetrics(fontID, info);
+        SkSafeUnref(fontInfo.get());  // SkRefPtr and Get both took a reference.
+    }
+
+    SkPDFFont* font = new SkPDFFont(fontInfo.get(), typeface, glyphID, false,
+                                    fontDescriptor);
+    FontRec newEntry(font, fontID, font->fFirstGlyphID);
+    index = canonicalFonts().count();
+    canonicalFonts().push(newEntry);
+    return font;  // Return the reference new SkPDFFont() created.
+}
+
+// static
+SkTDArray<SkPDFFont::FontRec>& SkPDFFont::canonicalFonts() {
+    // This initialization is only thread safe with gcc.
+    static SkTDArray<FontRec> gCanonicalFonts;
+    return gCanonicalFonts;
+}
+
+// static
+SkMutex& SkPDFFont::canonicalFontsMutex() {
+    // This initialization is only thread safe with gcc.
+    static SkMutex 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(class SkAdvancedTypefaceMetrics* fontInfo,
+                     SkTypeface* typeface,
+                     uint16_t glyphID,
+                     bool descendantFont,
+                     SkPDFDict* fontDescriptor)
+        : SkPDFDict("Font"),
+          fTypeface(typeface),
+          fType(fontInfo ? fontInfo->fType :
+                           SkAdvancedTypefaceMetrics::kNotEmbeddable_Font),
+#ifdef SK_DEBUG
+          fDescendant(descendantFont),
+#endif
+          fMultiByteGlyphs(false),
+          fFirstGlyphID(1),
+          fLastGlyphID(fontInfo ? fontInfo->fLastGlyphID : 0),
+          fFontInfo(fontInfo),
+          fDescriptor(fontDescriptor) {
+    if (fontInfo && fontInfo->fMultiMaster) {
+        NOT_IMPLEMENTED(true, true);
+        fType = SkAdvancedTypefaceMetrics::kOther_Font;
+    }
+    if (fType == SkAdvancedTypefaceMetrics::kType1CID_Font ||
+        fType == SkAdvancedTypefaceMetrics::kTrueType_Font) {
+        if (descendantFont) {
+            populateCIDFont();
+        } else {
+            populateType0Font();
+        }
+        // No need to hold onto the font info for fonts types that
+        // support multibyte glyphs.
+        fFontInfo = NULL;
+        return;
+    }
+
+    if (fType == SkAdvancedTypefaceMetrics::kType1_Font &&
+        populateType1Font(glyphID)) {
+        return;
+    }
+
+    SkASSERT(fType == SkAdvancedTypefaceMetrics::kType1_Font ||
+             fType == SkAdvancedTypefaceMetrics::kCFF_Font ||
+             fType == SkAdvancedTypefaceMetrics::kOther_Font ||
+             fType == SkAdvancedTypefaceMetrics::kNotEmbeddable_Font);
+    populateType3Font(glyphID);
+}
+
+void SkPDFFont::populateType0Font() {
+    fMultiByteGlyphs = true;
+
+    insert("Subtype", new SkPDFName("Type0"))->unref();
+    insert("BaseFont", new SkPDFName(fFontInfo->fFontName))->unref();
+    insert("Encoding",  new SkPDFName("Identity-H"))->unref();
+
+    SkRefPtr<SkPDFArray> descendantFonts = new SkPDFArray();
+    descendantFonts->unref();  // SkRefPtr and new took a reference.
+
+    // Pass ref new created to fResources.
+    fResources.push(
+        new SkPDFFont(fFontInfo.get(), fTypeface.get(), 1, true, NULL));
+    descendantFonts->append(new SkPDFObjRef(fResources.top()))->unref();
+    insert("DescendantFonts", descendantFonts.get());
+
+    populateToUnicodeTable();
+}
+
+void SkPDFFont::populateToUnicodeTable() {
+    if (fFontInfo.get() == NULL ||
+        fFontInfo->fGlyphToUnicode.begin() == NULL) {
+        return;
+    }
+
+    SkDynamicMemoryWStream cmap;
+    append_tounicode_header(&cmap);
+    append_cmap_bfchar_sections(fFontInfo->fGlyphToUnicode, &cmap);
+    append_cmap_footer(&cmap);
+    SkRefPtr<SkMemoryStream> cmapStream = new SkMemoryStream();
+    cmapStream->unref();  // SkRefPtr and new took a reference.
+    cmapStream->setMemoryOwned(cmap.detach(), cmap.getOffset());
+    SkRefPtr<SkPDFStream> pdfCmap = new SkPDFStream(cmapStream.get());
+    fResources.push(pdfCmap.get());  // Pass reference from new.
+    insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref();
+}
+
+void SkPDFFont::populateCIDFont() {
+    fMultiByteGlyphs = true;
+    insert("BaseFont", new SkPDFName(fFontInfo->fFontName))->unref();
+
+    if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kType1CID_Font) {
+        insert("Subtype", new SkPDFName("CIDFontType0"))->unref();
+    } else if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kTrueType_Font) {
+        insert("Subtype", new SkPDFName("CIDFontType2"))->unref();
+        insert("CIDToGIDMap", new SkPDFName("Identity"))->unref();
+    } 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->insert("Supplement", new SkPDFInt(0))->unref();
+    insert("CIDSystemInfo", sysInfo.get());
+
+    addFontDescriptor(0);
+
+    if (fFontInfo->fGlyphWidths.get()) {
+        int16_t defaultWidth = 0;
+        SkRefPtr<SkPDFArray> widths =
+            composeAdvanceData(fFontInfo->fGlyphWidths.get(),
+                               fFontInfo->fEmSize, &appendWidth, &defaultWidth);
+        widths->unref();  // SkRefPtr and compose both took a reference.
+        if (widths->size())
+            insert("W", widths.get());
+        if (defaultWidth != 0) {
+            insert("DW", new SkPDFScalar(scaleFromFontUnits(
+                    defaultWidth, fFontInfo->fEmSize)))->unref();
+        }
+    }
+    if (fFontInfo->fVerticalMetrics.get()) {
+        struct SkAdvancedTypefaceMetrics::VerticalMetric defaultAdvance;
+        defaultAdvance.fVerticalAdvance = 0;
+        defaultAdvance.fOriginXDisp = 0;
+        defaultAdvance.fOriginYDisp = 0;
+        SkRefPtr<SkPDFArray> advances =
+            composeAdvanceData(fFontInfo->fVerticalMetrics.get(),
+                               fFontInfo->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,
+                                                fFontInfo->fEmSize,
+                                                new SkPDFArray))->unref();
+        }
+    }
+}
+
+bool SkPDFFont::populateType1Font(int16_t glyphID) {
+    SkASSERT(!fFontInfo->fVerticalMetrics.get());
+    SkASSERT(fFontInfo->fGlyphWidths.get());
+
+    adjustGlyphRangeForSingleByteEncoding(glyphID);
+
+    int16_t defaultWidth = 0;
+    const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry = NULL;
+    const SkAdvancedTypefaceMetrics::WidthRange* widthEntry;
+    for (widthEntry = fFontInfo.get()->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;
+
+    insert("Subtype", new SkPDFName("Type1"))->unref();
+    insert("BaseFont", new SkPDFName(fFontInfo->fFontName))->unref();
+
+    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(fLastGlyphID - fFirstGlyphID + 2);
+    encDiffs->append(new SkPDFInt(1))->unref();
+    for (int gID = fFirstGlyphID; gID <= fLastGlyphID; gID++) {
+        encDiffs->append(
+            new SkPDFName(fFontInfo->fGlyphNames->get()[gID]))->unref();
+    }
+
+    if (fFontInfo->fLastGlyphID <= 255)
+        fFontInfo = NULL;
+    return true;
+}
+
+void SkPDFFont::populateType3Font(int16_t glyphID) {
+    SkPaint paint;
+    paint.setTypeface(fTypeface.get());
+    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 (fLastGlyphID == 0) {
+        fLastGlyphID = cache->getGlyphCount() - 1;
+    }
+
+    adjustGlyphRangeForSingleByteEncoding(glyphID);
+
+    insert("Subtype", new SkPDFName("Type3"))->unref();
+    // 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(fLastGlyphID - fFirstGlyphID + 2);
+    encDiffs->append(new SkPDFInt(1))->unref();
+
+    SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
+    widthArray->unref();  // SkRefPtr and new both took a ref.
+
+    SkIRect bbox = SkIRect::MakeEmpty();
+    for (int gID = fFirstGlyphID; gID <= fLastGlyphID; gID++) {
+        SkString characterName;
+        characterName.printf("gid%d", gID);
+        encDiffs->append(new SkPDFName(characterName))->unref();
+
+        const SkGlyph& glyph = cache->getGlyphIDMetrics(gID);
+        widthArray->append(new SkPDFScalar(SkFixedToScalar(glyph.fAdvanceX)))->unref();
+        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, &content);
+            SkPDFUtils::PaintPath(paint.getStyle(), path->getFillType(),
+                                  &content);
+        }
+        SkRefPtr<SkMemoryStream> glyphStream = new SkMemoryStream();
+        glyphStream->unref();  // SkRefPtr and new both took a ref.
+        glyphStream->setMemoryOwned(content.detach(), content.getOffset());
+
+        SkRefPtr<SkPDFStream> glyphDescription =
+            new SkPDFStream(glyphStream.get());
+        // SkRefPtr and new both ref()'d charProcs, pass one.
+        fResources.push(glyphDescription.get());
+        charProcs->insert(characterName.c_str(),
+                          new SkPDFObjRef(glyphDescription.get()))->unref();
+    }
+
+    insert("FontBBox", makeFontBBox(bbox, 1000))->unref();
+    insert("FirstChar", new SkPDFInt(fFirstGlyphID))->unref();
+    insert("LastChar", new SkPDFInt(fLastGlyphID))->unref();
+    insert("Widths", widthArray.get());
+    insert("CIDToGIDMap", new SkPDFName("Identity"))->unref();
+
+    if (fFontInfo && fFontInfo->fLastGlyphID <= 255)
+        fFontInfo = NULL;
+
+    populateToUnicodeTable();
+}
+
+bool SkPDFFont::addFontDescriptor(int16_t defaultWidth) {
+    if (fDescriptor.get() != NULL) {
+        fResources.push(fDescriptor.get());
+        fDescriptor->ref();
+        insert("FontDescriptor", new SkPDFObjRef(fDescriptor.get()))->unref();
+        return true;
+    }
+
+    fDescriptor = new SkPDFDict("FontDescriptor");
+    fDescriptor->unref();  // SkRefPtr and new both took a ref.
+
+    switch (fFontInfo->fType) {
+        case SkAdvancedTypefaceMetrics::kType1_Font: {
+            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(fTypeface.get()));
+            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.
+            fResources.push(fontStream.get());
+            fontStream->insert("Length1", new SkPDFInt(header))->unref();
+            fontStream->insert("Length2", new SkPDFInt(data))->unref();
+            fontStream->insert("Length3", new SkPDFInt(trailer))->unref();
+            fDescriptor->insert("FontFile",
+                                new SkPDFObjRef(fontStream.get()))->unref();
+            break;
+        }
+        case SkAdvancedTypefaceMetrics::kTrueType_Font: {
+            SkRefPtr<SkStream> fontData =
+                SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get()));
+            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.
+            fResources.push(fontStream.get());
+
+            fontStream->insert("Length1",
+                               new SkPDFInt(fontData->getLength()))->unref();
+            fDescriptor->insert("FontFile2",
+                                new SkPDFObjRef(fontStream.get()))->unref();
+            break;
+        }
+        case SkAdvancedTypefaceMetrics::kCFF_Font:
+        case SkAdvancedTypefaceMetrics::kType1CID_Font: {
+            SkRefPtr<SkStream> fontData =
+                SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get()));
+            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.
+            fResources.push(fontStream.get());
+
+            if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kCFF_Font) {
+                fontStream->insert("Subtype", new SkPDFName("Type1C"))->unref();
+            } else {
+                fontStream->insert("Subtype",
+                        new SkPDFName("CIDFontType0c"))->unref();
+            }
+            fDescriptor->insert("FontFile3",
+                                new SkPDFObjRef(fontStream.get()))->unref();
+            break;
+        }
+        default:
+            SkASSERT(false);
+    }
+
+    const uint16_t emSize = fFontInfo->fEmSize;
+    fResources.push(fDescriptor.get());
+    fDescriptor->ref();
+    insert("FontDescriptor", new SkPDFObjRef(fDescriptor.get()))->unref();
+
+    fDescriptor->insert("FontName", new SkPDFName(
+            fFontInfo->fFontName))->unref();
+    fDescriptor->insert("Flags", new SkPDFInt(fFontInfo->fStyle))->unref();
+    fDescriptor->insert("Ascent", new SkPDFScalar(
+            scaleFromFontUnits(fFontInfo->fAscent, emSize)))->unref();
+    fDescriptor->insert("Descent", new SkPDFScalar(
+            scaleFromFontUnits(fFontInfo->fDescent, emSize)))->unref();
+    fDescriptor->insert("StemV", new SkPDFScalar(
+            scaleFromFontUnits(fFontInfo->fStemV, emSize)))->unref();
+    fDescriptor->insert("CapHeight", new SkPDFScalar(
+            scaleFromFontUnits(fFontInfo->fCapHeight, emSize)))->unref();
+    fDescriptor->insert("ItalicAngle", new SkPDFInt(
+            fFontInfo->fItalicAngle))->unref();
+    fDescriptor->insert("FontBBox", makeFontBBox(fFontInfo->fBBox,
+                                                 fFontInfo->fEmSize))->unref();
+
+    if (defaultWidth > 0) {
+        fDescriptor->insert("MissingWidth", new SkPDFScalar(
+                scaleFromFontUnits(defaultWidth, emSize)))->unref();
+    }
+    return true;
+}
+void SkPDFFont::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 = fFontInfo->fEmSize;
+        int startIndex = fFirstGlyphID - widthRangeEntry->fStartId;
+        int endIndex = startIndex + fLastGlyphID - fFirstGlyphID + 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());
+    }
+    insert("FirstChar", new SkPDFInt(firstChar))->unref();
+    insert("LastChar",
+           new SkPDFInt(firstChar + widthArray->size() - 1))->unref();
+    insert("Widths", widthArray.get());
+}
+
+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) {
+}
diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp
new file mode 100644
index 0000000..40a5564
--- /dev/null
+++ b/src/pdf/SkPDFFormXObject.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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);
+
+    SkRefPtr<SkStream> content = device->content();
+    content->unref();  // SkRefPtr and content() both took a reference.
+    fStream = new SkPDFStream(content.get());
+    fStream->unref();  // SkRefPtr and new both took a reference.
+
+    insert("Type", new SkPDFName("XObject"))->unref();
+    insert("Subtype", new SkPDFName("Form"))->unref();
+    insert("BBox", device->getMediaBox().get());
+    insert("Resources", device->getResourceDict().get());
+
+    // 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;
+        inverse.reset();
+        device->initialTransform().invert(&inverse);
+        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->insert("S", new SkPDFName("Transparency"))->unref();
+    group->insert("I", new SkPDFBool(true))->unref();  // Isolated.
+    insert("Group", group.get());
+}
+
+SkPDFFormXObject::~SkPDFFormXObject() {
+    fResources.unrefAll();
+}
+
+void SkPDFFormXObject::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                             bool indirect) {
+    if (indirect)
+        return emitIndirectObject(stream, catalog);
+
+    fStream->emitObject(stream, catalog, indirect);
+}
+
+size_t SkPDFFormXObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    if (indirect)
+        return getIndirectOutputSize(catalog);
+
+    return fStream->getOutputSize(catalog, indirect);
+}
+
+void SkPDFFormXObject::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();
+    }
+}
+
+SkPDFObject* SkPDFFormXObject::insert(SkPDFName* key, SkPDFObject* value) {
+    return fStream->insert(key, value);
+}
+
+SkPDFObject* SkPDFFormXObject::insert(const char key[], SkPDFObject* value) {
+    return fStream->insert(key, value);
+}
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
new file mode 100644
index 0000000..b08bf24
--- /dev/null
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#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);
+        canonicalPaints().removeShuffle(index);
+    }
+    fResources.unrefAll();
+}
+
+void SkPDFGraphicState::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();
+        fResources[i]->getResources(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
+SkMutex& SkPDFGraphicState::canonicalPaintsMutex() {
+    // This initialization is only thread safe with gcc.
+    static SkMutex 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->append(new SkPDFInt(0))->unref();
+        domainAndRange->append(new SkPDFInt(1))->unref();
+
+        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->insert("FunctionType", new SkPDFInt(4))->unref();
+        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->insert("S", new SkPDFName("Alpha"))->unref();
+    sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
+
+    SkPDFGraphicState* result = new SkPDFGraphicState;
+    result->fPopulated = true;
+    result->fSMask = true;
+    result->insert("Type", new SkPDFName("ExtGState"))->unref();
+    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->insert("Type", new SkPDFName("ExtGState"))->unref();
+        noSMaskGS->insert("SMask", new SkPDFName("None"))->unref();
+    }
+    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;
+        insert("Type", new SkPDFName("ExtGState"))->unref();
+
+        SkRefPtr<SkPDFScalar> alpha =
+            new SkPDFScalar(fPaint.getAlpha() * SkScalarInvert(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);
+        insert("LC", new SkPDFInt(fPaint.getStrokeCap()))->unref();
+
+        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);
+        insert("LJ", new SkPDFInt(fPaint.getStrokeJoin()))->unref();
+
+        insert("LW", new SkPDFScalar(fPaint.getStrokeWidth()))->unref();
+        insert("ML", new SkPDFScalar(fPaint.getStrokeMiter()))->unref();
+        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);
+        }
+        insert("BM",
+               new SkPDFName(blend_mode_from_xfermode(xfermode)))->unref();
+    }
+}
+
+// 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/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp
new file mode 100644
index 0000000..be69f7f
--- /dev/null
+++ b/src/pdf/SkPDFImage.cpp
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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->append(new SkPDFName("Indexed"))->unref();
+    result->append(new SkPDFName("DeviceRGB"))->unref();;
+    result->append(new SkPDFInt(table->count() - 1))->unref();
+
+    // 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::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                             bool indirect) {
+    if (indirect)
+        return emitIndirectObject(stream, catalog);
+
+    fStream->emitObject(stream, catalog, indirect);
+}
+
+size_t SkPDFImage::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    if (indirect)
+        return getIndirectOutputSize(catalog);
+
+    return fStream->getOutputSize(catalog, indirect);
+}
+
+void SkPDFImage::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+    if (fResources.count()) {
+        resourceList->setReserve(resourceList->count() + fResources.count());
+        for (int i = 0; i < fResources.count(); i++) {
+            resourceList->push(fResources[i]);
+            fResources[i]->ref();
+            fResources[i]->getResources(resourceList);
+        }
+    }
+}
+
+SkPDFImage::SkPDFImage(SkStream* imageData, const SkBitmap& bitmap,
+                       const SkIRect& srcRect, bool doingAlpha,
+                       const SkPaint& paint) {
+    fStream = new SkPDFStream(imageData);
+    fStream->unref();  // SkRefPtr and new both took a reference.
+
+    SkBitmap::Config config = bitmap.getConfig();
+    bool alphaOnly = (config == SkBitmap::kA1_Config ||
+                      config == SkBitmap::kA8_Config);
+
+    insert("Type", new SkPDFName("XObject"))->unref();
+    insert("Subtype", new SkPDFName("Image"))->unref();
+
+    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 {
+        insert("Width", new SkPDFInt(srcRect.width()))->unref();
+        insert("Height", new SkPDFInt(srcRect.height()))->unref();
+    }
+
+    // if (!image mask) {
+    if (doingAlpha || alphaOnly) {
+        insert("ColorSpace", new SkPDFName("DeviceGray"))->unref();
+    } else if (config == SkBitmap::kIndex8_Config ||
+        config == SkBitmap::kRLE_Index8_Config) {
+        insert("ColorSpace",
+               makeIndexedColorSpace(bitmap.getColorTable()))->unref();
+    } else {
+        insert("ColorSpace", new SkPDFName("DeviceRGB"))->unref();
+    }
+    // }
+
+    int bitsPerComp = 8;
+    if (config == SkBitmap::kARGB_4444_Config)
+        bitsPerComp = 4;
+    else if (doingAlpha && config == SkBitmap::kA1_Config)
+        bitsPerComp = 1;
+    insert("BitsPerComponent", new SkPDFInt(bitsPerComp))->unref();
+
+    if (config == SkBitmap::kRGB_565_Config) {
+        SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
+        zeroVal->unref();  // SkRefPtr and new both took a reference.
+        SkRefPtr<SkPDFScalar> scale5Val =
+                new SkPDFScalar(8.2258f);  // 255/2^5-1
+        scale5Val->unref();  // SkRefPtr and new both took a reference.
+        SkRefPtr<SkPDFScalar> scale6Val =
+                new SkPDFScalar(4.0476f);  // 255/2^6-1
+        scale6Val->unref();  // SkRefPtr and new both took a reference.
+        SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
+        decodeValue->unref();  // SkRefPtr and new both took a reference.
+        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());
+    }
+}
+
+SkPDFObject* SkPDFImage::insert(SkPDFName* key, SkPDFObject* value) {
+    return fStream->insert(key, value);
+}
+
+SkPDFObject* SkPDFImage::insert(const char key[], SkPDFObject* value) {
+    return fStream->insert(key, value);
+}
diff --git a/src/pdf/SkPDFPage.cpp b/src/pdf/SkPDFPage.cpp
new file mode 100644
index 0000000..2a8183d
--- /dev/null
+++ b/src/pdf/SkPDFPage.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
+#include "SkPDFPage.h"
+#include "SkStream.h"
+
+SkPDFPage::SkPDFPage(const SkRefPtr<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().get());
+        insert("MediaBox", fDevice->getMediaBox().get());
+
+        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);
+}
+
+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]);
+                }
+            }
+
+            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();
+}
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
new file mode 100644
index 0000000..6845e09
--- /dev/null
+++ b/src/pdf/SkPDFShader.cpp
@@ -0,0 +1,778 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "SkPDFShader.h"
+
+#include "SkCanvas.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 void transformBBox(const SkMatrix& matrix, SkRect* bbox) {
+    SkMatrix inverse;
+    inverse.reset();
+    matrix.invert(&inverse);
+    inverse.mapRect(bbox);
+}
+
+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;
+}
+
+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;
+}
+
+SkPDFShader::~SkPDFShader() {
+    SkAutoMutexAcquire lock(canonicalShadersMutex());
+    ShaderCanonicalEntry entry(this, fState.get());
+    int index = canonicalShaders().find(entry);
+    SkASSERT(index >= 0);
+    canonicalShaders().removeShuffle(index);
+    fResources.unrefAll();
+}
+
+void SkPDFShader::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                             bool indirect) {
+    if (indirect)
+        return emitIndirectObject(stream, catalog);
+
+    fContent->emitObject(stream, catalog, indirect);
+}
+
+size_t SkPDFShader::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    if (indirect)
+        return getIndirectOutputSize(catalog);
+
+    return fContent->getOutputSize(catalog, indirect);
+}
+
+void SkPDFShader::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();
+    }
+}
+
+// static
+SkPDFShader* SkPDFShader::getPDFShader(const SkShader& shader,
+                                       const SkMatrix& matrix,
+                                       const SkIRect& surfaceBBox) {
+    SkRefPtr<SkPDFShader> pdfShader;
+    SkAutoMutexAcquire lock(canonicalShadersMutex());
+    SkAutoTDelete<State> shaderState(new State(shader, matrix, surfaceBBox));
+
+    ShaderCanonicalEntry entry(NULL, shaderState.get());
+    int index = canonicalShaders().find(entry);
+    if (index >= 0) {
+        SkPDFShader* result = canonicalShaders()[index].fPDFShader;
+        result->ref();
+        return result;
+    }
+    // The PDFShader takes ownership of the shaderSate.
+    pdfShader = new SkPDFShader(shaderState.detach());
+    // Check for a valid shader.
+    if (pdfShader->fContent.get() == NULL) {
+        pdfShader->unref();
+        return NULL;
+    }
+    entry.fPDFShader = pdfShader.get();
+    canonicalShaders().push(entry);
+    return pdfShader.get();  // 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
+SkMutex& SkPDFShader::canonicalShadersMutex() {
+    // This initialization is only thread safe with gcc.
+    static SkMutex gCanonicalShadersMutex;
+    return gCanonicalShadersMutex;
+}
+
+// static
+SkPDFObject* SkPDFShader::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->append(new SkPDFInt(0))->unref();
+        range->append(new SkPDFInt(1))->unref();
+        range->append(new SkPDFInt(0))->unref();
+        range->append(new SkPDFInt(1))->unref();
+        range->append(new SkPDFInt(0))->unref();
+        range->append(new SkPDFInt(1))->unref();
+    }
+    return range;
+}
+
+SkPDFShader::SkPDFShader(State* state) : fState(state) {
+    if (fState.get()->fType == SkShader::kNone_GradientType) {
+        doImageShader();
+    } else {
+        doFunctionShader();
+    }
+}
+
+void SkPDFShader::doFunctionShader() {
+    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.  Not setting fContent will
+            // cause the higher level code to detect the resulting object
+            // as invalid.
+            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::kSweep_GradientType:
+            transformPoints[1] = transformPoints[0];
+            transformPoints[1].fX += 1;
+            codeFunction = &sweepCode;
+            break;
+        case SkShader::kColor_GradientType:
+        case SkShader::kNone_GradientType:
+            SkASSERT(false);
+            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);
+    transformBBox(finalMatrix, &bbox);
+
+    SkRefPtr<SkPDFArray> domain = new SkPDFArray;
+    domain->unref();  // SkRefPtr and new both took a reference.
+    domain->reserve(4);
+    domain->append(new SkPDFScalar(bbox.fLeft))->unref();
+    domain->append(new SkPDFScalar(bbox.fRight))->unref();
+    domain->append(new SkPDFScalar(bbox.fTop))->unref();
+    domain->append(new SkPDFScalar(bbox.fBottom))->unref();
+
+    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;
+        mapperMatrix.invert(&inverseMapperMatrix);
+        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->insert("ShadingType", new SkPDFInt(1))->unref();
+    pdfShader->insert("ColorSpace", new SkPDFName("DeviceRGB"))->unref();
+    pdfShader->insert("Domain", domain.get());
+    pdfShader->insert("Function", new SkPDFObjRef(function.get()))->unref();
+
+    fContent = new SkPDFDict("Pattern");
+    fContent->unref();  // SkRefPtr and new both took a reference.
+    fContent->insert("PatternType", new SkPDFInt(2))->unref();
+    fContent->insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();
+    fContent->insert("Shading", pdfShader.get());
+}
+
+// SkShader* shader, SkMatrix matrix, const SkRect& surfaceBBox
+void SkPDFShader::doImageShader() {
+    fState.get()->fImage.lockPixels();
+
+    SkMatrix finalMatrix = fState.get()->fCanvasTransform;
+    finalMatrix.preConcat(fState.get()->fShaderTransform);
+    SkRect surfaceBBox;
+    surfaceBBox.set(fState.get()->fBBox);
+    transformBBox(finalMatrix, &surfaceBBox);
+
+    SkMatrix unflip;
+    unflip.setTranslate(0, SkScalarRound(surfaceBBox.height()));
+    unflip.preScale(1, -1);
+    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;
+    int width = image->width();
+    int height = 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(1, -1);
+        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(width - 1, 0));
+            canvas.drawRect(rect, paint);
+        }
+
+        rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight,
+                                surfaceBBox.fBottom);
+        if (!rect.isEmpty()) {
+            paint.setColor(image->getColor(width - 1, height - 1));
+            canvas.drawRect(rect, paint);
+        }
+
+        rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0,
+                                surfaceBBox.fBottom);
+        if (!rect.isEmpty()) {
+            paint.setColor(image->getColor(0, height - 1));
+            canvas.drawRect(rect, paint);
+        }
+    }
+
+    // Then expand the left, right, top, then bottom.
+    if (tileModes[0] == SkShader::kClamp_TileMode) {
+        SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, height);
+        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(1, -1);
+                leftMatrix.postTranslate(0, 2 * height);
+                canvas.drawBitmapMatrix(left, leftMatrix);
+            }
+            patternBBox.fLeft = 0;
+        }
+
+        if (surfaceBBox.fRight > width) {
+            SkBitmap right;
+            subset.offset(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(1, -1);
+                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, width, 1);
+        if (surfaceBBox.fTop < 0) {
+            SkBitmap top;
+            SkAssertResult(image->extractSubset(&top, subset));
+
+            SkMatrix topMatrix;
+            topMatrix.setScale(1, -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, height - 1);
+            SkAssertResult(image->extractSubset(&bottom, subset));
+
+            SkMatrix bottomMatrix;
+            bottomMatrix.setScale(1, 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->append(new SkPDFScalar(patternBBox.fLeft))->unref();
+    patternBBoxArray->append(new SkPDFScalar(patternBBox.fTop))->unref();
+    patternBBoxArray->append(new SkPDFScalar(patternBBox.fRight))->unref();
+    patternBBoxArray->append(new SkPDFScalar(patternBBox.fBottom))->unref();
+
+    // 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);
+
+    fContent = new SkPDFStream(content.get());
+    fContent->unref();  // SkRefPtr and new both took a reference.
+    fContent->insert("Type", new SkPDFName("Pattern"))->unref();
+    fContent->insert("PatternType", new SkPDFInt(1))->unref();
+    fContent->insert("PaintType", new SkPDFInt(1))->unref();
+    fContent->insert("TilingType", new SkPDFInt(1))->unref();
+    fContent->insert("BBox", patternBBoxArray.get());
+    fContent->insert("XStep", new SkPDFScalar(patternBBox.width()))->unref();
+    fContent->insert("YStep", new SkPDFScalar(patternBBox.height()))->unref();
+    fContent->insert("Resources", pattern.getResourceDict().get());
+    fContent->insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();
+
+    fState.get()->fImage.unlockPixels();
+}
+
+SkPDFStream* SkPDFShader::makePSFunction(const SkString& psCode,
+                                         SkPDFArray* domain) {
+    SkRefPtr<SkMemoryStream> funcStream =
+        new SkMemoryStream(psCode.c_str(), psCode.size(), true);
+    funcStream->unref();  // SkRefPtr and new both took a reference.
+
+    SkPDFStream* result = new SkPDFStream(funcStream.get());
+    result->insert("FunctionType", new SkPDFInt(4))->unref();
+    result->insert("Domain", domain);
+    result->insert("Range", rangeObject());
+    return result;
+}
+
+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:
+                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) {
+
+    fInfo.fColorCount = 0;
+    fInfo.fColors = NULL;
+    fInfo.fColorOffsets = NULL;
+    shader.getLocalMatrix(&fShaderTransform);
+
+    fType = shader.asAGradient(&fInfo);
+
+    if (fType == SkShader::kNone_GradientType) {
+        SkShader::BitmapType bitmapType;
+        SkMatrix matrix;
+        bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes, NULL);
+        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 = (SkColor*)fColorData.get();
+        fInfo.fColorOffsets = (SkScalar*)(fInfo.fColors + fInfo.fColorCount);
+        shader.asAGradient(&fInfo);
+    }
+}
diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp
new file mode 100644
index 0000000..b1bd5ff
--- /dev/null
+++ b/src/pdf/SkPDFStream.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include "SkFlate.h"
+#include "SkPDFCatalog.h"
+#include "SkPDFStream.h"
+#include "SkStream.h"
+
+SkPDFStream::SkPDFStream(SkStream* stream) {
+    if (SkFlate::HaveFlate())
+        SkAssertResult(SkFlate::Deflate(stream, &fCompressedData));
+
+    if (SkFlate::HaveFlate() &&
+            fCompressedData.getOffset() < stream->getLength()) {
+        fLength = fCompressedData.getOffset();
+        insert("Filter", new SkPDFName("FlateDecode"))->unref();
+    } else {
+        fCompressedData.reset();
+        fPlainData = stream;
+        fLength = fPlainData->getLength();
+    }
+    insert("Length", new SkPDFInt(fLength))->unref();
+}
+
+SkPDFStream::~SkPDFStream() {
+}
+
+void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                             bool indirect) {
+    if (indirect)
+        return emitIndirectObject(stream, catalog);
+
+    this->INHERITED::emitObject(stream, catalog, false);
+    stream->writeText(" stream\n");
+    if (fPlainData.get())
+        stream->write(fPlainData->getMemoryBase(), fLength);
+    else
+        stream->write(fCompressedData.getStream(), fLength);
+    stream->writeText("\nendstream");
+}
+
+size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    if (indirect)
+        return getIndirectOutputSize(catalog);
+
+    return this->INHERITED::getOutputSize(catalog, false) +
+        strlen(" stream\n\nendstream") + fLength;
+}
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
new file mode 100644
index 0000000..b9420eb
--- /dev/null
+++ b/src/pdf/SkPDFTypes.cpp
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "SkPDFCatalog.h"
+#include "SkPDFTypes.h"
+#include "SkStream.h"
+
+#ifdef SK_BUILD_FOR_WIN
+    #define SNPRINTF    _snprintf
+#else
+    #define SNPRINTF    snprintf
+#endif
+
+SkPDFObject::SkPDFObject() {}
+SkPDFObject::~SkPDFObject() {}
+
+size_t SkPDFObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    SkDynamicMemoryWStream buffer;
+    emitObject(&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");
+    emitObject(stream, catalog, false);
+    stream->writeText("\nendobj\n");
+}
+
+SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {}
+SkPDFObjRef::~SkPDFObjRef() {}
+
+size_t SkPDFObject::getIndirectOutputSize(SkPDFCatalog* catalog) {
+    return catalog->getObjectNumberSize(this) + strlen(" obj\n") +
+        this->getOutputSize(catalog, false) + strlen("\nendobj\n");
+}
+
+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() {}
+
+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);
+
+    SkString result("/");
+    for (size_t i = 0; i < input.size(); i++) {
+        if (input[i] & 0x80 || input[i] < '!' || input[i] == '#') {
+            result.append("#");
+            result.appendHex(input[i], 2);
+        } else {
+            result.append(input.c_str() + i, 1);
+        }
+    }
+
+    return result;
+}
+
+SkPDFArray::SkPDFArray() {}
+SkPDFArray::~SkPDFArray() {
+    fValue.safeUnrefAll();
+}
+
+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]->emitObject(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());
+    SkSafeUnref(fValue[offset]);
+    fValue[offset] = value;
+    SkSafeRef(fValue[offset]);
+    return value;
+}
+
+SkPDFObject* SkPDFArray::append(SkPDFObject* value) {
+    SkASSERT(fValue.count() < kMaxLen);
+    SkSafeRef(value);
+    fValue.push(value);
+    return value;
+}
+
+SkPDFDict::SkPDFDict() {}
+
+SkPDFDict::SkPDFDict(const char type[]) {
+    insert("Type", new SkPDFName(type))->unref();
+}
+
+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->emitObject(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) {
+    struct Rec* newEntry = fValue.append();
+    newEntry->key = key;
+    SkSafeRef(newEntry->key);
+    newEntry->value = value;
+    SkSafeRef(newEntry->value);
+    return value;
+}
+
+SkPDFObject* SkPDFDict::insert(const char key[], SkPDFObject* value) {
+    SkRefPtr<SkPDFName> keyName = new SkPDFName(key);
+    keyName->unref();  // SkRefPtr and new both took a reference.
+    return insert(keyName.get(), value);
+}
+
+void SkPDFDict::clear() {
+    for (int i = 0; i < fValue.count(); i++) {
+        SkSafeUnref(fValue[i].key);
+        SkSafeUnref(fValue[i].value);
+    }
+    fValue.reset();
+}
diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp
new file mode 100644
index 0000000..a838427
--- /dev/null
+++ b/src/pdf/SkPDFUtils.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#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];
+    SkAssertResult(matrix.pdfTransform(values));
+
+    SkPDFArray* result = new SkPDFArray;
+    result->reserve(6);
+    for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
+        result->append(new SkPDFScalar(values[i]))->unref();
+    }
+    return result;
+}
+
+// static
+void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
+    SkScalar values[6];
+    SkAssertResult(matrix.pdfTransform(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, SkWStream* content) {
+    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, content);
+                break;
+            case SkPath::kLine_Verb:
+                AppendLine(args[1].fX, args[1].fY, content);
+                break;
+            case SkPath::kQuad_Verb: {
+                // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
+                const SkScalar three = SkIntToScalar(3);
+                args[1].scale(SkIntToScalar(2));
+                SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
+                SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
+                SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
+                SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
+                AppendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY,
+                            content);
+                break;
+            }
+            case SkPath::kCubic_Verb:
+                AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
+                            args[3].fX, args[3].fY, content);
+                break;
+            case SkPath::kClose_Verb:
+                ClosePath(content);
+                break;
+            case SkPath::kDone_Verb:
+                break;
+            default:
+                SkASSERT(false);
+                break;
+        }
+    }
+}
+
+// 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/pdf_files.mk b/src/pdf/pdf_files.mk
new file mode 100644
index 0000000..c9b4fb4
--- /dev/null
+++ b/src/pdf/pdf_files.mk
@@ -0,0 +1,13 @@
+SOURCE := \
+    SkPDFCatalog.cpp \
+    SkPDFDevice.cpp \
+    SkPDFDocument.cpp \
+    SkPDFFont.cpp \
+    SkPDFFormXObject.cpp \
+    SkPDFGraphicState.cpp \
+    SkPDFImage.cpp \
+    SkPDFPage.cpp \
+    SkPDFShader.cpp \
+    SkPDFStream.cpp \
+    SkPDFTypes.cpp \
+    SkPDFUtils.cpp
diff --git a/src/pipe/SkGPipePriv.h b/src/pipe/SkGPipePriv.h
new file mode 100644
index 0000000..fb15536
--- /dev/null
+++ b/src/pipe/SkGPipePriv.h
@@ -0,0 +1,213 @@
+/*
+    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 SkGPipePriv_DEFINED
+#define SkGPipePriv_DEFINED
+
+#include "SkTypes.h"
+
+#define UNIMPLEMENTED
+
+// these must be contiguous, 0...N-1
+enum PaintFlats {
+    kColorFilter_PaintFlat,
+    kDrawLooper_PaintFlat,
+    kMaskFilter_PaintFlat,
+    kPathEffect_PaintFlat,
+    kRasterizer_PaintFlat,
+    kShader_PaintFlat,
+    kXfermode_PaintFlat,
+
+    kLast_PaintFlat = kXfermode_PaintFlat
+};
+#define kCount_PaintFlats   (kLast_PaintFlat + 1)
+
+enum DrawOps {
+    kSkip_DrawOp,   // skip an addition N bytes (N == data)
+
+    // these match Canvas apis
+    kClipPath_DrawOp,
+    kClipRegion_DrawOp,
+    kClipRect_DrawOp,
+    kConcat_DrawOp,
+    kDrawBitmap_DrawOp,
+    kDrawBitmapMatrix_DrawOp,
+    kDrawBitmapRect_DrawOp,
+    kDrawClear_DrawOp,
+    kDrawData_DrawOp,
+    kDrawPaint_DrawOp,
+    kDrawPath_DrawOp,
+    kDrawPicture_DrawOp,
+    kDrawPoints_DrawOp,
+    kDrawPosText_DrawOp,
+    kDrawPosTextH_DrawOp,
+    kDrawRect_DrawOp,
+    kDrawShape_DrawOp,
+    kDrawSprite_DrawOp,
+    kDrawText_DrawOp,
+    kDrawTextOnPath_DrawOp,
+    kDrawVertices_DrawOp,
+    kRestore_DrawOp,
+    kRotate_DrawOp,
+    kSave_DrawOp,
+    kSaveLayer_DrawOp,
+    kScale_DrawOp,
+    kSetMatrix_DrawOp,
+    kSkew_DrawOp,
+    kTranslate_DrawOp,
+
+    kPaintOp_DrawOp,
+
+    kDef_Typeface_DrawOp,
+    kDef_Flattenable_DrawOp,
+
+    kName_Flattenable_DrawOp,   // index <--> name
+
+    // these are signals to playback, not drawing verbs
+    kDone_DrawOp,
+};
+
+/**
+ *  DrawOp packs into a 32bit int as follows
+ *
+ *  DrawOp:8 - Flags:4 - Data:20
+ *
+ *  Flags and Data are called out separately, so we can reuse Data between
+ *  different Ops that might have different Flags. e.g. Data might be a Paint
+ *  index for both drawRect (no flags) and saveLayer (does have flags).
+ *
+ *  All Ops that take a SkPaint use their Data field to store the index to
+ *  the paint (previously defined with kPaintOp_DrawOp).
+ */
+
+#define DRAWOPS_OP_BITS     8
+#define DRAWOPS_FLAG_BITS   4
+#define DRAWOPS_DATA_BITS   20
+
+#define DRAWOPS_OP_MASK     ((1 << DRAWOPS_OP_BITS) - 1)
+#define DRAWOPS_FLAG_MASK   ((1 << DRAWOPS_FLAG_BITS) - 1)
+#define DRAWOPS_DATA_MASK   ((1 << DRAWOPS_DATA_BITS) - 1)
+
+static unsigned DrawOp_unpackOp(uint32_t op32) {
+    return (op32 >> (DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS));
+}
+
+static unsigned DrawOp_unpackFlags(uint32_t op32) {
+    return (op32 >> DRAWOPS_DATA_BITS) & DRAWOPS_FLAG_MASK;
+}
+
+static unsigned DrawOp_unpackData(uint32_t op32) {
+    return op32 & DRAWOPS_DATA_MASK;
+}
+
+static uint32_t DrawOp_packOpFlagData(DrawOps op, unsigned flags, unsigned data) {
+    SkASSERT(0 == (op & ~DRAWOPS_OP_MASK));
+    SkASSERT(0 == (flags & ~DRAWOPS_FLAG_MASK));
+    SkASSERT(0 == (data & ~DRAWOPS_DATA_MASK));
+
+    return (op << DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS) |
+           (flags << DRAWOPS_DATA_BITS) |
+            data;
+}
+
+/** DrawOp specific flag bits
+ */
+
+enum {
+    kSaveLayer_HasBounds_DrawOpFlag = 1 << 0,
+    kSaveLayer_HasPaint_DrawOpFlag = 1 << 1,
+};
+enum {
+    kClear_HasColor_DrawOpFlag  = 1 << 0
+};
+enum {
+    kDrawTextOnPath_HasMatrix_DrawOpFlag = 1 << 0
+};
+enum {
+    kDrawVertices_HasTexs_DrawOpFlag     = 1 << 0,
+    kDrawVertices_HasColors_DrawOpFlag   = 1 << 1,
+    kDrawVertices_HasIndices_DrawOpFlag  = 1 << 2,
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum PaintOps {
+    kReset_PaintOp,     // no arg
+    
+    kFlags_PaintOp,     // arg inline
+    kColor_PaintOp,     // arg 32
+    kStyle_PaintOp,     // arg inline
+    kJoin_PaintOp,      // arg inline
+    kCap_PaintOp,       // arg inline
+    kWidth_PaintOp,     // arg scalar
+    kMiter_PaintOp,// arg scalar
+    
+    kEncoding_PaintOp,  // arg inline - text
+    kHinting_PaintOp,   // arg inline - text
+    kAlign_PaintOp,     // arg inline - text
+    kTextSize_PaintOp,  // arg scalar - text
+    kTextScaleX_PaintOp,// arg scalar - text
+    kTextSkewX_PaintOp, // arg scalar - text
+    kTypeface_PaintOp,  // arg inline (index) - text
+
+    kFlatIndex_PaintOp, // flags=paintflat, data=index
+};
+
+#define PAINTOPS_OP_BITS     8
+#define PAINTOPS_FLAG_BITS   4
+#define PAINTOPS_DATA_BITS   20
+
+#define PAINTOPS_OP_MASK     ((1 << PAINTOPS_OP_BITS) - 1)
+#define PAINTOPS_FLAG_MASK   ((1 << PAINTOPS_FLAG_BITS) - 1)
+#define PAINTOPS_DATA_MASK   ((1 << PAINTOPS_DATA_BITS) - 1)
+
+static unsigned PaintOp_unpackOp(uint32_t op32) {
+    return (op32 >> (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS));
+}
+
+static unsigned PaintOp_unpackFlags(uint32_t op32) {
+    return (op32 >> PAINTOPS_DATA_BITS) & PAINTOPS_FLAG_MASK;
+}
+
+static unsigned PaintOp_unpackData(uint32_t op32) {
+    return op32 & PAINTOPS_DATA_MASK;
+}
+
+static uint32_t PaintOp_packOp(PaintOps op) {
+    SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
+    
+    return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS);
+}
+
+static uint32_t PaintOp_packOpData(PaintOps op, unsigned data) {
+    SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
+    SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK));
+    
+    return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS) | data;
+}
+
+static uint32_t PaintOp_packOpFlagData(PaintOps op, unsigned flags, unsigned data) {
+    SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
+    SkASSERT(0 == (flags & ~PAINTOPS_FLAG_MASK));
+    SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK));
+    
+    return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS) |
+    (flags << PAINTOPS_DATA_BITS) |
+    data;
+}
+
+#endif
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
new file mode 100644
index 0000000..ecb19fd
--- /dev/null
+++ b/src/pipe/SkGPipeRead.cpp
@@ -0,0 +1,570 @@
+/*
+    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.
+ */
+
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkGPipe.h"
+#include "SkGPipePriv.h"
+#include "SkReader32.h"
+#include "SkStream.h"
+
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkMaskFilter.h"
+#include "SkPathEffect.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+static void set_paintflat(SkPaint* paint, SkFlattenable* obj, unsigned paintFlat) {
+    SkASSERT(paintFlat < kCount_PaintFlats);
+    switch (paintFlat) {
+        case kColorFilter_PaintFlat:
+            paint->setColorFilter((SkColorFilter*)obj);
+            break;
+        case kDrawLooper_PaintFlat:
+            paint->setLooper((SkDrawLooper*)obj);
+            break;
+        case kMaskFilter_PaintFlat:
+            paint->setMaskFilter((SkMaskFilter*)obj);
+            break;
+        case kPathEffect_PaintFlat:
+            paint->setPathEffect((SkPathEffect*)obj);
+            break;
+        case kRasterizer_PaintFlat:
+            paint->setRasterizer((SkRasterizer*)obj);
+            break;
+        case kShader_PaintFlat:
+            paint->setShader((SkShader*)obj);
+            break;
+        case kXfermode_PaintFlat:
+            paint->setXfermode((SkXfermode*)obj);
+            break;
+        default:
+            SkASSERT(!"never gets here");
+    }
+}
+
+template <typename T> class SkRefCntTDArray : public SkTDArray<T> {
+public:
+    ~SkRefCntTDArray() { this->unrefAll(); }
+};
+
+class SkGPipeState {
+public:
+    SkGPipeState();
+    ~SkGPipeState();
+
+    void setReader(SkFlattenableReadBuffer* reader) {
+        fReader = reader;
+        fReader->setFactoryPlayback(fFactoryArray.begin(), fFactoryArray.count());
+    }
+
+    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, unsigned index) {
+        SkASSERT(index == fFlatArray.count() + 1);
+        SkFlattenable* obj = fReader->readFlattenable();
+        *fFlatArray.append() = obj;
+    }
+
+    void nameFlattenable(PaintFlats pf, unsigned index) {
+        SkASSERT(index == fFactoryArray.count() + 1);
+        const char* name = fReader->readString();
+        SkFlattenable::Factory fact = SkFlattenable::NameToFactory(name);
+        *fFactoryArray.append() = fact;
+
+        // update this each time we grow the array
+        fReader->setFactoryPlayback(fFactoryArray.begin(), fFactoryArray.count());
+    }
+    
+    void addTypeface() {
+        size_t size = fReader->readU32();
+        const void* data = fReader->skip(SkAlign4(size));
+        SkMemoryStream stream(data, size, false);
+        *fTypefaces.append() = SkTypeface::Deserialize(&stream);
+    }    
+    void setTypeface(SkPaint* paint, unsigned id) {
+        paint->setTypeface(id ? fTypefaces[id - 1] : NULL);
+    }
+    
+    SkFlattenableReadBuffer* fReader;
+
+private:
+    SkPaint                   fPaint;
+    SkTDArray<SkFlattenable*> fFlatArray;
+    SkTDArray<SkTypeface*>    fTypefaces;
+    SkTDArray<SkFlattenable::Factory> fFactoryArray;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> const T* skip(SkReader32* reader, int count = 1) {
+    SkASSERT(count >= 0);
+    size_t size = sizeof(T) * count;
+    SkASSERT(SkAlign4(size) == size);
+    return reinterpret_cast<const T*>(reader->skip(size));
+}
+
+template <typename T> const T* skipAlign(SkReader32* reader, int count = 1) {
+    SkASSERT(count >= 0);
+    size_t size = SkAlign4(sizeof(T) * count);
+    return reinterpret_cast<const T*>(reader->skip(size));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void clipPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    SkPath path;
+    path.unflatten(*reader);
+    canvas->clipPath(path, (SkRegion::Op)DrawOp_unpackData(op32));
+}
+
+static void clipRegion_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    SkRegion rgn;
+    SkReadRegion(reader, &rgn);
+    canvas->clipRegion(rgn, (SkRegion::Op)DrawOp_unpackData(op32));
+}
+
+static void clipRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    canvas->clipRect(*skip<SkRect>(reader), (SkRegion::Op)DrawOp_unpackData(op32));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void setMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    SkMatrix matrix;
+    SkReadMatrix(reader, &matrix);
+    canvas->setMatrix(matrix);
+}
+
+static void concat_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    SkMatrix matrix;
+    SkReadMatrix(reader, &matrix);
+    canvas->concat(matrix);
+}
+
+static void scale_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    const SkScalar* param = skip<SkScalar>(reader, 2);
+    canvas->scale(param[0], param[1]);
+}
+
+static void skew_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    const SkScalar* param = skip<SkScalar>(reader, 2);
+    canvas->skew(param[0], param[1]);
+}
+
+static void rotate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    canvas->rotate(reader->readScalar());
+}
+
+static void translate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    const SkScalar* param = skip<SkScalar>(reader, 2);
+    canvas->translate(param[0], param[1]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void save_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                    SkGPipeState* state) {
+    canvas->save((SkCanvas::SaveFlags)DrawOp_unpackData(op32));
+}
+
+static void saveLayer_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    unsigned flags = DrawOp_unpackFlags(op32);
+    SkCanvas::SaveFlags saveFlags = (SkCanvas::SaveFlags)DrawOp_unpackData(op32);
+
+    const SkRect* bounds = NULL;
+    if (flags & kSaveLayer_HasBounds_DrawOpFlag) {
+        bounds = skip<SkRect>(reader);
+    }
+    const SkPaint* paint = NULL;
+    if (flags & kSaveLayer_HasPaint_DrawOpFlag) {
+        paint = &state->paint();
+    }
+    canvas->saveLayer(bounds, paint, saveFlags);
+}
+
+static void restore_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                       SkGPipeState* state) {
+    canvas->restore();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawClear_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    SkColor color = 0;
+    if (DrawOp_unpackFlags(op32) & kClear_HasColor_DrawOpFlag) {
+        color = reader->readU32();
+    }
+    canvas->clear(color);
+}
+
+static void drawPaint_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    canvas->drawPaint(state->paint());
+}
+
+static void drawPoints_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    SkCanvas::PointMode mode = (SkCanvas::PointMode)DrawOp_unpackFlags(op32);
+    size_t count = reader->readU32();
+    const SkPoint* pts = skip<SkPoint>(reader, count);
+    canvas->drawPoints(mode, count, pts, state->paint());
+}
+
+static void drawRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    canvas->drawRect(*skip<SkRect>(reader), state->paint());
+}
+
+static void drawPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    SkPath path;
+    path.unflatten(*reader);
+    canvas->drawPath(path, state->paint());
+}
+
+static void drawVertices_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                            SkGPipeState* state) {
+    unsigned flags = DrawOp_unpackFlags(op32);
+
+    SkCanvas::VertexMode mode = (SkCanvas::VertexMode)reader->readU32();
+    int vertexCount = reader->readU32();
+    const SkPoint* verts = skip<SkPoint>(reader, vertexCount);
+
+    const SkPoint* texs = NULL;
+    if (flags & kDrawVertices_HasTexs_DrawOpFlag) {
+        texs = skip<SkPoint>(reader, vertexCount);
+    }
+
+    const SkColor* colors = NULL;
+    if (flags & kDrawVertices_HasColors_DrawOpFlag) {
+        colors = skip<SkColor>(reader, vertexCount);
+    }
+
+    // TODO: flatten/unflatten xfermodes
+    SkXfermode* xfer = NULL;
+
+    int indexCount = 0;
+    const uint16_t* indices = NULL;
+    if (flags & kDrawVertices_HasIndices_DrawOpFlag) {
+        indexCount = reader->readU32();
+        indices = skipAlign<uint16_t>(reader, indexCount);
+    }
+
+    canvas->drawVertices(mode, vertexCount, verts, texs, colors, xfer,
+                         indices, indexCount, state->paint());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    size_t len = reader->readU32();
+    const void* text = reader->skip(SkAlign4(len));
+    const SkScalar* xy = skip<SkScalar>(reader, 2);
+    canvas->drawText(text, len, xy[0], xy[1], state->paint());
+}
+
+static void drawPosText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    size_t len = reader->readU32();
+    const void* text = reader->skip(SkAlign4(len));
+    size_t posCount = reader->readU32();    // compute by our writer
+    const SkPoint* pos = skip<SkPoint>(reader, posCount);
+    canvas->drawPosText(text, len, pos, state->paint());
+}
+
+static void drawPosTextH_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    size_t len = reader->readU32();
+    const void* text = reader->skip(SkAlign4(len));
+    size_t posCount = reader->readU32();    // compute by our writer
+    const SkScalar* xpos = skip<SkScalar>(reader, posCount);
+    SkScalar constY = reader->readScalar();
+    canvas->drawPosTextH(text, len, xpos, constY, state->paint());
+}
+
+static void drawTextOnPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                              SkGPipeState* state) {
+    size_t len = reader->readU32();
+    const void* text = reader->skip(SkAlign4(len));
+
+    SkPath path;
+    path.unflatten(*reader);
+
+    SkMatrix matrixStorage;
+    const SkMatrix* matrix = NULL;
+    if (DrawOp_unpackFlags(op32) & kDrawTextOnPath_HasMatrix_DrawOpFlag) {
+        SkReadMatrix(reader, &matrixStorage);
+        matrix = &matrixStorage;
+    }
+
+    canvas->drawTextOnPath(text, len, path, matrix, state->paint());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawBitmap_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+static void drawBitmapMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                                SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+static void drawBitmapRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                              SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+static void drawSprite_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawData_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    // since we don't have a paint, we can use data for our (small) sizes
+    size_t size = DrawOp_unpackData(op32);
+    if (0 == size) {
+        size = reader->readU32();
+    }
+    const void* data = reader->skip(SkAlign4(size));
+    canvas->drawData(data, size);
+}
+
+static void drawShape_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+static void drawPicture_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                           SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void paintOp_rp(SkCanvas*, SkReader32* reader, uint32_t op32,
+                       SkGPipeState* state) {
+    size_t offset = reader->offset();
+    size_t stop = offset + PaintOp_unpackData(op32);
+    SkPaint* p = state->editPaint();
+
+    do {
+        uint32_t p32 = reader->readU32();
+        unsigned op = PaintOp_unpackOp(p32);
+        unsigned data = PaintOp_unpackData(p32);
+
+//        SkDebugf(" read %08X op=%d flags=%d data=%d\n", p32, op, done, data);
+
+        switch (op) {
+            case kReset_PaintOp: p->reset(); break;
+            case kFlags_PaintOp: p->setFlags(data); break;
+            case kColor_PaintOp: p->setColor(reader->readU32()); break;
+            case kStyle_PaintOp: p->setStyle((SkPaint::Style)data); break;
+            case kJoin_PaintOp: p->setStrokeJoin((SkPaint::Join)data); break;
+            case kCap_PaintOp: p->setStrokeCap((SkPaint::Cap)data); break;
+            case kWidth_PaintOp: p->setStrokeWidth(reader->readScalar()); break;
+            case kMiter_PaintOp: p->setStrokeMiter(reader->readScalar()); break;
+            case kEncoding_PaintOp:
+                p->setTextEncoding((SkPaint::TextEncoding)data);
+                break;
+            case kHinting_PaintOp: p->setHinting((SkPaint::Hinting)data); break;
+            case kAlign_PaintOp: p->setTextAlign((SkPaint::Align)data); break;
+            case kTextSize_PaintOp: p->setTextSize(reader->readScalar()); break;
+            case kTextScaleX_PaintOp: p->setTextScaleX(reader->readScalar()); break;
+            case kTextSkewX_PaintOp: p->setTextSkewX(reader->readScalar()); break;
+
+            case kFlatIndex_PaintOp: {
+                PaintFlats pf = (PaintFlats)PaintOp_unpackFlags(p32);
+                unsigned index = data;
+                set_paintflat(p, state->getFlat(index), pf);
+                break;
+            }
+
+            case kTypeface_PaintOp: state->setTypeface(p, data); break;
+            default: SkASSERT(!"bad paintop"); return;
+        }
+        SkASSERT(reader->offset() <= stop);
+    } while (reader->offset() < stop);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void def_Typeface_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState* state) {
+    state->addTypeface();
+}
+
+static void def_PaintFlat_rp(SkCanvas*, SkReader32*, uint32_t op32,
+                             SkGPipeState* state) {
+    PaintFlats pf = (PaintFlats)DrawOp_unpackFlags(op32);
+    unsigned index = DrawOp_unpackData(op32);
+    state->defFlattenable(pf, index);
+}
+
+static void name_PaintFlat_rp(SkCanvas*, SkReader32*, uint32_t op32,
+                              SkGPipeState* state) {
+    PaintFlats pf = (PaintFlats)DrawOp_unpackFlags(op32);
+    unsigned index = DrawOp_unpackData(op32);
+    state->nameFlattenable(pf, index);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void skip_rp(SkCanvas*, SkReader32* reader, uint32_t op32, SkGPipeState*) {
+    size_t bytes = DrawOp_unpackData(op32);
+    (void)reader->skip(bytes);
+}
+
+static void done_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState*) {}
+
+typedef void (*ReadProc)(SkCanvas*, SkReader32*, uint32_t op32, SkGPipeState*);
+
+static const ReadProc gReadTable[] = {
+    skip_rp,
+    clipPath_rp,
+    clipRegion_rp,
+    clipRect_rp,
+    concat_rp,
+    drawBitmap_rp,
+    drawBitmapMatrix_rp,
+    drawBitmapRect_rp,
+    drawClear_rp,
+    drawData_rp,
+    drawPaint_rp,
+    drawPath_rp,
+    drawPicture_rp,
+    drawPoints_rp,
+    drawPosText_rp,
+    drawPosTextH_rp,
+    drawRect_rp,
+    drawShape_rp,
+    drawSprite_rp,
+    drawText_rp,
+    drawTextOnPath_rp,
+    drawVertices_rp,
+    restore_rp,
+    rotate_rp,
+    save_rp,
+    saveLayer_rp,
+    scale_rp,
+    setMatrix_rp,
+    skew_rp,
+    translate_rp,
+
+    paintOp_rp,
+    def_Typeface_rp,
+    def_PaintFlat_rp,
+    name_PaintFlat_rp,
+
+    done_rp
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGPipeState::SkGPipeState() {}
+
+SkGPipeState::~SkGPipeState() {
+    fTypefaces.unrefAll();
+    fFlatArray.unrefAll();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGPipe.h"
+
+SkGPipeReader::SkGPipeReader(SkCanvas* target) {
+    SkSafeRef(target);
+    fCanvas = target;
+    fState = NULL;
+}
+
+SkGPipeReader::~SkGPipeReader() {
+    SkSafeUnref(fCanvas);
+    delete fState;
+}
+
+SkGPipeReader::Status SkGPipeReader::playback(const void* data, size_t length,
+                                              size_t* bytesRead) {
+    if (NULL == fCanvas) {
+        return kError_Status;
+    }
+
+    if (NULL == fState) {
+        fState = new SkGPipeState;
+    }
+
+    SkASSERT(SK_ARRAY_COUNT(gReadTable) == (kDone_DrawOp + 1));
+
+    const ReadProc* table = gReadTable;
+    SkFlattenableReadBuffer reader(data, length);
+    SkCanvas* canvas = fCanvas;
+    Status status = kEOF_Status;
+
+    fState->setReader(&reader);
+    while (!reader.eof()) {
+        uint32_t op32 = reader.readU32();
+        unsigned op = DrawOp_unpackOp(op32);
+        SkDEBUGCODE(DrawOps drawOp = (DrawOps)op;)
+        
+        if (op >= SK_ARRAY_COUNT(gReadTable)) {
+            SkDebugf("---- bad op during GPipeState::playback\n");
+            status = kError_Status;
+            break;
+        }
+        if (kDone_DrawOp == op) {
+            status = kDone_Status;
+            break;
+        }
+        table[op](canvas, &reader, op32, fState);
+    }
+
+    if (bytesRead) {
+        *bytesRead = reader.offset();
+    }
+    return status;
+}
+
+
diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp
new file mode 100644
index 0000000..428f864
--- /dev/null
+++ b/src/pipe/SkGPipeWrite.cpp
@@ -0,0 +1,813 @@
+/*
+    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.
+ */
+
+
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkGPipe.h"
+#include "SkGPipePriv.h"
+#include "SkStream.h"
+#include "SkTSearch.h"
+#include "SkTypeface.h"
+#include "SkWriter32.h"
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkMaskFilter.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+
+static SkFlattenable* get_paintflat(const SkPaint& paint, unsigned paintFlat) {
+    SkASSERT(paintFlat < kCount_PaintFlats);
+    switch (paintFlat) {
+        case kColorFilter_PaintFlat:    return paint.getColorFilter();
+        case kDrawLooper_PaintFlat:     return paint.getLooper();
+        case kMaskFilter_PaintFlat:     return paint.getMaskFilter();
+        case kPathEffect_PaintFlat:     return paint.getPathEffect();
+        case kRasterizer_PaintFlat:     return paint.getRasterizer();
+        case kShader_PaintFlat:         return paint.getShader();
+        case kXfermode_PaintFlat:       return paint.getXfermode();
+    }
+    SkASSERT(!"never gets here");
+    return NULL;
+}
+
+static size_t estimateFlattenSize(const SkPath& path) {
+    int n = path.countPoints();
+    size_t bytes = 3 * sizeof(int32_t);
+    bytes += n * sizeof(SkPoint);
+    bytes += SkAlign4(n + 2);    // verbs: add 2 for move/close extras
+
+#ifdef SK_DEBUG
+    {
+        SkWriter32 writer(1024);
+        path.flatten(writer);
+        SkASSERT(writer.size() <= bytes);
+    }
+#endif
+    return bytes;
+}
+
+static size_t writeTypeface(SkWriter32* writer, SkTypeface* typeface) {
+    SkASSERT(typeface);
+    SkDynamicMemoryWStream stream;
+    typeface->serialize(&stream);
+    size_t size = stream.getOffset();
+    if (writer) {
+        writer->write32(size);
+        writer->write(stream.getStream(), size);
+    }
+    return 4 + size;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkGPipeCanvas : public SkCanvas {
+public:
+    SkGPipeCanvas(SkGPipeController*, SkWriter32*, SkFactorySet*);
+    virtual ~SkGPipeCanvas();
+
+    void finish() {
+        if (!fDone) {
+            this->writeOp(kDone_DrawOp);
+            this->doNotify();
+            fDone = true;
+        }
+    }
+
+    // overrides from SkCanvas
+    virtual int save(SaveFlags);
+    virtual int saveLayer(const SkRect* bounds, const SkPaint*, SaveFlags);
+    virtual void restore();
+    virtual bool translate(SkScalar dx, SkScalar dy);
+    virtual bool scale(SkScalar sx, SkScalar sy);
+    virtual bool rotate(SkScalar degrees);
+    virtual bool skew(SkScalar sx, SkScalar sy);
+    virtual bool concat(const SkMatrix& matrix);
+    virtual void setMatrix(const SkMatrix& matrix);
+    virtual bool clipRect(const SkRect& rect, SkRegion::Op op);
+    virtual bool clipPath(const SkPath& path, SkRegion::Op op);
+    virtual bool clipRegion(const SkRegion& region, SkRegion::Op op);
+    virtual void clear(SkColor);
+    virtual void drawPaint(const SkPaint& paint);
+    virtual void drawPoints(PointMode, size_t count, const SkPoint pts[],
+                            const SkPaint&);
+    virtual void drawRect(const SkRect& rect, const SkPaint&);
+    virtual void drawPath(const SkPath& path, const SkPaint&);
+    virtual void drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+                            const SkPaint*);
+    virtual void drawBitmapRect(const SkBitmap&, const SkIRect* src,
+                                const SkRect& dst, const SkPaint*);
+    virtual void drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
+                                  const SkPaint*);
+    virtual void drawSprite(const SkBitmap&, int left, int top,
+                            const SkPaint*);
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x, 
+                          SkScalar y, const SkPaint&);
+    virtual void drawPosText(const void* text, size_t byteLength, 
+                             const SkPoint pos[], const SkPaint&);
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                      const SkScalar xpos[], SkScalar constY, const SkPaint&);
+    virtual void drawTextOnPath(const void* text, size_t byteLength, 
+                            const SkPath& path, const SkMatrix* matrix, 
+                                const SkPaint&);
+    virtual void drawPicture(SkPicture& picture);
+    virtual void drawShape(SkShape*);
+    virtual void drawVertices(VertexMode, int vertexCount,
+                          const SkPoint vertices[], const SkPoint texs[],
+                          const SkColor colors[], SkXfermode*,
+                          const uint16_t indices[], int indexCount,
+                              const SkPaint&);
+    virtual void drawData(const void*, size_t);
+
+private:
+    SkFactorySet* fFactorySet;  // optional, only used if cross-process
+    SkGPipeController* fController;
+    SkWriter32& fWriter;
+    size_t      fBlockSize; // amount allocated for writer
+    size_t      fBytesNotified;
+    bool        fDone;
+
+    SkRefCntSet fTypefaceSet;
+
+    uint32_t getTypefaceID(SkTypeface*);
+
+    inline void writeOp(DrawOps op, unsigned flags, unsigned data) {
+        fWriter.write32(DrawOp_packOpFlagData(op, flags, data));
+    }
+    
+    inline void writeOp(DrawOps op) {
+        fWriter.write32(DrawOp_packOpFlagData(op, 0, 0));
+    }
+
+    bool needOpBytes(size_t size = 0);
+
+    inline void doNotify() {
+        if (!fDone) {
+            size_t bytes = fWriter.size() - fBytesNotified;
+            fController->notifyWritten(bytes);
+            fBytesNotified += bytes;
+        }
+    }
+
+    struct FlatData {
+        uint32_t    fIndex; // always > 0
+        uint32_t    fSize;
+
+        void*       data() { return (char*)this + sizeof(*this); }
+        
+        static int Compare(const FlatData* a, const FlatData* b) {
+            return memcmp(&a->fSize, &b->fSize, a->fSize + sizeof(a->fSize));
+        }
+    };
+    SkTDArray<FlatData*> fFlatArray;
+    int fCurrFlatIndex[kCount_PaintFlats];
+    int flattenToIndex(SkFlattenable* obj, PaintFlats);
+
+    SkPaint fPaint;
+    void writePaint(const SkPaint&);
+
+    class AutoPipeNotify {
+    public:
+        AutoPipeNotify(SkGPipeCanvas* canvas) : fCanvas(canvas) {}
+        ~AutoPipeNotify() { fCanvas->doNotify(); }
+    private:
+        SkGPipeCanvas* fCanvas;
+    };
+    friend class AutoPipeNotify;
+
+    typedef SkCanvas INHERITED;
+};
+
+// return 0 for NULL (or unflattenable obj), or index-base-1
+int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) {
+    if (NULL == obj) {
+        return 0;
+    }
+    
+    SkFlattenable::Factory fact = obj->getFactory();
+    if (NULL == fact) {
+        return 0;
+    }
+
+    if (fFactorySet) {
+        uint32_t id = fFactorySet->find((void*)fact);
+        if (0 == id) {
+            const char* name = SkFlattenable::FactoryToName(fact);
+            if (NULL == name) {
+                return 0;
+            }
+            size_t len = strlen(name);
+            size_t size = SkWriter32::WriteStringSize(name, len);
+            if (!this->needOpBytes(size)) {
+                return 0;
+            }
+            unsigned id = fFactorySet->add(fact);
+            this->writeOp(kName_Flattenable_DrawOp, paintflat, id);
+            fWriter.writeString(name, len);
+        }
+    }
+    
+    SkFlattenableWriteBuffer tmpWriter(1024);
+    tmpWriter.setFactoryRecorder(fFactorySet);
+
+    tmpWriter.writeFlattenable(obj);
+    size_t len = tmpWriter.size();
+    size_t allocSize = len + sizeof(FlatData);
+
+    SkAutoSMalloc<1024> storage(allocSize);
+    FlatData* flat = (FlatData*)storage.get();
+    flat->fSize = len;
+    tmpWriter.flatten(flat->data());
+
+    int index = SkTSearch<FlatData>((const FlatData**)fFlatArray.begin(),
+                                    fFlatArray.count(), flat, sizeof(flat),
+                                    &FlatData::Compare);
+    if (index < 0) {
+        index = ~index;
+        FlatData* copy = (FlatData*)sk_malloc_throw(allocSize);
+        memcpy(copy, flat, allocSize);
+        *fFlatArray.insert(index) = copy;
+        // call this after the insert, so that count() will have been grown
+        copy->fIndex = fFlatArray.count();
+//        SkDebugf("--- add flattenable[%d] size=%d index=%d\n", paintflat, len, copy->fIndex);
+
+        if (this->needOpBytes(len)) {
+            this->writeOp(kDef_Flattenable_DrawOp, paintflat, copy->fIndex);
+            fWriter.write(copy->data(), len);
+        }
+    }
+    return fFlatArray[index]->fIndex;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define MIN_BLOCK_SIZE  (16 * 1024)
+
+SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller,
+                             SkWriter32* writer, SkFactorySet* fset)
+        : fWriter(*writer), fFactorySet(fset) {
+    fController = controller;
+    fDone = false;
+    fBlockSize = 0; // need first block from controller
+    sk_bzero(fCurrFlatIndex, sizeof(fCurrFlatIndex));
+
+    // we need a device to limit our clip
+    // should the caller give us the bounds?
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32767, 32767);
+    SkDevice* device = SkNEW_ARGS(SkDevice, (this, bitmap, false));
+    this->setDevice(device)->unref();
+}
+
+SkGPipeCanvas::~SkGPipeCanvas() {
+    this->finish();
+
+    fFlatArray.freeAll();
+}
+
+bool SkGPipeCanvas::needOpBytes(size_t needed) {
+    if (fDone) {
+        return false;
+    }
+
+    needed += 4;  // size of DrawOp atom
+    if (fWriter.size() + needed > fBlockSize) {
+        void* block = fController->requestBlock(MIN_BLOCK_SIZE, &fBlockSize);
+        if (NULL == block) {
+            fDone = true;
+            return false;
+        }
+        fWriter.reset(block, fBlockSize);
+        fBytesNotified = 0;
+    }
+    return true;
+}
+
+uint32_t SkGPipeCanvas::getTypefaceID(SkTypeface* face) {
+    uint32_t id = 0; // 0 means default/null typeface
+    if (face) {
+        id = fTypefaceSet.find(face);
+        if (0 == id) {
+            id = fTypefaceSet.add(face);
+            size_t size = writeTypeface(NULL, face);
+            if (this->needOpBytes(size)) {
+                this->writeOp(kDef_Typeface_DrawOp);
+                writeTypeface(&fWriter, face);
+            }
+        }
+    }
+    return id;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define NOTIFY_SETUP(canvas)    \
+    AutoPipeNotify apn(canvas)
+
+int SkGPipeCanvas::save(SaveFlags flags) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes()) {
+        this->writeOp(kSave_DrawOp, 0, flags);
+    }
+    return this->INHERITED::save(flags);
+}
+
+int SkGPipeCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                             SaveFlags saveFlags) {
+    NOTIFY_SETUP(this);
+    size_t size = 0;
+    unsigned opFlags = 0;
+    
+    if (bounds) {
+        opFlags |= kSaveLayer_HasBounds_DrawOpFlag;
+        size += sizeof(SkRect);
+    }
+    if (paint) {
+        opFlags |= kSaveLayer_HasPaint_DrawOpFlag;
+        this->writePaint(*paint);
+    }
+
+    if (this->needOpBytes(size)) {
+        this->writeOp(kSaveLayer_DrawOp, opFlags, saveFlags);
+        if (bounds) {
+            fWriter.writeRect(*bounds);
+        }
+    }
+    
+    // we just pass on the save, so we don't create a layer
+    return this->INHERITED::save(saveFlags);
+}
+
+void SkGPipeCanvas::restore() {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes()) {
+        this->writeOp(kRestore_DrawOp);
+    }
+    this->INHERITED::restore();
+}
+
+bool SkGPipeCanvas::translate(SkScalar dx, SkScalar dy) {
+    if (dx || dy) {
+        NOTIFY_SETUP(this);
+        if (this->needOpBytes(2 * sizeof(SkScalar))) {
+            this->writeOp(kTranslate_DrawOp);
+            fWriter.writeScalar(dx);
+            fWriter.writeScalar(dy);
+        }
+    }
+    return this->INHERITED::translate(dx, dy);
+}
+
+bool SkGPipeCanvas::scale(SkScalar sx, SkScalar sy) {
+    if (sx || sy) {
+        NOTIFY_SETUP(this);
+        if (this->needOpBytes(2 * sizeof(SkScalar))) {
+            this->writeOp(kScale_DrawOp);
+            fWriter.writeScalar(sx);
+            fWriter.writeScalar(sy);
+        }
+    }
+    return this->INHERITED::scale(sx, sy);
+}
+
+bool SkGPipeCanvas::rotate(SkScalar degrees) {
+    if (degrees) {
+        NOTIFY_SETUP(this);
+        if (this->needOpBytes(sizeof(SkScalar))) {
+            this->writeOp(kRotate_DrawOp);
+            fWriter.writeScalar(degrees);
+        }
+    }
+    return this->INHERITED::rotate(degrees);
+}
+
+bool SkGPipeCanvas::skew(SkScalar sx, SkScalar sy) {
+    if (sx || sy) {
+        NOTIFY_SETUP(this);
+        if (this->needOpBytes(2 * sizeof(SkScalar))) {
+            this->writeOp(kSkew_DrawOp);
+            fWriter.writeScalar(sx);
+            fWriter.writeScalar(sy);
+        }
+    }
+    return this->INHERITED::skew(sx, sy);
+}
+
+bool SkGPipeCanvas::concat(const SkMatrix& matrix) {
+    if (!matrix.isIdentity()) {
+        NOTIFY_SETUP(this);
+        if (this->needOpBytes(matrix.flatten(NULL))) {
+            this->writeOp(kConcat_DrawOp);
+            SkWriteMatrix(&fWriter, matrix);
+        }
+    }
+    return this->INHERITED::concat(matrix);
+}
+
+void SkGPipeCanvas::setMatrix(const SkMatrix& matrix) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(matrix.flatten(NULL))) {
+        this->writeOp(kSetMatrix_DrawOp);
+        SkWriteMatrix(&fWriter, matrix);
+    }
+    this->INHERITED::setMatrix(matrix);
+}
+
+bool SkGPipeCanvas::clipRect(const SkRect& rect, SkRegion::Op rgnOp) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(sizeof(SkRect))) {
+        this->writeOp(kClipRect_DrawOp, 0, rgnOp);
+        fWriter.writeRect(rect);
+    }
+    return this->INHERITED::clipRect(rect, rgnOp);
+}
+
+bool SkGPipeCanvas::clipPath(const SkPath& path, SkRegion::Op rgnOp) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(estimateFlattenSize(path))) {
+        this->writeOp(kClipPath_DrawOp, 0, rgnOp);
+        path.flatten(fWriter);
+    }
+    // we just pass on the bounds of the path
+    return this->INHERITED::clipRect(path.getBounds(), rgnOp);
+}
+
+bool SkGPipeCanvas::clipRegion(const SkRegion& region, SkRegion::Op rgnOp) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(region.flatten(NULL))) {
+        this->writeOp(kClipRegion_DrawOp, 0, rgnOp);
+        SkWriteRegion(&fWriter, region);
+    }
+    return this->INHERITED::clipRegion(region, rgnOp);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGPipeCanvas::clear(SkColor color) {
+    NOTIFY_SETUP(this);
+    unsigned flags = 0;
+    if (color) {
+        flags |= kClear_HasColor_DrawOpFlag;
+    }
+    if (this->needOpBytes(sizeof(SkColor))) {
+        this->writeOp(kDrawClear_DrawOp, flags, 0);
+        if (color) {
+            fWriter.write32(color);
+        }
+    }
+}
+
+void SkGPipeCanvas::drawPaint(const SkPaint& paint) {
+    NOTIFY_SETUP(this);
+    this->writePaint(paint);
+    if (this->needOpBytes()) {
+        this->writeOp(kDrawPaint_DrawOp);
+    }
+}
+
+void SkGPipeCanvas::drawPoints(PointMode mode, size_t count,
+                                   const SkPoint pts[], const SkPaint& paint) {
+    if (count) {
+        NOTIFY_SETUP(this);
+        this->writePaint(paint);
+        if (this->needOpBytes(4 + count * sizeof(SkPoint))) {
+            this->writeOp(kDrawPoints_DrawOp, mode, 0);
+            fWriter.write32(count);
+            fWriter.write(pts, count * sizeof(SkPoint));
+        }
+    }
+}
+
+void SkGPipeCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+    NOTIFY_SETUP(this);
+    this->writePaint(paint);
+    if (this->needOpBytes(sizeof(SkRect))) {
+        this->writeOp(kDrawRect_DrawOp);
+        fWriter.writeRect(rect);
+    }
+}
+
+void SkGPipeCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    NOTIFY_SETUP(this);
+    this->writePaint(paint);
+    if (this->needOpBytes(estimateFlattenSize(path))) {
+        this->writeOp(kDrawPath_DrawOp);
+        path.flatten(fWriter);
+    }
+}
+
+void SkGPipeCanvas::drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+                                   const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawBitmapRect(const SkBitmap&, const SkIRect* src,
+                                       const SkRect& dst, const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
+                                         const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawSprite(const SkBitmap&, int left, int top,
+                                   const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawText(const void* text, size_t byteLength, SkScalar x, 
+                                 SkScalar y, const SkPaint& paint) {
+    if (byteLength) {
+        NOTIFY_SETUP(this);
+        this->writePaint(paint);
+        if (this->needOpBytes(4 + SkAlign4(byteLength) + 2 * sizeof(SkScalar))) {
+            this->writeOp(kDrawText_DrawOp);
+            fWriter.write32(byteLength);
+            fWriter.writePad(text, byteLength);
+            fWriter.writeScalar(x);
+            fWriter.writeScalar(y);
+        }
+    }
+}
+
+void SkGPipeCanvas::drawPosText(const void* text, size_t byteLength, 
+                                const SkPoint pos[], const SkPaint& paint) {
+    if (byteLength) {
+        NOTIFY_SETUP(this);
+        this->writePaint(paint);
+        int count = paint.textToGlyphs(text, byteLength, NULL);
+        if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkPoint))) {
+            this->writeOp(kDrawPosText_DrawOp);
+            fWriter.write32(byteLength);
+            fWriter.writePad(text, byteLength);
+            fWriter.write32(count);
+            fWriter.write(pos, count * sizeof(SkPoint));
+        }
+    }
+}
+
+void SkGPipeCanvas::drawPosTextH(const void* text, size_t byteLength,
+                                 const SkScalar xpos[], SkScalar constY,
+                                 const SkPaint& paint) {
+    if (byteLength) {
+        NOTIFY_SETUP(this);
+        this->writePaint(paint);
+        int count = paint.textToGlyphs(text, byteLength, NULL);
+        if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkScalar) + 4)) {
+            this->writeOp(kDrawPosTextH_DrawOp);
+            fWriter.write32(byteLength);
+            fWriter.writePad(text, byteLength);
+            fWriter.write32(count);
+            fWriter.write(xpos, count * sizeof(SkScalar));
+            fWriter.writeScalar(constY);
+        }
+    }
+}
+
+void SkGPipeCanvas::drawTextOnPath(const void* text, size_t byteLength, 
+                                   const SkPath& path, const SkMatrix* matrix, 
+                                   const SkPaint& paint) {
+    if (byteLength) {
+        NOTIFY_SETUP(this);
+        unsigned flags = 0;
+        size_t size = 4 + SkAlign4(byteLength) + estimateFlattenSize(path);
+        if (matrix) {
+            flags |= kDrawTextOnPath_HasMatrix_DrawOpFlag;
+            size += matrix->flatten(NULL);
+        }
+        this->writePaint(paint);
+        if (this->needOpBytes(size)) {
+            this->writeOp(kDrawTextOnPath_DrawOp, flags, 0);
+
+            fWriter.write32(byteLength);
+            fWriter.writePad(text, byteLength);
+
+            path.flatten(fWriter);
+            if (matrix) {
+                SkWriteMatrix(&fWriter, *matrix);
+            }
+        }
+    }
+}
+
+void SkGPipeCanvas::drawPicture(SkPicture& picture) {
+    // we want to playback the picture into individual draw calls
+    this->INHERITED::drawPicture(picture);
+}
+
+void SkGPipeCanvas::drawShape(SkShape* shape) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawVertices(VertexMode mode, int vertexCount,
+                                 const SkPoint vertices[], const SkPoint texs[],
+                                 const SkColor colors[], SkXfermode*,
+                                 const uint16_t indices[], int indexCount,
+                                 const SkPaint& paint) {
+    if (0 == vertexCount) {
+        return;
+    }
+
+    NOTIFY_SETUP(this);
+    size_t size = 4 + vertexCount * sizeof(SkPoint);
+    this->writePaint(paint);
+    unsigned flags = 0;
+    if (texs) {
+        flags |= kDrawVertices_HasTexs_DrawOpFlag;
+        size += vertexCount * sizeof(SkPoint);
+    }
+    if (colors) {
+        flags |= kDrawVertices_HasColors_DrawOpFlag;
+        size += vertexCount * sizeof(SkColor);
+    }
+    if (indices && indexCount > 0) {
+        flags |= kDrawVertices_HasIndices_DrawOpFlag;
+        size += 4 + SkAlign4(indexCount * sizeof(uint16_t));
+    }
+    
+    if (this->needOpBytes(size)) {
+        this->writeOp(kDrawVertices_DrawOp, flags, 0);
+        fWriter.write32(mode);
+        fWriter.write32(vertexCount);
+        fWriter.write(vertices, vertexCount * sizeof(SkPoint));
+        if (texs) {
+            fWriter.write(texs, vertexCount * sizeof(SkPoint));
+        }
+        if (colors) {
+            fWriter.write(colors, vertexCount * sizeof(SkColor));
+        }
+
+        // TODO: flatten xfermode
+
+        if (indices && indexCount > 0) {
+            fWriter.write32(indexCount);
+            fWriter.writePad(indices, indexCount * sizeof(uint16_t));
+        }
+    }
+}
+
+void SkGPipeCanvas::drawData(const void* ptr, size_t size) {
+    if (size && ptr) {
+        NOTIFY_SETUP(this);
+        unsigned data = 0;
+        if (size < (1 << DRAWOPS_DATA_BITS)) {
+            data = (unsigned)size;
+        }
+        if (this->needOpBytes(4 + SkAlign4(size))) {
+            this->writeOp(kDrawData_DrawOp, 0, data);
+            if (0 == data) {
+                fWriter.write32(size);
+            }
+            fWriter.writePad(ptr, size);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> uint32_t castToU32(T value) {
+    union {
+        T           fSrc;
+        uint32_t    fDst;
+    } data;
+    data.fSrc = value;
+    return data.fDst;
+}
+
+void SkGPipeCanvas::writePaint(const SkPaint& paint) {
+    SkPaint& base = fPaint;
+    uint32_t storage[32];
+    uint32_t* ptr = storage;
+
+    if (base.getFlags() != paint.getFlags()) {
+        *ptr++ = PaintOp_packOpData(kFlags_PaintOp, paint.getFlags());
+        base.setFlags(paint.getFlags());
+    }
+    if (base.getColor() != paint.getColor()) {
+        *ptr++ = PaintOp_packOp(kColor_PaintOp);
+        *ptr++ = paint.getColor();
+        base.setColor(paint.getColor());
+    }
+    if (base.getStyle() != paint.getStyle()) {
+        *ptr++ = PaintOp_packOpData(kStyle_PaintOp, paint.getStyle());
+        base.setStyle(paint.getStyle());
+    }
+    if (base.getStrokeJoin() != paint.getStrokeJoin()) {
+        *ptr++ = PaintOp_packOpData(kJoin_PaintOp, paint.getStrokeJoin());
+        base.setStrokeJoin(paint.getStrokeJoin());
+    }
+    if (base.getStrokeCap() != paint.getStrokeCap()) {
+        *ptr++ = PaintOp_packOpData(kCap_PaintOp, paint.getStrokeCap());
+        base.setStrokeCap(paint.getStrokeCap());
+    }
+    if (base.getStrokeWidth() != paint.getStrokeWidth()) {
+        *ptr++ = PaintOp_packOp(kWidth_PaintOp);
+        *ptr++ = castToU32(paint.getStrokeWidth());
+        base.setStrokeWidth(paint.getStrokeWidth());
+    }
+    if (base.getStrokeMiter() != paint.getStrokeMiter()) {
+        *ptr++ = PaintOp_packOp(kMiter_PaintOp);
+        *ptr++ = castToU32(paint.getStrokeMiter());
+        base.setStrokeMiter(paint.getStrokeMiter());
+    }
+    if (base.getTextEncoding() != paint.getTextEncoding()) {
+        *ptr++ = PaintOp_packOpData(kEncoding_PaintOp, paint.getTextEncoding());
+        base.setTextEncoding(paint.getTextEncoding());
+    }
+    if (base.getHinting() != paint.getHinting()) {
+        *ptr++ = PaintOp_packOpData(kHinting_PaintOp, paint.getHinting());
+        base.setHinting(paint.getHinting());
+    }
+    if (base.getTextAlign() != paint.getTextAlign()) {
+        *ptr++ = PaintOp_packOpData(kAlign_PaintOp, paint.getTextAlign());
+        base.setTextAlign(paint.getTextAlign());
+    }
+    if (base.getTextSize() != paint.getTextSize()) {
+        *ptr++ = PaintOp_packOp(kTextSize_PaintOp);
+        *ptr++ = castToU32(paint.getTextSize());
+        base.setTextSize(paint.getTextSize());
+    }
+    if (base.getTextScaleX() != paint.getTextScaleX()) {
+        *ptr++ = PaintOp_packOp(kTextScaleX_PaintOp);
+        *ptr++ = castToU32(paint.getTextScaleX());
+        base.setTextScaleX(paint.getTextScaleX());
+    }
+    if (base.getTextSkewX() != paint.getTextSkewX()) {
+        *ptr++ = PaintOp_packOp(kTextSkewX_PaintOp);
+        *ptr++ = castToU32(paint.getTextSkewX());
+        base.setTextSkewX(paint.getTextSkewX());
+    }
+
+    if (!SkTypeface::Equal(base.getTypeface(), paint.getTypeface())) {
+        uint32_t id = this->getTypefaceID(paint.getTypeface());
+        *ptr++ = PaintOp_packOpData(kTypeface_PaintOp, id);
+        base.setTypeface(paint.getTypeface());
+    }
+
+    for (int i = 0; i < kCount_PaintFlats; i++) {
+        int index = this->flattenToIndex(get_paintflat(paint, i), (PaintFlats)i);
+        SkASSERT(index >= 0 && index <= fFlatArray.count());
+        if (index != fCurrFlatIndex[i]) {
+            *ptr++ = PaintOp_packOpFlagData(kFlatIndex_PaintOp, i, index);
+            fCurrFlatIndex[i] = index;
+        }
+    }
+
+    size_t size = (char*)ptr - (char*)storage;
+    if (size && this->needOpBytes(size)) {
+        this->writeOp(kPaintOp_DrawOp, 0, size);
+        fWriter.write(storage, size);
+        for (size_t i = 0; i < size/4; i++) {
+//            SkDebugf("[%d] %08X\n", i, storage[i]);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGPipe.h"
+
+SkGPipeWriter::SkGPipeWriter() : fWriter(0) {
+    fCanvas = NULL;
+}
+
+SkGPipeWriter::~SkGPipeWriter() {
+    this->endRecording();
+    SkSafeUnref(fCanvas);
+}
+
+SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller,
+                                        uint32_t flags) {
+    if (NULL == fCanvas) {
+        fWriter.reset(NULL, 0);
+        fFactorySet.reset();
+        fCanvas = SkNEW_ARGS(SkGPipeCanvas, (controller, &fWriter,
+                                             (flags & kCrossProcess_Flag) ?
+                                             &fFactorySet : NULL));
+    }
+    return fCanvas;
+}
+
+void SkGPipeWriter::endRecording() {
+    if (fCanvas) {
+        fCanvas->finish();
+        fCanvas->unref();
+        fCanvas = NULL;
+    }
+}
+
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index b3cc783..de1ce73 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -72,6 +72,14 @@
 
 using namespace skia_advanced_typeface_metrics_utils;
 
+// SK_FREETYPE_LCD_LERP should be 0...256
+//   0 means no color reduction (e.g. just as returned from FreeType)
+//   256 means 100% color reduction (e.g. gray)
+//
+#ifndef SK_FREETYPE_LCD_LERP
+    #define SK_FREETYPE_LCD_LERP    96
+#endif
+
 //////////////////////////////////////////////////////////////////////////
 
 struct SkFaceRec;
@@ -906,7 +914,7 @@
     }
 
     switch ( fFace->glyph->format ) {
-      case FT_GLYPH_FORMAT_OUTLINE:
+      case FT_GLYPH_FORMAT_OUTLINE: {
         FT_BBox bbox;
 
         if (fRec.fFlags & kEmbolden_Flag) {
@@ -934,6 +942,7 @@
         glyph->fTop     = -SkToS16(bbox.yMax >> 6);
         glyph->fLeft    = SkToS16(bbox.xMin >> 6);
         break;
+      }
 
       case FT_GLYPH_FORMAT_BITMAP:
         if (fRec.fFlags & kEmbolden_Flag) {
@@ -979,6 +988,22 @@
 using namespace skia_freetype_support;
 #endif
 
+static int lerp(int start, int end) {
+    SkASSERT((unsigned)SK_FREETYPE_LCD_LERP <= 256);
+    return start + ((end - start) * (SK_FREETYPE_LCD_LERP) >> 8);
+}
+
+static uint16_t packTriple(unsigned r, unsigned g, unsigned b) {
+    if (SK_FREETYPE_LCD_LERP) {
+        // want (a+b+c)/3, but we approx to avoid the divide
+        unsigned ave = (5 * (r + g + b) + b) >> 4;
+        r = lerp(r, ave);
+        g = lerp(g, ave);
+        b = lerp(b, ave);
+    }
+    return SkPackRGB16(r >> 3, g >> 2, b >> 3);
+}
+
 static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap) {
     SkASSERT(glyph.fWidth * 3 == bitmap.width - 6);
     SkASSERT(glyph.fHeight == bitmap.rows);
@@ -991,7 +1016,7 @@
     for (int y = 0; y < glyph.fHeight; y++) {
         const uint8_t* triple = src;
         for (int x = 0; x < width; x++) {
-            dst[x] = SkPackRGB16(triple[0] >> 3, triple[1] >> 2, triple[2] >> 3);
+            dst[x] = packTriple(triple[0], triple[1], triple[2]);
             triple += 3;
         }
         src += bitmap.pitch;
diff --git a/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp
index 9aabce7..37c2c35 100644
--- a/src/ports/SkFontHost_linux.cpp
+++ b/src/ports/SkFontHost_linux.cpp
@@ -391,7 +391,7 @@
 static void load_system_fonts() {
     // check if we've already be called
     if (NULL != gDefaultNormal) {
-        printf("---- default font %p\n", gDefaultNormal);
+//        printf("---- default font %p\n", gDefaultNormal);
         return;
     }
 
diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp
index e637877..ad57e16 100644
--- a/src/ports/SkFontHost_mac_coretext.cpp
+++ b/src/ports/SkFontHost_mac_coretext.cpp
@@ -18,13 +18,17 @@
 
 #include "SkFontHost.h"
 #include "SkDescriptor.h"
+#include "SkEndian.h"
 #include "SkFloatingPoint.h"
 #include "SkPaint.h"
 #include "SkString.h"
+#include "SkStream.h"
 #include "SkTypeface_mac.h"
 #include "SkUtils.h"
 #include "SkTypefaceCache.h"
 
+using namespace skia_advanced_typeface_metrics_utils;
+
 static const size_t FONT_CACHE_MEMORY_BUDGET    = 1024 * 1024;
 static const char FONT_DEFAULT_NAME[]           = "Lucida Sans";
 
@@ -400,6 +404,28 @@
     }
 }
 
+#if 1
+static inline int r32_to_16(int x) { return SkR32ToR16(x); }
+static inline int g32_to_16(int x) { return SkG32ToG16(x); }
+static inline int b32_to_16(int x) { return SkB32ToB16(x); }
+#else
+static inline int round8to5(int x) {
+    return (x + 3 - (x >> 5) + (x >> 7)) >> 3;
+}
+static inline int round8to6(int x) {
+    int xx = (x + 1 - (x >> 6) + (x >> 7)) >> 2;
+    SkASSERT((unsigned)xx <= 63);
+
+    int ix = x >> 2;
+    SkASSERT(SkAbs32(xx - ix) <= 1);
+    return xx;
+}
+
+static inline int r32_to_16(int x) { return round8to5(x); }
+static inline int g32_to_16(int x) { return round8to6(x); }
+static inline int b32_to_16(int x) { return round8to5(x); }
+#endif
+
 static inline uint16_t rgb_to_lcd16(uint32_t rgb) {
     int r = (rgb >> 16) & 0xFF;
     int g = (rgb >>  8) & 0xFF;
@@ -411,7 +437,7 @@
     g = 255 - g;
     b = 255 - b;
 
-    return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
+    return SkPackRGB16(r32_to_16(r), g32_to_16(g), b32_to_16(b));
 }
 
 #define BITMAP_INFO_RGB     (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
@@ -604,12 +630,141 @@
     return SkFontHost::CreateTypeface(NULL, NULL, NULL, NULL, SkTypeface::kNormal);
 }
 
+// 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);
+    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 = advance.width;
+    return true;
+}
+
 // static
 SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
         uint32_t fontID,
         SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
-    SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
-    return NULL;
+    CTFontRef ctFont = GetFontRefFromFontID(fontID);
+    SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
+    CFStringRef fontName = CTFontCopyPostScriptName(ctFont);
+    int length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(fontName),
+        kCFStringEncodingUTF8);
+    info->fFontName.resize(length); 
+    CFStringGetCString(fontName, info->fFontName.writable_str(), length,
+        kCFStringEncodingUTF8);
+    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);
+    }
+
+    // TODO: get font type, ala:
+    //  CFTypeRef attr = CTFontCopyAttribute(ctFont, kCTFontFormatAttribute);
+    info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+    info->fStyle = 0;
+    CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
+    if (symbolicTraits & kCTFontMonoSpaceTrait) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+    }
+    if (symbolicTraits & kCTFontItalicTrait) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+    }
+    CTFontStylisticClass stylisticClass = symbolicTraits &
+            kCTFontClassMaskTrait;
+    if (stylisticClass & kCTFontSymbolicClass) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
+    }
+    if (stylisticClass >= kCTFontOldStyleSerifsClass
+            && stylisticClass <= kCTFontSlabSerifsClass) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
+    } else if (stylisticClass & kCTFontScriptsClass) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
+    }
+    info->fItalicAngle = CTFontGetSlantAngle(ctFont);
+    info->fAscent = CTFontGetAscent(ctFont);
+    info->fDescent = CTFontGetDescent(ctFont);
+    info->fCapHeight = CTFontGetCapHeight(ctFont);
+    CGRect bbox = CTFontGetBoundingBox(ctFont);
+    info->fBBox = SkIRect::MakeXYWH(bbox.origin.x, bbox.origin.y,
+        bbox.size.width, bbox.size.height);
+
+    // Figure out a good guess for StemV - Min width of i, I, !, 1.
+    // This probably isn't very good with an italic font.
+    int16_t min_width = SHRT_MAX;
+    info->fStemV = 0;
+    static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
+    const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
+    CGGlyph glyphs[count];
+    CGRect boundingRects[count];
+    if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
+        CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation, 
+            glyphs, boundingRects, count);
+        for (size_t i = 0; i < count; i++) {
+            int16_t width = boundingRects[i].size.width;
+            if (width > 0 && width < min_width) {
+                min_width = width;
+                info->fStemV = min_width;
+            }
+        }
+    }
+
+    if (false) { // TODO: haven't figured out how to know if font is embeddable
+        // (information is in the OS/2 table)
+        info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+    } else if (perGlyphInfo &
+               SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+        info->fGlyphWidths.reset(
+            getAdvanceData(ctFont, glyphCount, &getWidthAdvance));
+    }
+
+    return info;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -618,9 +773,88 @@
     return SkTypefaceCache::FindByID(fontID) != NULL;
 }
 
+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 CalcTableCheckSum(uint32 *table, uint32 numberOfBytesInTable) {
+    uint32 sum = 0;
+    uint32 nLongs = (numberOfBytesInTable + 3) / 4;
+
+    while (nLongs-- > 0) {
+        sum += SkEndian_SwapBE32(*table++);
+    }
+    return sum;
+}
+
 SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
-    SkASSERT(!"SkFontHost::OpenStream unimplemented");
-    return(NULL);
+    // 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*)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,
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index d9404f3..f5d126e 100755
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -83,7 +83,8 @@
 
 static unsigned calculateGlyphCount(HDC hdc) {
     // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
-    const DWORD maxpTag = *(DWORD*) "maxp";
+    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);
@@ -271,6 +272,10 @@
     HFONT        fFont;
     SCRIPT_CACHE fSC;
     int          fGlyphCount;
+
+    HFONT        fHiResFont;
+    MAT2         fMat22Identity;
+    SkMatrix     fHiResMatrix;
 };
 
 static float mul2float(SkScalar a, SkScalar b) {
@@ -283,6 +288,16 @@
 
 static SkMutex 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];
+}
+
 SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
         : SkScalerContext(desc), fDDC(0), fFont(0), fSavefont(0), fSC(0)
         , fGlyphCount(-1) {
@@ -303,6 +318,7 @@
     fMat22.eM22 = float2FIXED(-fXform.eM22);
 
     fDDC = ::CreateCompatibleDC(NULL);
+    SetGraphicsMode(fDDC, GM_ADVANCED);
     SetBkMode(fDDC, TRANSPARENT);
 
     // Scaling by the DPI is inconsistent with how Skia draws elsewhere
@@ -311,6 +327,21 @@
     GetLogFontByID(fRec.fFontID, &lf);
     lf.lfHeight = -gCanonicalTextSize;
     fFont = CreateFontIndirect(&lf);
+
+    // if we're rotated, or want fractional widths, create a hires font
+    fHiResFont = 0;
+    if (needHiResMetrics(fRec.fPost2x2) || (fRec.fFlags & kSubpixelPositioning_Flag)) {
+        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);
 }
 
@@ -322,6 +353,9 @@
     if (fFont) {
         ::DeleteObject(fFont);
     }
+    if (fHiResFont) {
+        ::DeleteObject(fHiResFont);
+    }
     if (fSC) {
         ::ScriptFreeCache(&fSC);
     }
@@ -368,7 +402,7 @@
     SkASSERT(fDDC);
 
     GLYPHMETRICS gm;
-    memset(&gm, 0, sizeof(gm));
+    sk_bzero(&gm, sizeof(gm));
 
     glyph->fRsbDelta = 0;
     glyph->fLsbDelta = 0;
@@ -399,6 +433,19 @@
             glyph->fTop -= 1;
             glyph->fLeft -= 1;
         }
+
+        if (fHiResFont) {
+            SelectObject(fDDC, fHiResFont);
+            sk_bzero(&gm, sizeof(gm));
+            ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity);
+            if (GDI_ERROR != ret) {
+                SkPoint advance;
+                fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
+                glyph->fAdvanceX = SkScalarToFixed(advance.fX);
+                glyph->fAdvanceY = SkScalarToFixed(advance.fY);
+            }
+            SelectObject(fDDC, fFont);
+        }
     } else {
         glyph->fWidth = 0;
     }
@@ -453,34 +500,53 @@
     return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
 }
 
+static int alignTo32(int n) {
+    return (n + 31) & ~31;
+}
+
+struct MyBitmapInfo : public BITMAPINFO {
+    RGBQUAD fMoreSpaceForColors[1];
+};
+
 void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
 
     SkAutoMutexAcquire  ac(gFTMutex);
 
     SkASSERT(fDDC);
 
-    if (SkMask::kLCD16_Format == fRec.fMaskFormat) {
+    const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
+    if ((SkMask::kLCD16_Format == fRec.fMaskFormat) || isBW) {
         HDC dc = CreateCompatibleDC(0);
         void* bits = 0;
-        BITMAPINFO info;
+        int biWidth = isBW ? alignTo32(glyph.fWidth) : glyph.fWidth;
+        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 = glyph.fWidth;
+        info.bmiHeader.biWidth = biWidth;
         info.bmiHeader.biHeight = glyph.fHeight;
         info.bmiHeader.biPlanes = 1;
-        info.bmiHeader.biBitCount = 32;
+        info.bmiHeader.biBitCount = isBW ? 1 : 32;
         info.bmiHeader.biCompression = BI_RGB;
+        if (isBW) {
+            info.bmiHeader.biClrUsed = 2;
+        }
         HBITMAP bm = CreateDIBSection(dc, &info, DIB_RGB_COLORS, &bits, 0, 0);
         SelectObject(dc, bm);
 
         // erase to white
-        size_t srcRB = glyph.fWidth << 2;
+        size_t srcRB = isBW ? (biWidth >> 3) : (glyph.fWidth << 2);
         size_t size = glyph.fHeight * srcRB;
-        memset(bits, 0xFF, size);
+        memset(bits, isBW ? 0 : 0xFF, size);
 
+        SetGraphicsMode(dc, GM_ADVANCED);
         SetBkMode(dc, TRANSPARENT);
         SetTextAlign(dc, TA_LEFT | TA_BASELINE);
-        SetGraphicsMode(dc, GM_ADVANCED);
 
         XFORM xform = fXform;
         xform.eDx = (float)-glyph.fLeft;
@@ -488,7 +554,7 @@
         SetWorldTransform(dc, &xform);
 
         HGDIOBJ prevFont = SelectObject(dc, fFont);
-        COLORREF color = SetTextColor(dc, 0); // black
+        COLORREF color = SetTextColor(dc, isBW ? 0xFFFFFF : 0);
         SkASSERT(color != CLR_INVALID);
         uint16_t glyphID = glyph.getGlyphID();
 #if defined(UNICODE)
@@ -501,15 +567,26 @@
         // downsample from rgba to rgb565
         int width = glyph.fWidth;
         size_t dstRB = glyph.rowBytes();
-        const uint32_t* src = (const uint32_t*)bits;
-        // gdi's bitmap is upside-down, so we reverse dst walking in Y
-        uint16_t* dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
-        for (int y = 0; y < glyph.fHeight; y++) {
-            for (int i = 0; i < width; i++) {
-                dst[i] = rgb_to_lcd16(src[i]);
+        if (isBW) {
+            const uint8_t* src = (const uint8_t*)bits;
+            // gdi's bitmap is upside-down, so we reverse dst walking in Y
+            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;
             }
-            src = (const uint32_t*)((const char*)src + srcRB);
-            dst = (uint16_t*)((char*)dst - dstRB);
+        } else {    // LCD16
+            const uint32_t* src = (const uint32_t*)bits;
+            // gdi's bitmap is upside-down, so we reverse dst walking in Y
+            uint16_t* dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
+            for (int y = 0; y < glyph.fHeight; y++) {
+                for (int i = 0; i < width; i++) {
+                    dst[i] = rgb_to_lcd16(src[i]);
+                }
+                src = (const uint32_t*)((const char*)src + srcRB);
+                dst = (uint16_t*)((char*)dst - dstRB);
+            }
         }
 
         DeleteDC(dc);
@@ -790,7 +867,8 @@
 }
 
 SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
-    const DWORD kTTCTag = *(DWORD*) "ttcf";
+    const DWORD kTTCTag =
+        SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
     LOGFONT lf;
     GetLogFontByID(uniqueID, &lf);
 
@@ -896,10 +974,6 @@
     if (SkMask::kLCD16_Format == rec->fMaskFormat) {
         return;
     }
-    // we never like BW format
-    if (SkMask::kBW_Format == rec->fMaskFormat) {
-        rec->fMaskFormat = SkMask::kA8_Format;
-    }
 
     if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) {
         rec->fMaskFormat = SkMask::kA8_Format;
diff --git a/src/svg/SkSVGParser.cpp b/src/svg/SkSVGParser.cpp
index df86178..f4ad198 100644
--- a/src/svg/SkSVGParser.cpp
+++ b/src/svg/SkSVGParser.cpp
@@ -76,8 +76,8 @@
 int SkSVGParser::findAttribute(SkSVGBase* element, const char* attrValue,
         size_t len, bool isPaint) {
     const SkSVGAttribute* attributes;
-    int count = element->getAttributes(&attributes);
-    int result = 0;
+    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 - 
@@ -200,7 +200,7 @@
     } else if (fInSVG == false)
         return false;
     const char* nextColon = strchr(name, ':');
-    if (nextColon && nextColon - name < len)
+    if (nextColon && (size_t)(nextColon - name) < len)
         return false;
     SkSVGTypes type = GetType(name, len);
 //    SkASSERT(type >= 0);
diff --git a/src/utils/SkEGLContext_none.cpp b/src/utils/SkEGLContext_none.cpp
index 1c55c95..cb08f40 100644
--- a/src/utils/SkEGLContext_none.cpp
+++ b/src/utils/SkEGLContext_none.cpp
@@ -1,6 +1,6 @@
 #include "SkEGLContext.h"
 
-SkEGLContext::SkEGLContext() : fContext(NULL) {
+SkEGLContext::SkEGLContext() {
 }
 
 SkEGLContext::~SkEGLContext() {
diff --git a/src/utils/mac/SkBitmap_Mac.cpp b/src/utils/mac/SkBitmap_Mac.cpp
new file mode 100644
index 0000000..06c2b27
--- /dev/null
+++ b/src/utils/mac/SkBitmap_Mac.cpp
@@ -0,0 +1,142 @@
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkMath.h"
+
+#if defined(SK_BUILD_FOR_MAC) && !defined(SK_USE_WXWIDGETS)
+
+#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..f4bda45
--- /dev/null
+++ b/src/utils/mac/SkCreateCGImageRef.cpp
@@ -0,0 +1,129 @@
+#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 SkBitmap* prepareForImageRef(const SkBitmap& bm,
+                                    size_t* bitsPerComponent,
+                                    CGBitmapInfo* info) {
+    bool upscaleTo32 = false;
+
+    switch (bm.config()) {
+        case SkBitmap::kRGB_565_Config:
+            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 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);
+    }
+}
+
+
+
diff --git a/src/utils/mac/SkEGLContext_mac.cpp b/src/utils/mac/SkEGLContext_mac.cpp
new file mode 100644
index 0000000..e601f35
--- /dev/null
+++ b/src/utils/mac/SkEGLContext_mac.cpp
@@ -0,0 +1,68 @@
+#include "SkEGLContext.h"
+//#include "SkTypes.h"
+#include <AGL/agl.h>
+
+SkEGLContext::SkEGLContext() : context(NULL) {
+}
+
+SkEGLContext::~SkEGLContext() {
+    if (this->context) {
+        aglDestroyContext(this->context);
+    }
+}
+
+bool SkEGLContext::init(int width, int height) {
+    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,
+/*
+        AGL_SAMPLE_BUFFERS_ARB, 1,
+        AGL_MULTISAMPLE,
+        AGL_SAMPLES_ARB, 2,
+*/
+        AGL_ACCELERATED,
+        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);
+    this->context = ctx;
+
+    // Now create our FBO render target
+
+    GLuint fboID;
+    GLuint cbID;
+    GLuint dsID;
+    glGenFramebuffersEXT(1, &fboID);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID);
+    glGenRenderbuffers(1, &cbID);
+    glBindRenderbuffer(GL_RENDERBUFFER, cbID);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, width, height);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbID);
+    glGenRenderbuffers(1, &dsID);
+    glBindRenderbuffer(GL_RENDERBUFFER, dsID);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsID);
+    glViewport(0, 0, width, height);
+    glClearStencil(0);
+    glClear(GL_STENCIL_BUFFER_BIT);
+
+    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    return GL_FRAMEBUFFER_COMPLETE == status;
+}
diff --git a/src/utils/mac/SkOSWindow_Mac.cpp b/src/utils/mac/SkOSWindow_Mac.cpp
new file mode 100644
index 0000000..f1a2926
--- /dev/null
+++ b/src/utils/mac/SkOSWindow_Mac.cpp
@@ -0,0 +1,534 @@
+#include "SkTypes.h"
+
+#if defined(SK_BUILD_FOR_MAC) && !defined(SK_USE_WXWIDGETS)
+
+#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)
+{
+    SkEvent* evt = new SkEvent("inval-imageview");
+    evt->post(this->getSinkID());
+}
+
+bool SkOSWindow::onEvent(const SkEvent& evt) {
+    if (evt.isType("inval-imageview")) {
+        this->update(NULL);
+
+        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::attachGL()
+{
+    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::detachGL() {
+    aglSetWindowRef((AGLContext)fAGLCtx, NULL);
+}
+
+void SkOSWindow::presentGL() {
+    aglSwapBuffers((AGLContext)fAGLCtx);
+}
+
+#endif
+
diff --git a/src/utils/mac/skia_mac.cpp b/src/utils/mac/skia_mac.cpp
new file mode 100644
index 0000000..a1345cf
--- /dev/null
+++ b/src/utils/mac/skia_mac.cpp
@@ -0,0 +1,43 @@
+#include <Carbon/Carbon.h>

+#include "SkApplication.h"

+#include "SkWindow.h"

+

+int main(int argc, char* argv[])

+{

+    WindowRef			window;

+    OSStatus			err = noErr;

+

+    Rect bounds = {100, 100, 500, 500};

+    WindowAttributes attrs = kWindowStandardHandlerAttribute | 

+                             kWindowLiveResizeAttribute |

+                             kWindowInWindowMenuAttribute | 

+                             kWindowCompositingAttribute |

+                             kWindowAsyncDragAttribute | 

+                             kWindowFullZoomAttribute | 

+                             kWindowFrameworkScaledAttribute;

+                             //kWindowDoesNotCycleAttribute;

+    CreateNewWindow(kDocumentWindowClass, attrs, &bounds, &window);

+

+    MenuRef menu;

+    CreateNewMenu(0, 0, &menu);

+

+    // if we get here, we can start our normal Skia sequence

+    {

+        application_init();

+        (void)create_sk_window(window);

+        SizeWindow(window, 640, 480, false);

+    }

+    

+    // The window was created hidden so show it.

+    ShowWindow( window );

+    

+    // Call the event loop

+    RunApplicationEventLoop();

+

+	application_term();

+

+CantCreateWindow:

+CantGetNibRef:

+	return err;

+}

+

diff --git a/src/utils/mesa/SkEGLContext_Mesa.cpp b/src/utils/mesa/SkEGLContext_Mesa.cpp
new file mode 100644
index 0000000..ed1b7cd
--- /dev/null
+++ b/src/utils/mesa/SkEGLContext_Mesa.cpp
@@ -0,0 +1,128 @@
+#include "SkEGLContext.h"
+#include "SkTypes.h"
+
+#include "GL/osmesa.h"
+#include "GL/glu.h"
+
+#define SK_GL_DECL_PROC(T, F) T F ## _func = NULL;
+#define SK_GL_GET_PROC(T, F) F ## _func = (T)OSMesaGetProcAddress(#F);
+#define SK_GL_GET_EXT_PROC(T, F) F ## _func = (T)OSMesaGetProcAddress(#F "EXT");
+
+SkEGLContext::SkEGLContext() : context(NULL), image(NULL) {
+}
+
+SkEGLContext::~SkEGLContext() {
+    if (this->image)
+        free(this->image);
+    
+    if (this->context)
+        OSMesaDestroyContext(this->context);
+}
+
+#if SK_B32_SHIFT < SK_G32_SHIFT &&\
+                   SK_G32_SHIFT < SK_R32_SHIFT &&\
+                                  SK_R32_SHIFT < SK_A32_SHIFT
+    #define SK_OSMESA_COLOR_ORDER OSMESA_BGRA
+#elif SK_R32_SHIFT < SK_G32_SHIFT &&\
+                     SK_G32_SHIFT < SK_B32_SHIFT &&\
+                                    SK_B32_SHIFT < SK_A32_SHIFT
+    #define SK_OSMESA_COLOR_ORDER OSMESA_RGBA
+#elif SK_A32_SHIFT < SK_R32_SHIFT && \
+                     SK_R32_SHIFT < SK_G32_SHIFT && \
+                                    SK_G32_SHIFT < SK_B32_SHIFT
+    #define SK_OSMESA_COLOR_ORDER OSMESA_ARGB
+#else
+    //Color order (rgba) SK_R32_SHIFT SK_G32_SHIFT SK_B32_SHIFT SK_A32_SHIFT
+    #define SK_OSMESA_COLOR_ORDER OSMESA_RGBA
+#endif
+
+bool SkEGLContext::init(const int width, const int height) {
+    /* Create an RGBA-mode context */
+#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305
+    /* specify Z, stencil, accum sizes */
+    OSMesaContext ctx = OSMesaCreateContextExt(SK_OSMESA_COLOR_ORDER, 16, 0, 0, NULL);
+#else
+    OSMesaContext ctx = OSMesaCreateContext(SK_OSMESA_COLOR_ORDER, NULL);
+#endif
+    if (!ctx) {
+        SkDebugf("OSMesaCreateContext failed!\n");
+        return false;
+    }
+    this->context = ctx;
+    
+    // Allocate the image buffer
+    GLfloat *buffer = (GLfloat *) malloc(width * height * 4 * sizeof(GLfloat));
+    if (!buffer) {
+        SkDebugf("Alloc image buffer failed!\n");
+        return false;
+    }
+    this->image = buffer;
+    
+    // Bind the buffer to the context and make it current
+    if (!OSMesaMakeCurrent(ctx, buffer, GL_FLOAT, width, height)) {
+        SkDebugf("OSMesaMakeCurrent failed!\n");
+        return false;
+    }
+    
+    //Setup the framebuffers
+    SK_GL_DECL_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffers)
+    SK_GL_DECL_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebuffer)
+    SK_GL_DECL_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers)
+    SK_GL_DECL_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer)
+    SK_GL_DECL_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage)
+    SK_GL_DECL_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer)
+    SK_GL_DECL_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatus)
+    
+    const GLubyte* glExts = glGetString(GL_EXTENSIONS);
+    if (gluCheckExtension(
+          reinterpret_cast<const GLubyte*>("GL_ARB_framebuffer_object")
+          , glExts))
+    {
+        SK_GL_GET_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffers)
+        SK_GL_GET_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebuffer)
+        SK_GL_GET_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers)
+        SK_GL_GET_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer)
+        SK_GL_GET_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage)
+        SK_GL_GET_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer)
+        SK_GL_GET_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatus)
+        
+    //osmesa on mac currently only supports EXT
+    } else if (gluCheckExtension(
+          reinterpret_cast<const GLubyte*>("GL_EXT_framebuffer_object")
+          , glExts))
+    {
+        SK_GL_GET_EXT_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffers)
+        SK_GL_GET_EXT_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebuffer)
+        SK_GL_GET_EXT_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers)
+        SK_GL_GET_EXT_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer)
+        SK_GL_GET_EXT_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage)
+        SK_GL_GET_EXT_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer)
+        SK_GL_GET_EXT_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatus)
+    } else {
+      SkDebugf("GL_ARB_framebuffer_object not found.\n");
+      return false;
+    }
+    
+    GLuint fboID;
+    GLuint cbID;
+    GLuint dsID;
+    glGenFramebuffers_func(1, &fboID);
+    glBindFramebuffer_func(GL_FRAMEBUFFER, fboID);
+    
+    glGenRenderbuffers_func(1, &cbID);
+    glBindRenderbuffer_func(GL_RENDERBUFFER, cbID);
+    glRenderbufferStorage_func(GL_RENDERBUFFER, OSMESA_RGBA, width, height);
+    glFramebufferRenderbuffer_func(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbID);
+    
+    glGenRenderbuffers_func(1, &dsID);
+    glBindRenderbuffer_func(GL_RENDERBUFFER_EXT, dsID);
+    glRenderbufferStorage_func(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
+    glFramebufferRenderbuffer_func(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsID);
+    
+    glViewport(0, 0, width, height);
+    glClearStencil(0);
+    glClear(GL_STENCIL_BUFFER_BIT);
+
+    GLenum status = glCheckFramebufferStatus_func(GL_FRAMEBUFFER);
+    return GL_FRAMEBUFFER_COMPLETE == status;
+}
diff --git a/src/utils/unix/SkEGLContext_Unix.cpp b/src/utils/unix/SkEGLContext_Unix.cpp
new file mode 100644
index 0000000..7921b8a
--- /dev/null
+++ b/src/utils/unix/SkEGLContext_Unix.cpp
@@ -0,0 +1,264 @@
+#include "SkEGLContext.h"
+#include "SkTypes.h"
+
+#include <GL/gl.h>
+#include <GL/glext.h>
+#include <GL/glu.h>
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+
+#define SK_GL_GET_PROC(T, F) T F = NULL; \
+        F = (T) glXGetProcAddressARB(reinterpret_cast<const GLubyte*>(#F));
+
+static bool ctxErrorOccurred = false;
+static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
+    ctxErrorOccurred = true;
+    return 0;
+}
+
+SkEGLContext::SkEGLContext() : context(NULL), display(NULL), pixmap(0), glxPixmap(0) {
+}
+
+SkEGLContext::~SkEGLContext() {
+    if (this->display) {
+        glXMakeCurrent(this->display, 0, 0);
+
+        if (this->context)
+            glXDestroyContext(this->display, this->context);
+
+        if (this->glxPixmap)
+            glXDestroyGLXPixmap(this->display, this->glxPixmap);
+
+        if (this->pixmap)
+            XFreePixmap(this->display, this->pixmap);
+
+        XCloseDisplay(this->display);
+    }
+}
+
+bool SkEGLContext::init(const int width, const int height) {
+    Display *display = XOpenDisplay(0);
+    this->display = display;
+
+    if (!display) {
+        SkDebugf("Failed to open X display.\n");
+        return false;
+    }
+
+    // Get a matching FB config
+    static int visual_attribs[] = {
+        GLX_X_RENDERABLE    , True,
+        GLX_DRAWABLE_TYPE   , GLX_PIXMAP_BIT,
+        GLX_RENDER_TYPE     , GLX_RGBA_BIT,
+        GLX_X_VISUAL_TYPE   , GLX_TRUE_COLOR,
+        GLX_RED_SIZE        , 8,
+        GLX_GREEN_SIZE      , 8,
+        GLX_BLUE_SIZE       , 8,
+        GLX_ALPHA_SIZE      , 8,
+        GLX_DEPTH_SIZE      , 24,
+        GLX_STENCIL_SIZE    , 8,
+        GLX_DOUBLEBUFFER    , True,
+        //GLX_SAMPLE_BUFFERS  , 1,
+        //GLX_SAMPLES         , 4,
+        None
+    };
+
+    int glx_major, glx_minor;
+
+    // FBConfigs were added in GLX version 1.3.
+    if (!glXQueryVersion( display, &glx_major, &glx_minor) ||
+            ( (glx_major == 1) && (glx_minor < 3) ) || (glx_major < 1))
+    {
+        SkDebugf("Invalid GLX version.");
+        return false;
+    }
+
+    //SkDebugf("Getting matching framebuffer configs.\n");
+    int fbcount;
+    GLXFBConfig *fbc = glXChooseFBConfig(display, DefaultScreen(display),
+                                          visual_attribs, &fbcount);
+    if (!fbc) {
+        SkDebugf("Failed to retrieve a framebuffer config.\n");
+        return false;
+    }
+    //SkDebugf("Found %d matching FB configs.\n", fbcount);
+
+    // Pick the FB config/visual with the most samples per pixel
+    //SkDebugf("Getting XVisualInfos.\n");
+    int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
+
+    int i;
+    for (i = 0; i < fbcount; ++i) {
+        XVisualInfo *vi = glXGetVisualFromFBConfig(display, fbc[i]);
+        if (vi) {
+            int samp_buf, samples;
+            glXGetFBConfigAttrib(display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
+            glXGetFBConfigAttrib(display, fbc[i], GLX_SAMPLES, &samples);
+
+            //SkDebugf("  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
+            //       " SAMPLES = %d\n",
+            //        i, (unsigned int)vi->visualid, samp_buf, samples);
+
+            if (best_fbc < 0 || (samp_buf && samples > best_num_samp))
+                best_fbc = i, best_num_samp = samples;
+            if (worst_fbc < 0 || !samp_buf || samples < worst_num_samp)
+                worst_fbc = i, worst_num_samp = samples;
+        }
+        XFree(vi);
+    }
+
+    GLXFBConfig bestFbc = fbc[best_fbc];
+
+    // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
+    XFree(fbc);
+
+    // Get a visual
+    XVisualInfo *vi = glXGetVisualFromFBConfig(display, bestFbc);
+    //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
+
+    Pixmap pixmap = XCreatePixmap(
+        display, RootWindow(display, vi->screen), width, height, vi->depth
+    );
+
+    this->pixmap = pixmap;
+    if (!pixmap) {
+        SkDebugf("Failed to create pixmap.\n");
+        return false;
+    }
+
+    GLXPixmap glxPixmap = glXCreateGLXPixmap(display, vi, pixmap);
+    this->glxPixmap = glxPixmap;
+
+    // Done with the visual info data
+    XFree(vi);
+
+    // Create the context
+    GLXContext ctx = 0;
+
+    // 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(
+        display, DefaultScreen(display)
+    );
+    // 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");
+        ctx = glXCreateNewContext(display, bestFbc, GLX_RGBA_TYPE, 0, True);
+
+    } else {
+        //SkDebugf("Creating context.\n");
+
+        SK_GL_GET_PROC(PFNGLXCREATECONTEXTATTRIBSARBPROC, 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
+        };
+        ctx = glXCreateContextAttribsARB(
+            display, bestFbc, 0, True, context_attribs
+        );
+
+        // Sync to ensure any errors generated are processed.
+        XSync(display, False);
+        if (!ctxErrorOccurred && ctx) {
+           //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");
+            ctx = glXCreateContextAttribsARB(
+                display, bestFbc, 0, True, context_attribs
+            );
+        }
+    }
+
+    // Sync to ensure any errors generated are processed.
+    XSync(display, False);
+
+    // Restore the original error handler
+    XSetErrorHandler(oldHandler);
+
+    if (ctxErrorOccurred || !ctx) {
+        SkDebugf("Failed to create an OpenGL context.\n");
+        return false;
+    }
+    this->context = ctx;
+
+    // Verify that context is a direct context
+    if (!glXIsDirect(display, ctx)) {
+        //SkDebugf("Indirect GLX rendering context obtained.\n");
+    } else {
+        //SkDebugf("Direct GLX rendering context obtained.\n");
+    }
+
+    //SkDebugf("Making context current.\n");
+    if (!glXMakeCurrent(display, glxPixmap, ctx)) {
+      SkDebugf("Could not set the context.\n");
+      return false;
+    }
+
+    //Setup the framebuffers
+    const GLubyte* glExts = glGetString(GL_EXTENSIONS);
+    if (!gluCheckExtension(
+          reinterpret_cast<const GLubyte*>("GL_EXT_framebuffer_object")
+          , glExts))
+    {
+      SkDebugf("GL_EXT_framebuffer_object not found.\n");
+      return false;
+    }
+    SK_GL_GET_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffersEXT)
+    SK_GL_GET_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebufferEXT)
+    SK_GL_GET_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffersEXT)
+    SK_GL_GET_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbufferEXT)
+    SK_GL_GET_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorageEXT)
+    SK_GL_GET_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbufferEXT)
+    SK_GL_GET_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatusEXT)
+
+    GLuint fboID;
+    GLuint cbID;
+    GLuint dsID;
+    glGenFramebuffersEXT(1, &fboID);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID);
+    glGenRenderbuffersEXT(1, &cbID);
+    glBindRenderbufferEXT(GL_RENDERBUFFER, cbID);
+    glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, width, height);
+    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbID);
+    glGenRenderbuffersEXT(1, &dsID);
+    glBindRenderbufferEXT(GL_RENDERBUFFER, dsID);
+    glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
+    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsID);
+    glViewport(0, 0, width, height);
+    glClearStencil(0);
+    glClear(GL_STENCIL_BUFFER_BIT);
+
+    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
+    return GL_FRAMEBUFFER_COMPLETE == status;
+}
diff --git a/src/utils/unix/SkOSWindow_Unix.cpp b/src/utils/unix/SkOSWindow_Unix.cpp
index 4ec0c74..ae881d5 100644
--- a/src/utils/unix/SkOSWindow_Unix.cpp
+++ b/src/utils/unix/SkOSWindow_Unix.cpp
@@ -76,7 +76,7 @@
     long event_mask = NoEventMask;
     XClientMessageEvent event;
     event.type = ClientMessage;
-    Atom myAtom;
+    Atom myAtom(0);
     event.message_type = myAtom;
     event.format = 32;
     event.data.l[0] = 0;
diff --git a/src/views/SkBGViewArtist.cpp b/src/views/SkBGViewArtist.cpp
new file mode 100644
index 0000000..07da123
--- /dev/null
+++ b/src/views/SkBGViewArtist.cpp
@@ -0,0 +1,24 @@
+#include "SkBGViewArtist.h"
+#include "SkCanvas.h"
+#include "SkParsePaint.h"
+
+SkBGViewArtist::SkBGViewArtist(SkColor c)
+{
+	fPaint.setColor(c);
+}
+
+SkBGViewArtist::~SkBGViewArtist()
+{
+}
+
+void SkBGViewArtist::onDraw(SkView*, SkCanvas* canvas)
+{
+	// only works for views that are clipped their bounds.
+	canvas->drawPaint(fPaint);
+}
+
+void SkBGViewArtist::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	SkPaint_Inflate(&fPaint, dom, node);
+}
+
diff --git a/src/views/SkBorderView.cpp b/src/views/SkBorderView.cpp
new file mode 100644
index 0000000..74a2477
--- /dev/null
+++ b/src/views/SkBorderView.cpp
@@ -0,0 +1,89 @@
+#include "SkBorderView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+#include "SkStackViewLayout.h"
+
+SkBorderView::SkBorderView() : fLeft(SkIntToScalar(0)),
+                               fRight(SkIntToScalar(0)),
+                               fTop(SkIntToScalar(0)),
+                               fBottom(SkIntToScalar(0))
+{
+	fAnim.setHostEventSink(this);
+	init_skin_anim(kBorder_SkinEnum, &fAnim);
+}
+
+SkBorderView::~SkBorderView()
+{
+	
+}
+
+void SkBorderView::setSkin(const char skin[])
+{
+	init_skin_anim(skin, &fAnim);
+}
+
+/* virtual */ void SkBorderView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+}
+
+/*virtual*/ void SkBorderView::onSizeChange()
+{
+	this->INHERITED::onSizeChange();
+	SkEvent evt("user");
+	evt.setString("id", "setDim");
+	evt.setScalar("dimX", this->width());
+	evt.setScalar("dimY", this->height());
+	fAnim.doUserEvent(evt);
+}
+
+/*virtual*/ void SkBorderView::onDraw(SkCanvas* canvas)
+{
+	SkPaint						paint;		
+	SkAnimator::DifferenceType	diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+	
+	if (diff == SkAnimator::kDifferent)
+		this->inval(NULL);
+	else if (diff == SkAnimator::kPartiallyDifferent)
+	{
+		SkRect	bounds;
+		fAnim.getInvalBounds(&bounds);
+		this->inval(&bounds);
+	}
+}
+
+/*virtual*/ bool SkBorderView::onEvent(const SkEvent& evt)
+{
+	if (evt.isType(SK_EventType_Inval))
+	{
+		this->inval(NULL);
+		return true;
+	}
+	if (evt.isType("recommendDim"))
+	{
+		evt.findScalar("leftMargin", &fLeft);
+		evt.findScalar("rightMargin", &fRight);
+		evt.findScalar("topMargin", &fTop);
+		evt.findScalar("bottomMargin", &fBottom);
+	
+		//setup_views.cpp uses SkView::Layout instead of SkStackViewLayout
+		//but that gives me an error
+		SkStackViewLayout* layout;
+		fMargin.set(fLeft, fTop, fRight, fBottom);
+		if (this->getLayout())
+		{
+			layout = (SkStackViewLayout*)this->getLayout();
+			layout->setMargin(fMargin);
+		}
+		else
+		{
+			layout = new SkStackViewLayout;
+			layout->setMargin(fMargin);
+			this->setLayout(layout)->unref();
+		}
+		this->invokeLayout();
+	}
+	return this->INHERITED::onEvent(evt);
+}
diff --git a/src/views/SkEvent.cpp b/src/views/SkEvent.cpp
new file mode 100644
index 0000000..ec4a7b4
--- /dev/null
+++ b/src/views/SkEvent.cpp
@@ -0,0 +1,580 @@
+/* libs/graphics/views/SkEvent.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 "SkEvent.h"
+
+void SkEvent::initialize(const char* type, size_t typeLen) {
+    fType = NULL;
+    setType(type, typeLen);
+    f32 = 0;
+#ifdef SK_DEBUG
+    fTargetID = 0;
+    fTime = 0;
+    fNextEvent = NULL;
+#endif
+    SkDEBUGCODE(fDebugTrace = false;)
+}
+
+SkEvent::SkEvent()
+{
+    initialize("", 0);
+}
+
+SkEvent::SkEvent(const SkEvent& src)
+{
+    *this = src;
+    if (((size_t) fType & 1) == 0)
+        setType(src.fType);
+}
+
+SkEvent::SkEvent(const SkString& type)
+{
+    initialize(type.c_str(), type.size());
+}
+
+SkEvent::SkEvent(const char type[])
+{
+    SkASSERT(type);
+    initialize(type, strlen(type));
+}
+
+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);
+}
+
+#if 0
+const char* SkEvent::getType() const 
+{ 
+    if ((size_t) fType & 1) {   // not a pointer
+        char chars[sizeof(size_t) + 1];
+        size_t len = makeCharArray(chars, (size_t) fType);
+        fType = (char*) sk_malloc_throw(len);
+        SkASSERT(((size_t) fType & 1) == 0);
+        memcpy(fType, chars, len);
+    }
+    return fType; 
+}
+#endif
+
+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:
+                SkASSERT(!"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 "SkGlobals.h"
+#include "SkThread.h"
+#include "SkTime.h"
+
+#define SK_Event_GlobalsTag     SkSetFourByteTag('e', 'v', 'n', 't')
+
+class SkEvent_Globals : public SkGlobals::Rec {
+public:
+    SkMutex     fEventMutex;
+    SkEvent*    fEventQHead, *fEventQTail;
+    SkEvent*    fDelayQHead;
+    SkDEBUGCODE(int fEventCounter;)
+};
+
+static SkGlobals::Rec* create_globals()
+{
+    SkEvent_Globals* rec = new SkEvent_Globals;
+    rec->fEventQHead = NULL;
+    rec->fEventQTail = NULL;
+    rec->fDelayQHead = NULL;
+    SkDEBUGCODE(rec->fEventCounter = 0;)
+    return rec;
+}
+
+bool SkEvent::Post(SkEvent* evt, SkEventSinkID sinkID, SkMSec delay)
+{
+    if (delay)
+        return SkEvent::PostTime(evt, sinkID, SkTime::GetMSecs() + delay);
+
+    SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+    evt->fTargetID = sinkID;
+
+#ifdef SK_TRACE_EVENTS
+    {
+        SkString    str("SkEvent::Post(");
+        str.append(evt->getType());
+        str.append(", 0x");
+        str.appendHex(sinkID);
+        str.append(", ");
+        str.appendS32(delay);
+        str.append(")");
+        event_log(str.c_str());
+    }
+#endif
+
+    globals.fEventMutex.acquire();
+    bool wasEmpty = SkEvent::Enqueue(evt);
+    globals.fEventMutex.release();
+
+    // call outside of us holding the mutex
+    if (wasEmpty)
+        SkEvent::SignalNonEmptyQueue();
+    return true;
+}
+
+#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS)
+SkMSec gMaxDrawTime;
+#endif
+
+bool SkEvent::PostTime(SkEvent* evt, SkEventSinkID sinkID, SkMSec time)
+{
+#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS)
+    gMaxDrawTime = time;
+#endif
+    SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+    evt->fTargetID = sinkID;
+
+#ifdef SK_TRACE_EVENTS
+    {
+        SkString    str("SkEvent::Post(");
+        str.append(evt->getType());
+        str.append(", 0x");
+        str.appendHex(sinkID);
+        str.append(", ");
+        str.appendS32(time);
+        str.append(")");
+        event_log(str.c_str());
+    }
+#endif
+
+    globals.fEventMutex.acquire();
+    SkMSec queueDelay = SkEvent::EnqueueTime(evt, time);
+    globals.fEventMutex.release();
+
+    // call outside of us holding the mutex
+    if ((int32_t)queueDelay != ~0)
+        SkEvent::SignalQueueTimer(queueDelay);
+    return true;
+}
+
+bool SkEvent::Enqueue(SkEvent* evt)
+{
+    SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+    //  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);
+//  SkDebugf("Enqueue: count=%d\n", gEventCounter);
+
+    return wasEmpty;
+}
+
+SkEvent* SkEvent::Dequeue(SkEventSinkID* sinkID)
+{
+    SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+    globals.fEventMutex.acquire();
+
+    SkEvent* evt = globals.fEventQHead;
+    if (evt)
+    {
+        SkDEBUGCODE(--globals.fEventCounter);
+
+        if (sinkID)
+            *sinkID = evt->fTargetID;
+
+        globals.fEventQHead = evt->fNextEvent;
+        if (globals.fEventQHead == NULL)
+            globals.fEventQTail = NULL;
+    }
+    globals.fEventMutex.release();
+
+//  SkDebugf("Dequeue: count=%d\n", gEventCounter);
+
+    return evt;
+}
+
+bool SkEvent::QHasEvents()
+{
+    SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+    // 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)
+{
+#ifdef SK_TRACE_EVENTS
+    SkDebugf("enqueue-delay %s %d (%d)", evt->getType(), time, gDelayDepth);
+    const char* idStr = evt->findString("id");
+    if (idStr)
+        SkDebugf(" (%s)", idStr);
+    SkDebugf("\n");
+    ++gDelayDepth;
+#endif
+
+    SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+    //  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()
+{
+    SkEventSinkID   sinkID;
+    SkEvent*        evt = SkEvent::Dequeue(&sinkID);
+    SkAutoTDelete<SkEvent>  autoDelete(evt);
+    bool            again = false;
+
+    EVENT_LOGN("ProcessEvent", (int32_t)evt);
+
+    if (evt)
+    {
+        (void)SkEventSink::DoEvent(*evt, sinkID);
+        again = SkEvent::QHasEvents();
+    }
+    return again;
+}
+
+void SkEvent::ServiceQueueTimer()
+{
+    SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+    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 = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+    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 = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+    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..c8fe35c
--- /dev/null
+++ b/src/views/SkEventSink.cpp
@@ -0,0 +1,345 @@
+/* libs/graphics/views/SkEventSink.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 "SkEventSink.h"
+#include "SkTagList.h"
+#include "SkThread.h"
+
+#include "SkGlobals.h"
+#include "SkThread.h"
+#include "SkTime.h"
+
+#define SK_EventSink_GlobalsTag     SkSetFourByteTag('e', 'v', 's', 'k')
+
+class SkEventSink_Globals : public SkGlobals::Rec {
+public:
+    SkMutex         fSinkMutex;
+    SkEventSinkID   fNextSinkID;
+    SkEventSink*    fSinkHead;
+};
+
+static SkGlobals::Rec* create_globals()
+{
+    SkEventSink_Globals* rec = new SkEventSink_Globals;
+    rec->fNextSinkID = 0;
+    rec->fSinkHead = NULL;
+    return rec;
+}
+
+SkEventSink::SkEventSink() : fTagHead(NULL)
+{
+    SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals);
+
+    globals.fSinkMutex.acquire();
+
+    fID = ++globals.fNextSinkID;
+    fNextSink = globals.fSinkHead;
+    globals.fSinkHead = this;
+
+    globals.fSinkMutex.release();
+}
+
+SkEventSink::~SkEventSink()
+{
+    SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals);
+
+    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)
+            (SkNEW_ARGS(SkEvent, (evt)))->post(*iter++, delay);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkEventSink::EventResult SkEventSink::DoEvent(const SkEvent& evt, SkEventSinkID sinkID)
+{
+    SkEventSink* sink = SkEventSink::FindSink(sinkID);
+
+    if (sink)
+    {
+#ifdef SK_DEBUG
+        if (evt.isDebugTrace())
+        {
+            SkString    etype;
+            evt.getType(&etype);
+            SkDebugf("SkEventTrace: dispatching event <%s> to 0x%x", etype.c_str(), sinkID);
+            const char* idStr = evt.findString("id");
+            if (idStr)
+                SkDebugf(" (%s)", idStr);
+            SkDebugf("\n");
+        }
+#endif
+        return sink->doEvent(evt) ? kHandled_EventResult : kNotHandled_EventResult;
+    }
+    else
+    {
+#ifdef SK_DEBUG
+        if (sinkID)
+            SkDebugf("DoEvent: Can't find sink for ID(%x)\n", sinkID);
+        else
+            SkDebugf("Event sent to 0 sinkID\n");
+
+        if (evt.isDebugTrace())
+        {
+            SkString    etype;
+            evt.getType(&etype);
+            SkDebugf("SkEventTrace: eventsink not found <%s> for 0x%x\n", etype.c_str(), sinkID);
+        }
+#endif
+        return kSinkNotFound_EventResult;
+    }
+}
+
+SkEventSink* SkEventSink::FindSink(SkEventSinkID sinkID)
+{
+    if (sinkID == 0)
+        return 0;
+
+    SkEventSink_Globals&    globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals);
+    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
+static SkMutex                  gNamedSinkMutex;
+static SkTDict<SkEventSinkID>   gNamedSinkIDs(kMinStringBufferSize);
+
+/** Register a name/id pair with the system. If the name already exists,
+    replace its ID with the new id. This pair will persist until UnregisterNamedSink()
+    is called.
+*/
+void SkEventSink::RegisterNamedSinkID(const char name[], SkEventSinkID id)
+{
+    if (id && name && *name)
+    {
+        SkAutoMutexAcquire  ac(gNamedSinkMutex);
+        gNamedSinkIDs.set(name, id);
+    }
+}
+
+/** Return the id that matches the specified name (from a previous call to
+    RegisterNamedSinkID(). If no match is found, return 0
+*/
+SkEventSinkID SkEventSink::FindNamedSinkID(const char name[])
+{
+    SkEventSinkID id = 0;
+
+    if (name && *name)
+    {
+        SkAutoMutexAcquire  ac(gNamedSinkMutex);
+        (void)gNamedSinkIDs.find(name, &id);
+    }
+    return id;
+}
+
+/** Remove all name/id pairs from the system. This is call internally
+    on shutdown, to ensure no memory leaks. It should not be called
+    before shutdown.
+*/
+void SkEventSink::RemoveAllNamedSinkIDs()
+{
+    SkAutoMutexAcquire  ac(gNamedSinkMutex);
+    (void)gNamedSinkIDs.reset();
+}
+#endif
diff --git a/src/views/SkImageView.cpp b/src/views/SkImageView.cpp
new file mode 100644
index 0000000..9c358c7
--- /dev/null
+++ b/src/views/SkImageView.cpp
@@ -0,0 +1,296 @@
+#include "SkImageView.h"
+#include "SkAnimator.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkMatrix.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+SkImageView::SkImageView()
+{
+	fMatrix		= NULL;
+	fScaleType	= kMatrix_ScaleType;
+
+	fData.fAnim	= NULL;		// handles initializing the other union values
+	fDataIsAnim	= true;
+	
+	fUriIsValid	= false;	// an empty string is not valid
+}
+
+SkImageView::~SkImageView()
+{
+	if (fMatrix)
+		sk_free(fMatrix);
+		
+	this->freeData();
+}
+
+void SkImageView::getUri(SkString* uri) const
+{
+	if (uri)
+		*uri = fUri;
+}
+
+void SkImageView::setUri(const char uri[])
+{
+	if (!fUri.equals(uri))
+	{
+		fUri.set(uri);
+		this->onUriChange();
+	}
+}
+
+void SkImageView::setUri(const SkString& uri)
+{
+	if (fUri != uri)
+	{
+		fUri = uri;
+		this->onUriChange();
+	}
+}
+
+void SkImageView::setScaleType(ScaleType st)
+{
+	SkASSERT((unsigned)st <= kFitEnd_ScaleType);
+
+	if ((ScaleType)fScaleType != st)
+	{
+		fScaleType = SkToU8(st);
+		if (fUriIsValid)
+			this->inval(NULL);
+	}
+}
+
+bool SkImageView::getImageMatrix(SkMatrix* matrix) const
+{
+	if (fMatrix)
+	{
+		SkASSERT(!fMatrix->isIdentity());
+		if (matrix)
+			*matrix = *fMatrix;
+		return true;
+	}
+	else
+	{
+		if (matrix)
+			matrix->reset();
+		return false;
+	}
+}
+
+void SkImageView::setImageMatrix(const SkMatrix* matrix)
+{
+	bool changed = false;
+
+	if (matrix && !matrix->isIdentity())
+	{
+		if (fMatrix == NULL)
+			fMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix));
+		*fMatrix = *matrix;
+		changed = true;
+	}
+	else	// set us to identity
+	{
+		if (fMatrix)
+		{
+			SkASSERT(!fMatrix->isIdentity());
+			sk_free(fMatrix);
+			fMatrix = NULL;
+			changed = true;
+		}
+	}
+
+	// only redraw if we changed our matrix and we're not in scaleToFit mode
+	if (changed && this->getScaleType() == kMatrix_ScaleType && fUriIsValid)
+		this->inval(NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImageView::onEvent(const SkEvent& evt)
+{
+	if (evt.isType(SK_EventType_Inval))
+	{
+		if (fUriIsValid)
+			this->inval(NULL);
+		return true;
+	}
+	return this->INHERITED::onEvent(evt);
+}
+
+static inline SkMatrix::ScaleToFit scaleTypeToScaleToFit(SkImageView::ScaleType st)
+{
+	SkASSERT(st != SkImageView::kMatrix_ScaleType);
+	SkASSERT((unsigned)st <= SkImageView::kFitEnd_ScaleType);
+
+	SkASSERT(SkImageView::kFitXY_ScaleType - 1 == SkMatrix::kFill_ScaleToFit);
+	SkASSERT(SkImageView::kFitStart_ScaleType - 1 == SkMatrix::kStart_ScaleToFit);
+	SkASSERT(SkImageView::kFitCenter_ScaleType - 1 == SkMatrix::kCenter_ScaleToFit);
+	SkASSERT(SkImageView::kFitEnd_ScaleType - 1 == SkMatrix::kEnd_ScaleToFit);
+	
+	return (SkMatrix::ScaleToFit)(st - 1);
+}
+
+void SkImageView::onDraw(SkCanvas* canvas)
+{
+	SkRect	src;
+	if (!this->getDataBounds(&src))
+	{
+		SkDEBUGCODE(canvas->drawColor(SK_ColorRED);)
+		return;		// nothing to draw
+	}
+		
+	SkAutoCanvasRestore	restore(canvas, true);
+	SkMatrix			matrix;
+	
+	if (this->getScaleType() == kMatrix_ScaleType)
+		(void)this->getImageMatrix(&matrix);
+	else
+	{
+		SkRect	dst;		
+		dst.set(0, 0, this->width(), this->height());
+		matrix.setRectToRect(src, dst, scaleTypeToScaleToFit(this->getScaleType()));
+	}
+	canvas->concat(matrix);
+
+	SkPaint	paint;
+	
+	paint.setAntiAlias(true);
+
+	if (fDataIsAnim)
+	{
+		SkMSec	now = SkTime::GetMSecs();
+		
+		SkAnimator::DifferenceType diff = fData.fAnim->draw(canvas, &paint, now);
+		
+SkDEBUGF(("SkImageView : now = %X[%12.3f], diff = %d\n", now, now/1000., diff));
+
+		if (diff == SkAnimator::kDifferent)
+			this->inval(NULL);
+		else if (diff == SkAnimator::kPartiallyDifferent)
+		{
+			SkRect	bounds;
+			fData.fAnim->getInvalBounds(&bounds);
+			matrix.mapRect(&bounds);	// get the bounds into view coordinates
+			this->inval(&bounds);
+		}
+	}
+	else
+		canvas->drawBitmap(*fData.fBitmap, 0, 0, &paint);
+}
+
+void SkImageView::onInflate(const SkDOM& dom, const SkDOMNode* node)
+{
+	this->INHERITED::onInflate(dom, node);
+	
+	const char* src = dom.findAttr(node, "src");
+	if (src)
+		this->setUri(src);
+
+	int	index = dom.findList(node, "scaleType", "matrix,fitXY,fitStart,fitCenter,fitEnd");
+	if (index >= 0)
+		this->setScaleType((ScaleType)index);
+		
+	// need inflate syntax/reader for matrix
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SkImageView::onUriChange()
+{
+	if (this->freeData())
+		this->inval(NULL);
+	fUriIsValid = true;		// give ensureUriIsLoaded() a shot at the new uri
+}
+
+bool SkImageView::freeData()
+{
+	if (fData.fAnim)	// test is valid for all union values
+	{
+		if (fDataIsAnim)
+			delete fData.fAnim;
+		else
+			delete fData.fBitmap;
+
+		fData.fAnim = NULL;	// valid for all union values
+		return true;
+	}
+	return false;
+}
+
+bool SkImageView::getDataBounds(SkRect* bounds)
+{
+	SkASSERT(bounds);
+
+	if (this->ensureUriIsLoaded())
+	{
+		SkScalar width, height;
+
+		if (fDataIsAnim)
+		{			
+			if (SkScalarIsNaN(width = fData.fAnim->getScalar("dimensions", "x")) ||
+				SkScalarIsNaN(height = fData.fAnim->getScalar("dimensions", "y")))
+			{
+				// cons up fake bounds
+				width = this->width();
+				height = this->height();
+			}
+		}
+		else
+		{
+			width = SkIntToScalar(fData.fBitmap->width());
+			height = SkIntToScalar(fData.fBitmap->height());
+		}
+		bounds->set(0, 0, width, height);
+		return true;
+	}
+	return false;
+}
+
+bool SkImageView::ensureUriIsLoaded()
+{
+	if (fData.fAnim)	// test is valid for all union values
+	{
+		SkASSERT(fUriIsValid);
+		return true;
+	}
+	if (!fUriIsValid)
+		return false;
+
+	// try to load the url
+	if (fUri.endsWith(".xml"))	// assume it is screenplay
+	{
+		SkAnimator* anim = new SkAnimator;
+		
+		if (!anim->decodeURI(fUri.c_str()))
+		{
+			delete anim;
+			fUriIsValid = false;
+			return false;
+		}
+		anim->setHostEventSink(this);
+
+		fData.fAnim = anim;
+		fDataIsAnim = true;
+	}
+	else	// assume it is an image format
+	{
+    #if 0
+		SkBitmap* bitmap = new SkBitmap;
+
+		if (!SkImageDecoder::DecodeURL(fUri.c_str(), bitmap))
+		{
+			delete bitmap;
+			fUriIsValid = false;
+			return false;
+		}
+		fData.fBitmap = bitmap;
+		fDataIsAnim = false;
+    #else
+        return false;
+    #endif
+	}
+	return true;
+}
+
diff --git a/src/views/SkListView.cpp b/src/views/SkListView.cpp
new file mode 100644
index 0000000..ba4f02a
--- /dev/null
+++ b/src/views/SkListView.cpp
@@ -0,0 +1,895 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkEvent.h"
+#include "SkKey.h"
+#include "SkParsePaint.h"
+#include "SkSystemEventTypes.h"
+
+#if 0
+
+SkEvent* SkListSource::getEvent(int index)
+{
+	return NULL;
+}
+
+#include "SkOSFile.h"
+
+class SkDirListSource : public SkListSource {
+public:
+	SkDirListSource(const char path[], const char suffix[], const char target[])
+		: fPath(path), fSuffix(suffix), fTarget(target)
+	{
+		fCount = -1;
+	}
+	virtual int	countRows()
+	{
+		if (fCount < 0)
+		{
+			fCount = 0;
+			fIter.reset(fPath.c_str(), fSuffix.c_str());
+			while (fIter.next(NULL))
+				fCount += 1;
+			fIter.reset(fPath.c_str(), fSuffix.c_str());
+			fIndex = 0;
+		}
+		return fCount;
+	}
+	virtual void getRow(int index, SkString* left, SkString* right)
+	{
+		(void)this->countRows();
+		SkASSERT((unsigned)index < (unsigned)fCount);
+
+		if (fIndex > index)
+		{
+			fIter.reset(fPath.c_str(), fSuffix.c_str());
+			fIndex = 0;
+		}
+
+		while (fIndex < index)
+		{
+			fIter.next(NULL);
+			fIndex += 1;
+		}
+
+		if (fIter.next(left))
+		{
+			if (left)
+				left->remove(left->size() - fSuffix.size(), fSuffix.size());
+		}
+		else
+		{
+			if (left)
+				left->reset();
+		}
+		if (right)	// only set to ">" if we know we're on a sub-directory
+			right->reset();
+
+		fIndex += 1;
+	}
+	virtual SkEvent* getEvent(int index)
+	{
+		SkASSERT((unsigned)index < (unsigned)fCount);
+
+		SkEvent*	evt = new SkEvent();
+		SkString	label;
+
+		this->getRow(index, &label, NULL);
+		evt->setString("name", label.c_str());
+
+		int c = fPath.c_str()[fPath.size() - 1];
+		if (c != '/' && c != '\\')
+			label.prepend("/");
+		label.prepend(fPath);
+		label.append(fSuffix);
+		evt->setString("path", label.c_str());
+		evt->setS32("index", index);
+		evt->setS32("duration", 22);
+		evt->setType(fTarget);
+		return evt;
+	}
+
+private:
+	SkString		fPath, fSuffix;
+	SkString		fTarget;
+	SkOSFile::Iter	fIter;
+	int				fCount;
+	int				fIndex;
+};
+
+SkListSource* SkListSource::CreateFromDir(const char path[], const char suffix[], const char target[])
+{
+	return new SkDirListSource(path, suffix, target);
+}
+
+//////////////////////////////////////////////////////////////////
+
+class SkDOMListSource : public SkListSource {
+public:
+	enum Type {
+		kUnknown_Type,
+		kDir_Type,
+		kToggle_Type
+	};
+	struct ItemRec {
+		SkString	fLabel;
+		SkString	fTail, fAltTail;
+		SkString	fTarget;
+		Type		fType;
+	};
+
+	SkDOMListSource(const SkDOM& dom, const SkDOM::Node* node) : fDirTail(">")
+	{
+		const SkDOM::Node* child = dom.getFirstChild(node, "item");
+		int	count = 0;
+
+		while (child)
+		{
+			count += 1;
+			child = dom.getNextSibling(child, "item");
+		}
+
+		fCount = count;
+		fList = NULL;
+		if (count)
+		{
+			ItemRec* rec = fList = new ItemRec[count];
+
+			child = dom.getFirstChild(node, "item");
+			while (child)
+			{
+				rec->fLabel.set(dom.findAttr(child, "label"));
+				rec->fTail.set(dom.findAttr(child, "tail"));
+				rec->fAltTail.set(dom.findAttr(child, "alt-tail"));
+				rec->fTarget.set(dom.findAttr(child, "target"));
+				rec->fType = kUnknown_Type;
+
+				int	index = dom.findList(child, "type", "dir,toggle");
+				if (index >= 0)
+					rec->fType = (Type)(index + 1);
+
+				child = dom.getNextSibling(child, "item");
+				rec += 1;
+			}
+		}
+	}
+	virtual ~SkDOMListSource()
+	{
+		delete[] fList;
+	}
+	virtual int	countRows()
+	{
+		return fCount;
+	}
+	virtual void getRow(int index, SkString* left, SkString* right)
+	{
+		SkASSERT((unsigned)index < (unsigned)fCount);
+
+		if (left)
+			*left = fList[index].fLabel;
+		if (right)
+			*right = fList[index].fType == kDir_Type ? fDirTail : fList[index].fTail;
+	}
+	virtual SkEvent* getEvent(int index)
+	{
+		SkASSERT((unsigned)index < (unsigned)fCount);
+
+		if (fList[index].fType == kDir_Type)
+		{
+			SkEvent* evt = new SkEvent();
+			evt->setType(fList[index].fTarget);
+			evt->setFast32(index);
+			return evt;
+		}
+		if (fList[index].fType == kToggle_Type)
+			fList[index].fTail.swap(fList[index].fAltTail);
+
+		return NULL;
+	}
+
+private:
+	int			fCount;
+	ItemRec*	fList;
+	SkString	fDirTail;
+};
+
+SkListSource* SkListSource::CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node)
+{
+	return new SkDOMListSource(dom, node);
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+SkListView::SkListView(U32 flags) : SkWidgetView(flags)
+{
+	fSource = NULL;
+	fScrollIndex = 0;
+	fCurrIndex = -1;
+	fRowHeight = SkIntToScalar(16);
+	fVisibleRowCount = 0;
+	fStrCache = NULL;
+
+	fPaint[kBG_Attr].setColor(0);
+	fPaint[kNormalText_Attr].setTextSize(SkIntToScalar(14));
+	fPaint[kHiliteText_Attr].setTextSize(SkIntToScalar(14));
+	fPaint[kHiliteText_Attr].setColor(SK_ColorWHITE);
+	fPaint[kHiliteCell_Attr].setColor(SK_ColorBLUE);
+}
+
+SkListView::~SkListView()
+{
+	delete[] fStrCache;
+	fSource->safeUnref();
+}
+
+void SkListView::setRowHeight(SkScalar height)
+{
+	SkASSERT(height >= 0);
+
+	if (fRowHeight != height)
+	{
+		fRowHeight = height;
+		this->inval(NULL);
+		this->onSizeChange();
+	}
+}
+
+void SkListView::setSelection(int index)
+{
+	if (fCurrIndex != index)
+	{
+		this->invalSelection();
+		fCurrIndex = index;
+		this->invalSelection();
+		this->ensureSelectionIsVisible();
+
+		{
+			SkEvent	evt;
+			evt.setType("listview-selection");
+			evt.setFast32(index);
+			this->sendEventToParents(evt);
+		}
+	}
+}
+
+void SkListView::moveSelectionUp()
+{
+	if (fSource)
+	{
+		int	index = fCurrIndex;
+		if (index < 0)	// no selection
+			index = fSource->countRows() - 1;
+		else
+			index = SkMax32(index - 1, 0);
+		this->setSelection(index);
+	}
+}
+
+void SkListView::moveSelectionDown()
+{
+	if (fSource)
+	{
+		int	index = fCurrIndex;
+		if (index < 0)	// no selection
+			index = 0;
+		else
+			index = SkMin32(index + 1, fSource->countRows() - 1);
+		this->setSelection(index);
+	}
+}
+
+void SkListView::invalSelection()
+{
+	SkRect	r;
+	if (this->getRowRect(fCurrIndex, &r))
+		this->inval(&r);
+}
+
+void SkListView::ensureSelectionIsVisible()
+{
+	if (fSource == NULL)
+		return;
+
+	if ((unsigned)fCurrIndex < (unsigned)fSource->countRows())
+	{
+		int index = this->logicalToVisualIndex(fCurrIndex);
+
+		if ((unsigned)index >= (unsigned)fVisibleRowCount)	// need to scroll
+		{
+			if (index < 0)	// too high
+				fScrollIndex = fCurrIndex;
+			else
+				fScrollIndex = fCurrIndex - fVisibleRowCount + 1;
+			SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows());
+
+			this->dirtyStrCache();
+			this->inval(NULL);
+		}
+	}
+}
+
+bool SkListView::getRowRect(int index, SkRect* r) const
+{
+	SkASSERT(r);
+	index = this->logicalToVisualIndex(index);
+	if (index >= 0)
+	{
+		SkScalar top = index * fRowHeight;
+
+		if (top < this->height())
+		{
+			if (r)
+				r->set(0, top, this->width(), top + fRowHeight);
+			return true;
+		}
+	}
+	return false;
+}
+
+SkPaint& SkListView::paint(Attr attr)
+{
+	SkASSERT((unsigned)attr < kAttrCount);
+	return fPaint[attr];
+}
+
+SkListSource* SkListView::setListSource(SkListSource* src)
+{
+	if (fSource != src)
+	{
+		SkRefCnt_SafeAssign(fSource, src);
+		this->dirtyStrCache();
+		this->ensureSelectionIsVisible();
+		this->inval(NULL);
+	}
+	return src;
+}
+
+void SkListView::onDraw(SkCanvas* canvas)
+{
+	this->INHERITED::onDraw(canvas);
+
+	canvas->drawPaint(fPaint[kBG_Attr]);
+
+	int	visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex);
+	if (visibleCount == 0)
+		return;
+
+	this->ensureStrCache(visibleCount);
+	int currIndex = this->logicalToVisualIndex(fCurrIndex);
+
+	if ((unsigned)currIndex < (unsigned)visibleCount)
+	{
+		SkAutoCanvasRestore	restore(canvas, true);
+		SkRect	r;
+
+		canvas->translate(0, currIndex * fRowHeight);
+		(void)this->getRowRect(fScrollIndex, &r);
+		canvas->drawRect(r, fPaint[kHiliteCell_Attr]);
+	}
+
+	SkPaint*	p;
+	SkScalar	y, x = SkIntToScalar(6);
+	SkScalar	rite = this->width() - x;
+
+	{
+		SkScalar ascent, descent;
+		fPaint[kNormalText_Attr].measureText(0, NULL, &ascent, &descent);
+		y = SkScalarHalf(fRowHeight - descent + ascent) - ascent;
+	}
+
+	for (int i = 0; i < visibleCount; i++)
+	{
+		if (i == currIndex)
+			p = &fPaint[kHiliteText_Attr];
+		else
+			p = &fPaint[kNormalText_Attr];
+
+		p->setTextAlign(SkPaint::kLeft_Align);
+		canvas->drawText(fStrCache[i].c_str(), fStrCache[i].size(), x, y, *p);
+		p->setTextAlign(SkPaint::kRight_Align);
+		canvas->drawText(fStrCache[i + visibleCount].c_str(), fStrCache[i + visibleCount].size(), rite, y, *p);
+		canvas->translate(0, fRowHeight);
+	}
+}
+
+void SkListView::onSizeChange()
+{
+	SkScalar count = SkScalarDiv(this->height(), fRowHeight);
+	int		 n = SkScalarFloor(count);
+
+	// only want to show rows that are mostly visible
+	if (n == 0 || count - SkIntToScalar(n) > SK_Scalar1*75/100)
+		n += 1;
+
+	if (fVisibleRowCount != n)
+	{
+		fVisibleRowCount = n;
+		this->ensureSelectionIsVisible();
+		this->dirtyStrCache();
+	}
+}
+
+void SkListView::dirtyStrCache()
+{
+	if (fStrCache)
+	{
+		delete[] fStrCache;
+		fStrCache = NULL;
+	}
+}
+
+void SkListView::ensureStrCache(int count)
+{
+	if (fStrCache == NULL)
+	{
+		fStrCache = new SkString[count << 1];
+
+		if (fSource)
+			for (int i = 0; i < count; i++)
+				fSource->getRow(i + fScrollIndex, &fStrCache[i], &fStrCache[i + count]);
+	}
+}
+
+bool SkListView::onEvent(const SkEvent& evt)
+{
+	if (evt.isType(SK_EventType_Key))
+	{
+		switch (evt.getFast32()) {
+		case kUp_SkKey:
+			this->moveSelectionUp();
+			return true;
+		case kDown_SkKey:
+			this->moveSelectionDown();
+			return true;
+		case kRight_SkKey:
+		case kOK_SkKey:
+			if (fSource && fCurrIndex >= 0)
+			{
+				SkEvent* evt = fSource->getEvent(fCurrIndex);
+				if (evt)
+				{
+					SkView* view = this->sendEventToParents(*evt);
+					delete evt;
+					return view != NULL;
+				}
+				else	// hack to make toggle work
+				{
+					this->dirtyStrCache();
+					this->inval(NULL);
+				}
+			}
+			break;
+		}
+	}
+	return this->INHERITED::onEvent(evt);
+}
+
+void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+
+	SkScalar			x;
+	const SkDOM::Node*	child;
+
+	if (dom.findScalar(node, "row-height", &x))
+		this->setRowHeight(x);
+
+	if ((child = dom.getFirstChild(node, "hilite-paint")) != NULL)
+		SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
+
+	// look for a listsource
+	{
+		SkListSource* src = NULL;
+
+		if ((child = dom.getFirstChild(node, "file-listsource")) != NULL)
+		{
+			const char* path = dom.findAttr(child, "path");
+			if (path)
+				src = SkListSource::CreateFromDir(	path,
+													dom.findAttr(child, "filter"),
+													dom.findAttr(child, "target"));
+		}
+		else if ((child = dom.getFirstChild(node, "xml-listsource")) != NULL)
+		{
+			src = SkListSource::CreateFromDOM(dom, child);
+		}
+
+		if (src)
+		{
+			this->setListSource(src)->unref();
+			this->setSelection(0);
+		}
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageDecoder.h"
+#include "SkShader.h"
+
+class SkScrollBarView : public SkView {
+public:
+	SkScrollBarView(const char bg[], const char fg[])
+	{
+		fBGRef = SkBitmapRef::Decode(bg, true);
+		fFGRef = SkBitmapRef::Decode(fg, true);
+
+		if (fBGRef)
+			this->setWidth(SkIntToScalar(fBGRef->bitmap().width()));
+	}
+	~SkScrollBarView()
+	{
+		delete fBGRef;
+		delete fFGRef;
+	}
+protected:
+	virtual void onDraw(SkCanvas* canvas)
+	{
+		if (fBGRef == NULL) return;
+
+		SkPaint	paint;
+
+		SkShader* shader = SkShader::CreateBitmapShader(fBGRef->bitmap(), false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode);
+		paint.setShader(shader)->unref();
+
+		canvas->drawPaint(paint);
+	}
+private:
+	SkBitmapRef*	fBGRef, *fFGRef;
+};
+
+SkGridView::SkGridView(U32 flags) : SkWidgetView(flags)
+{
+	fSource = NULL;
+	fCurrIndex = -1;
+	fVisibleCount.set(0, 0);
+
+	fPaint[kBG_Attr].setColor(SK_ColorWHITE);
+	fPaint[kHiliteCell_Attr].setColor(SK_ColorYELLOW);
+	fPaint[kHiliteCell_Attr].setStyle(SkPaint::kStroke_Style);
+	fPaint[kHiliteCell_Attr].setAntiAliasOn(true);
+	fPaint[kHiliteCell_Attr].setStrokeWidth(SK_Scalar1*3);
+
+	fScrollBar = new SkScrollBarView("icons/scrollbarGrey.jpg", "icons/scrollbarBlue.jpg");
+	this->attachChildToFront(fScrollBar)->unref();
+	fScrollBar->setVisibleP(true);
+}
+
+SkGridView::~SkGridView()
+{
+	fSource->safeUnref();
+}
+
+void SkGridView::getCellSize(SkPoint* size) const
+{
+	if (size)
+		*size = fCellSize;
+}
+
+void SkGridView::setCellSize(SkScalar x, SkScalar y)
+{
+	SkASSERT(x >= 0 && y >= 0);
+
+	if (!fCellSize.equals(x, y))
+	{
+		fCellSize.set(x, y);
+		this->inval(NULL);
+	}
+}
+
+void SkGridView::setSelection(int index)
+{
+	if (fCurrIndex != index)
+	{
+		this->invalSelection();
+		fCurrIndex = index;
+		this->invalSelection();
+		this->ensureSelectionIsVisible();
+
+		// this generates the click
+		{
+			SkEvent	evt;
+			evt.setType("listview-selection");
+			evt.setFast32(index);
+			this->sendEventToParents(evt);
+		}
+	}
+}
+
+void SkGridView::moveSelectionUp()
+{
+	if (fSource)
+	{
+		int	index = fCurrIndex;
+		if (index < 0)	// no selection
+			index = fSource->countRows() - 1;
+		else
+			index = SkMax32(index - 1, 0);
+		this->setSelection(index);
+	}
+}
+
+void SkGridView::moveSelectionDown()
+{
+	if (fSource)
+	{
+		int	index = fCurrIndex;
+		if (index < 0)	// no selection
+			index = 0;
+		else
+			index = SkMin32(index + 1, fSource->countRows() - 1);
+		this->setSelection(index);
+	}
+}
+
+void SkGridView::invalSelection()
+{
+	SkRect	r;
+	if (this->getCellRect(fCurrIndex, &r))
+	{
+		SkScalar inset = 0;
+		if (fPaint[kHiliteCell_Attr].getStyle() != SkPaint::kFill_Style)
+			inset += fPaint[kHiliteCell_Attr].getStrokeWidth() / 2;
+		if (fPaint[kHiliteCell_Attr].isAntiAliasOn())
+			inset += SK_Scalar1;
+		r.inset(-inset, -inset);
+		this->inval(&r);
+	}
+}
+
+void SkGridView::ensureSelectionIsVisible()
+{
+	if (fSource == NULL)
+		return;
+#if 0
+	if ((unsigned)fCurrIndex < (unsigned)fSource->countRows())
+	{
+		int index = this->logicalToVisualIndex(fCurrIndex);
+
+		if ((unsigned)index >= (unsigned)fVisibleRowCount)	// need to scroll
+		{
+			if (index < 0)	// too high
+				fScrollIndex = fCurrIndex;
+			else
+				fScrollIndex = fCurrIndex - fVisibleRowCount + 1;
+			SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows());
+
+			this->dirtyStrCache();
+			this->inval(NULL);
+		}
+	}
+#endif
+}
+
+bool SkGridView::getCellRect(int index, SkRect* r) const
+{
+	if (fVisibleCount.fY == 0)
+		return false;
+
+	index = this->logicalToVisualIndex(index);
+	if (index >= 0)
+	{
+		SkRect	bounds;
+		int row = index / fVisibleCount.fY;
+		int col = index % fVisibleCount.fY;
+
+		bounds.set(0, 0, fCellSize.fX, fCellSize.fY);
+		bounds.offset(col * (fCellSize.fX + SkIntToScalar(col > 0)),
+					  row * (fCellSize.fY + SkIntToScalar(row > 0)));
+
+		if (bounds.fTop < this->height())
+		{
+			if (r)
+				*r = bounds;
+			return true;
+		}
+	}
+	return false;
+}
+
+SkPaint& SkGridView::paint(Attr attr)
+{
+	SkASSERT((unsigned)attr < kAttrCount);
+	return fPaint[attr];
+}
+
+SkListSource* SkGridView::setListSource(SkListSource* src)
+{
+	if (fSource != src)
+	{
+		SkRefCnt_SafeAssign(fSource, src);
+	//	this->dirtyStrCache();
+		this->ensureSelectionIsVisible();
+		this->inval(NULL);
+	}
+	return src;
+}
+
+#include "SkShader.h"
+
+static void copybits(SkCanvas* canvas, const SkBitmap& bm, const SkRect& dst, const SkPaint& paint)
+{
+	SkRect		src;
+	SkMatrix	matrix;
+
+	src.set(0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()));
+	if (matrix.setRectToRect(src, dst))
+	{
+		SkPaint	  p(paint);
+		SkShader* shader = SkShader::CreateBitmapShader(bm, false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode);
+		p.setShader(shader)->unref();
+
+		shader->setLocalMatrix(matrix);
+		canvas->drawRect(dst, p);
+	}
+}
+
+#include "SkImageDecoder.h"
+
+void SkGridView::onDraw(SkCanvas* canvas)
+{
+	this->INHERITED::onDraw(canvas);
+
+	canvas->drawPaint(fPaint[kBG_Attr]);
+
+	if (fSource == NULL)
+		return;
+
+#if 0
+	int	visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex);
+	if (visibleCount == 0)
+		return;
+
+	this->ensureStrCache(visibleCount);
+	int currIndex = this->logicalToVisualIndex(fCurrIndex);
+#endif
+
+	SkPaint	p;
+	for (int i = 0; i < fSource->countRows(); i++)
+	{
+		bool	 forced = false;
+		SkEvent* evt = fSource->getEvent(i);
+		SkASSERT(evt);
+		SkString path(evt->findString("path"));
+		delete evt;
+
+		SkBitmapRef* bmr = SkBitmapRef::Decode(path.c_str(), false);
+		if (bmr == NULL)
+		{
+			bmr = SkBitmapRef::Decode(path.c_str(), true);
+			if (bmr)
+				forced = true;
+		}
+
+		if (bmr)
+		{
+			SkAutoTDelete<SkBitmapRef>	autoRef(bmr);
+			SkRect	r;
+			if (!this->getCellRect(i, &r))
+				break;
+			copybits(canvas, bmr->bitmap(), r, p);
+		}
+		// only draw one forced bitmap at a time
+		if (forced)
+		{
+			this->inval(NULL);	// could inval only the remaining visible cells...
+			break;
+		}
+	}
+
+	// draw the hilite
+	{
+		SkRect	r;
+		if (fCurrIndex >= 0 && this->getCellRect(fCurrIndex, &r))
+			canvas->drawRect(r, fPaint[kHiliteCell_Attr]);
+	}
+}
+
+static int check_count(int n, SkScalar s)
+{
+	// only want to show cells that are mostly visible
+	if (n == 0 || s - SkIntToScalar(n) > SK_Scalar1*75/100)
+		n += 1;
+	return n;
+}
+
+void SkGridView::onSizeChange()
+{
+	fScrollBar->setHeight(this->height());
+	fScrollBar->setLoc(this->locX() + this->width() - fScrollBar->width(), 0);
+
+	if (fCellSize.equals(0, 0))
+	{
+		fVisibleCount.set(0, 0);
+		return;
+	}
+
+	SkScalar rows = SkScalarDiv(this->height(), fCellSize.fY);
+	SkScalar cols = SkScalarDiv(this->width(), fCellSize.fX);
+	int		 y = SkScalarFloor(rows);
+	int		 x = SkScalarFloor(cols);
+
+	y = check_count(y, rows);
+	x = check_count(x, cols);
+
+	if (!fVisibleCount.equals(x, y))
+	{
+		fVisibleCount.set(x, y);
+		this->ensureSelectionIsVisible();
+	//	this->dirtyStrCache();
+	}
+}
+
+bool SkGridView::onEvent(const SkEvent& evt)
+{
+	if (evt.isType(SK_EventType_Key))
+	{
+		switch (evt.getFast32()) {
+		case kUp_SkKey:
+			this->moveSelectionUp();
+			return true;
+		case kDown_SkKey:
+			this->moveSelectionDown();
+			return true;
+		case kRight_SkKey:
+		case kOK_SkKey:
+			if (fSource && fCurrIndex >= 0)
+			{
+				SkEvent* evt = fSource->getEvent(fCurrIndex);
+				if (evt)
+				{
+					// augment the event with our local rect
+					(void)this->getCellRect(fCurrIndex, (SkRect*)evt->setScalars("local-rect", 4, NULL));
+
+					SkView* view = this->sendEventToParents(*evt);
+					delete evt;
+					return view != NULL;
+				}
+			}
+			break;
+		}
+	}
+	return this->INHERITED::onEvent(evt);
+}
+
+void SkGridView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+
+	SkScalar			x[2];
+	const SkDOM::Node*	child;
+
+	if (dom.findScalars(node, "cell-size", x, 2))
+		this->setCellSize(x[0], x[1]);
+
+	if ((child = dom.getFirstChild(node, "hilite-paint")) != NULL)
+		SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
+
+	// look for a listsource
+	{
+		SkListSource* src = NULL;
+
+		if ((child = dom.getFirstChild(node, "file-listsource")) != NULL)
+		{
+			const char* path = dom.findAttr(child, "path");
+			if (path)
+				src = SkListSource::CreateFromDir(	path,
+													dom.findAttr(child, "filter"),
+													dom.findAttr(child, "target"));
+		}
+		else if ((child = dom.getFirstChild(node, "xml-listsource")) != NULL)
+		{
+			src = SkListSource::CreateFromDOM(dom, child);
+		}
+
+		if (src)
+		{
+			this->setListSource(src)->unref();
+			this->setSelection(0);
+		}
+	}
+	this->onSizeChange();
+}
+
+#endif
diff --git a/src/views/SkListWidget.cpp b/src/views/SkListWidget.cpp
new file mode 100644
index 0000000..89008e7
--- /dev/null
+++ b/src/views/SkListWidget.cpp
@@ -0,0 +1,623 @@
+#include "SkWidgetViews.h"
+
+#include "SkAnimator.h"
+#include "SkScrollBarView.h"
+
+extern void init_skin_anim(const char name[], SkAnimator*);
+
+struct SkListView::BindingRec {
+	SkString	fSlotName;
+	int			fFieldIndex;
+};
+
+SkListView::SkListView()
+{
+	fSource = NULL;				// our list-source
+	fScrollBar = NULL;
+	fAnims = NULL;				// array of animators[fVisibleRowCount]
+	fBindings = NULL;			// our fields->slot array
+	fBindingCount = 0;			// number of entries in fSlots array
+	fScrollIndex = 0;			// number of cells to skip before first visible cell
+	fCurrIndex = -1;			// index of "selected" cell
+	fVisibleRowCount = 0;		// number of cells that can fit in our bounds
+	fAnimContentDirty = true;	// true if fAnims[] have their correct content
+	fAnimFocusDirty = true;
+
+	fHeights[kNormal_Height] = SkIntToScalar(16);
+	fHeights[kSelected_Height] = SkIntToScalar(16);
+	
+	this->setFlags(this->getFlags() | kFocusable_Mask);
+}
+
+SkListView::~SkListView()
+{
+	SkSafeUnref(fScrollBar);
+	SkSafeUnref(fSource);
+	delete[] fAnims;
+	delete[] fBindings;
+}
+
+void SkListView::setHasScrollBar(bool hasSB)
+{
+	if (hasSB != this->hasScrollBar())
+	{
+		if (hasSB)
+		{
+			SkASSERT(fScrollBar == NULL);
+			fScrollBar = (SkScrollBarView*)SkWidgetFactory(kScroll_WidgetEnum);
+			fScrollBar->setVisibleP(true);
+			this->attachChildToFront(fScrollBar);
+			fScrollBar->setHeight(this->height());	// assume it auto-sets its width
+		//	fScrollBar->setLoc(this->getContentWidth(), 0);
+			fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
+		}
+		else
+		{
+			SkASSERT(fScrollBar);
+			fScrollBar->detachFromParent();
+			fScrollBar->unref();
+			fScrollBar = NULL;
+		}
+		this->dirtyCache(kAnimContent_DirtyFlag);
+	}
+}
+
+void SkListView::setSelection(int index)
+{
+	if (fCurrIndex != index)
+	{
+		fAnimFocusDirty = true;
+		this->inval(NULL);
+
+		this->invalSelection();
+		fCurrIndex = index;
+		this->invalSelection();
+		this->ensureSelectionIsVisible();
+	}
+}
+
+bool SkListView::moveSelectionUp()
+{
+	if (fSource)
+	{
+		int	index = fCurrIndex;
+		if (index < 0)	// no selection
+			index = fSource->countRecords() - 1;
+		else
+			index = SkMax32(index - 1, 0);
+		
+		if (fCurrIndex != index)
+		{
+			this->setSelection(index);
+			return true;
+		}
+	}
+	return false;
+}
+
+bool SkListView::moveSelectionDown()
+{
+	if (fSource)
+	{
+		int	index = fCurrIndex;
+		if (index < 0)	// no selection
+			index = 0;
+		else
+			index = SkMin32(index + 1, fSource->countRecords() - 1);
+		
+		if (fCurrIndex != index)
+		{
+			this->setSelection(index);
+			return true;
+		}
+	}
+	return false;
+}
+
+void SkListView::invalSelection()
+{
+	SkRect	r;
+	if (this->getRowRect(fCurrIndex, &r))
+		this->inval(&r);
+}
+
+void SkListView::ensureSelectionIsVisible()
+{
+	if (fSource && (unsigned)fCurrIndex < (unsigned)fSource->countRecords())
+	{
+		int index = this->logicalToVisualIndex(fCurrIndex);
+
+		if ((unsigned)index >= (unsigned)fVisibleRowCount)	// need to scroll
+		{
+			int newIndex;
+			
+			if (index < 0)	// too high
+				newIndex = fCurrIndex;
+			else
+				newIndex = fCurrIndex - fVisibleRowCount + 1;
+			SkASSERT((unsigned)newIndex < (unsigned)fSource->countRecords());
+			this->inval(NULL);
+			
+			if (fScrollIndex != newIndex)
+			{
+				fScrollIndex = newIndex;
+				if (fScrollBar)
+					fScrollBar->setStart(newIndex);
+				this->dirtyCache(kAnimContent_DirtyFlag);
+			}
+		}
+	}
+}
+
+SkScalar SkListView::getContentWidth() const
+{
+	SkScalar width = this->width();
+	
+	if (fScrollBar)
+	{
+		width -= fScrollBar->width();
+		if (width < 0)
+			width = 0;
+	}
+	return width;
+}
+
+bool SkListView::getRowRect(int index, SkRect* r) const
+{
+	SkASSERT(r);
+
+	index = this->logicalToVisualIndex(index);
+	if (index >= 0)
+	{
+		int	selection = this->logicalToVisualIndex(fCurrIndex);
+		
+		SkScalar height = fHeights[index == selection ? kSelected_Height : kNormal_Height];
+		SkScalar top = index * fHeights[kNormal_Height];
+
+		if (index > selection && selection >= 0)
+			top += fHeights[kSelected_Height] - fHeights[kNormal_Height];	
+
+		if (top < this->height())
+		{
+			if (r)
+				r->set(0, top, this->getContentWidth(), top + height);
+			return true;
+		}
+	}
+	return false;
+}
+
+SkListSource* SkListView::setListSource(SkListSource* src)
+{
+	if (fSource != src)
+	{
+		SkRefCnt_SafeAssign(fSource, src);
+		this->ensureSelectionIsVisible();
+		this->inval(NULL);
+		
+		if (fScrollBar)
+			fScrollBar->setTotal(fSource->countRecords());
+	}
+	return src;
+}
+
+void SkListView::dirtyCache(unsigned dirtyFlags)
+{
+	if (dirtyFlags & kAnimCount_DirtyFlag)
+	{
+		delete fAnims;
+		fAnims = NULL;
+		fAnimContentDirty = true;
+		fAnimFocusDirty = true;
+	}
+	if (dirtyFlags & kAnimContent_DirtyFlag)
+	{
+		if (!fAnimContentDirty)
+		{
+			this->inval(NULL);
+			fAnimContentDirty = true;
+		}
+		fAnimFocusDirty = true;
+	}
+}
+
+bool SkListView::ensureCache()
+{
+	if (fSkinName.size() == 0)
+		return false;
+
+	if (fAnims == NULL)
+	{
+		int n = SkMax32(1, fVisibleRowCount);
+
+		SkASSERT(fAnimContentDirty);
+		fAnims = new SkAnimator[n];
+		for (int i = 0; i < n; i++)
+		{
+			fAnims[i].setHostEventSink(this);
+			init_skin_anim(fSkinName.c_str(), &fAnims[i]);
+		}
+		
+		fHeights[kNormal_Height] = fAnims[0].getScalar("idleHeight", "value");
+		fHeights[kSelected_Height] = fAnims[0].getScalar("focusedHeight", "value");
+
+		fAnimFocusDirty = true;
+	}
+
+	if (fAnimContentDirty && fSource)
+	{
+		fAnimContentDirty = false;
+
+		SkString	str;
+		SkEvent		evt("user");
+		evt.setString("id", "setFields");
+		evt.setS32("rowCount", fVisibleRowCount);
+		
+		SkEvent	dimEvt("user");
+		dimEvt.setString("id", "setDim");
+		dimEvt.setScalar("dimX", this->getContentWidth());
+		dimEvt.setScalar("dimY", this->height());
+
+		for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
+		{
+			evt.setS32("relativeIndex", i - fScrollIndex);
+			for (int j = 0; j < fBindingCount; j++)
+			{
+				fSource->getRecord(i, fBindings[j].fFieldIndex, &str);
+//SkDEBUGF(("getRecord(%d,%d,%s) slot(%s)\n", i, fBindings[j].fFieldIndex, str.c_str(), fBindings[j].fSlotName.c_str()));
+				evt.setString(fBindings[j].fSlotName.c_str(), str.c_str());
+			}
+			(void)fAnims[i % fVisibleRowCount].doUserEvent(evt);
+			(void)fAnims[i % fVisibleRowCount].doUserEvent(dimEvt);
+		}
+		fAnimFocusDirty = true;
+	}
+
+	if (fAnimFocusDirty)
+	{
+//SkDEBUGF(("service fAnimFocusDirty\n"));
+		fAnimFocusDirty = false;
+
+		SkEvent		focusEvt("user");
+		focusEvt.setString("id", "setFocus");
+
+		for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
+		{
+			focusEvt.setS32("FOCUS", i == fCurrIndex);
+			(void)fAnims[i % fVisibleRowCount].doUserEvent(focusEvt);
+		}
+	}
+
+	return true;
+}
+
+void SkListView::ensureVisibleRowCount()
+{
+	SkScalar	height = this->height();
+	int			n = 0;
+	
+	if (height > 0)
+	{
+		n = 1;
+		height -= fHeights[kSelected_Height];
+		if (height > 0)
+		{
+			SkScalar count = SkScalarDiv(height, fHeights[kNormal_Height]);
+			n += SkScalarFloor(count);
+			if (count - SkIntToScalar(n) > SK_Scalar1*3/4)
+				n += 1;
+				
+		//	SkDebugf("count %g, n %d\n", count/65536., n);
+		}
+	}
+
+	if (fVisibleRowCount != n)
+	{
+		if (fScrollBar)
+			fScrollBar->setShown(n);
+
+		fVisibleRowCount = n;
+		this->ensureSelectionIsVisible();
+		this->dirtyCache(kAnimCount_DirtyFlag | kAnimContent_DirtyFlag);
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+void SkListView::onSizeChange()
+{
+	this->INHERITED::onSizeChange();
+
+	if (fScrollBar)
+		fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
+
+	this->ensureVisibleRowCount();
+}
+
+void SkListView::onDraw(SkCanvas* canvas)
+{
+	this->INHERITED::onDraw(canvas);
+
+	this->ensureVisibleRowCount();
+
+	int	visibleCount = SkMin32(fVisibleRowCount, fSource->countRecords() - fScrollIndex);
+	if (visibleCount == 0 || !this->ensureCache())
+		return;
+
+//SkDebugf("visibleCount %d scrollIndex %d currIndex %d\n", visibleCount, fScrollIndex, fCurrIndex);
+
+	SkAutoCanvasRestore	ar(canvas, true);
+	SkMSec				now = SkTime::GetMSecs();
+	SkRect				bounds;
+
+	bounds.fLeft	= 0;
+	bounds.fRight	= this->getContentWidth();
+	bounds.fBottom	= 0;
+	// assign bounds.fTop inside the loop
+
+	// hack to reveal our bounds for debugging
+	if (this->hasFocus())
+		canvas->drawARGB(0x11, 0, 0, 0xFF);
+	else
+		canvas->drawARGB(0x11, 0x88, 0x88, 0x88);
+
+	for (int i = fScrollIndex; i < fScrollIndex + visibleCount; i++)
+	{
+		SkPaint	 paint;
+		SkScalar height = fHeights[i == fCurrIndex ? kSelected_Height : kNormal_Height];
+
+		bounds.fTop = bounds.fBottom;
+		bounds.fBottom += height;
+		
+		canvas->save();
+		if (fAnims[i % fVisibleRowCount].draw(canvas, &paint, now) != SkAnimator::kNotDifferent)
+			this->inval(&bounds);
+		canvas->restore();
+
+		canvas->translate(0, height);
+	}
+}
+
+bool SkListView::onEvent(const SkEvent& evt)
+{
+	if (evt.isType(SK_EventType_Key))
+	{
+		switch (evt.getFast32()) {
+		case kUp_SkKey:
+			return this->moveSelectionUp();
+		case kDown_SkKey:
+			return this->moveSelectionDown();
+		case kRight_SkKey:
+		case kOK_SkKey:
+			this->postWidgetEvent();
+			return true;
+		default:
+			break;
+		}
+	}
+	return this->INHERITED::onEvent(evt);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static const char gListViewEventSlot[] = "sk-listview-slot-name";
+
+/*virtual*/ bool SkListView::onPrepareWidgetEvent(SkEvent* evt)
+{
+	if (fSource && fCurrIndex >= 0 && this->INHERITED::onPrepareWidgetEvent(evt) &&
+		fSource->prepareWidgetEvent(evt, fCurrIndex))
+	{
+		evt->setS32(gListViewEventSlot, fCurrIndex);
+		return true;
+	}
+	return false;
+}
+
+int SkListView::GetWidgetEventListIndex(const SkEvent& evt)
+{
+	int32_t	index;
+
+	return evt.findS32(gListViewEventSlot, &index) ? index : -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+	
+	{
+		bool hasScrollBar;
+		if (dom.findBool(node, "scrollBar", &hasScrollBar))
+			this->setHasScrollBar(hasScrollBar);
+	}
+
+	const SkDOM::Node*	child;
+
+	if ((child = dom.getFirstChild(node, "bindings")) != NULL)
+	{
+		delete[] fBindings;
+		fBindings = NULL;
+		fBindingCount = 0;
+
+		SkListSource* listSrc = SkListSource::Factory(dom.findAttr(child, "data-fields"));
+		SkASSERT(listSrc);
+		fSkinName.set(dom.findAttr(child, "skin-slots"));
+		SkASSERT(fSkinName.size());
+
+		this->setListSource(listSrc)->unref();
+			
+		int count = dom.countChildren(child, "bind");
+		if (count > 0)
+		{
+			fBindings = new BindingRec[count];
+			count = 0;	// reuse this to count up to the number of valid bindings
+
+			child = dom.getFirstChild(child, "bind");
+			SkASSERT(child);
+			do {
+				const char* fieldName = dom.findAttr(child, "field");
+				const char* slotName = dom.findAttr(child, "slot");
+				if (fieldName && slotName)
+				{
+					fBindings[count].fFieldIndex = listSrc->findFieldIndex(fieldName);
+					if (fBindings[count].fFieldIndex >= 0)
+						fBindings[count++].fSlotName.set(slotName);
+				}
+			} while ((child = dom.getNextSibling(child, "bind")) != NULL);
+
+			fBindingCount = SkToU16(count);
+			if (count == 0)
+			{
+				SkDEBUGF(("SkListView::onInflate: no valid <bind> elements in <listsource>\n"));
+				delete[] fBindings;
+			}
+		}
+		this->dirtyCache(kAnimCount_DirtyFlag);
+		this->setSelection(0);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkXMLListSource : public SkListSource {
+public:
+	SkXMLListSource(const char doc[], size_t len);
+	virtual ~SkXMLListSource()
+	{
+		delete[] fFields;
+		delete[] fRecords;
+	}
+
+	virtual int countFields() { return fFieldCount; }
+	virtual void getFieldName(int index, SkString* field)
+	{
+		SkASSERT((unsigned)index < (unsigned)fFieldCount);
+		if (field)
+			*field = fFields[index];
+	}
+	virtual int findFieldIndex(const char field[])
+	{
+		for (int i = 0; i < fFieldCount; i++)
+			if (fFields[i].equals(field))
+				return i;
+		return -1;
+	}
+
+	virtual int	countRecords() { return fRecordCount; }
+	virtual void getRecord(int rowIndex, int fieldIndex, SkString* data)
+	{
+		SkASSERT((unsigned)rowIndex < (unsigned)fRecordCount);
+		SkASSERT((unsigned)fieldIndex < (unsigned)fFieldCount);
+		if (data)
+			*data = fRecords[rowIndex * fFieldCount + fieldIndex];
+	}
+
+	virtual bool prepareWidgetEvent(SkEvent* evt, int rowIndex)
+	{
+		// hack, for testing right now. Need the xml to tell us what to jam in and where
+		SkString	data;
+		
+		this->getRecord(rowIndex, 0, &data);
+		evt->setString("xml-listsource", data.c_str());
+		return true;
+	}
+	
+private:
+	SkString*	fFields;	// [fFieldCount]
+	SkString*	fRecords;	// [fRecordCount][fFieldCount]
+	int			fFieldCount, fRecordCount;
+};
+
+#include "SkDOM.h"
+
+SkXMLListSource::SkXMLListSource(const char doc[], size_t len)
+{
+	fFieldCount = fRecordCount = 0;
+	fFields = fRecords = NULL;
+
+	SkDOM	dom;
+
+	const SkDOM::Node* node = dom.build(doc, len);
+	SkASSERT(node);
+	const SkDOM::Node*	child;	
+
+	child = dom.getFirstChild(node, "fields");
+	if (child)
+	{
+		fFieldCount = dom.countChildren(child, "field");
+		fFields = new SkString[fFieldCount];
+
+		int n = 0;
+		child = dom.getFirstChild(child, "field");
+		while (child)
+		{
+			fFields[n].set(dom.findAttr(child, "name"));
+			child = dom.getNextSibling(child, "field");
+			n += 1;
+		}
+		SkASSERT(n == fFieldCount);
+	}
+	
+	child = dom.getFirstChild(node, "records");
+	if (child)
+	{
+		fRecordCount = dom.countChildren(child, "record");
+		fRecords = new SkString[fRecordCount * fFieldCount];
+
+		int n = 0;
+		child = dom.getFirstChild(child, "record");
+		while (child)
+		{
+			for (int i = 0; i < fFieldCount; i++)
+				fRecords[n * fFieldCount + i].set(dom.findAttr(child, fFields[i].c_str()));
+			child = dom.getNextSibling(child, "record");
+			n += 1;
+		}
+		SkASSERT(n == fRecordCount);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+SkListSource* SkListSource::Factory(const char name[])
+{
+	static const char gDoc[] =
+		"<db name='contacts.db'>"
+			"<fields>"
+				"<field name='name'/>"
+				"<field name='work-num'/>"
+				"<field name='home-num'/>"
+				"<field name='type'/>"
+			"</fields>"
+			"<records>"
+				"<record name='Andy McFadden' work-num='919 357-1234' home-num='919 123-4567' type='0'/>"
+				"<record name='Brian Swetland' work-num='919 123-1234' home-num='929 123-4567' type='1' />"
+				"<record name='Chris Desalvo' work-num='919 345-1234' home-num='949 123-4567' type='1' />"
+				"<record name='Chris White' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+				"<record name='Dan Bornstein' work-num='919 357-1234' home-num='919 123-4567' type='0' />"
+				"<record name='Don Cung' work-num='919 123-1234' home-num='929 123-4567' type='2' />"
+				"<record name='Eric Fischer' work-num='919 345-1234' home-num='949 123-4567' type='2' />"
+				"<record name='Ficus Kirkpatric' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+				"<record name='Jack Veenstra' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+				"<record name='Jeff Yaksick' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+				"<record name='Joe Onorato' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+				"<record name='Mathias Agopian' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+				"<record name='Mike Fleming' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+				"<record name='Nick Sears' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+				"<record name='Rich Miner' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+				"<record name='Tracey Cole' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+				"<record name='Wei Huang' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+			"</records>"
+		"</db>";
+		
+//SkDebugf("doc size %d\n", sizeof(gDoc)-1);
+	return new SkXMLListSource(gDoc, sizeof(gDoc) - 1);
+}
+
+
+
diff --git a/src/views/SkOSMenu.cpp b/src/views/SkOSMenu.cpp
new file mode 100644
index 0000000..3760ddd
--- /dev/null
+++ b/src/views/SkOSMenu.cpp
@@ -0,0 +1,53 @@
+#include "SkOSMenu.h"
+
+static int gOSMenuCmd = 7000;
+
+SkOSMenu::SkOSMenu(const char title[])
+{
+	fTitle = title;
+}
+
+SkOSMenu::~SkOSMenu()
+{
+}
+
+int SkOSMenu::countItems() const
+{
+	return fItems.count();
+}
+
+void SkOSMenu::appendItem(const char title[], const char eventType[], int32_t eventData)
+{
+	Item* item = fItems.append();
+
+	item->fTitle	 = title;
+	item->fEventType = eventType;
+	item->fEventData = eventData;
+	item->fOSCmd	 = ++gOSMenuCmd;
+}
+
+SkEvent* SkOSMenu::createEvent(uint32_t os_cmd)
+{
+	const Item* iter = fItems.begin();
+	const Item*	stop = fItems.end();
+
+	while (iter < stop)
+	{
+		if (iter->fOSCmd == os_cmd)
+		{
+			SkEvent* evt = new SkEvent(iter->fEventType);
+			evt->setFast32(iter->fEventData);
+			return evt;
+		}
+		iter++;
+	}
+	return NULL;
+}
+
+const char* SkOSMenu::getItem(int index, uint32_t* cmdID) const
+{
+	if (cmdID)
+		*cmdID = fItems[index].fOSCmd;
+	return fItems[index].fTitle;
+}
+
diff --git a/src/views/SkParsePaint.cpp b/src/views/SkParsePaint.cpp
new file mode 100644
index 0000000..a79f30c
--- /dev/null
+++ b/src/views/SkParsePaint.cpp
@@ -0,0 +1,103 @@
+#include "SkParsePaint.h"
+#include "SkTSearch.h"
+#include "SkParse.h"
+#include "SkImageDecoder.h"
+#include "SkGradientShader.h"
+
+static SkShader* inflate_shader(const SkDOM& dom, const SkDOM::Node* node)
+{
+	if ((node = dom.getFirstChild(node, "shader")) == NULL)
+		return NULL;
+
+	const char* str;
+
+	if (dom.hasAttr(node, "type", "linear-gradient"))
+	{
+		SkColor		colors[2];
+		SkPoint		pts[2];
+
+		colors[0] = colors[1] = SK_ColorBLACK;	// need to initialized the alpha to opaque, since FindColor doesn't set it
+		if ((str = dom.findAttr(node, "c0")) != NULL &&
+			SkParse::FindColor(str, &colors[0]) &&
+			(str = dom.findAttr(node, "c1")) != NULL &&
+			SkParse::FindColor(str, &colors[1]) &&
+			dom.findScalars(node, "p0", &pts[0].fX, 2) &&
+			dom.findScalars(node, "p1", &pts[1].fX, 2))
+		{
+			SkShader::TileMode	mode = SkShader::kClamp_TileMode;
+			int					index;
+
+			if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
+				mode = (SkShader::TileMode)index;
+			return SkGradientShader::CreateLinear(pts, colors, NULL, 2, mode);
+		}
+	}
+	else if (dom.hasAttr(node, "type", "bitmap"))
+	{
+		if ((str = dom.findAttr(node, "src")) == NULL)
+			return NULL;
+
+		SkBitmap	bm;
+
+		if (SkImageDecoder::DecodeFile(str, &bm))
+		{
+			SkShader::TileMode	mode = SkShader::kRepeat_TileMode;
+			int					index;
+
+			if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
+				mode = (SkShader::TileMode)index;
+
+			return SkShader::CreateBitmapShader(bm, mode, mode);
+		}
+	}
+	return NULL;
+}
+
+void SkPaint_Inflate(SkPaint* paint, const SkDOM& dom, const SkDOM::Node* node)
+{
+	SkASSERT(paint);
+	SkASSERT(&dom);
+	SkASSERT(node);
+
+	SkScalar x;
+
+	if (dom.findScalar(node, "stroke-width", &x))
+		paint->setStrokeWidth(x);
+	if (dom.findScalar(node, "text-size", &x))
+		paint->setTextSize(x);
+	
+	bool	b;
+
+	SkASSERT("legacy: use is-stroke" && !dom.findBool(node, "is-frame", &b));
+
+	if (dom.findBool(node, "is-stroke", &b))
+		paint->setStyle(b ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
+	if (dom.findBool(node, "is-antialias", &b))
+		paint->setAntiAlias(b);
+	if (dom.findBool(node, "is-lineartext", &b))
+		paint->setLinearText(b);
+
+	const char* str = dom.findAttr(node, "color");
+	if (str)
+	{
+		SkColor	c = paint->getColor();
+		if (SkParse::FindColor(str, &c))
+			paint->setColor(c);
+	}
+
+	// do this AFTER parsing for the color
+	if (dom.findScalar(node, "opacity", &x))
+	{
+		x = SkMaxScalar(0, SkMinScalar(x, SK_Scalar1));
+		paint->setAlpha(SkScalarRound(x * 255));
+	}
+
+	int	index = dom.findList(node, "text-anchor", "left,center,right");
+	if (index >= 0)
+		paint->setTextAlign((SkPaint::Align)index);
+
+	SkShader* shader = inflate_shader(dom, node);
+	if (shader)
+		paint->setShader(shader)->unref();
+}
+
diff --git a/src/views/SkProgressBarView.cpp b/src/views/SkProgressBarView.cpp
new file mode 100644
index 0000000..a9fd516
--- /dev/null
+++ b/src/views/SkProgressBarView.cpp
@@ -0,0 +1,102 @@
+#include "SkProgressBarView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkTime.h"
+#include "SkSystemEventTypes.h"
+
+SkProgressBarView::SkProgressBarView()
+{
+	init_skin_anim(kProgress_SkinEnum, &fAnim);
+	fAnim.setHostEventSink(this);
+	fProgress = 0;
+	fMax = 100;
+	
+}
+
+void SkProgressBarView::changeProgress(int diff)
+{
+	int newProg = fProgress + diff;
+	if (newProg > 0 && newProg < fMax)
+		this->setProgress(newProg);
+	//otherwise i'll just leave it as it is
+	//this implies that if a new max and progress are set, max must be set first
+}
+
+/*virtual*/ void SkProgressBarView::onDraw(SkCanvas* canvas)
+{
+	SkPaint						paint;		
+	SkAnimator::DifferenceType	diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+	
+	if (diff == SkAnimator::kDifferent)
+		this->inval(NULL);
+	else if (diff == SkAnimator::kPartiallyDifferent)
+	{
+		SkRect	bounds;
+		fAnim.getInvalBounds(&bounds);
+		this->inval(&bounds);
+	}
+}
+	
+/*virtual*/ bool SkProgressBarView::onEvent(const SkEvent& evt)
+{
+	if (evt.isType(SK_EventType_Inval))
+	{
+		this->inval(NULL);
+		return true;
+	}
+	if (evt.isType("recommendDim"))
+	{
+		SkScalar	height;
+		
+		if (evt.findScalar("y", &height))
+			this->setHeight(height);
+		return true;
+	}
+	return this->INHERITED::onEvent(evt);
+}
+
+/*virtual*/ void SkProgressBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+	int32_t temp;
+	if (dom.findS32(node, "max", &temp))
+		this->setMax(temp);
+	if (dom.findS32(node, "progress", &temp))
+		this->setProgress(temp);
+}
+
+/*virtual*/ void SkProgressBarView::onSizeChange()
+{
+	this->INHERITED::onSizeChange();
+	SkEvent evt("user");
+	evt.setString("id", "setDim");
+	evt.setScalar("dimX", this->width());
+	evt.setScalar("dimY", this->height());
+	fAnim.doUserEvent(evt);
+}
+
+void SkProgressBarView::reset()
+{
+	fProgress = 0;
+	SkEvent e("user");
+	e.setString("id", "reset");
+	fAnim.doUserEvent(e);
+}
+
+void SkProgressBarView::setMax(int max)
+{
+	fMax = max;
+	SkEvent e("user");
+	e.setString("id", "setMax");
+	e.setS32("newMax", max);
+	fAnim.doUserEvent(e);
+}
+
+void SkProgressBarView::setProgress(int progress)
+{
+	fProgress = progress;
+	SkEvent e("user");
+	e.setString("id", "setProgress");
+	e.setS32("newProgress", progress);
+	fAnim.doUserEvent(e);
+}
diff --git a/src/views/SkProgressView.cpp b/src/views/SkProgressView.cpp
new file mode 100644
index 0000000..0c81bcc
--- /dev/null
+++ b/src/views/SkProgressView.cpp
@@ -0,0 +1,125 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkShader.h"
+#include "SkInterpolator.h"
+#include "SkTime.h"
+
+SkProgressView::SkProgressView(uint32_t flags) : SkView(flags), fOnShader(NULL), fOffShader(NULL)
+{
+	fValue = 0;
+	fMax = 0;
+	fInterp = NULL;
+	fDoInterp = false;
+}
+
+SkProgressView::~SkProgressView()
+{
+	delete fInterp;
+	SkSafeUnref(fOnShader);
+	SkSafeUnref(fOffShader);
+}
+
+void SkProgressView::setMax(U16CPU max)
+{
+	if (fMax != max)
+	{
+		fMax = SkToU16(max);
+		if (fValue > 0)
+			this->inval(NULL);
+	}
+}
+
+void SkProgressView::setValue(U16CPU value)
+{
+	if (fValue != value)
+	{
+		if (fDoInterp)
+		{
+			if (fInterp)
+				delete fInterp;
+			fInterp = new SkInterpolator(1, 2);
+			SkScalar x = (SkScalar)(fValue << 8);
+			fInterp->setKeyFrame(0, SkTime::GetMSecs(), &x, 0);
+			x = (SkScalar)(value << 8);
+			fInterp->setKeyFrame(1, SkTime::GetMSecs() + 333, &x);
+		}
+		fValue = SkToU16(value);
+		this->inval(NULL);
+	}
+}
+
+void SkProgressView::onDraw(SkCanvas* canvas)
+{
+	if (fMax == 0)
+		return;
+
+	SkFixed	percent;
+
+	if (fInterp)
+	{
+		SkScalar x;
+		if (fInterp->timeToValues(SkTime::GetMSecs(), &x) == SkInterpolator::kFreezeEnd_Result)
+		{
+			delete fInterp;
+			fInterp = NULL;
+		}
+		percent = (SkFixed)x;	// now its 16.8
+		percent = SkMax32(0, SkMin32(percent, fMax << 8));	// now its pinned
+		percent = SkFixedDiv(percent, fMax << 8);	// now its 0.16
+		this->inval(NULL);
+	}
+	else
+	{
+		U16CPU value = SkMax32(0, SkMin32(fValue, fMax));
+		percent = SkFixedDiv(value, fMax);
+	}
+
+
+	SkRect	r;
+	SkPaint	p;
+
+	r.set(0, 0, this->width(), this->height());
+	p.setAntiAlias(true);
+	
+	r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent));
+	p.setStyle(SkPaint::kFill_Style);
+
+	p.setColor(SK_ColorDKGRAY);
+	p.setShader(fOnShader);
+	canvas->drawRect(r, p);
+
+	p.setColor(SK_ColorWHITE);
+	p.setShader(fOffShader);
+	r.fLeft = r.fRight;
+	r.fRight = this->width() - SK_Scalar1;
+	if (r.width() > 0)
+		canvas->drawRect(r, p);
+}
+
+#include "SkImageDecoder.h"
+
+static SkShader* inflate_shader(const char file[])
+{
+	SkBitmap	bm;
+
+	return SkImageDecoder::DecodeFile(file, &bm) ?
+			SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) :
+			NULL;
+}
+
+void SkProgressView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+
+	const char* s;
+
+	SkASSERT(fOnShader == NULL);
+	SkASSERT(fOffShader == NULL);
+
+	if ((s = dom.findAttr(node, "src-on")) != NULL)
+		fOnShader = inflate_shader(s);
+	if ((s = dom.findAttr(node, "src-off")) != NULL)
+		fOffShader = inflate_shader(s);
+	(void)dom.findBool(node, "do-interp", &fDoInterp);
+}
+
diff --git a/src/views/SkScrollBarView.cpp b/src/views/SkScrollBarView.cpp
new file mode 100644
index 0000000..2e087bd
--- /dev/null
+++ b/src/views/SkScrollBarView.cpp
@@ -0,0 +1,139 @@
+#include "SkScrollBarView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+//see SkProgressBarView.cpp
+//#include "SkWidgetViews.cpp"
+
+SkScrollBarView::SkScrollBarView()
+{
+	fAnim.setHostEventSink(this);
+	init_skin_anim(kScroll_SkinEnum, &fAnim);
+
+	fTotalLength = 0;
+	fStartPoint = 0;
+	fShownLength = 0;
+
+	this->adjust();
+}
+
+void SkScrollBarView::setStart(unsigned start)
+{
+	if ((int)start < 0)
+		start = 0;
+	
+	if (fStartPoint != start)
+	{
+		fStartPoint = start;
+		this->adjust();
+	}
+}
+
+void SkScrollBarView::setShown(unsigned shown)
+{
+	if ((int)shown < 0)
+		shown = 0;
+
+	if (fShownLength != shown)
+	{
+		fShownLength = shown;
+		this->adjust();
+	}
+}
+
+void SkScrollBarView::setTotal(unsigned total)
+{
+	if ((int)total < 0)
+		total = 0;
+
+	if (fTotalLength != total)
+	{
+		fTotalLength = total;
+		this->adjust();
+	}
+}
+
+/* virtual */ void SkScrollBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+	
+	int32_t value;
+	if (dom.findS32(node, "total", &value))
+		this->setTotal(value);
+	if (dom.findS32(node, "shown", &value))
+		this->setShown(value);
+}
+
+/*virtual*/ void SkScrollBarView::onSizeChange()
+{
+	this->INHERITED::onSizeChange();
+	SkEvent evt("user");
+	evt.setString("id", "setDim");
+	evt.setScalar("dimX", this->width());
+	evt.setScalar("dimY", this->height());
+	fAnim.doUserEvent(evt);
+}
+
+/*virtual*/ void SkScrollBarView::onDraw(SkCanvas* canvas)
+{
+	SkPaint						paint;		
+	SkAnimator::DifferenceType	diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+	
+	if (diff == SkAnimator::kDifferent)
+		this->inval(NULL);
+	else if (diff == SkAnimator::kPartiallyDifferent)
+	{
+		SkRect	bounds;
+		fAnim.getInvalBounds(&bounds);
+		this->inval(&bounds);
+	}
+}
+
+/*virtual*/ bool SkScrollBarView::onEvent(const SkEvent& evt)
+{
+	if (evt.isType(SK_EventType_Inval))
+	{
+		this->inval(NULL);
+		return true;
+	}
+	if (evt.isType("recommendDim"))
+	{
+		SkScalar	width;
+		
+		if (evt.findScalar("x", &width))
+			this->setWidth(width);
+		return true;
+	}
+
+	return this->INHERITED::onEvent(evt);
+}
+
+void SkScrollBarView::adjust()
+{
+	int total = fTotalLength;
+	int start = fStartPoint;
+	int shown = fShownLength;
+	int hideBar = 0;
+	
+	if (total <= 0 || shown <= 0 || shown >= total)	// no bar to show
+	{
+		total = 1;		// avoid divide-by-zero. should be done by skin/script
+		hideBar = 1;	// signal we don't want a thumb
+	}
+	else
+	{
+		if (start + shown > total)
+			start = total - shown;
+	}
+	
+	SkEvent e("user");
+	e.setString("id", "adjustScrollBar");
+	e.setScalar("_totalLength", SkIntToScalar(total));
+	e.setScalar("_startPoint", SkIntToScalar(start));
+	e.setScalar("_shownLength", SkIntToScalar(shown));
+//	e.setS32("hideBar", hideBar);
+	fAnim.doUserEvent(e);
+}
+
diff --git a/src/views/SkStackViewLayout.cpp b/src/views/SkStackViewLayout.cpp
new file mode 100644
index 0000000..ae2e9e8
--- /dev/null
+++ b/src/views/SkStackViewLayout.cpp
@@ -0,0 +1,262 @@
+#include "SkStackViewLayout.h"
+
+SkStackViewLayout::SkStackViewLayout()
+{
+	fMargin.set(0, 0, 0, 0);
+	fSpacer	= 0;
+	fOrient	= kHorizontal_Orient;
+	fPack	= kStart_Pack;
+	fAlign	= kStart_Align;
+	fRound	= false;
+}
+
+void SkStackViewLayout::setOrient(Orient ori)
+{
+	SkASSERT((unsigned)ori < kOrientCount);
+	fOrient = SkToU8(ori);
+}
+
+void SkStackViewLayout::getMargin(SkRect* margin) const
+{
+	if (margin)
+		*margin = fMargin;
+}
+
+void SkStackViewLayout::setMargin(const SkRect& margin)
+{
+	fMargin = margin;
+}
+
+void SkStackViewLayout::setSpacer(SkScalar spacer)
+{
+	fSpacer = spacer;
+}
+
+void SkStackViewLayout::setPack(Pack pack)
+{
+	SkASSERT((unsigned)pack < kPackCount);
+	fPack = SkToU8(pack);
+}
+
+void SkStackViewLayout::setAlign(Align align)
+{
+	SkASSERT((unsigned)align < kAlignCount);
+	fAlign = SkToU8(align);
+}
+
+void SkStackViewLayout::setRound(bool r)
+{
+	fRound = SkToU8(r);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+typedef SkScalar (*AlignProc)(SkScalar childLimit, SkScalar parentLimit);
+typedef SkScalar (SkView::*GetSizeProc)() const;
+typedef void (SkView::*SetLocProc)(SkScalar coord);
+typedef void (SkView::*SetSizeProc)(SkScalar coord);
+
+static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
+static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); }
+static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; }
+static SkScalar fill_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
+
+/*	Measure the main-dimension for all the children. If a child is marked flex in that direction
+	ignore its current value but increment the counter for flexChildren
+*/
+static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count,
+									   uint32_t flexMask, int* flexCount)
+{
+	SkView::B2FIter	iter(parent);
+	SkView*			child;
+	SkScalar		limit = 0;
+	int				n = 0, flex = 0;
+
+	while ((child = iter.next()) != NULL)
+	{
+		n += 1;
+		if (child->getFlags() & flexMask)
+			flex += 1;
+		else
+			limit += (child->*sizeProc)();
+	}
+	if (count)
+		*count = n;
+	if (flexCount)
+		*flexCount = flex;
+	return limit;
+}
+
+void SkStackViewLayout::onLayoutChildren(SkView* parent)
+{
+	static AlignProc gAlignProcs[] = {
+		left_align_proc,
+		center_align_proc,
+		right_align_proc,
+		fill_align_proc
+	};
+
+	SkScalar			startM, endM, crossStartM, crossLimit;
+	GetSizeProc			mainGetSizeP, crossGetSizeP;
+	SetLocProc			mainLocP, crossLocP;
+	SetSizeProc			mainSetSizeP, crossSetSizeP;
+	SkView::Flag_Mask	flexMask;
+
+	if (fOrient == kHorizontal_Orient)
+	{
+		startM		= fMargin.fLeft;
+		endM		= fMargin.fRight;
+		crossStartM	= fMargin.fTop;
+		crossLimit	= -fMargin.fTop - fMargin.fBottom;
+
+		mainGetSizeP	= &SkView::width;
+		crossGetSizeP	= &SkView::height;
+		mainLocP	= &SkView::setLocX;
+		crossLocP	= &SkView::setLocY;
+
+		mainSetSizeP  = &SkView::setWidth;
+		crossSetSizeP = &SkView::setHeight;
+
+		flexMask	= SkView::kFlexH_Mask;
+	}
+	else
+	{
+		startM		= fMargin.fTop;
+		endM		= fMargin.fBottom;
+		crossStartM	= fMargin.fLeft;
+		crossLimit	= -fMargin.fLeft - fMargin.fRight;
+
+		mainGetSizeP	= &SkView::height;
+		crossGetSizeP	= &SkView::width;
+		mainLocP	= &SkView::setLocY;
+		crossLocP	= &SkView::setLocX;
+
+		mainSetSizeP  = &SkView::setHeight;
+		crossSetSizeP = &SkView::setWidth;
+
+		flexMask	= SkView::kFlexV_Mask;
+	}
+	crossLimit += (parent->*crossGetSizeP)();
+	if (fAlign != kStretch_Align)
+		crossSetSizeP = NULL;
+
+	int			childCount, flexCount;
+	SkScalar	childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount);
+
+	if (childCount == 0)
+		return;
+
+	childLimit += (childCount - 1) * fSpacer;
+
+	SkScalar		parentLimit = (parent->*mainGetSizeP)() - startM - endM;
+	SkScalar		pos = startM + gAlignProcs[fPack](childLimit, parentLimit);
+	SkScalar		flexAmount = 0;
+	SkView::B2FIter	iter(parent);
+	SkView*			child;
+
+	if (flexCount > 0 && parentLimit > childLimit)
+		flexAmount = (parentLimit - childLimit) / flexCount;
+
+	while ((child = iter.next()) != NULL)
+	{
+		if (fRound)
+			pos = SkIntToScalar(SkScalarRound(pos));
+		(child->*mainLocP)(pos);
+		SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit);
+		if (fRound)
+			crossLoc = SkIntToScalar(SkScalarRound(crossLoc));
+		(child->*crossLocP)(crossLoc);
+
+		if (crossSetSizeP)
+			(child->*crossSetSizeP)(crossLimit);
+		if (child->getFlags() & flexMask)
+			(child->*mainSetSizeP)(flexAmount);
+		pos += (child->*mainGetSizeP)() + fSpacer;
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+	static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+	{
+		const char* value = dom.findAttr(node, attr);
+		if (value)
+			SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+	}
+#else
+	#define assert_no_attr(dom, node, attr)
+#endif
+
+void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	int			index;
+	SkScalar	value[4];
+
+	if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0)
+		this->setOrient((Orient)index);
+	else
+		assert_no_attr(dom, node, "orient");
+
+	if (dom.findScalars(node, "margin", value, 4))
+	{
+		SkRect	margin;
+		margin.set(value[0], value[1], value[2], value[3]);
+		this->setMargin(margin);
+	}
+	else
+		assert_no_attr(dom, node, "margin");
+
+	if (dom.findScalar(node, "spacer", value))
+		this->setSpacer(value[0]);
+	else
+		assert_no_attr(dom, node, "spacer");
+
+	if ((index = dom.findList(node, "pack", "start,center,end")) >= 0)
+		this->setPack((Pack)index);
+	else
+		assert_no_attr(dom, node, "pack");
+
+	if ((index = dom.findList(node, "align", "start,center,end,stretch")) >= 0)
+		this->setAlign((Align)index);
+	else
+		assert_no_attr(dom, node, "align");
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+SkFillViewLayout::SkFillViewLayout()
+{
+	fMargin.setEmpty();
+}
+
+void SkFillViewLayout::getMargin(SkRect* r) const
+{
+	if (r)
+		*r = fMargin;
+}
+
+void SkFillViewLayout::setMargin(const SkRect& margin)
+{
+	fMargin = margin;
+}
+
+void SkFillViewLayout::onLayoutChildren(SkView* parent)
+{
+	SkView::B2FIter	iter(parent);
+	SkView*			child;
+
+	while ((child = iter.next()) != NULL)
+	{
+		child->setLoc(fMargin.fLeft, fMargin.fTop);
+		child->setSize(	parent->width() - fMargin.fRight - fMargin.fLeft,
+						parent->height() - fMargin.fBottom - fMargin.fTop);
+	}
+}
+
+void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+	(void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4);
+}
+
diff --git a/src/views/SkStaticTextView.cpp b/src/views/SkStaticTextView.cpp
new file mode 100644
index 0000000..8fb8bc1
--- /dev/null
+++ b/src/views/SkStaticTextView.cpp
@@ -0,0 +1,177 @@
+#include "SkWidgetViews.h"
+#include "SkTextBox.h"
+
+#ifdef SK_DEBUG
+static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+{
+    const char* value = dom.findAttr(node, attr);
+    if (value)
+        SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+}
+#else
+    #define assert_no_attr(dom, node, attr)
+#endif
+
+SkStaticTextView::SkStaticTextView()
+{
+	fMargin.set(0, 0);
+	fMode = kFixedSize_Mode;
+	fSpacingAlign = SkTextBox::kStart_SpacingAlign;
+	
+//	init_skin_paint(kStaticText_SkinEnum, &fPaint);
+}
+
+SkStaticTextView::~SkStaticTextView()
+{
+}
+
+void SkStaticTextView::computeSize()
+{
+	if (fMode == kAutoWidth_Mode)
+	{
+		SkScalar width = fPaint.measureText(fText.c_str(), fText.size());
+		this->setWidth(width + fMargin.fX * 2);
+	}
+	else if (fMode == kAutoHeight_Mode)
+	{
+		SkScalar width = this->width() - fMargin.fX * 2;
+		int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;
+
+		this->setHeight(lines * fPaint.getFontSpacing() + fMargin.fY * 2);
+	}
+}
+
+void SkStaticTextView::setMode(Mode mode)
+{
+	SkASSERT((unsigned)mode < kModeCount);
+
+	if (fMode != mode)
+	{
+		fMode = SkToU8(mode);
+		this->computeSize();
+	}
+}
+
+void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align)
+{
+	fSpacingAlign = SkToU8(align);
+	this->inval(NULL);
+}
+
+void SkStaticTextView::getMargin(SkPoint* margin) const
+{
+	if (margin)
+		*margin = fMargin;
+}
+
+void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy)
+{
+	if (fMargin.fX != dx || fMargin.fY != dy)
+	{
+		fMargin.set(dx, dy);
+		this->computeSize();
+		this->inval(NULL);
+	}
+}
+
+size_t SkStaticTextView::getText(SkString* text) const
+{
+	if (text)
+		*text = fText;
+	return fText.size();
+}
+
+size_t SkStaticTextView::getText(char text[]) const
+{
+	if (text)
+		memcpy(text, fText.c_str(), fText.size());
+	return fText.size();
+}
+
+void SkStaticTextView::setText(const SkString& text)
+{
+	this->setText(text.c_str(), text.size());
+}
+
+void SkStaticTextView::setText(const char text[])
+{
+	if (text == NULL)
+		text = "";
+	this->setText(text, strlen(text));
+}
+
+void SkStaticTextView::setText(const char text[], size_t len)
+{
+	if (!fText.equals(text, len))
+	{
+		fText.set(text, len);
+		this->computeSize();
+		this->inval(NULL);
+	}
+}
+
+void SkStaticTextView::getPaint(SkPaint* paint) const
+{
+	if (paint)
+		*paint = fPaint;
+}
+
+void SkStaticTextView::setPaint(const SkPaint& paint)
+{
+	if (fPaint != paint)
+	{
+		fPaint = paint;
+		this->computeSize();
+		this->inval(NULL);
+	}
+}
+
+void SkStaticTextView::onDraw(SkCanvas* canvas)
+{
+	this->INHERITED::onDraw(canvas);
+
+	if (fText.isEmpty())
+		return;
+
+	SkTextBox	box;
+
+	box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
+	box.setSpacingAlign(this->getSpacingAlign());
+	box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
+	box.draw(canvas, fText.c_str(), fText.size(), fPaint);
+}
+
+void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+#if 0
+	this->INHERITED::onInflate(dom, node);
+
+	int	index;
+	if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0)
+		this->setMode((Mode)index);
+	else
+		assert_no_attr(dom, node, "mode");
+
+	if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0)
+		this->setSpacingAlign((SkTextBox::SpacingAlign)index);
+	else
+		assert_no_attr(dom, node, "spacing-align");
+
+	SkScalar s[2];
+	if (dom.findScalars(node, "margin", s, 2))
+		this->setMargin(s[0], s[1]);
+	else
+		assert_no_attr(dom, node, "margin");
+
+	const char* text = dom.findAttr(node, "text");
+	if (text)
+		this->setText(text);
+
+	if ((node = dom.getFirstChild(node, "paint")) != NULL &&
+		(node = dom.getFirstChild(node, "screenplay")) != NULL)
+	{
+		inflate_paint(dom, node, &fPaint);
+	}
+#endif
+}
+
diff --git a/src/views/SkTagList.cpp b/src/views/SkTagList.cpp
new file mode 100644
index 0000000..4576ce6
--- /dev/null
+++ b/src/views/SkTagList.cpp
@@ -0,0 +1,71 @@
+/* libs/graphics/views/SkTagList.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 "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..5f428e4
--- /dev/null
+++ b/src/views/SkTagList.h
@@ -0,0 +1,51 @@
+/* libs/graphics/views/SkTagList.h
+**
+** 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.
+*/
+
+#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..0e31ac6
--- /dev/null
+++ b/src/views/SkTextBox.cpp
@@ -0,0 +1,237 @@
+/* libs/graphics/views/SkTextBox.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 "SkTextBox.h"
+#include "../core/SkGlyphCache.h"
+#include "SkUtils.h"
+#include "SkAutoKern.h"
+
+static inline int is_ws(int c)
+{
+    return !((c - 1) >> 5);
+}
+
+static size_t linebreak(const char text[], const char stop[], const SkPaint& paint, SkScalar margin)
+{
+    const char* start = text;
+
+    SkAutoGlyphCache    ac(paint, NULL);
+    SkGlyphCache*       cache = ac.getCache();
+    SkFixed             w = 0;
+    SkFixed             limit = SkScalarToFixed(margin);
+    SkAutoKern          autokern;
+
+    const char* word_start = text;
+    int         prevWS = true;
+
+    while (text < stop)
+    {
+        const char* prevText = text;
+        SkUnichar   uni = SkUTF8_NextUnichar(&text);
+        int         currWS = is_ws(uni);
+        const SkGlyph&  glyph = cache->getUnicharMetrics(uni);
+
+        if (!currWS && prevWS)
+            word_start = prevText;
+        prevWS = currWS;
+
+        w += autokern.adjust(glyph) + glyph.fAdvanceX;
+        if (w > limit)
+        {
+            if (currWS) // eat the rest of the whitespace
+            {
+                while (text < stop && is_ws(SkUTF8_ToUnichar(text)))
+                    text += SkUTF8_CountUTF8Bytes(text);
+            }
+            else    // backup until a whitespace (or 1 char)
+            {
+                if (word_start == start)
+                {
+                    if (prevText > start)
+                        text = prevText;
+                }
+                else
+                    text = word_start;
+            }
+            break;
+        }
+    }
+    return text - start;
+}
+
+int SkTextLineBreaker::CountLines(const char text[], size_t len, const SkPaint& paint, SkScalar width)
+{
+    const char* stop = text + len;
+    int         count = 0;
+
+    if (width > 0)
+    {
+        do {
+            count += 1;
+            text += linebreak(text, stop, paint, width);
+        } while (text < stop);
+    }
+    return count;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SkTextBox::SkTextBox()
+{
+    fBox.setEmpty();
+    fSpacingMul = SK_Scalar1;
+    fSpacingAdd = 0;
+    fMode = kLineBreak_Mode;
+    fSpacingAlign = kStart_SpacingAlign;
+}
+
+void SkTextBox::setMode(Mode mode)
+{
+    SkASSERT((unsigned)mode < kModeCount);
+    fMode = SkToU8(mode);
+}
+
+void SkTextBox::setSpacingAlign(SpacingAlign align)
+{
+    SkASSERT((unsigned)align < kSpacingAlignCount);
+    fSpacingAlign = SkToU8(align);
+}
+
+void SkTextBox::getBox(SkRect* box) const
+{
+    if (box)
+        *box = fBox;
+}
+
+void SkTextBox::setBox(const SkRect& box)
+{
+    fBox = box;
+}
+
+void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
+{
+    fBox.set(left, top, right, bottom);
+}
+
+void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const
+{
+    if (mul)
+        *mul = fSpacingMul;
+    if (add)
+        *add = fSpacingAdd;
+}
+
+void SkTextBox::setSpacing(SkScalar mul, SkScalar add)
+{
+    fSpacingMul = mul;
+    fSpacingAdd = add;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint)
+{
+    SkASSERT(canvas && &paint && (text || len == 0));
+
+    SkScalar marginWidth = fBox.width();
+
+    if (marginWidth <= 0 || len == 0)
+        return;
+
+    const char* textStop = text + len;
+
+    SkScalar                x, y, scaledSpacing, height, fontHeight;
+    SkPaint::FontMetrics    metrics;
+
+    switch (paint.getTextAlign()) {
+    case SkPaint::kLeft_Align:
+        x = 0;
+        break;
+    case SkPaint::kCenter_Align:
+        x = SkScalarHalf(marginWidth);
+        break;
+    default:
+        x = marginWidth;
+        break;
+    }
+    x += fBox.fLeft;
+
+    fontHeight = paint.getFontMetrics(&metrics);
+    scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd;
+    height = fBox.height();
+
+    //  compute Y position for first line
+    {
+        SkScalar textHeight = fontHeight;
+
+        if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign)
+        {
+            int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth);
+            SkASSERT(count > 0);
+            textHeight += scaledSpacing * (count - 1);
+        }
+
+        switch (fSpacingAlign) {
+        case kStart_SpacingAlign:
+            y = 0;
+            break;
+        case kCenter_SpacingAlign:
+            y = SkScalarHalf(height - textHeight);
+            break;
+        default:
+            SkASSERT(fSpacingAlign == kEnd_SpacingAlign);
+            y = height - textHeight;
+            break;
+        }
+        y += fBox.fTop - metrics.fAscent;
+    }
+
+    for (;;)
+    {
+        len = linebreak(text, textStop, paint, marginWidth);
+        if (y + metrics.fDescent + metrics.fLeading > 0)
+            canvas->drawText(text, len, x, y, paint);
+        text += len;
+        if (text >= textStop)
+            break;
+        y += scaledSpacing;
+        if (y + metrics.fAscent >= height)
+            break;
+    } 
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkTextBox::setText(const char text[], size_t len, const SkPaint& paint) {
+    fText = text;
+    fLen = len;
+    fPaint = &paint;
+}
+
+void SkTextBox::draw(SkCanvas* canvas) {
+    this->draw(canvas, fText, fLen, *fPaint);
+}
+
+int SkTextBox::countLines() const {
+    return SkTextLineBreaker::CountLines(fText, fLen, *fPaint, fBox.width());
+}
+
+SkScalar SkTextBox::getTextHeight() const {
+    SkScalar spacing = SkScalarMul(fPaint->getTextSize(), fSpacingMul) + fSpacingAdd;
+    return this->countLines() * spacing;
+}
+
diff --git a/src/views/SkTouchGesture.cpp b/src/views/SkTouchGesture.cpp
new file mode 100644
index 0000000..1732176
--- /dev/null
+++ b/src/views/SkTouchGesture.cpp
@@ -0,0 +1,336 @@
+/*
+    Copyright 2010 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.
+ */
+
+
+#include "SkTouchGesture.h"
+#include "SkMatrix.h"
+#include "SkTime.h"
+
+#define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER   true
+
+static const float MAX_FLING_SPEED = 1500;
+
+static float pin_max_fling(float speed) {
+    if (speed > MAX_FLING_SPEED) {
+        speed = MAX_FLING_SPEED;
+    }
+    return speed;
+}
+
+static double getseconds() {
+    return SkTime::GetMSecs() * 0.001;
+}
+
+// returns +1 or -1, depending on the sign of x
+// returns +1 if 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 SkFlingState::reset(float sx, float sy) {
+    fActive = true;
+    fDirection.set(sx, sy);
+    fSpeed0 = SkPoint::Normalize(&fDirection);
+    fSpeed0 = pin_max_fling(fSpeed0);
+    fTime0 = getseconds();
+
+    unit_axis_align(&fDirection);
+//    printf("---- speed %g dir %g %g\n", fSpeed0, fDirection.fX, fDirection.fY);
+}
+
+bool SkFlingState::evaluateMatrix(SkMatrix* matrix) {
+    if (!fActive) {
+        return false;
+    }
+
+    const float t =  (float)(getseconds() - fTime0);
+    const float MIN_SPEED = 2;
+    const float K0 = 5;
+    const float K1 = 0.02f;
+    const float speed = fSpeed0 * (sk_float_exp(- K0 * t) - K1);
+    if (speed <= MIN_SPEED) {
+        fActive = false;
+        return false;
+    }
+    float dist = (fSpeed0 - speed) / K0;
+
+//    printf("---- time %g speed %g dist %g\n", t, speed, dist);
+    float tx = fDirection.fX * dist;
+    float ty = fDirection.fY * dist;
+    if (DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER) {
+        tx = (float)sk_float_round2int(tx);
+        ty = (float)sk_float_round2int(ty);
+    }
+    matrix->setTranslate(tx, ty);
+//    printf("---- evaluate (%g %g)\n", tx, ty);
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkMSec MAX_DBL_TAP_INTERVAL = 300;
+static const float MAX_DBL_TAP_DISTANCE = 100;
+static const float MAX_JITTER_RADIUS = 2;
+
+// if true, then ignore the touch-move, 'cause its probably just jitter
+static bool close_enough_for_jitter(float x0, float y0, float x1, float y1) {
+    return  sk_float_abs(x0 - x1) <= MAX_JITTER_RADIUS &&
+            sk_float_abs(y0 - y1) <= MAX_JITTER_RADIUS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTouchGesture::SkTouchGesture() {
+    this->reset();
+}
+
+SkTouchGesture::~SkTouchGesture() {
+}
+
+void SkTouchGesture::reset() {
+    fTouches.reset();
+    fState = kEmpty_State;
+    fLocalM.reset();
+    fGlobalM.reset();
+
+    fLastUpT = SkTime::GetMSecs() - 2*MAX_DBL_TAP_INTERVAL;
+    fLastUpP.set(0, 0);
+}
+
+void SkTouchGesture::flushLocalM() {
+    fGlobalM.postConcat(fLocalM);
+    fLocalM.reset();
+}
+
+const SkMatrix& SkTouchGesture::localM() {
+    if (fFlinger.isActive()) {
+        if (!fFlinger.evaluateMatrix(&fLocalM)) {
+            this->flushLocalM();
+        }
+    }
+    return fLocalM;
+}
+
+void SkTouchGesture::appendNewRec(void* owner, float x, float y) {
+    Rec* rec = fTouches.append();
+    rec->fOwner = owner;
+    rec->fStartX = rec->fPrevX = rec->fLastX = x;
+    rec->fStartY = rec->fPrevY = rec->fLastY = y;
+    rec->fLastT = rec->fPrevT = SkTime::GetMSecs();
+}
+
+void SkTouchGesture::touchBegin(void* owner, float x, float y) {
+//    GrPrintf("--- %d touchBegin %p %g %g\n", fTouches.count(), owner, x, y);
+
+    int index = this->findRec(owner);
+    if (index >= 0) {
+        this->flushLocalM();
+        fTouches.removeShuffle(index);
+        SkDebugf("---- already exists, removing\n");
+    }
+
+    if (fTouches.count() == 2) {
+        return;
+    }
+
+    this->flushLocalM();
+    fFlinger.stop();
+
+    this->appendNewRec(owner, x, y);
+
+    switch (fTouches.count()) {
+        case 1:
+            fState = kTranslate_State;
+            break;
+        case 2:
+            fState = kZoom_State;
+            break;
+        default:
+            break;
+    }
+}
+
+int SkTouchGesture::findRec(void* owner) const {
+    for (int i = 0; i < fTouches.count(); i++) {
+        if (owner == fTouches[i].fOwner) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static float center(float pos0, float pos1) {
+    return (pos0 + pos1) * 0.5f;
+}
+
+static const float MAX_ZOOM_SCALE = 4;
+static const float MIN_ZOOM_SCALE = 0.25f;
+
+float SkTouchGesture::limitTotalZoom(float scale) const {
+    // this query works 'cause we know that we're square-scale w/ no skew/rotation
+    const float curr = fGlobalM[0];
+    
+    if (scale > 1 && curr * scale > MAX_ZOOM_SCALE) {
+        scale = MAX_ZOOM_SCALE / curr;
+    } else if (scale < 1 && curr * scale < MIN_ZOOM_SCALE) {
+        scale = MIN_ZOOM_SCALE / curr;
+    }
+    return scale;
+}
+
+void SkTouchGesture::touchMoved(void* owner, float x, float y) {
+//    GrPrintf("--- %d touchMoved %p %g %g\n", fTouches.count(), owner, x, y);
+
+    SkASSERT(kEmpty_State != fState);
+
+    int index = this->findRec(owner);
+    if (index < 0) {
+        // not found, so I guess we should add it...
+        SkDebugf("---- add missing begin\n");
+        this->appendNewRec(owner, x, y);
+        index = fTouches.count() - 1;
+    }
+
+    Rec& rec = fTouches[index];
+
+    // not sure how valuable this is
+    if (fTouches.count() == 2) {
+        if (close_enough_for_jitter(rec.fLastX, rec.fLastY, x, y)) {
+//            GrPrintf("--- drop touchMove, withing jitter tolerance %g %g\n", rec.fLastX - x, rec.fLastY - y);
+            return;
+        }
+    }
+
+    rec.fPrevX = rec.fLastX; rec.fLastX = x;
+    rec.fPrevY = rec.fLastY; rec.fLastY = y;
+    rec.fPrevT = rec.fLastT; rec.fLastT = SkTime::GetMSecs();
+
+    switch (fTouches.count()) {
+        case 1: {
+            float dx = rec.fLastX - rec.fStartX;
+            float dy = rec.fLastY - rec.fStartY;
+            dx = (float)sk_float_round2int(dx);
+            dy = (float)sk_float_round2int(dy);
+            fLocalM.setTranslate(dx, dy);
+        } break;
+        case 2: {
+            SkASSERT(kZoom_State == fState);
+            const Rec& rec0 = fTouches[0];
+            const Rec& rec1 = fTouches[1];
+            
+            float scale = this->computePinch(rec0, rec1);
+            scale = this->limitTotalZoom(scale);
+
+            fLocalM.setTranslate(-center(rec0.fStartX, rec1.fStartX),
+                                 -center(rec0.fStartY, rec1.fStartY));
+            fLocalM.postScale(scale, scale);
+            fLocalM.postTranslate(center(rec0.fLastX, rec1.fLastX),
+                                  center(rec0.fLastY, rec1.fLastY));
+        } break;
+        default:
+            break;
+    }
+}
+
+void SkTouchGesture::touchEnd(void* owner) {
+//    GrPrintf("--- %d touchEnd   %p\n", fTouches.count(), owner);
+
+    int index = this->findRec(owner);
+    if (index < 0) {
+        SkDebugf("--- not found\n");
+        return;
+    }
+
+    const Rec& rec = fTouches[index];
+    if (this->handleDblTap(rec.fLastX, rec.fLastY)) {
+        return;
+    }
+
+    // count() reflects the number before we removed the owner
+    switch (fTouches.count()) {
+        case 1: {
+            this->flushLocalM();
+            float dx = rec.fLastX - rec.fPrevX;
+            float dy = rec.fLastY - rec.fPrevY;
+            float dur = (rec.fLastT - rec.fPrevT) * 0.001f;
+            if (dur > 0) {
+                fFlinger.reset(dx / dur, dy / dur);
+            }
+            fState = kEmpty_State;
+        } break;
+        case 2:
+            this->flushLocalM();
+            SkASSERT(kZoom_State == fState);
+            fState = kEmpty_State;
+            break;
+        default:
+            SkASSERT(kZoom_State == fState);
+            break;
+    }
+
+    fTouches.removeShuffle(index);
+}
+
+float SkTouchGesture::computePinch(const Rec& rec0, const Rec& rec1) {
+    double dx = rec0.fStartX - rec1.fStartX;
+    double dy = rec0.fStartY - rec1.fStartY;
+    double dist0 = sqrt(dx*dx + dy*dy);
+
+    dx = rec0.fLastX - rec1.fLastX;
+    dy = rec0.fLastY - rec1.fLastY;
+    double dist1 = sqrt(dx*dx + dy*dy);
+
+    double scale = dist1 / dist0;
+    return (float)scale;
+}
+
+bool SkTouchGesture::handleDblTap(float x, float y) {
+    bool found = false;
+    SkMSec now = SkTime::GetMSecs();
+    if (now - fLastUpT <= MAX_DBL_TAP_INTERVAL) {
+        if (SkPoint::Length(fLastUpP.fX - x,
+                            fLastUpP.fY - y) <= MAX_DBL_TAP_DISTANCE) {
+            fFlinger.stop();
+            fLocalM.reset();
+            fGlobalM.reset();
+            fTouches.reset();
+            fState = kEmpty_State;
+            found = true;
+        }
+    }
+
+    fLastUpT = now;
+    fLastUpP.set(x, y);
+    return found;
+}
+
+
diff --git a/src/views/SkView.cpp b/src/views/SkView.cpp
new file mode 100644
index 0000000..1cd6339
--- /dev/null
+++ b/src/views/SkView.cpp
@@ -0,0 +1,793 @@
+#include "SkView.h"
+#include "SkCanvas.h"
+
+////////////////////////////////////////////////////////////////////////
+
+SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags))
+{
+	fWidth = fHeight = 0;
+	fLoc.set(0, 0);
+	fParent = fFirstChild = fNextSibling = fPrevSibling = NULL;
+	
+	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::draw(SkCanvas* canvas)
+{
+	if (fWidth && fHeight && this->isVisible())
+	{
+		SkRect	r;
+		r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight);
+		if (this->isClipToBounds() &&
+            canvas->quickReject(r, SkCanvas::kBW_EdgeType)) {
+                return;
+        }
+
+		SkAutoCanvasRestore	as(canvas, true);
+
+        if (this->isClipToBounds()) {
+            canvas->clipRect(r);
+        }
+		canvas->translate(fLoc.fX, fLoc.fY);
+
+        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;
+}
+
+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 false;
+    }
+
+    if (this->onSendClickToChildren(x, y)) {
+        F2BIter	iter(this);
+        SkView*	child;
+
+        while ((child = iter.next()) != NULL)
+        {
+            Click* click = child->findClickHandler(x - child->fLoc.fX,
+                                                   y - child->fLoc.fY);
+            if (click) {
+                return click;
+            }
+        }
+    }
+	return this->onFindClickHandler(x, y);
+}
+
+void SkView::DoClickDown(Click* click, int x, int y)
+{
+	SkASSERT(click);
+
+	SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+	if (target == NULL)
+		return;
+
+	click->fIOrig.set(x, y);
+	click->fICurr = click->fIPrev = click->fIOrig;
+
+	click->fOrig.iset(x, y);
+	target->globalToLocal(&click->fOrig);
+	click->fPrev = click->fCurr = click->fOrig;
+
+	click->fState = Click::kDown_State;
+	target->onClick(click);
+}
+
+void SkView::DoClickMoved(Click* click, int x, int y)
+{
+	SkASSERT(click);
+
+	SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+	if (target == NULL)
+		return;
+
+	click->fIPrev = click->fICurr;
+	click->fICurr.set(x, y);
+
+	click->fPrev = click->fCurr;
+	click->fCurr.iset(x, y);
+	target->globalToLocal(&click->fCurr);
+
+	click->fState = Click::kMoved_State;
+	target->onClick(click);
+}
+
+void SkView::DoClickUp(Click* click, int x, int y)
+{
+	SkASSERT(click);
+
+	SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+	if (target == NULL)
+		return;
+
+	click->fIPrev = click->fICurr;
+	click->fICurr.set(x, y);
+
+	click->fPrev = click->fCurr;
+	click->fCurr.iset(x, y);
+	target->globalToLocal(&click->fCurr);
+
+	click->fState = Click::kUp_State;
+	target->onClick(click);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::invokeLayout() {
+	SkView::Layout* layout = this->getLayout();
+
+	if (layout) {
+		layout->layoutChildren(this);
+    }
+}
+
+void SkView::onDraw(SkCanvas* canvas) {
+	Artist* artist = this->getArtist();
+
+	if (artist) {
+		artist->draw(this, canvas);
+    }
+}
+
+void SkView::onSizeChange() {}
+
+bool SkView::onSendClickToChildren(SkScalar x, SkScalar y) {
+    return true;
+}
+
+SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y) {
+	return NULL;
+}
+
+bool SkView::onClick(Click*) {
+	return false;
+}
+
+bool SkView::handleInval(const SkRect*) {
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::getLocalBounds(SkRect* bounds) const
+{
+	if (bounds)
+		bounds->set(0, 0, fWidth, fHeight);
+}
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+void SkView::detachFromParent_NoLayout()
+{
+	if (fParent == NULL)
+		return;
+
+	if (fContainsFocus)
+		(void)this->setFocusView(NULL);
+
+	this->inval(NULL);
+
+	SkView*	next = NULL;
+
+	if (fNextSibling != this)	// do we have any siblings
+	{
+		fNextSibling->fPrevSibling = fPrevSibling;
+		fPrevSibling->fNextSibling = fNextSibling;
+		next = fNextSibling;
+	}
+
+	if (fParent->fFirstChild == this)
+		fParent->fFirstChild = next;
+
+	fParent = fNextSibling = fPrevSibling = NULL;
+
+	this->unref();
+}
+
+void SkView::detachFromParent()
+{
+	SkView* parent = fParent;
+
+	if (parent)
+	{
+		this->detachFromParent_NoLayout();
+		parent->invokeLayout();
+	}
+}
+
+SkView* SkView::attachChildToBack(SkView* child)
+{
+	SkASSERT(child != this);
+
+	if (child == NULL || fFirstChild == child)
+		goto DONE;
+
+	child->ref();
+	child->detachFromParent_NoLayout();
+
+	if (fFirstChild == NULL)
+	{
+		child->fNextSibling = child;
+		child->fPrevSibling = child;
+	}
+	else
+	{
+		child->fNextSibling = fFirstChild;
+		child->fPrevSibling = fFirstChild->fPrevSibling;
+		fFirstChild->fPrevSibling->fNextSibling = child;
+		fFirstChild->fPrevSibling = child;
+	}
+
+	fFirstChild = child;
+	child->fParent = this;
+	child->inval(NULL);
+
+	this->invokeLayout();
+DONE:
+	return child;
+}
+
+SkView* SkView::attachChildToFront(SkView* child)
+{
+	SkASSERT(child != this);
+
+	if (child == NULL || (fFirstChild && fFirstChild->fPrevSibling == child))
+		goto DONE;
+
+	child->ref();
+	child->detachFromParent_NoLayout();
+
+	if (fFirstChild == NULL)
+	{
+		fFirstChild = child;
+		child->fNextSibling = child;
+		child->fPrevSibling = child;
+	}
+	else
+	{
+		child->fNextSibling = fFirstChild;
+		child->fPrevSibling = fFirstChild->fPrevSibling;
+		fFirstChild->fPrevSibling->fNextSibling = child;
+		fFirstChild->fPrevSibling = child;
+	}
+
+	child->fParent = this;
+	child->inval(NULL);
+
+	this->invokeLayout();
+DONE:
+	return child;
+}
+
+void SkView::detachAllChildren()
+{
+	while (fFirstChild)
+		fFirstChild->detachFromParent_NoLayout();
+}
+
+void SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
+{
+	SkASSERT(this);
+
+	if (local)
+	{
+		const SkView* view = this;
+		while (view)
+		{
+			x -= view->fLoc.fX;
+			y -= view->fLoc.fY;
+			view = view->fParent;
+		}
+		local->set(x, y);
+	}
+}
+
+//////////////////////////////////////////////////////////////////
+
+/*	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..8f5489d
--- /dev/null
+++ b/src/views/SkViewInflate.cpp
@@ -0,0 +1,139 @@
+#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..b03ca8c
--- /dev/null
+++ b/src/views/SkViewPriv.cpp
@@ -0,0 +1,97 @@
+#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..06ce59b
--- /dev/null
+++ b/src/views/SkViewPriv.h
@@ -0,0 +1,38 @@
+#ifndef SkViewPriv_DEFINED
+#define SkViewPriv_DEFINED
+
+#include "SkView.h"
+#include "SkTagList.h"
+
+struct Layout_SkTagList : SkTagList {
+	SkView::Layout*	fLayout;
+
+	Layout_SkTagList(SkView::Layout* layout)
+		: SkTagList(kViewLayout_SkTagList), fLayout(layout)
+	{
+		SkASSERT(layout);
+		layout->ref();
+	}
+	virtual ~Layout_SkTagList()
+	{
+		fLayout->unref();
+	}
+};
+
+struct Artist_SkTagList : SkTagList {
+	SkView::Artist*	fArtist;
+
+	Artist_SkTagList(SkView::Artist* artist)
+		: SkTagList(kViewArtist_SkTagList), fArtist(artist)
+	{
+		SkASSERT(artist);
+		artist->ref();
+	}
+	virtual ~Artist_SkTagList()
+	{
+		fArtist->unref();
+	}
+};
+
+#endif
+
diff --git a/src/views/SkWidget.cpp b/src/views/SkWidget.cpp
new file mode 100644
index 0000000..4d055c4
--- /dev/null
+++ b/src/views/SkWidget.cpp
@@ -0,0 +1,323 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkInterpolator.h"
+#include "SkTime.h"
+#include "SkParsePaint.h"
+
+#if 0
+SkWidgetView::SkWidgetView(U32 flags) : SkView(flags)
+{
+}
+
+SkWidgetView::~SkWidgetView()
+{
+}
+
+const char* SkWidgetView::GetEventType()
+{
+	return "SkWidgetView";
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+class SkTextView::Interp {
+public:
+	Interp(const SkString& old, SkMSec now, SkMSec dur, AnimaDir dir) : fOldText(old), fInterp(1, 2)
+	{
+		SkScalar x = 0;
+		fInterp.setKeyFrame(0, now, &x, 0);
+		x = SK_Scalar1;
+		if (dir == kBackward_AnimDir)
+			x = -x;
+		fInterp.setKeyFrame(1, now + dur, &x);
+	}
+	bool draw(SkCanvas* canvas, const SkString& newText, SkScalar x, SkScalar y, SkPaint& paint)
+	{
+		SkScalar scale;
+
+		if (fInterp.timeToValues(SkTime::GetMSecs(), &scale) == SkInterpolator::kFreezeEnd_Result)
+		{
+			canvas->drawText(newText.c_str(), newText.size(), x, y, paint);
+			return false;
+		}
+		else
+		{
+			U8 alpha = paint.getAlpha();
+			SkScalar above, below;
+			(void)paint.measureText(NULL, 0, &above, &below);
+			SkScalar height = below - above;
+			SkScalar dy = SkScalarMul(height, scale);
+			if (scale < 0)
+				height = -height;
+
+			// draw the old
+			paint.setAlpha((U8)SkScalarMul(alpha, SK_Scalar1 - SkScalarAbs(scale)));
+			canvas->drawText(fOldText.c_str(), fOldText.size(), x, y - dy, paint);
+			// draw the new
+			paint.setAlpha((U8)SkScalarMul(alpha, SkScalarAbs(scale)));
+			canvas->drawText(newText.c_str(), newText.size(), x, y + height - dy, paint);
+			// restore the paint
+			paint.setAlpha(alpha);
+			return true;
+		}
+	}
+
+private:
+	SkString		fOldText;
+	SkInterpolator	fInterp;
+};
+
+SkTextView::SkTextView(U32 flags) : SkView(flags), fInterp(NULL), fDoInterp(false)
+{
+	fMargin.set(0, 0);
+}
+
+SkTextView::~SkTextView()
+{
+	delete fInterp;
+}
+
+void SkTextView::getText(SkString* str) const
+{
+	if (str)
+		str->set(fText);
+}
+
+void SkTextView::setText(const char text[], AnimaDir dir)
+{
+	if (!fText.equals(text))
+	{
+		SkString tmp(text);
+		this->privSetText(tmp, dir);
+	}
+}
+
+void SkTextView::setText(const char text[], size_t len, AnimaDir dir)
+{
+	if (!fText.equals(text))
+	{
+		SkString tmp(text, len);
+		this->privSetText(tmp, dir);
+	}
+}
+
+void SkTextView::setText(const SkString& src, AnimaDir dir)
+{
+	if (fText != src)
+		this->privSetText(src, dir);
+}
+
+void SkTextView::privSetText(const SkString& src, AnimaDir dir)
+{
+	SkASSERT(fText != src);
+
+	if (fDoInterp)
+	{
+		if (fInterp)
+			delete fInterp;
+		fInterp = new Interp(fText, SkTime::GetMSecs(), 500, dir);
+	}
+	fText = src;
+	this->inval(NULL);
+}
+
+/////////////////////////////////////////////////////////////////
+
+void SkTextView::getMargin(SkPoint* margin) const
+{
+	if (margin)
+		*margin = fMargin;
+}
+
+void SkTextView::setMargin(const SkPoint& margin)
+{
+	if (fMargin != margin)
+	{
+		fMargin = margin;
+		this->inval(NULL);
+	}
+}
+
+void SkTextView::onDraw(SkCanvas* canvas)
+{
+	this->INHERITED::onDraw(canvas);
+
+	if (fText.size() == 0)
+		return;
+
+	SkPaint::Align	align = fPaint.getTextAlign();
+	SkScalar		x, y;
+
+	switch (align) {
+	case SkPaint::kLeft_Align:
+		x = fMargin.fX;
+		break;
+	case SkPaint::kCenter_Align:
+		x = SkScalarHalf(this->width());
+		break;
+	default:
+		SkASSERT(align == SkPaint::kRight_Align);
+		x = this->width() - fMargin.fX;
+		break;
+	}
+
+	fPaint.measureText(NULL, 0, &y, NULL);
+	y = fMargin.fY - y;
+
+	if (fInterp)
+	{
+		if (fInterp->draw(canvas, fText, x, y, fPaint))
+			this->inval(NULL);
+		else
+		{
+			delete fInterp;
+			fInterp = NULL;
+		}
+	}
+	else
+		canvas->drawText(fText.c_str(), fText.size(), x, y, fPaint);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+void SkTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+
+	const char* text = dom.findAttr(node, "text");
+	if (text)
+		this->setText(text);
+
+	SkPoint	margin;
+	if (dom.findScalars(node, "margin", (SkScalar*)&margin, 2))
+		this->setMargin(margin);
+	(void)dom.findBool(node, "do-interp", &fDoInterp);
+
+	SkPaint_Inflate(&fPaint, dom, node);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+SkSliderView::SkSliderView(U32 flags) : SkWidgetView(flags)
+{
+	fValue = 0;
+	fMax = 0;
+}
+
+static U16 actual_value(U16CPU value, U16CPU max)
+{
+	return SkToU16(SkMax32(0, SkMin32(value, max)));
+}
+
+void SkSliderView::setMax(U16CPU max)
+{
+	if (fMax != max)
+	{
+		fMax = SkToU16(max);
+		if (fValue > 0)
+			this->inval(NULL);
+	}
+}
+
+void SkSliderView::setValue(U16CPU value)
+{
+	if (fValue != value)
+	{
+		U16 prev = actual_value(fValue, fMax);
+		U16 next = actual_value(value, fMax);
+
+		fValue = SkToU16(value);
+		if (prev != next)
+		{
+			this->inval(NULL);
+
+			if (this->hasListeners())
+			{
+				SkEvent	evt;
+				
+				evt.setType(SkWidgetView::GetEventType());
+				evt.setFast32(this->getSinkID());
+				evt.setS32("sliderValue", next);
+				this->postToListeners(evt);
+			}
+		}
+	}
+}
+
+#include "SkGradientShader.h"
+
+static void setgrad(SkPaint* paint, const SkRect& r)
+{
+	SkPoint	pts[2];
+	SkColor	colors[2];
+
+#if 0
+	pts[0].set(r.fLeft, r.fTop);
+	pts[1].set(r.fLeft + r.height(), r.fBottom);
+#else
+	pts[0].set(r.fRight, r.fBottom);
+	pts[1].set(r.fRight - r.height(), r.fTop);
+#endif
+	colors[0] = SK_ColorBLUE;
+	colors[1] = SK_ColorWHITE;
+
+	paint->setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kMirror_TileMode))->unref();
+}
+
+void SkSliderView::onDraw(SkCanvas* canvas)
+{
+	this->INHERITED::onDraw(canvas);
+
+	U16CPU value = SkMax32(0, SkMin32(fValue, fMax));
+
+	SkRect	r;
+	SkPaint	p;
+
+	r.set(0, 0, this->width(), this->height());
+
+	p.setAntiAliasOn(true);
+	p.setStyle(SkPaint::kStroke_Style);
+	p.setStrokeWidth(SK_Scalar1);
+	r.inset(SK_Scalar1/2, SK_Scalar1/2);
+	canvas->drawRect(r, p);
+
+	if (fMax)
+	{
+		SkFixed percent = SkFixedDiv(value, fMax);
+		
+		r.inset(SK_Scalar1/2, SK_Scalar1/2);
+		r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent));
+		p.setStyle(SkPaint::kFill_Style);
+		setgrad(&p, r);
+		canvas->drawRect(r, p);
+	}
+
+#if 0
+	r.set(0, 0, this->width(), this->height());
+	r.inset(SK_Scalar1, SK_Scalar1);
+	r.inset(r.width()/2, 0);
+	p.setColor(SK_ColorBLACK);
+	canvas->drawLine(*(SkPoint*)&r.fLeft, *(SkPoint*)&r.fRight, p);
+#endif
+}
+
+SkView::Click* SkSliderView::onFindClickHandler(SkScalar x, SkScalar y)
+{
+	return new Click(this);
+}
+
+bool SkSliderView::onClick(Click* click)
+{
+	if (fMax)
+	{
+		SkScalar percent = SkScalarDiv(click->fCurr.fX + SK_Scalar1, this->width() - SK_Scalar1*2);
+		percent = SkMaxScalar(0, SkMinScalar(percent, SK_Scalar1));
+		this->setValue(SkScalarRound(percent * fMax));
+		return true;
+	}
+	return false;
+}
+
+#endif
+
diff --git a/src/views/SkWidgetViews.cpp b/src/views/SkWidgetViews.cpp
new file mode 100644
index 0000000..2705307
--- /dev/null
+++ b/src/views/SkWidgetViews.cpp
@@ -0,0 +1,405 @@
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkStream.h"
+#include "SkSystemEventTypes.h"
+
+#ifdef SK_DEBUG
+	static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+	{
+		const char* value = dom.findAttr(node, attr);
+		if (value)
+			SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+	}
+#else
+	#define assert_no_attr(dom, node, attr)
+#endif
+/*
+I have moved this to SkWidgetViews.h
+enum SkinEnum {
+	kButton_SkinEnum,
+	kProgress_SkinEnum,
+	kScroll_SkinEnum,
+	kStaticText_SkinEnum,
+	
+	kSkinEnumCount
+};
+*/
+
+const char* get_skin_enum_path(SkinEnum se)
+{
+	SkASSERT((unsigned)se < kSkinEnumCount);
+
+	static const char* gSkinPaths[] = {
+            "common/default/default/skins/border3.xml",
+            "common/default/default/skins/button.xml",
+            "common/default/default/skins/progressBar.xml",
+            "common/default/default/skins/scrollBar.xml",
+            "common/default/default/skins/statictextpaint.xml"
+	};
+
+	return gSkinPaths[se];
+}
+
+void init_skin_anim(const char path[], SkAnimator* anim)
+{
+	SkASSERT(path && anim);
+
+	SkFILEStream	stream(path);
+
+	if (!stream.isValid())
+	{
+		SkDEBUGF(("init_skin_anim: loading skin failed <%s>\n", path));
+		sk_throw();
+	}
+
+	if (!anim->decodeStream(&stream))
+	{
+		SkDEBUGF(("init_skin_anim: decoding skin failed <%s>\n", path));
+		sk_throw();
+	}
+}
+
+void init_skin_anim(SkinEnum se, SkAnimator* anim)
+{
+	init_skin_anim(get_skin_enum_path(se), anim);
+}
+
+void init_skin_paint(SkinEnum se, SkPaint* paint)
+{
+	SkASSERT(paint);
+
+	SkAnimator	anim;
+	SkCanvas	canvas;
+	
+	init_skin_anim(se, &anim);
+	anim.draw(&canvas, paint, 0);
+}
+
+void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint)
+{
+	SkASSERT(paint);
+
+	SkAnimator	anim;
+	SkCanvas	canvas;
+	
+	if (!anim.decodeDOM(dom, node))
+	{
+		SkDEBUGF(("inflate_paint: decoding dom failed\n"));
+		SkDEBUGCODE(dom.dump(node);)
+		sk_throw();
+	}	
+	anim.draw(&canvas, paint, 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+SkWidgetView::SkWidgetView() : SkView(SkView::kFocusable_Mask | SkView::kEnabled_Mask)
+{
+}
+
+const char* SkWidgetView::getLabel() const
+{
+	return fLabel.c_str();
+}
+	
+void SkWidgetView::getLabel(SkString* label) const
+{
+	if (label)
+		*label = fLabel;
+}
+
+void SkWidgetView::setLabel(const char label[])
+{
+	this->setLabel(label, label ? strlen(label) : 0);
+}
+
+void SkWidgetView::setLabel(const char label[], size_t len)
+{
+	if ((label == NULL && fLabel.size() != 0) || !fLabel.equals(label, len))
+	{
+		SkString	tmp(label, len);
+
+		this->onLabelChange(fLabel.c_str(), tmp.c_str());
+		fLabel.swap(tmp);
+	}
+}
+
+void SkWidgetView::setLabel(const SkString& label)
+{
+	if (fLabel != label)
+	{
+		this->onLabelChange(fLabel.c_str(), label.c_str());
+		fLabel = label;
+	}
+}
+
+bool SkWidgetView::postWidgetEvent()
+{
+	if (!fEvent.isType(""))
+	{
+		SkEvent	evt(fEvent);	// make a copy since onPrepareWidgetEvent may edit the event
+
+		if (this->onPrepareWidgetEvent(&evt))
+		{
+			SkDEBUGCODE(evt.dump("SkWidgetView::postWidgetEvent");)
+
+			this->postToListeners(evt);	// wonder if this should return true if there are > 0 listeners...
+			return true;
+		}
+	}
+	return false;
+}
+
+/*virtual*/ void SkWidgetView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+
+	const char* label = dom.findAttr(node, "label");
+	if (label)
+		this->setLabel(label);
+		
+	if ((node = dom.getFirstChild(node, "event")) != NULL)
+		fEvent.inflate(dom, node);
+}
+
+/*virtual*/ void SkWidgetView::onLabelChange(const char oldLabel[], const char newLabel[])
+{
+	this->inval(NULL);
+}
+
+static const char gWidgetEventSinkIDSlotName[] = "sk-widget-sinkid-slot";
+
+/*virtual*/ bool SkWidgetView::onPrepareWidgetEvent(SkEvent* evt)
+{
+	evt->setS32(gWidgetEventSinkIDSlotName, this->getSinkID());
+	return true;
+}
+
+SkEventSinkID SkWidgetView::GetWidgetEventSinkID(const SkEvent& evt)
+{
+	int32_t	sinkID;
+	
+	return evt.findS32(gWidgetEventSinkIDSlotName, &sinkID) ? (SkEventSinkID)sinkID : 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*virtual*/ bool SkButtonView::onEvent(const SkEvent& evt)
+{
+	if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
+	{
+		this->postWidgetEvent();
+		return true;
+	}
+	return this->INHERITED::onEvent(evt);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkCheckButtonView::SkCheckButtonView() : fCheckState(kOff_CheckState)
+{
+}
+
+void SkCheckButtonView::setCheckState(CheckState state)
+{
+	SkASSERT((unsigned)state <= kUnknown_CheckState);
+	
+	if (fCheckState != state)
+	{
+		this->onCheckStateChange(this->getCheckState(), state);
+		fCheckState = SkToU8(state);
+	}
+}
+	
+/*virtual*/ void SkCheckButtonView::onCheckStateChange(CheckState oldState, CheckState newState)
+{
+	this->inval(NULL);
+}
+
+/*virtual*/ void SkCheckButtonView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+	this->INHERITED::onInflate(dom, node);
+	
+	int index = dom.findList(node, "check-state", "off,on,unknown");
+	if (index >= 0)
+		this->setCheckState((CheckState)index);
+}
+
+static const char gCheckStateSlotName[] = "sk-checkbutton-check-slot";
+
+/*virtual*/ bool SkCheckButtonView::onPrepareWidgetEvent(SkEvent* evt)
+{
+	// could check if we're "disabled", and return false...
+
+	evt->setS32(gCheckStateSlotName, this->getCheckState());
+	return true;
+}
+
+bool SkCheckButtonView::GetWidgetEventCheckState(const SkEvent& evt, CheckState* state)
+{
+	int32_t	state32;
+	
+	if (evt.findS32(gCheckStateSlotName, &state32))
+	{
+		if (state)
+			*state = (CheckState)state32;
+		return true;
+	}
+	return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTime.h"
+#include <stdio.h>
+
+class SkAnimButtonView : public SkButtonView {
+public:
+	SkAnimButtonView()
+	{
+		fAnim.setHostEventSink(this);
+		init_skin_anim(kButton_SkinEnum, &fAnim);
+	}
+
+protected:
+	virtual void onLabelChange(const char oldLabel[], const char newLabel[])
+	{
+		this->INHERITED::onLabelChange(oldLabel, newLabel);
+
+		SkEvent evt("user");
+		evt.setString("id", "setLabel");
+		evt.setString("LABEL", newLabel);
+		fAnim.doUserEvent(evt);
+	}
+	
+	virtual void onFocusChange(bool gainFocus)
+	{
+		this->INHERITED::onFocusChange(gainFocus);
+
+		SkEvent evt("user");
+		evt.setString("id", "setFocus");
+		evt.setS32("FOCUS", gainFocus);
+		fAnim.doUserEvent(evt);
+	}
+
+	virtual void onSizeChange()
+	{
+		this->INHERITED::onSizeChange();
+
+		SkEvent evt("user");
+		evt.setString("id", "setDim");
+		evt.setScalar("dimX", this->width());
+		evt.setScalar("dimY", this->height());
+		fAnim.doUserEvent(evt);
+	}
+
+	virtual void onDraw(SkCanvas* canvas)
+	{
+		SkPaint						paint;		
+		SkAnimator::DifferenceType	diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+		
+		if (diff == SkAnimator::kDifferent)
+			this->inval(NULL);
+		else if (diff == SkAnimator::kPartiallyDifferent)
+		{
+			SkRect	bounds;
+			fAnim.getInvalBounds(&bounds);
+			this->inval(&bounds);
+		}
+	}
+	
+	virtual bool onEvent(const SkEvent& evt)
+	{
+		if (evt.isType(SK_EventType_Inval))
+		{
+			this->inval(NULL);
+			return true;
+		}
+		if (evt.isType("recommendDim"))
+		{
+			SkScalar	height;
+			
+			if (evt.findScalar("y", &height))
+				this->setHeight(height);
+			return true;
+		}
+		return this->INHERITED::onEvent(evt);
+	}
+	
+	virtual bool onPrepareWidgetEvent(SkEvent* evt)
+	{
+		if (this->INHERITED::onPrepareWidgetEvent(evt))
+		{
+			SkEvent	e("user");
+			e.setString("id", "handlePress");
+			(void)fAnim.doUserEvent(e);
+			return true;
+		}
+		return false;
+	}
+
+private:
+	SkAnimator	fAnim;
+	
+	typedef SkButtonView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////
+
+SkView* SkWidgetFactory(const char name[])
+{
+	if (name == NULL)
+		return NULL;
+	
+	// must be in the same order as the SkSkinWidgetEnum is declared
+	static const char* gNames[] = {
+		"sk-border",
+		"sk-button",
+		"sk-image",
+		"sk-list",
+		"sk-progress",
+		"sk-scroll",
+		"sk-text"
+		
+	};
+
+	for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++)
+		if (!strcmp(gNames[i], name))
+			return SkWidgetFactory((SkWidgetEnum)i);
+
+	return NULL;
+}
+
+#include "SkImageView.h"
+#include "SkProgressBarView.h"
+#include "SkScrollBarView.h"
+#include "SkBorderView.h"
+
+SkView* SkWidgetFactory(SkWidgetEnum sw)
+{
+	switch (sw) {
+	case kBorder_WidgetEnum:
+		return new SkBorderView;
+	case kButton_WidgetEnum:
+		return new SkAnimButtonView;
+	case kImage_WidgetEnum:
+		return new SkImageView;
+	case kList_WidgetEnum:
+		return new SkListView;
+	case kProgress_WidgetEnum:
+		return new SkProgressBarView;
+	case kScroll_WidgetEnum:
+		return new SkScrollBarView;
+	case kText_WidgetEnum:
+		return new SkStaticTextView;
+	default:
+		SkASSERT(!"unknown enum passed to SkWidgetFactory");
+		break;
+	}
+	return NULL;
+}
diff --git a/src/views/SkWidgets.cpp b/src/views/SkWidgets.cpp
new file mode 100644
index 0000000..dba9ab3
--- /dev/null
+++ b/src/views/SkWidgets.cpp
@@ -0,0 +1,554 @@
+#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..db4faee
--- /dev/null
+++ b/src/views/SkWindow.cpp
@@ -0,0 +1,412 @@
+#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)
+{
+	fClick = NULL;
+	fWaitingOnInval = false;
+
+#ifdef SK_BUILD_FOR_WINCE
+	fConfig = SkBitmap::kRGB_565_Config;
+#else
+	fConfig = SkBitmap::kARGB_8888_Config;
+#endif
+
+    fMatrix.reset();
+}
+
+SkWindow::~SkWindow()
+{
+	delete fClick;
+
+	fMenus.deleteAll();
+}
+
+void SkWindow::setMatrix(const SkMatrix& matrix) {
+    if (fMatrix != matrix) {
+        fMatrix = matrix;
+        this->inval(NULL);
+    }
+}
+
+void SkWindow::preConcat(const SkMatrix& matrix) {
+    SkMatrix m;
+    m.setConcat(fMatrix, matrix);
+    this->setMatrix(m);
+}
+
+void SkWindow::postConcat(const SkMatrix& matrix) {
+    SkMatrix m;
+    m.setConcat(matrix, fMatrix);
+    this->setMatrix(m);
+}
+
+void SkWindow::setConfig(SkBitmap::Config config)
+{
+	this->resize(fBitmap.width(), fBitmap.height(), config);
+}
+
+void SkWindow::resize(int width, int height, SkBitmap::Config config)
+{
+	if (config == SkBitmap::kNo_Config)
+		config = fConfig;
+
+	if (width != fBitmap.width() || height != fBitmap.height() || config != fConfig)
+	{
+		fConfig = config;
+		fBitmap.setConfig(config, width, height);
+		fBitmap.allocPixels();
+        fBitmap.setIsOpaque(true);
+
+		this->setSize(SkIntToScalar(width), SkIntToScalar(height));
+		this->inval(NULL);
+	}
+}
+
+void SkWindow::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
+{
+	fBitmap.eraseARGB(a, r, g, b);
+}
+
+void SkWindow::eraseRGB(U8CPU r, U8CPU g, U8CPU b)
+{
+	fBitmap.eraseRGB(r, g, b);
+}
+
+bool SkWindow::handleInval(const SkRect* localR)
+{
+	SkIRect	ir;
+
+    if (localR) {
+        SkRect devR;
+        SkMatrix inverse;
+        if (!fMatrix.invert(&inverse)) {
+            return false;
+        }
+        fMatrix.mapRect(&devR, *localR);
+        devR.round(&ir);
+    } else {
+        ir.set(0, 0,
+			   SkScalarRound(this->width()),
+			   SkScalarRound(this->height()));
+    }
+	fDirtyRgn.op(ir, SkRegion::kUnion_Op);
+
+	this->onHandleInval(ir);
+	return true;
+}
+
+void SkWindow::forceInvalAll() {
+    fDirtyRgn.setRect(0, 0,
+                      SkScalarCeil(this->width()),
+                      SkScalarCeil(this->height()));
+}
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+	#include <windows.h>
+	#include <gx.h>
+	extern GXDisplayProperties gDisplayProps;
+#endif
+
+#ifdef SK_SIMULATE_FAILED_MALLOC
+extern bool gEnableControlledThrow;
+#endif
+
+bool SkWindow::update(SkIRect* updateArea, SkCanvas* canvas)
+{
+	if (!fDirtyRgn.isEmpty())
+	{
+		SkBitmap bm = this->getBitmap();
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+		char* buffer = (char*)GXBeginDraw();
+		SkASSERT(buffer);
+
+		RECT	rect;
+		GetWindowRect((HWND)((SkOSWindow*)this)->getHWND(), &rect);
+		buffer += rect.top * gDisplayProps.cbyPitch + rect.left * gDisplayProps.cbxPitch;
+
+		bm.setPixels(buffer);
+#endif
+
+		SkCanvas	rasterCanvas;
+        SkDevice*   device;
+
+        if (NULL == canvas) {
+            canvas = &rasterCanvas;
+            device = new SkDevice(canvas, bm, false);
+            canvas->setDevice(device)->unref();
+        } else {
+            canvas->setBitmapDevice(bm);
+        }
+
+		canvas->clipRegion(fDirtyRgn);
+		if (updateArea)
+			*updateArea = fDirtyRgn.getBounds();
+
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->concat(fMatrix);
+
+		// empty this now, so we can correctly record any inval calls that
+		// might be made during the draw call.
+		fDirtyRgn.setEmpty();
+
+#ifdef TEST_BOUNDER
+		test_bounder	b(bm);
+		canvas->setBounder(&b);
+#endif
+#ifdef SK_SIMULATE_FAILED_MALLOC
+		gEnableControlledThrow = true;
+#endif
+#ifdef SK_BUILD_FOR_WIN32
+		//try {
+			this->draw(canvas);
+		//}
+		//catch (...) {
+		//}
+#else
+		this->draw(canvas);
+#endif
+#ifdef SK_SIMULATE_FAILED_MALLOC
+		gEnableControlledThrow = false;
+#endif
+#ifdef TEST_BOUNDER
+		canvas->setBounder(NULL);
+#endif
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+		GXEndDraw();
+#endif
+
+		return true;
+	}
+	return false;
+}
+
+bool SkWindow::handleChar(SkUnichar uni)
+{
+	if (this->onHandleChar(uni))
+		return true;
+
+	SkView* focus = this->getFocusView();
+	if (focus == NULL)
+		focus = this;
+
+	SkEvent evt(SK_EventType_Unichar);
+	evt.setFast32(uni);
+	return focus->doEvent(evt);
+}
+
+bool SkWindow::handleKey(SkKey key)
+{
+	if (key == kNONE_SkKey)
+		return false;
+
+	if (this->onHandleKey(key))
+		return true;
+
+	// send an event to the focus-view
+	{
+		SkView* focus = this->getFocusView();
+		if (focus == NULL)
+			focus = this;
+
+		SkEvent evt(SK_EventType_Key);
+		evt.setFast32(key);
+		if (focus->doEvent(evt))
+			return true;
+	}
+
+	if (key == kUp_SkKey || key == kDown_SkKey)
+	{
+		if (this->moveFocus(key == kUp_SkKey ? kPrev_FocusDirection : kNext_FocusDirection) == NULL)
+			this->onSetFocusView(NULL);
+		return true;
+	}
+	return false;
+}
+
+bool SkWindow::handleKeyUp(SkKey key)
+{
+    if (key == kNONE_SkKey)
+        return false;
+        
+    if (this->onHandleKeyUp(key))
+        return true;
+    
+    //send an event to the focus-view
+    {
+        SkView* focus = this->getFocusView();
+        if (focus == NULL)
+            focus = this;
+            
+        //should this one be the same?
+        SkEvent evt(SK_EventType_KeyUp);
+        evt.setFast32(key);
+        if (focus->doEvent(evt))
+            return true;
+    }
+    return false;
+}
+
+void SkWindow::addMenu(SkOSMenu* menu)
+{
+	*fMenus.append() = menu;
+	this->onAddMenu(menu);
+}
+
+void SkWindow::setTitle(const char title[]) {
+    if (NULL == title) {
+        title = "";
+    }
+    fTitle.set(title);
+    this->onSetTitle(title);
+}
+
+bool SkWindow::handleMenu(uint32_t cmd)
+{
+	for (int i = 0; i < fMenus.count(); i++)
+	{
+		SkEvent* evt = fMenus[i]->createEvent(cmd);
+		if (evt)
+		{
+			evt->post(this->getSinkID());
+			return true;
+		}
+	}
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+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) {
+    return this->onDispatchClick(x, y, state);
+}
+
+bool SkWindow::onDispatchClick(int x, int y, Click::State state) {
+	bool handled = false;
+
+	switch (state) {
+	case Click::kDown_State:
+		if (fClick)
+			delete fClick;
+		fClick = this->findClickHandler(SkIntToScalar(x), SkIntToScalar(y));
+		if (fClick)
+		{
+			SkView::DoClickDown(fClick, x, y);
+			handled = true;
+		}
+		break;
+	case Click::kMoved_State:
+		if (fClick)
+		{
+			SkView::DoClickMoved(fClick, x, y);
+			handled = true;
+		}
+		break;
+	case Click::kUp_State:
+		if (fClick)
+		{
+			SkView::DoClickUp(fClick, x, y);
+			delete fClick;
+			fClick = NULL;
+			handled = true;
+		}
+		break;
+	}
+	return handled;
+}
+
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/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/Android.mk b/tests/Android.mk
index 09c7739..5823b0e 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -26,6 +26,7 @@
   ParsePathTest.cpp \
   PathMeasureTest.cpp \
   PathTest.cpp \
+  Reader32Test.cpp \
   RefDictTest.cpp \
   RegionTest.cpp \
   Sk64Test.cpp \
@@ -36,6 +37,7 @@
   Test.cpp \
   TestSize.cpp \
   UtilsTest.cpp \
+  Writer32Test.cpp \
   XfermodeTest.cpp
 
 # The name of the file with a main function must
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index 4ef33ff..eafdd69 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -107,7 +107,8 @@
     // all of the above rects should have been intersected, leaving only 1 rect
     SkClipStack::B2FIter iter(stack);
     const SkClipStack::B2FIter::Clip* clip = iter.next();
-    const SkRect answer = { 25, 25, 75, 75 };
+    SkRect answer;
+    answer.iset(25, 25, 75, 75);
 
     REPORTER_ASSERT(reporter, clip);
     REPORTER_ASSERT(reporter, clip->fRect);
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
index 49a98c2..4125f9f 100644
--- a/tests/MatrixTest.cpp
+++ b/tests/MatrixTest.cpp
@@ -2,10 +2,12 @@
 #include "SkMatrix.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 float tolerance = 0.000005f;
+    const SkScalar tolerance = SK_Scalar1 / 200000;
 #else
-    const int32_t tolerance = 8;
+    const SkScalar tolerance = SK_Scalar1 / 1024;
 #endif
 
     return SkScalarAbs(a - b) <= tolerance;
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index 6feab51..9a58fa6 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -122,15 +122,14 @@
     realHalf->unref();  // SkRefPtr and new both took a reference.
     CheckObjectOutput(reporter, realHalf.get(), "0.5", true);
 
+#if defined(SK_SCALAR_IS_FLOAT)
     SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75);
     bigScalar->unref();  // SkRefPtr and new both took a reference.
-#if defined(SK_SCALAR_IS_FIXED) || !defined(SK_ALLOW_LARGE_PDF_SCALARS)
+#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
     CheckObjectOutput(reporter, bigScalar.get(), "111000", true);
 #else
     CheckObjectOutput(reporter, bigScalar.get(), "110999.75", true);
-#endif
 
-#if defined(SK_SCALAR_IS_FLOAT) && defined(SK_ALLOW_LARGE_PDF_SCALARS)
     SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1);
     biggerScalar->unref();  // SkRefPtr and new both took a reference.
     CheckObjectOutput(reporter, biggerScalar.get(), "50000000", true);
@@ -139,6 +138,7 @@
     smallestScalar->unref();  // SkRefPtr and new both took a reference.
     CheckObjectOutput(reporter, smallestScalar.get(), "0.00001526", true);
 #endif
+#endif
 
     SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo");
     stringSimple->unref();  // SkRefPtr and new both took a reference.
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index 3884308..7e4e6bc 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -13,15 +13,13 @@
     SkPath pt;
     pt.moveTo(0, 0);
     pt.close();
-//    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
-    check_convexity(reporter, pt, SkPath::kUnknown_Convexity);
+    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
     
     SkPath line;
     line.moveTo(12, 20);
     line.lineTo(-12, -20);
     line.close();
-    //    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
-    check_convexity(reporter, pt, SkPath::kUnknown_Convexity);
+    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
     
     SkPath triLeft;
     triLeft.moveTo(0, 0);
@@ -79,21 +77,21 @@
     
     SkPath spiral;
     spiral.moveTo(0, 0);
-    spiral.lineTo(1, 0);
-    spiral.lineTo(1, 1);
-    spiral.lineTo(0, 1);
-    spiral.lineTo(0,.5);
-    spiral.lineTo(.5,.5);
-    spiral.lineTo(.5,.75);
+    spiral.lineTo(100, 0);
+    spiral.lineTo(100, 100);
+    spiral.lineTo(0, 100);
+    spiral.lineTo(0, 50);
+    spiral.lineTo(50, 50);
+    spiral.lineTo(50, 75);
     spiral.close();
     check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
     
     SkPath dent;
-    dent.moveTo(0, 0);
-    dent.lineTo(1, 1);
-    dent.lineTo(0, 1);
-    dent.lineTo(-.5,2);
-    dent.lineTo(-2, 1);
+    dent.moveTo(SkIntToScalar(0), SkIntToScalar(0));
+    dent.lineTo(SkIntToScalar(100), SkIntToScalar(100));
+    dent.lineTo(SkIntToScalar(0), SkIntToScalar(100));
+    dent.lineTo(SkIntToScalar(-50), SkIntToScalar(200));
+    dent.lineTo(SkIntToScalar(-200), SkIntToScalar(100));
     dent.close();
     check_convexity(reporter, dent, SkPath::kConcave_Convexity);
 }
@@ -133,13 +131,12 @@
 }
 
 static void test_convexity(skiatest::Reporter* reporter) {
-    static const SkPath::Convexity U = SkPath::kUnknown_Convexity;
     static const SkPath::Convexity C = SkPath::kConcave_Convexity;
     static const SkPath::Convexity V = SkPath::kConvex_Convexity;
 
     SkPath path;
 
-    REPORTER_ASSERT(reporter, U == SkPath::ComputeConvexity(path));
+    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
     path.addCircle(0, 0, 10);
     REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
     path.addCircle(0, 0, 10);   // 2nd circle
@@ -155,8 +152,9 @@
         const char*         fPathStr;
         SkPath::Convexity   fExpectedConvexity;
     } gRec[] = {
-        { "0 0", SkPath::kUnknown_Convexity },
-        { "0 0 10 10", SkPath::kUnknown_Convexity },
+        { "", 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 },
@@ -188,7 +186,7 @@
     SkRect  bounds, bounds2;
 
     REPORTER_ASSERT(reporter, p.isEmpty());
-    REPORTER_ASSERT(reporter, !p.isConvex());
+    REPORTER_ASSERT(reporter, p.isConvex());
     REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
     REPORTER_ASSERT(reporter, !p.isInverseFillType());
     REPORTER_ASSERT(reporter, p == p2);
@@ -198,17 +196,14 @@
 
     bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
 
-    p.setIsConvex(false);
     p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
     check_convex_bounds(reporter, p, bounds);
 
     p.reset();
-    p.setIsConvex(false);
     p.addOval(bounds);
     check_convex_bounds(reporter, p, bounds);
 
     p.reset();
-    p.setIsConvex(false);
     p.addRect(bounds);
     check_convex_bounds(reporter, p, bounds);
 
@@ -245,18 +240,6 @@
     p.getLastPt(&pt);
     REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
 
-    // check that reset and rewind clear the convex hint back to false
-    p.setIsConvex(false);
-    REPORTER_ASSERT(reporter, !p.isConvex());
-    p.setIsConvex(true);
-    REPORTER_ASSERT(reporter, p.isConvex());
-    p.reset();
-    REPORTER_ASSERT(reporter, !p.isConvex());
-    p.setIsConvex(true);
-    REPORTER_ASSERT(reporter, p.isConvex());
-    p.rewind();
-    REPORTER_ASSERT(reporter, !p.isConvex());
-
     test_convexity(reporter);
     test_convexity2(reporter);
 }
diff --git a/tests/Reader32Test.cpp b/tests/Reader32Test.cpp
new file mode 100644
index 0000000..c752b0f
--- /dev/null
+++ b/tests/Reader32Test.cpp
@@ -0,0 +1,93 @@
+/*
+    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.
+ */
+
+
+#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 };
+    char buffer[SkMax32(sizeof(data), sizeof(data2))];
+
+    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/UtilsTest.cpp b/tests/UtilsTest.cpp
index a051af2..4f36afe 100644
--- a/tests/UtilsTest.cpp
+++ b/tests/UtilsTest.cpp
@@ -64,7 +64,7 @@
     REPORTER_ASSERT(reporter, 1 == obj.getRefCnt());
 }
 
-//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
 
 #define kSEARCH_COUNT   91
 
diff --git a/tests/Writer32Test.cpp b/tests/Writer32Test.cpp
new file mode 100644
index 0000000..63b1209
--- /dev/null
+++ b/tests/Writer32Test.cpp
@@ -0,0 +1,90 @@
+/*
+    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.
+ */
+
+
+#include "SkReader32.h"
+#include "SkWriter32.h"
+#include "Test.h"
+
+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));
+    }
+    REPORTER_ASSERT(reporter, reader.eof());
+}
+
+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);
+    }
+    
+    // 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);
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Writer32", Writer32Class, Tests)
+
diff --git a/tests/tests_files.mk b/tests/tests_files.mk
index 28d5fe7..9b90179 100644
--- a/tests/tests_files.mk
+++ b/tests/tests_files.mk
@@ -22,6 +22,7 @@
     ParsePathTest.cpp \
     PathMeasureTest.cpp \
     PathTest.cpp \
+    Reader32Test.cpp \
     RefDictTest.cpp \
     RegionTest.cpp \
     Sk64Test.cpp \
@@ -33,4 +34,5 @@
     Test.cpp \
     TestSize.cpp \
     UtilsTest.cpp \
+    Writer32Test.cpp \
     XfermodeTest.cpp