grab from latest android



git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/SkSetPoly3To3.cpp b/experimental/SkSetPoly3To3.cpp
new file mode 100644
index 0000000..1420eba
--- /dev/null
+++ b/experimental/SkSetPoly3To3.cpp
@@ -0,0 +1,60 @@
+#include "SkMatrix.h"
+
+static void computeOuterProduct(SkScalar op[4],
+                                const SkPoint pts0[3], const SkPoint& ave0,
+                                const SkPoint pts1[3], const SkPoint& ave1) {
+    bzero(op, 4 * sizeof(op[0]));
+    for (int i = 0; i < 3; i++) {
+        SkScalar x0 = pts0[i].fX - ave0.fX;
+        SkScalar y0 = pts0[i].fY - ave0.fY;
+        SkScalar x1 = pts1[i].fX - ave1.fX;
+        SkScalar y1 = pts1[i].fY - ave1.fY;
+        op[0] += SkScalarMul(x0, x1);
+        op[1] += SkScalarMul(x0, y1);
+        op[2] += SkScalarMul(y0, x1);
+        op[3] += SkScalarMul(y0, y1);
+    }
+}
+
+static SkScalar dot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) {
+    return SkScalarMul(ax, bx) + SkScalarMul(ay, by);
+}
+
+bool SkSetPoly3To3(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]) {
+    const SkPoint& srcAve = src[0];
+    const SkPoint& dstAve = dst[0];
+    
+    SkScalar srcOP[4], dstOP[4];
+    
+    computeOuterProduct(srcOP, src, srcAve, src, srcAve);
+    computeOuterProduct(dstOP, src, srcAve, dst, dstAve);
+
+    SkScalar det = SkScalarMul(srcOP[0], srcOP[3]) - SkScalarMul(srcOP[1], srcOP[2]);
+
+    // need SkScalarNearlyZeroSquared for this (to match Chrome's fix)
+    if (SkScalarNearlyZero(det)) {
+        return false;
+    }
+    
+    SkScalar invDet = SkScalarInvert(det);
+    
+    // now compute invDet * [srcOP]T * [dstOP]
+    
+    // scale and transpose
+    const SkScalar srcOP0 = SkScalarMul( srcOP[3], invDet);
+    const SkScalar srcOP1 = SkScalarMul(-srcOP[1], invDet);
+    const SkScalar srcOP2 = SkScalarMul(-srcOP[2], invDet);
+    const SkScalar srcOP3 = SkScalarMul( srcOP[0], invDet);
+
+    matrix->reset();
+    matrix->setScaleX(dot(srcOP0, srcOP1, dstOP[0], dstOP[2]));
+    matrix->setSkewX( dot(srcOP2, srcOP3, dstOP[0], dstOP[2]));
+    matrix->setSkewY (dot(srcOP0, srcOP1, dstOP[1], dstOP[3]));
+    matrix->setScaleY(dot(srcOP2, srcOP3, dstOP[1], dstOP[3]));
+    matrix->setTranslateX(dstAve.fX - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getScaleX(), matrix->getSkewX()));
+    matrix->setTranslateY(dstAve.fY - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getSkewY(), matrix->getScaleY()));
+    return true;
+}
+
diff --git a/experimental/SkSetPoly3To3_A.cpp b/experimental/SkSetPoly3To3_A.cpp
new file mode 100644
index 0000000..cc953f5
--- /dev/null
+++ b/experimental/SkSetPoly3To3_A.cpp
@@ -0,0 +1,90 @@
+#include "SkMatrix.h"
+
+#ifdef SK_SCALAR_IS_FIXED
+    typedef int64_t SkDScalar;
+
+    static SkScalar SkDScalar_toScalar(SkDScalar value) {
+        SkDScalar result = (value + (1 << 15)) >> 16;
+        int top = result >> 31;
+        SkASSERT(top == 0 || top == -1);
+        return (SkScalar)result;
+    }
+    static SkScalar div(SkDScalar numer, SkDScalar denom) {
+        denom >>= 16;
+        return numer / denom;
+    }
+#else
+    typedef double SkDScalar;
+
+    static SkScalar SkDScalar_toScalar(SkDScalar value) {
+        return static_cast<float>(value);
+    }
+    static SkScalar div(SkDScalar numer, SkDScalar denom) {
+        return static_cast<float>(numer / denom);
+    }
+#endif
+
+static SkDScalar SkDScalar_setMul(SkScalar a, SkScalar b) {
+    return (SkDScalar)a * b;
+}
+
+static void computeOuterProduct(SkScalar op[4],
+                                const SkPoint pts0[3], const SkPoint& ave0,
+                                const SkPoint pts1[3], const SkPoint& ave1) {
+    bzero(op, 4 * sizeof(op[0]));
+    for (int i = 0; i < 3; i++) {
+        SkScalar x0 = pts0[i].fX - ave0.fX;
+        SkScalar y0 = pts0[i].fY - ave0.fY;
+        SkScalar x1 = pts1[i].fX - ave1.fX;
+        SkScalar y1 = pts1[i].fY - ave1.fY;
+        op[0] += SkScalarMul(x0, x1);
+        op[1] += SkScalarMul(x0, y1);
+        op[2] += SkScalarMul(y0, x1);
+        op[3] += SkScalarMul(y0, y1);
+    }
+}
+
+static SkDScalar ddot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) {
+    return SkDScalar_setMul(ax, bx) + SkDScalar_setMul(ay, by);
+}
+
+static SkScalar dot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) {
+    return SkDScalar_toScalar(ddot(ax, ay, bx, by));
+}
+
+bool SkSetPoly3To3(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]) {
+    const SkPoint& srcAve = src[0];
+    const SkPoint& dstAve = dst[0];
+    
+    SkScalar srcOP[4], dstOP[4];
+    
+    computeOuterProduct(srcOP, src, srcAve, src, srcAve);
+    computeOuterProduct(dstOP, src, srcAve, dst, dstAve);
+
+    SkDScalar det = SkDScalar_setMul(srcOP[0], srcOP[3]) -
+                    SkDScalar_setMul(srcOP[1], srcOP[2]);
+
+    SkDScalar M[4];
+    
+    const SkScalar srcOP0 = srcOP[3];
+    const SkScalar srcOP1 = -srcOP[1];
+    const SkScalar srcOP2 = -srcOP[2];
+    const SkScalar srcOP3 = srcOP[0];
+    
+    M[0] = ddot(srcOP0, srcOP1, dstOP[0], dstOP[2]);
+    M[1] = ddot(srcOP2, srcOP3, dstOP[0], dstOP[2]);
+    M[2] = ddot(srcOP0, srcOP1, dstOP[1], dstOP[3]);
+    M[3] = ddot(srcOP2, srcOP3, dstOP[1], dstOP[3]);
+    
+    matrix->reset();
+    matrix->setScaleX(div(M[0], det));
+    matrix->setSkewX( div(M[1], det));
+    matrix->setSkewY (div(M[2], det));
+    matrix->setScaleY(div(M[3], det));
+    matrix->setTranslateX(dstAve.fX - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getScaleX(), matrix->getSkewX()));
+    matrix->setTranslateY(dstAve.fY - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getSkewY(), matrix->getScaleY()));
+    return true;
+}
+
diff --git a/experimental/SkSetPoly3To3_D.cpp b/experimental/SkSetPoly3To3_D.cpp
new file mode 100644
index 0000000..72ee9b1
--- /dev/null
+++ b/experimental/SkSetPoly3To3_D.cpp
@@ -0,0 +1,65 @@
+#include "SkMatrix.h"
+
+typedef int64_t SkDScalar;
+
+static SkScalar SkDScalar_toScalar(SkDScalar value) {
+    SkDScalar result = (value + (1 << 15)) >> 16;
+    int top = result >> 31;
+    SkASSERT(top == 0 || top == -1);
+    return (SkScalar)result;
+}
+
+static SkDScalar SkDScalar_setMul(SkScalar a, SkScalar b) {
+    return (SkDScalar)a * b;
+}
+
+static void computeOuterProduct(SkMatrix* matrix,
+                                const SkPoint pts0[3], const SkPoint& ave0,
+                                const SkPoint pts1[3], const SkPoint& ave1) {
+    SkDScalar tmp[4];
+    bzero(tmp, sizeof(tmp));
+    
+    for (int i = 0; i < 3; i++) {
+        SkScalar x0 = pts0[i].fX - ave0.fX;
+        SkScalar y0 = pts0[i].fY - ave0.fY;
+        SkScalar x1 = pts1[i].fX - ave1.fX;
+        SkScalar y1 = pts1[i].fY - ave1.fY;
+        tmp[0] += SkDScalar_setMul(x0, x1);
+        tmp[1] += SkDScalar_setMul(x0, y1);
+        tmp[2] += SkDScalar_setMul(y0, x1);
+        tmp[3] += SkDScalar_setMul(y0, y1);
+    }
+    matrix->reset();
+    matrix->setScaleX(SkDScalar_toScalar(tmp[0]));
+    matrix->setSkewY( SkDScalar_toScalar(tmp[1]));
+    matrix->setSkewX( SkDScalar_toScalar(tmp[2]));
+    matrix->setScaleY(SkDScalar_toScalar(tmp[3]));
+}
+
+static SkScalar dot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) {
+    return SkDScalar_toScalar(SkDScalar_setMul(ax, bx) +
+                              SkDScalar_setMul(ay, by));
+}
+
+bool SkSetPoly3To3(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]) {
+    const SkPoint& srcAve = src[0];
+    const SkPoint& dstAve = dst[0];
+    
+    SkMatrix srcOP, dstOP;
+    
+    computeOuterProduct(&srcOP, src, srcAve, src, srcAve);
+
+    if (!srcOP.invert(&srcOP)) {
+        return false;
+    }
+
+    computeOuterProduct(&dstOP, src, srcAve, dst, dstAve);
+
+    matrix->setConcat(dstOP, srcOP);
+    matrix->setTranslateX(dstAve.fX - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getScaleX(), matrix->getSkewX()));
+    matrix->setTranslateY(dstAve.fY - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getSkewY(), matrix->getScaleY()));
+    return true;
+}
+
diff --git a/include/animator/SkAnimator.h b/include/animator/SkAnimator.h
new file mode 100644
index 0000000..04d342c
--- /dev/null
+++ b/include/animator/SkAnimator.h
@@ -0,0 +1,508 @@
+/*
+ * 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 SkAnimator_DEFINED
+#define SkAnimator_DEFINED
+
+#include "SkScalar.h"
+#include "SkKey.h"
+#include "SkEventSink.h"
+
+class SkAnimateMaker;
+class SkCanvas;
+class SkDisplayable;
+class SkEvent;
+class SkExtras;
+struct SkMemberInfo;
+class SkPaint;
+struct SkRect;
+class SkStream;
+class SkTypedArray;
+class SkXMLParserError;
+class SkDOM;
+struct SkDOMNode;
+
+/** SkElementType is the type of element: a rectangle, a color, an animator, and so on.
+    This enum is incomplete and will be fleshed out in a future release */
+enum SkElementType {
+    kElementDummyType
+};
+/** SkFieldType is the type of field: a scalar, a string, an integer, a boolean, and so on.
+    This enum is incomplete and will be fleshed out in a future release */
+enum SkFieldType {
+    kFieldDummyType
+};
+
+/** \class SkAnimator
+
+    The SkAnimator class decodes an XML stream into a display list. The
+    display list can be drawn statically as a picture, or can drawn 
+    different elements at different times to form a moving animation.
+
+    SkAnimator does not read the system time on its own; it relies on the
+    caller to pass the current time. The caller can pause, speed up, or
+    reverse the animation by varying the time passed in.
+
+    The XML describing the display list must conform to the schema 
+    described by SkAnimateSchema.xsd. 
+
+    The XML must contain an <event> element to draw. Usually, it contains
+    an <event kind="onload" /> block to add some drawing elements to the
+    display list when the document is first decoded.
+
+    Here's an "Hello World" XML sample:
+
+    <screenplay>
+        <event kind="onload" >
+            <text text="Hello World" y="20" />
+        </event>
+    </screenplay>
+
+    To read and draw this sample:
+
+        // choose one of these two
+        SkAnimator animator; // declare an animator instance on the stack
+    //  SkAnimator* animator = new SkAnimator() // or one could instantiate the class
+
+        // choose one of these three
+        animator.decodeMemory(buffer, size); // to read from RAM
+        animator.decodeStream(stream); // to read from a user-defined stream (e.g., a zip file)
+        animator.decodeURI(filename); // to read from a web location, or from a local text file
+
+        // to draw to the current window:
+        SkCanvas canvas(getBitmap()); // create a canvas
+        animator.draw(canvas, &paint, 0); // draw the scene
+*/
+class SkAnimator : public SkEventSink {
+public:
+    SkAnimator();
+    virtual ~SkAnimator();
+
+    /** Add a drawable extension to the graphics engine. Experimental. 
+        @param extras A derived class that implements methods that identify and instantiate the class
+    */
+    void addExtras(SkExtras* extras);
+
+    /** Read in XML from a stream, and append it to the current
+        animator. Returns false if an error was encountered.
+        Error diagnostics are stored in fErrorCode and fLineNumber.
+        @param stream  The stream to append.
+        @return true if the XML was parsed successfully.
+    */
+    bool appendStream(SkStream* stream);
+
+    /** Read in XML from memory. Returns true if the file can be 
+        read without error. Returns false if an error was encountered.
+        Error diagnostics are stored in fErrorCode and fLineNumber.
+        @param buffer  The XML text as UTF-8 characters.
+        @param size  The XML text length in bytes.
+        @return true if the XML was parsed successfully.
+    */
+    bool decodeMemory(const void* buffer, size_t size);
+
+    /** Read in XML from a stream. Returns true if the file can be 
+        read without error. Returns false if an error was encountered.
+        Error diagnostics are stored in fErrorCode and fLineNumber.
+        @param stream  The stream containg the XML text as UTF-8 characters.
+        @return true if the XML was parsed successfully.
+    */
+    virtual bool decodeStream(SkStream* stream);
+
+    /** Parse the DOM tree starting at the specified node. Returns true if it can be 
+        parsed without error. Returns false if an error was encountered.
+        Error diagnostics are stored in fErrorCode and fLineNumber.
+        @return true if the DOM was parsed successfully.
+    */
+    virtual bool decodeDOM(const SkDOM&, const SkDOMNode*);
+
+    /** Read in XML from a URI. Returns true if the file can be 
+        read without error. Returns false if an error was encountered.
+        Error diagnostics are stored in fErrorCode and fLineNumber.
+        @param uri The complete url path to be read (either ftp, http or https).
+        @return true if the XML was parsed successfully.
+    */
+    bool decodeURI(const char uri[]);
+
+    /** Pass a char event, usually a keyboard symbol, to the animator.
+        This triggers events of the form <event kind="keyChar" key="... />
+        @param ch  The character to match against <event> element "key" 
+            attributes.
+        @return true if the event was dispatched successfully.
+    */
+    bool doCharEvent(SkUnichar ch);
+
+    /** Experimental:
+        Pass a mouse click event along with the mouse coordinates to 
+        the animator. This triggers events of the form <event kind="mouseDown" ... />
+        and other mouse events.
+        @param state The mouse state, described by SkView::Click::State : values are
+        down == 0, moved == 1, up == 2
+        @param x    The x-position of the mouse
+        @param y The y-position of the mouse
+        @return true if the event was dispatched successfully.
+    */
+    bool doClickEvent(int state, SkScalar x, SkScalar y);
+
+    /** Pass a meta-key event, such as an arrow , to the animator.
+        This triggers events of the form <event kind="keyPress" code="... />
+        @param code  The key to match against <event> element "code" 
+            attributes.
+        @return true if the event was dispatched successfully.
+    */
+    bool doKeyEvent(SkKey code);
+    bool doKeyUpEvent(SkKey code);
+    
+    /** Send an event to the animator. The animator's clock is set 
+        relative to the current time.
+        @return true if the event was dispatched successfully.
+    */
+    bool doUserEvent(const SkEvent& evt);
+
+    /** The possible results from the draw function. 
+    */
+    enum DifferenceType {
+        kNotDifferent,
+        kDifferent,
+        kPartiallyDifferent
+    };
+    /** Draws one frame of the animation. The first call to draw always 
+        draws the initial frame of the animation. Subsequent calls draw 
+        the offset into the animation by 
+        subtracting the initial time from the current time.
+        @param canvas  The canvas to draw into.
+        @param paint     The paint to draw with.
+        @param time  The offset into the current animation.
+        @return kNotDifferent if there are no active animations; kDifferent if there are active animations; and
+        kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal 
+        redraw area.
+    */
+    DifferenceType draw(SkCanvas* canvas, SkPaint* paint, SkMSec time);
+
+    /** Draws one frame of the animation, using a new Paint each time.
+        The first call to draw always 
+        draws the initial frame of the animation. Subsequent calls draw 
+        the offset into the animation by 
+        subtracting the initial time from the current time.
+        @param canvas  The canvas to draw into.
+        @param time  The offset into the current animation.
+        @return kNotDifferent if there are no active animations; kDifferent if there are active animations; and
+        kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal 
+        redraw area.
+    */
+    DifferenceType draw(SkCanvas* canvas, SkMSec time);
+
+    /** Experimental:
+        Helper to choose whether to return a SkView::Click handler.
+        @param x ignored
+        @param y ignored
+        @return true if a mouseDown event handler is enabled.
+    */
+    bool findClickEvent(SkScalar x, SkScalar y); 
+
+
+    /** Get the nested animator associated with this element, if any.
+        Use this to access a movie's event sink, to send events to movies.
+        @param element the value returned by getElement
+        @return the internal animator.
+    */
+    const SkAnimator* getAnimator(const SkDisplayable* element) const;
+
+    /** Returns the scalar value of the specified element's attribute[index]
+        @param element the value returned by getElement
+        @param field the value returned by getField
+        @param index the array entry
+        @return the integer value to retrieve, or SK_NaN32 if unsuccessful
+    */
+    int32_t getArrayInt(const SkDisplayable* element, const SkMemberInfo* field, int index);
+
+    /** Returns the scalar value of the specified element's attribute[index]
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @param index the array entry
+        @return the integer value to retrieve, or SK_NaN32 if unsuccessful
+    */
+    int32_t getArrayInt(const char* elementID, const char* fieldName, int index);
+
+    /** Returns the scalar value of the specified element's attribute[index]
+        @param element the value returned by getElement
+        @param field the value returned by getField
+        @param index the array entry
+        @return the scalar value to retrieve, or SK_ScalarNaN if unsuccessful
+    */
+    SkScalar getArrayScalar(const SkDisplayable* element, const SkMemberInfo* field, int index);
+
+    /** Returns the scalar value of the specified element's attribute[index]
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @param index the array entry
+        @return the scalar value to retrieve, or SK_ScalarNaN if unsuccessful
+    */
+    SkScalar getArrayScalar(const char* elementID, const char* fieldName, int index);
+
+    /** Returns the string value of the specified element's attribute[index]
+        @param element is a value returned by getElement
+        @param field is a value returned by getField  
+        @param index the array entry
+        @return the string value to retrieve, or null if unsuccessful
+    */
+    const char* getArrayString(const SkDisplayable* element, const SkMemberInfo* field, int index);
+
+    /** Returns the string value of the specified element's attribute[index]
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @param index the array entry
+        @return the string value to retrieve, or null if unsuccessful
+    */
+    const char* getArrayString(const char* elementID, const char* fieldName, int index);
+
+    /** Returns the XML element corresponding to the given ID.
+        @param elementID is the value of the id attribute in the XML of this element 
+        @return the element matching the ID, or null if the element can't be found
+    */
+    const SkDisplayable* getElement(const char* elementID);
+
+    /** Returns the element type corresponding to the XML element.
+        The element type matches the element name; for instance, <line> returns kElement_LineType
+        @param element is a value returned by getElement  
+        @return element type, or 0 if the element can't be found
+    */
+    SkElementType getElementType(const SkDisplayable* element);
+
+    /** Returns the element type corresponding to the given ID.
+        @param elementID is the value of the id attribute in the XML of this element 
+        @return element type, or 0 if the element can't be found
+    */
+    SkElementType getElementType(const char* elementID);
+
+    /** Returns the XML field of the named attribute in the XML element.
+        @param element is a value returned by getElement
+        @param fieldName is the attribute to return  
+        @return the attribute matching the fieldName, or null if the element can't be found
+    */
+    const SkMemberInfo* getField(const SkDisplayable* element, const char* fieldName);
+
+    /** Returns the XML field of the named attribute in the XML element matching the elementID.
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName is the attribute to return  
+        @return the attribute matching the fieldName, or null if the element can't be found
+    */
+    const SkMemberInfo* getField(const char* elementID, const char* fieldName);
+
+    /** Returns the value type coresponding to the element's attribute.
+        The value type matches the XML schema: and may be kField_BooleanType, kField_ScalarType, etc.
+        @param field is a value returned by getField  
+        @return the attribute type, or 0 if the element can't be found
+    */
+    SkFieldType getFieldType(const SkMemberInfo* field);
+
+    /** Returns the value type coresponding to the element's attribute.
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @return the attribute type, or 0 if the element can't be found
+    */
+    SkFieldType getFieldType(const char* elementID, const char* fieldName);
+
+    /** Returns the recommended animation interval. Returns zero if no
+        interval is specified.
+    */
+    SkMSec getInterval();
+
+    /** Returns the partial rectangle to invalidate after drawing. Call after draw() returns
+    kIsPartiallyDifferent to do a mimimal inval(). */
+    void getInvalBounds(SkRect* inval); 
+
+    /** Returns the details of any error encountered while parsing the XML. 
+    */
+    const SkXMLParserError* getParserError();
+    
+    /** Returns the details of any error encountered while parsing the XML as string. 
+    */
+    const char* getParserErrorString();
+    
+    /** Returns the scalar value of the specified element's attribute
+        @param element is a value returned by getElement
+        @param field is a value returned by getField  
+        @return the integer value to retrieve, or SK_NaN32 if not found
+    */
+    int32_t getInt(const SkDisplayable* element, const SkMemberInfo* field);
+
+    /** Returns the scalar value of the specified element's attribute
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @return the integer value to retrieve, or SK_NaN32 if not found
+    */
+    int32_t getInt(const char* elementID, const char* fieldName);
+
+    /** Returns the scalar value of the specified element's attribute
+        @param element is a value returned by getElement
+        @param field is a value returned by getField  
+        @return the scalar value to retrieve, or SK_ScalarNaN if not found
+    */
+    SkScalar getScalar(const SkDisplayable* element, const SkMemberInfo* field);
+
+    /** Returns the scalar value of the specified element's attribute
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @return the scalar value to retrieve, or SK_ScalarNaN if not found
+    */
+    SkScalar getScalar(const char* elementID, const char* fieldName);
+
+    /** Returns the string value of the specified element's attribute
+        @param element is a value returned by getElement
+        @param field is a value returned by getField  
+        @return the string value to retrieve, or null if not found
+    */
+    const char* getString(const SkDisplayable* element, const SkMemberInfo* field);
+
+    /** Returns the string value of the specified element's attribute
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @return the string value to retrieve, or null if not found
+    */
+    const char* getString(const char* elementID, const char* fieldName);
+
+    /** Gets the file default directory of the URL base path set explicitly or by reading the last URL. */
+    const char* getURIBase();
+
+    /** Resets the animator to a newly created state with no animation data. */
+    void initialize();
+
+    /** Experimental. Resets any active animations so that the next time passed is treated as 
+        time zero. */
+    void reset();
+    
+    /** Sets the scalar value of the specified element's attribute
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @param array is the c-style array of integers
+        @param count is the length of the array
+        @return true if the value was set successfully
+    */
+    bool setArrayInt(const char* elementID, const char* fieldName, const int* array, int count);
+    
+    /** Sets the scalar value of the specified element's attribute
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @param array is the c-style array of strings
+        @param count is the length of the array
+        @return true if the value was set successfully
+    */
+    bool setArrayString(const char* elementID, const char* fieldName, const char** array, int count);
+    
+    /** Sets the scalar value of the specified element's attribute
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @param data the integer value to set
+        @return true if the value was set successfully
+    */
+    bool setInt(const char* elementID, const char* fieldName, int32_t data);
+
+    /** Sets the scalar value of the specified element's attribute
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @param data the scalar value to set
+        @return true if the value was set successfully
+    */
+    bool setScalar(const char* elementID, const char* fieldName, SkScalar data);
+
+    /** Sets the string value of the specified element's attribute
+        @param elementID is the value of the id attribute in the XML of this element
+        @param fieldName specifies the name of the attribute  
+        @param data the string value to set
+        @return true if the value was set successfully
+    */
+    bool setString(const char* elementID, const char* fieldName, const char* data);
+
+    /** Sets the file default directory of the URL base path 
+        @param path the directory path 
+    */
+    void setURIBase(const char* path);
+
+    typedef void* Handler;
+    // This guy needs to be exported to java, so don't make it virtual
+    void setHostHandler(Handler handler) {
+        this->onSetHostHandler(handler);
+    }
+
+    /** \class Timeline
+    Returns current time to animator. To return a custom timeline, create a child
+    class and override the getMSecs method.
+    */
+    class Timeline {
+    public:
+        virtual ~Timeline() {}
+
+        /** Returns the current time in milliseconds */
+        virtual SkMSec getMSecs() const = 0;
+    };
+
+    /** Sets a user class to return the current time to the animator. 
+        Optional; if not called, the system clock will be used by calling SkTime::GetMSecs instead.
+        @param callBack the time function
+    */
+    void setTimeline(const Timeline& );
+
+    static void Init(bool runUnitTests);
+    static void Term();
+    
+    /** The event sink events generated by the animation are posted to. 
+        Screenplay also posts an inval event to this event sink after processing an
+        event to force a redraw.
+        @param target the event sink id
+    */
+    void setHostEventSinkID(SkEventSinkID hostID);
+    SkEventSinkID getHostEventSinkID() const;
+    
+    // helper
+    void setHostEventSink(SkEventSink* sink) {
+        this->setHostEventSinkID(sink ? sink->getSinkID() : 0);
+    }
+    
+    virtual void setJavaOwner(Handler owner);
+    
+#ifdef SK_DEBUG
+    virtual void eventDone(const SkEvent& evt);
+    virtual bool isTrackingEvents();
+    static bool NoLeaks();
+#endif  
+    
+protected:
+    virtual void onSetHostHandler(Handler handler);
+    virtual void onEventPost(SkEvent*, SkEventSinkID);
+    virtual void onEventPostTime(SkEvent*, SkEventSinkID, SkMSec time);
+
+private:
+// helper functions for setters
+    bool setArray(SkDisplayable* element, const SkMemberInfo* field, SkTypedArray array);
+    bool setArray(const char* elementID, const char* fieldName, SkTypedArray array);
+    bool setInt(SkDisplayable* element, const SkMemberInfo* field, int32_t data);
+    bool setScalar(SkDisplayable* element, const SkMemberInfo* field, SkScalar data);
+    bool setString(SkDisplayable* element, const SkMemberInfo* field, const char* data);
+    
+    virtual bool onEvent(const SkEvent&);
+    SkAnimateMaker* fMaker;
+    friend class SkAnimateMaker;
+    friend class SkAnimatorScript;
+    friend class SkAnimatorScript2;
+    friend class SkApply;
+    friend class SkDisplayMovie;
+    friend class SkDisplayType;
+    friend class SkPost;
+    friend class SkXMLAnimatorWriter;
+};
+
+#endif
+
diff --git a/include/animator/SkAnimatorView.h b/include/animator/SkAnimatorView.h
new file mode 100644
index 0000000..3c6c8a1
--- /dev/null
+++ b/include/animator/SkAnimatorView.h
@@ -0,0 +1,47 @@
+/*
+ * 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 SkAnimatorView_DEFINED
+#define SkAnimatorView_DEFINED
+
+#include "SkView.h"
+#include "SkAnimator.h"
+
+class SkAnimatorView : public SkView {
+public:
+            SkAnimatorView();
+    virtual ~SkAnimatorView();
+
+    SkAnimator* getAnimator() const { return fAnimator; }
+
+    bool    decodeFile(const char path[]);
+    bool    decodeMemory(const void* buffer, size_t size);
+    bool    decodeStream(SkStream* stream);
+
+protected:
+    // overrides
+    virtual bool onEvent(const SkEvent&);
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+
+private:
+    SkAnimator* fAnimator;
+
+    typedef SkView INHERITED;
+};
+
+#endif
+
diff --git a/include/core/Sk64.h b/include/core/Sk64.h
new file mode 100644
index 0000000..c4ae41e
--- /dev/null
+++ b/include/core/Sk64.h
@@ -0,0 +1,245 @@
+/*
+ * 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 Sk64_DEFINED
+#define Sk64_DEFINED
+
+#include "SkFixed.h"
+#include "SkMath.h"
+
+/** \class Sk64
+
+    Sk64 is a 64-bit math package that does not require long long support from the compiler.
+*/
+struct Sk64 {
+    int32_t  fHi;   //!< the high 32 bits of the number (including sign)
+    uint32_t fLo;   //!< the low 32 bits of the number
+
+    /** Returns non-zero if the Sk64 can be represented as a signed 32 bit integer
+    */
+    SkBool is32() const { return fHi == ((int32_t)fLo >> 31); }
+
+    /** Returns non-zero if the Sk64 cannot be represented as a signed 32 bit integer
+    */
+    SkBool is64() const { return fHi != ((int32_t)fLo >> 31); }
+
+    /** Returns non-zero if the Sk64 can be represented as a signed 48 bit integer. Used to know
+        if we can shift the value down by 16 to treat it as a SkFixed.
+    */
+    SkBool isFixed() const;
+
+    /** Return the signed 32 bit integer equivalent. Asserts that is32() returns non-zero.
+    */
+    int32_t get32() const { SkASSERT(this->is32()); return (int32_t)fLo; }
+
+    /** Return the number >> 16. Asserts that this does not loose any significant high bits.
+    */
+    SkFixed getFixed() const {
+        SkASSERT(this->isFixed());
+
+        uint32_t sum = fLo + (1 << 15);
+        int32_t  hi = fHi;
+        if (sum < fLo) {
+            hi += 1;
+        }
+        return (hi << 16) | (sum >> 16);
+    }
+
+    /** Return the number >> 30. Asserts that this does not loose any
+        significant high bits.
+    */
+    SkFract getFract() const;
+
+    /** Returns the square-root of the number as a signed 32 bit value. */
+    int32_t getSqrt() const;
+
+    /** Returns the number of leading zeros of the absolute value of this.
+        Will return in the range [0..64]
+    */
+    int getClzAbs() const;
+
+    /** Returns non-zero if the number is zero */
+    SkBool  isZero() const { return (fHi | fLo) == 0; }
+
+    /** Returns non-zero if the number is non-zero */
+    SkBool  nonZero() const { return fHi | fLo; }
+
+    /** Returns non-zero if the number is negative (number < 0) */
+    SkBool  isNeg() const { return (uint32_t)fHi >> 31; }
+
+    /** Returns non-zero if the number is positive (number > 0) */
+    SkBool  isPos() const { return ~(fHi >> 31) & (fHi | fLo); }
+
+    /** Returns -1,0,+1 based on the sign of the number */
+    int     sign() const { return (fHi >> 31) | Sk32ToBool(fHi | fLo); }
+
+    /** Negate the number */
+    void    negate();
+
+    /** If the number < 0, negate the number
+    */
+    void    abs();
+
+    /** Returns the number of bits needed to shift the Sk64 to the right
+        in order to make it fit in a signed 32 bit integer.
+    */
+    int     shiftToMake32() const;
+
+    /** Set the number to zero */
+    void    setZero() { fHi = fLo = 0; }
+
+    /** Set the high and low 32 bit values of the number */
+    void    set(int32_t hi, uint32_t lo) { fHi = hi; fLo = lo; }
+
+    /** Set the number to the specified 32 bit integer */
+    void    set(int32_t a) { fHi = a >> 31; fLo = a; }
+
+    /** Set the number to the product of the two 32 bit integers */
+    void    setMul(int32_t a, int32_t b);
+
+    /** extract 32bits after shifting right by bitCount.
+        Note: itCount must be [0..63].
+        Asserts that no significant high bits were lost.
+    */
+    int32_t getShiftRight(unsigned bitCount) const;
+
+    /** Shift the number left by the specified number of bits.
+        @param bits How far to shift left, must be [0..63]
+    */
+    void    shiftLeft(unsigned bits);
+
+    /** Shift the number right by the specified number of bits.
+        @param bits How far to shift right, must be [0..63]. This
+        performs an arithmetic right-shift (sign extending).
+    */
+    void    shiftRight(unsigned bits);
+
+    /** Shift the number right by the specified number of bits, but
+        round the result.
+        @param bits How far to shift right, must be [0..63]. This
+        performs an arithmetic right-shift (sign extending).
+    */
+    void    roundRight(unsigned bits);
+
+    /** Add the specified 32 bit integer to the number */
+    void add(int32_t lo) {
+        int32_t  hi = lo >> 31; // 0 or -1
+        uint32_t sum = fLo + (uint32_t)lo;
+
+        fHi = fHi + hi + (sum < fLo);
+        fLo = sum;
+    }
+    
+    /** Add the specified Sk64 to the number */
+    void add(int32_t hi, uint32_t lo) {
+        uint32_t sum = fLo + lo;
+
+        fHi = fHi + hi + (sum < fLo);
+        fLo = sum;
+    }
+    
+    /** Add the specified Sk64 to the number */
+    void    add(const Sk64& other) { this->add(other.fHi, other.fLo); }
+    
+    /** Subtract the specified Sk64 from the number. (*this) = (*this) - num
+    */
+    void    sub(const Sk64& num);
+    
+    /** Subtract the number from the specified Sk64. (*this) = num - (*this)
+    */
+    void    rsub(const Sk64& num);
+    
+    /** Multiply the number by the specified 32 bit integer
+    */
+    void    mul(int32_t);
+
+    enum DivOptions {
+        kTrunc_DivOption,   //!< truncate the result when calling div()
+        kRound_DivOption    //!< round the result when calling div()
+    };
+    
+    /** Divide the number by the specified 32 bit integer, using the specified
+        divide option (either truncate or round).
+    */
+    void    div(int32_t, DivOptions);
+
+    /** return (this + other >> 16) as a 32bit result */
+    SkFixed addGetFixed(const Sk64& other) const {
+        return this->addGetFixed(other.fHi, other.fLo);
+    }
+
+    /** return (this + Sk64(hi, lo) >> 16) as a 32bit result */
+    SkFixed addGetFixed(int32_t hi, uint32_t lo) const {
+#ifdef SK_DEBUG
+        Sk64    tmp(*this);
+        tmp.add(hi, lo);
+#endif
+
+        uint32_t sum = fLo + lo;
+        hi += fHi + (sum < fLo);
+        lo = sum;
+
+        sum = lo + (1 << 15);
+        if (sum < lo)
+            hi += 1;
+
+        hi = (hi << 16) | (sum >> 16);
+        SkASSERT(hi == tmp.getFixed());
+        return hi;
+    }
+
+    /** Return the result of dividing the number by denom, treating the answer
+        as a SkFixed. (*this) << 16 / denom. It is an error for denom to be 0.
+    */
+    SkFixed getFixedDiv(const Sk64& denom) const;
+
+    friend bool operator==(const Sk64& a, const Sk64& b) {
+        return a.fHi == b.fHi && a.fLo == b.fLo;
+    }
+
+    friend bool operator!=(const Sk64& a, const Sk64& b) {
+        return a.fHi != b.fHi || a.fLo != b.fLo;
+    }
+    
+    friend bool operator<(const Sk64& a, const Sk64& b) {
+        return a.fHi < b.fHi || (a.fHi == b.fHi && a.fLo < b.fLo);
+    }
+    
+    friend bool operator<=(const Sk64& a, const Sk64& b) {
+        return a.fHi < b.fHi || (a.fHi == b.fHi && a.fLo <= b.fLo);
+    }
+    
+    friend bool operator>(const Sk64& a, const Sk64& b) {
+        return a.fHi > b.fHi || (a.fHi == b.fHi && a.fLo > b.fLo);
+    }
+    
+    friend bool operator>=(const Sk64& a, const Sk64& b) {
+        return a.fHi > b.fHi || (a.fHi == b.fHi && a.fLo >= b.fLo);
+    }
+
+#ifdef SkLONGLONG
+    SkLONGLONG getLongLong() const;
+#endif
+
+#ifdef SK_DEBUG
+  /** @cond UNIT_TEST */
+    static void UnitTest();
+  /** @endcond */
+#endif
+};
+
+#endif
+
diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h
new file mode 100644
index 0000000..02c8cd9
--- /dev/null
+++ b/include/core/SkBitmap.h
@@ -0,0 +1,684 @@
+/*
+ * 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 SkBitmap_DEFINED
+#define SkBitmap_DEFINED
+
+#include "Sk64.h"
+#include "SkColor.h"
+#include "SkPoint.h"
+#include "SkRefCnt.h"
+
+#if defined(SK_BUILD_FOR_MAC)
+#include <carbon/carbon.h>
+#endif
+
+struct SkIRect;
+class SkColorTable;
+class SkPaint;
+class SkPixelRef;
+class SkRegion;
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+
+/** \class SkBitmap
+
+    The SkBitmap class specifies a raster bitmap. A bitmap has an integer width
+    and height, and a format (config), and a pointer to the actual pixels.
+    Bitmaps can be drawn into a SkCanvas, but they are also used to specify the target
+    of a SkCanvas' drawing operations.
+*/
+class SkBitmap {
+public:
+    class Allocator;
+
+    enum Config {
+        kNo_Config,         //!< bitmap has not been configured
+        kA1_Config,         //!< 1-bit per pixel, (0 is transparent, 1 is opaque)
+        kA8_Config,         //!< 8-bits per pixel, with only alpha specified (0 is transparent, 0xFF is opaque)
+        kIndex8_Config,     //!< 8-bits per pixel, using SkColorTable to specify the colors
+        kRGB_565_Config,    //!< 16-bits per pixel, (see SkColorPriv.h for packing)
+        kARGB_4444_Config,  //!< 16-bits per pixel, (see SkColorPriv.h for packing)
+        kARGB_8888_Config,  //!< 32-bits per pixel, (see SkColorPriv.h for packing)
+        kRLE_Index8_Config,
+
+        kConfigCount
+    };
+
+    /** Default construct creates a bitmap with zero width and height, and no pixels.
+        Its config is set to kNo_Config.
+    */
+    SkBitmap();
+    /** Constructor initializes the new bitmap by copying the src bitmap. All fields are copied,
+        but ownership of the pixels remains with the src bitmap.
+    */
+    SkBitmap(const SkBitmap& src);
+    /** Decrements our (shared) pixel ownership if needed.
+    */
+    ~SkBitmap();
+
+    /** Copies the src bitmap into this bitmap. Ownership of the src bitmap's pixels remains
+        with the src bitmap.
+    */
+    SkBitmap& operator=(const SkBitmap& src);
+    /** Swap the fields of the two bitmaps. This routine is guaranteed to never fail or throw.
+    */
+    //  This method is not exported to java.
+    void swap(SkBitmap& other);
+    
+    /** Return true iff the bitmap has empty dimensions.
+    */
+    bool empty() const { return 0 == fWidth || 0 == fHeight; }
+    
+    /** Return true iff the bitmap has no pixels nor a pixelref. Note: this can
+        return true even if the dimensions of the bitmap are > 0 (see empty()).
+    */
+    bool isNull() const { return NULL == fPixels && NULL == fPixelRef; }
+
+    /** Return the config for the bitmap.
+    */
+    Config  config() const { return (Config)fConfig; }
+    /** DEPRECATED, use config()
+    */
+    Config  getConfig() const { return this->config(); }
+    /** Return the bitmap's width, in pixels.
+    */
+    int width() const { return fWidth; }
+    /** Return the bitmap's height, in pixels.
+    */
+    int height() const { return fHeight; }
+    /** Return the number of bytes between subsequent rows of the bitmap.
+    */
+    int rowBytes() const { return fRowBytes; }
+    
+    /** Return the shift amount per pixel (i.e. 0 for 1-byte per pixel, 1 for
+        2-bytes per pixel configs, 2 for 4-bytes per pixel configs). Return 0
+        for configs that are not at least 1-byte per pixel (e.g. kA1_Config
+        or kNo_Config)
+    */
+    int shiftPerPixel() const { return fBytesPerPixel >> 1; }
+
+    /** Return the number of bytes per pixel based on the config. If the config
+        does not have at least 1 byte per (e.g. kA1_Config) then 0 is returned.
+    */
+    int bytesPerPixel() const { return fBytesPerPixel; }
+
+    /** Return the rowbytes expressed as a number of pixels (like width and
+        height). Note, for 1-byte per pixel configs like kA8_Config, this will
+        return the same as rowBytes(). Is undefined for configs that are less
+        than 1-byte per pixel (e.g. kA1_Config)
+    */
+    int rowBytesAsPixels() const { return fRowBytes >> (fBytesPerPixel >> 1); }
+
+    /** Return the address of the pixels for this SkBitmap.
+    */
+    void* getPixels() const { return fPixels; }
+
+    /** Return the byte size of the pixels, based on the height and rowBytes.
+        Note this truncates the result to 32bits. Call getSize64() to detect
+        if the real size exceeds 32bits.
+    */
+    size_t getSize() const { return fHeight * fRowBytes; }
+    
+    /** Return the byte size of the pixels, based on the height and rowBytes.
+        This routine is slightly slower than getSize(), but does not truncate
+        the answer to 32bits.
+    */
+    Sk64 getSize64() const {
+        Sk64 size;
+        size.setMul(fHeight, fRowBytes);
+        return size;
+    }
+    
+    /** Returns true if the bitmap is opaque (has no translucent/transparent pixels).
+    */
+    bool isOpaque() const;
+    /** Specify if this bitmap's pixels are all opaque or not. Is only meaningful for configs
+        that support per-pixel alpha (RGB32, A1, A8).
+    */
+    void setIsOpaque(bool);
+
+    /** Reset the bitmap to its initial state (see default constructor). If we are a (shared)
+        owner of the pixels, that ownership is decremented.
+    */
+    void reset();
+
+    /** Given a config and a width, this computes the optimal rowBytes value. This is called automatically
+        if you pass 0 for rowBytes to setConfig().
+    */
+    static int ComputeRowBytes(Config c, int width);
+
+    /** Return the bytes-per-pixel for the specified config. If the config is
+        not at least 1-byte per pixel, return 0, including for kNo_Config.
+    */
+    static int ComputeBytesPerPixel(Config c);
+
+    /** Return the shift-per-pixel for the specified config. If the config is
+     not at least 1-byte per pixel, return 0, including for kNo_Config.
+     */
+    static int ComputeShiftPerPixel(Config c) {
+        return ComputeBytesPerPixel(c) >> 1;
+    }
+    
+    static Sk64 ComputeSize64(Config, int width, int height);
+    static size_t ComputeSize(Config, int width, int height);
+
+    /** Set the bitmap's config and dimensions. If rowBytes is 0, then
+        ComputeRowBytes() is called to compute the optimal value. This resets
+        any pixel/colortable ownership, just like reset().
+    */
+    void setConfig(Config, int width, int height, int rowBytes = 0);
+    /** Use this to assign a new pixel address for an existing bitmap. This
+        will automatically release any pixelref previously installed. Only call
+        this if you are handling ownership/lifetime of the pixel memory.
+     
+        If the bitmap retains a reference to the colortable (assuming it is
+        not null) it will take care of incrementing the reference count.
+
+        @param pixels   Address for the pixels, managed by the caller.
+        @param ctable   ColorTable (or null) that matches the specified pixels
+    */
+    void setPixels(void* p, SkColorTable* ctable = NULL);
+
+    /** Use the standard HeapAllocator to create the pixelref that manages the
+        pixel memory. It will be sized based on the current width/height/config.
+        If this is called multiple times, a new pixelref object will be created
+        each time.
+        
+        If the bitmap retains a reference to the colortable (assuming it is
+        not null) it will take care of incrementing the reference count.
+
+        @param ctable   ColorTable (or null) to use with the pixels that will
+                        be allocated. Only used if config == Index8_Config
+        @return true if the allocation succeeds. If not the pixelref field of
+                     the bitmap will be unchanged.
+    */
+    bool allocPixels(SkColorTable* ctable = NULL) {
+        return this->allocPixels(NULL, ctable);
+    }
+    
+    /** Use the specified Allocator to create the pixelref that manages the
+        pixel memory. It will be sized based on the current width/height/config.
+        If this is called multiple times, a new pixelref object will be created
+        each time.
+        
+        If the bitmap retains a reference to the colortable (assuming it is
+        not null) it will take care of incrementing the reference count.
+     
+        @param allocator The Allocator to use to create a pixelref that can
+                         manage the pixel memory for the current
+                         width/height/config. If allocator is NULL, the standard
+                         HeapAllocator will be used.
+        @param ctable   ColorTable (or null) to use with the pixels that will
+                        be allocated. Only used if config == Index8_Config.
+                        If it is non-null and the config is not Index8, it will
+                        be ignored.
+        @return true if the allocation succeeds. If not the pixelref field of
+                     the bitmap will be unchanged.
+    */
+    bool allocPixels(Allocator* allocator, SkColorTable* ctable);
+    
+    /** Return the current pixelref object, of any
+    */
+    SkPixelRef* pixelRef() const { return fPixelRef; }
+    /** Return the offset into the pixelref, if any. Will return 0 if there is
+        no pixelref installed.
+    */
+    size_t pixelRefOffset() const { return fPixelRefOffset; }
+    /** Assign a pixelref and optional offset. Pixelrefs are reference counted,
+        so the existing one (if any) will be unref'd and the new one will be
+        ref'd.
+    */
+    SkPixelRef* setPixelRef(SkPixelRef* pr, size_t offset = 0);
+    
+    /** Call this to ensure that the bitmap points to the current pixel address
+        in the pixelref. Balance it with a call to unlockPixels(). These calls
+        are harmless if there is no pixelref.
+    */
+    void lockPixels() const;
+    /** When you are finished access the pixel memory, call this to balance a
+        previous call to lockPixels(). This allows pixelrefs that implement
+        cached/deferred image decoding to know when there are active clients of
+        a given image.
+    */
+    void unlockPixels() const;
+    
+    /** Call this to be sure that the bitmap is valid enough to be drawn (i.e.
+        it has non-null pixels, and if required by its config, it has a
+        non-null colortable. Returns true if all of the above are met.
+    */
+    bool readyToDraw() const {
+        return this->getPixels() != NULL &&
+               ((this->config() != kIndex8_Config && this->config() != kRLE_Index8_Config) ||
+                       fColorTable != NULL);
+    }
+
+    /** Return the bitmap's colortable (if any). Does not affect the colortable's
+        reference count.
+    */
+    SkColorTable* getColorTable() const { return fColorTable; }
+
+    /** Returns a non-zero, unique value corresponding to the pixels in our
+        pixelref, or 0 if we do not have a pixelref. Each time the pixels are
+        changed (and notifyPixelsChanged is called), a different generation ID
+        will be returned.
+    */
+    uint32_t getGenerationID() const;
+    
+    /** Call this if you have changed the contents of the pixels. This will in-
+        turn cause a different generation ID value to be returned from
+        getGenerationID().
+    */
+    void notifyPixelsChanged() const;
+
+    /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format
+        for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is ignored.
+        If the config is kA8_Config, then the r,g,b parameters are ignored.
+    */
+    void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const;
+    /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format
+        for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is presumed
+        to be 0xFF. If the config is kA8_Config, then the r,g,b parameters are ignored and the
+        pixels are all set to 0xFF.
+    */
+    void eraseRGB(U8CPU r, U8CPU g, U8CPU b) const {
+        this->eraseARGB(0xFF, r, g, b);
+    }
+    /** Initialize the bitmap's pixels with the specified color, automatically converting into the correct format
+        for the bitmap's config. If the config is kRGB_565_Config, then the color's alpha value is presumed
+        to be 0xFF. If the config is kA8_Config, then only the color's alpha value is used.
+    */
+    void eraseColor(SkColor c) const {
+        this->eraseARGB(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c),
+                        SkColorGetB(c));
+    }
+    
+    /** Scroll (a subset of) the contents of this bitmap by dx/dy. If there are
+        no pixels allocated (i.e. getPixels() returns null) the method will
+        still update the inval region (if present).
+
+        @param subset The subset of the bitmap to scroll/move. To scroll the
+                      entire contents, specify [0, 0, width, height] or just
+                      pass null.
+        @param dx The amount to scroll in X
+        @param dy The amount to scroll in Y
+        @param inval Optional (may be null). Returns the area of the bitmap that
+                     was scrolled away. E.g. if dx = dy = 0, then inval would
+                     be set to empty. If dx >= width or dy >= height, then
+                     inval would be set to the entire bounds of the bitmap.
+        @return true if the scroll was doable. Will return false if the bitmap
+                     uses an unsupported config for scrolling (only kA8,
+                     kIndex8, kRGB_565, kARGB_4444, kARGB_8888 are supported).
+                     If no pixels are present (i.e. getPixels() returns false)
+                     inval will still be updated, and true will be returned.
+    */
+    bool scrollRect(const SkIRect* subset, int dx, int dy,
+                    SkRegion* inval = NULL) const;
+
+    /** Returns the address of the specified pixel. This performs a runtime
+        check to know the size of the pixels, and will return the same answer
+        as the corresponding size-specific method (e.g. getAddr16). Since the
+        check happens at runtime, it is much slower than using a size-specific
+        version. Unlike the size-specific methods, this routine also checks if
+        getPixels() returns null, and returns that. The size-specific routines
+        perform a debugging assert that getPixels() is not null, but they do
+        not do any runtime checks.
+    */
+    void* getAddr(int x, int y) const;
+
+    /** Returns the address of the pixel specified by x,y for 32bit pixels.
+    */
+    inline uint32_t* getAddr32(int x, int y) const;
+    /** Returns the address of the pixel specified by x,y for 16bit pixels.
+    */
+    inline uint16_t* getAddr16(int x, int y) const;
+    /** Returns the address of the pixel specified by x,y for 8bit pixels.
+    */
+    inline uint8_t* getAddr8(int x, int y) const;
+    /** Returns the address of the byte containing the pixel specified by x,y
+        for 1bit pixels.
+        */
+    inline uint8_t* getAddr1(int x, int y) const;
+
+    /** Returns the color corresponding to the pixel specified by x,y for
+        colortable based bitmaps.
+    */
+    inline SkPMColor getIndex8Color(int x, int y) const;
+
+    //  OS-specific helpers
+#ifndef SK_USE_WXWIDGETS
+#ifdef SK_BUILD_FOR_WIN
+    /** On Windows and PocketPC builds, this will draw the SkBitmap onto the
+        specified HDC
+    */
+    void drawToHDC(HDC, int left, int top) const;
+#elif defined(SK_BUILD_FOR_MAC)
+    /** On Mac OS X and Carbon builds, this will draw the SkBitmap onto the
+        specified WindowRef
+    */
+    void drawToPort(WindowRef, CGContextRef) const;
+#endif
+#endif
+
+    /** Set dst to be a setset of this bitmap. If possible, it will share the
+        pixel memory, and just point into a subset of it. However, if the config
+        does not support this, a local copy will be made and associated with
+        the dst bitmap. If the subset rectangle, intersected with the bitmap's
+        dimensions is empty, or if there is an unsupported config, false will be
+        returned and dst will be untouched.
+        @param dst  The bitmap that will be set to a subset of this bitmap
+        @param subset The rectangle of pixels in this bitmap that dst will
+                      reference.
+        @return true if the subset copy was successfully made.
+    */
+    bool extractSubset(SkBitmap* dst, const SkIRect& subset) const;
+
+    /** Tries to make a new bitmap based on the dimensions of this bitmap,
+        setting the new bitmap's config to the one specified, and then copying
+        this bitmap's pixels into the new bitmap. If the conversion is not
+        supported, or the allocator fails, then this method returns false and
+        dst is left unchanged.
+        @param dst  The bitmap to be sized and allocated
+        @param c The desired config for dst
+        @param allocator Allocator used to allocate the pixelref for the dst
+                         bitmap. If this is null, the standard HeapAllocator
+                         will be used.
+        @return true if the copy could be made.
+    */
+    bool copyTo(SkBitmap* dst, Config c, Allocator* allocator = NULL) const;
+
+    bool hasMipMap() const;
+    void buildMipMap(bool forceRebuild = false);
+    void freeMipMap();
+
+    /** Given scale factors sx, sy, determine the miplevel available in the
+        bitmap, and return it (this is the amount to shift matrix iterators
+        by). If dst is not null, it is set to the correct level.
+    */
+    int extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy);
+
+    void extractAlpha(SkBitmap* dst) const {
+        this->extractAlpha(dst, NULL, NULL);
+    }
+
+    void extractAlpha(SkBitmap* dst, const SkPaint* paint,
+                      SkIPoint* offset) const;
+    
+    void flatten(SkFlattenableWriteBuffer&) const;
+    void unflatten(SkFlattenableReadBuffer&);
+
+    SkDEBUGCODE(void validate() const;)
+
+    class Allocator : public SkRefCnt {
+    public:
+        /** Allocate the pixel memory for the bitmap, given its dimensions and
+            config. Return true on success, where success means either setPixels
+            or setPixelRef was called. The pixels need not be locked when this
+            returns. If the config requires a colortable, it also must be
+            installed via setColorTable. If false is returned, the bitmap and
+            colortable should be left unchanged.
+        */
+        virtual bool allocPixelRef(SkBitmap*, SkColorTable*) = 0;
+    };
+
+    /** Subclass of Allocator that returns a pixelref that allocates its pixel
+        memory from the heap. This is the default Allocator invoked by
+        allocPixels().
+    */
+    class HeapAllocator : public Allocator {
+    public:
+        virtual bool allocPixelRef(SkBitmap*, SkColorTable*);
+    };
+
+    class RLEPixels {
+    public:
+        RLEPixels(int width, int height);
+        virtual ~RLEPixels();
+        
+        uint8_t* packedAtY(int y) const {
+            SkASSERT((unsigned)y < (unsigned)fHeight);
+            return fYPtrs[y];
+        }
+        
+        // called by subclasses during creation
+        void setPackedAtY(int y, uint8_t* addr) {
+            SkASSERT((unsigned)y < (unsigned)fHeight);
+            fYPtrs[y] = addr;
+        }
+            
+    private:
+        uint8_t** fYPtrs;
+        int       fHeight;
+    };
+        
+private:
+#ifdef SK_SUPPORT_MIPMAP
+    struct MipMap;
+    mutable MipMap* fMipMap;
+#endif
+
+    mutable SkPixelRef* fPixelRef;
+    mutable size_t      fPixelRefOffset;
+    mutable int         fPixelLockCount;
+    // either user-specified (in which case it is not treated as mutable)
+    // or a cache of the returned value from fPixelRef->lockPixels()
+    mutable void*       fPixels;
+    mutable SkColorTable* fColorTable;    // only meaningful for kIndex8
+
+    enum Flags {
+        kImageIsOpaque_Flag  = 0x01
+    };
+
+    uint32_t    fRowBytes;
+    uint16_t    fWidth, fHeight;
+    uint8_t     fConfig;
+    uint8_t     fFlags;
+    uint8_t     fBytesPerPixel; // based on config
+
+    /*  Unreference any pixelrefs or colortables
+    */
+    void freePixels();
+    void updatePixelsFromRef() const;
+    
+    static SkFixed ComputeMipLevel(SkFixed sx, SkFixed dy);
+};
+
+/** \class SkColorTable
+
+    SkColorTable holds an array SkPMColors (premultiplied 32-bit colors) used by
+    8-bit bitmaps, where the bitmap bytes are interpreted as indices into the colortable.
+*/
+class SkColorTable : public SkRefCnt {
+public:
+    /** Constructs an empty color table (zero colors).
+    */
+    explicit SkColorTable(int count);
+    explicit SkColorTable(SkFlattenableReadBuffer&);
+    SkColorTable(const SkPMColor colors[], int count);
+    virtual ~SkColorTable();
+
+    enum Flags {
+        kColorsAreOpaque_Flag   = 0x01  //!< if set, all of the colors in the table are opaque (alpha==0xFF)
+    };
+    /** Returns the flag bits for the color table. These can be changed with setFlags().
+    */
+    unsigned getFlags() const { return fFlags; }
+    /** Set the flags for the color table. See the Flags enum for possible values.
+    */
+    void    setFlags(unsigned flags);
+
+    /** Returns the number of colors in the table.
+    */
+    int count() const { return fCount; }
+
+    /** Returns the specified color from the table. In the debug build, this asserts that
+        the index is in range (0 <= index < count).
+    */
+    SkPMColor operator[](int index) const {
+        SkASSERT(fColors != NULL && (unsigned)index < fCount);
+        return fColors[index];
+    }
+
+    /** Specify the number of colors in the color table. This does not initialize the colors
+        to any value, just allocates memory for them. To initialize the values, either call
+        setColors(array, count), or follow setCount(count) with a call to
+        lockColors()/{set the values}/unlockColors(true).
+    */
+//    void    setColors(int count) { this->setColors(NULL, count); }
+//    void    setColors(const SkPMColor[], int count);
+
+    /** Return the array of colors for reading and/or writing. This must be
+        balanced by a call to unlockColors(changed?), telling the colortable if
+        the colors were changed during the lock.
+    */
+    SkPMColor* lockColors() {
+        SkDEBUGCODE(fColorLockCount += 1;)
+        return fColors;
+    }
+    /** Balancing call to lockColors(). If the colors have been changed, pass true.
+    */
+    void unlockColors(bool changed);
+
+    /** Similar to lockColors(), lock16BitCache() returns the array of
+        RGB16 colors that mirror the 32bit colors. However, this function
+        will return null if kColorsAreOpaque_Flag is not set.
+        Also, unlike lockColors(), the returned array here cannot be modified.
+    */
+    const uint16_t* lock16BitCache();
+    /** Balancing call to lock16BitCache().
+    */
+    void unlock16BitCache() {
+        SkASSERT(f16BitCacheLockCount > 0);
+        SkDEBUGCODE(f16BitCacheLockCount -= 1);
+    }
+
+    void flatten(SkFlattenableWriteBuffer&) const;
+
+private:
+    SkPMColor*  fColors;
+    uint16_t*   f16BitCache;
+    uint16_t    fCount;
+    uint8_t     fFlags;
+    SkDEBUGCODE(int fColorLockCount;)
+    SkDEBUGCODE(int f16BitCacheLockCount;)
+
+    void inval16BitCache();
+};
+
+class SkAutoLockPixels {
+public:
+    SkAutoLockPixels(const SkBitmap& bitmap) : fBitmap(bitmap) {
+        bitmap.lockPixels();
+    }
+    ~SkAutoLockPixels() {
+        fBitmap.unlockPixels();
+    }
+
+private:
+    const SkBitmap& fBitmap;
+};
+
+/** Helper class that performs the lock/unlockColors calls on a colortable.
+    The destructor will call unlockColors(false) if it has a bitmap's colortable
+*/
+class SkAutoLockColors : public SkNoncopyable {
+public:
+    /** Initialize with no bitmap. Call lockColors(bitmap) to lock bitmap's
+        colortable
+     */
+    SkAutoLockColors() : fCTable(NULL), fColors(NULL) {}
+    /** Initialize with bitmap, locking its colortable if present
+     */
+    explicit SkAutoLockColors(const SkBitmap& bm) {
+        fCTable = bm.getColorTable();
+        fColors = fCTable ? fCTable->lockColors() : NULL;
+    }
+    /** Initialize with a colortable (may be null)
+     */
+    explicit SkAutoLockColors(SkColorTable* ctable) {
+        fCTable = ctable;
+        fColors = ctable ? ctable->lockColors() : NULL;
+    }
+    ~SkAutoLockColors() {
+        if (fCTable) {
+            fCTable->unlockColors(false);
+        }
+    }
+    
+    /** Return the currently locked colors, or NULL if no bitmap's colortable
+        is currently locked.
+    */
+    const SkPMColor* colors() const { return fColors; }
+    
+    /** If a previous bitmap has been locked by this object, unlock its colors
+        first. If the specified bitmap has a colortable, lock its colors and
+        return them.
+    */
+    const SkPMColor* lockColors(const SkBitmap& bm) {
+        if (fCTable) {
+            fCTable->unlockColors(false);
+        }
+        fCTable = bm.getColorTable();
+        fColors = fCTable ? fCTable->lockColors() : NULL;
+        return fColors;
+    }
+
+private:
+    SkColorTable*    fCTable;
+    const SkPMColor* fColors;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+inline uint32_t* SkBitmap::getAddr32(int x, int y) const {
+    SkASSERT(fPixels);
+    SkASSERT(fConfig == kARGB_8888_Config);
+    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    return (uint32_t*)((char*)fPixels + y * fRowBytes + (x << 2));
+}
+
+inline uint16_t* SkBitmap::getAddr16(int x, int y) const {
+    SkASSERT(fPixels);
+    SkASSERT(fConfig == kRGB_565_Config || fConfig == kARGB_4444_Config);
+    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    return (uint16_t*)((char*)fPixels + y * fRowBytes + (x << 1));
+}
+
+inline uint8_t* SkBitmap::getAddr8(int x, int y) const {
+    SkASSERT(fPixels);
+    SkASSERT(fConfig == kA8_Config || fConfig == kIndex8_Config);
+    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    return (uint8_t*)fPixels + y * fRowBytes + x;
+}
+
+inline SkPMColor SkBitmap::getIndex8Color(int x, int y) const {
+    SkASSERT(fPixels);
+    SkASSERT(fConfig == kIndex8_Config);
+    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    SkASSERT(fColorTable);
+    return (*fColorTable)[*((const uint8_t*)fPixels + y * fRowBytes + x)];
+}
+
+// returns the address of the byte that contains the x coordinate
+inline uint8_t* SkBitmap::getAddr1(int x, int y) const {
+    SkASSERT(fPixels);
+    SkASSERT(fConfig == kA1_Config);
+    SkASSERT((unsigned)x < fWidth && (unsigned)y < fHeight);
+    return (uint8_t*)fPixels + y * fRowBytes + (x >> 3);
+}
+
+#endif
+
diff --git a/include/core/SkBounder.h b/include/core/SkBounder.h
new file mode 100644
index 0000000..f20961d
--- /dev/null
+++ b/include/core/SkBounder.h
@@ -0,0 +1,72 @@
+/*
+ * 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 SkBounder_DEFINED
+#define SkBounder_DEFINED
+
+#include "SkTypes.h"
+#include "SkRefCnt.h"
+
+struct SkIRect;
+struct SkPoint;
+struct SkRect;
+class SkPaint;
+class SkPath;
+class SkRegion;
+
+/** \class SkBounder
+
+    Base class for intercepting the device bounds of shapes before they are drawn.
+    Install a subclass of this in your canvas.
+*/
+class SkBounder : public SkRefCnt {
+public:
+    /* Call to perform a clip test before calling onIRect. 
+       Returns the result from onIRect.
+    */
+    bool doIRect(const SkIRect&);
+
+protected:
+    /** Override in your subclass. This is called with the device bounds of an
+        object (text, geometry, image) just before it is drawn. If your method
+        returns false, the drawing for that shape is aborted. If your method
+        returns true, drawing continues. The bounds your method receives have already
+        been transformed in to device coordinates, and clipped to the current clip.
+    */
+    virtual bool onIRect(const SkIRect&) = 0;
+
+    /** Called after each shape has been drawn. The default implementation does
+        nothing, but your override could use this notification to signal itself
+        that the offscreen being rendered into needs to be updated to the screen.
+    */
+    virtual void commit();
+
+private:
+    bool doHairline(const SkPoint&, const SkPoint&, const SkPaint&);
+    bool doRect(const SkRect&, const SkPaint&);
+    bool doPath(const SkPath&, const SkPaint&, bool doFill);
+    void setClip(const SkRegion* clip) { fClip = clip; }
+
+    const SkRegion* fClip;
+    friend class SkAutoBounderCommit;
+    friend class SkDraw;
+    friend class SkDrawIter;
+    friend struct Draw1Glyph;
+    friend class SkMaskFilter;
+};
+
+#endif
+
diff --git a/include/core/SkBuffer.h b/include/core/SkBuffer.h
new file mode 100644
index 0000000..bc11a1e
--- /dev/null
+++ b/include/core/SkBuffer.h
@@ -0,0 +1,141 @@
+/*
+ * 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 SkBuffer_DEFINED
+#define SkBuffer_DEFINED
+
+#include "SkScalar.h"
+
+/** \class SkRBuffer
+
+    Light weight class for reading data from a memory block.
+    The RBuffer is given the buffer to read from, with either a specified size
+    or no size (in which case no range checking is performed). It is iillegal
+    to attempt to read a value from an empty RBuffer (data == null). 
+*/
+class SkRBuffer : SkNoncopyable {
+public:
+    SkRBuffer() : fData(0), fPos(0), fStop(0) {}
+    /** Initialize RBuffer with a data pointer, but no specified length.
+        This signals the RBuffer to not perform range checks during reading.
+    */
+    SkRBuffer(const void* data)
+    {
+        fData = (const char*)data;
+        fPos = (const char*)data;
+        fStop = 0;  // no bounds checking
+    }
+    /** Initialize RBuffer with a data point and length.
+    */
+    SkRBuffer(const void* data, size_t size)
+    {
+        SkASSERT(data != 0 || size == 0);
+        fData = (const char*)data;
+        fPos = (const char*)data;
+        fStop = (const char*)data + size;
+    }
+    
+    /** Return the number of bytes that have been read from the beginning
+        of the data pointer.
+    */
+    size_t  pos() const { return fPos - fData; }
+    /** Return the total size of the data pointer. Only defined if the length was
+        specified in the constructor or in a call to reset().
+    */
+    size_t  size() const { return fStop - fData; }
+    /** Return true if the buffer has read to the end of the data pointer.
+        Only defined if the length was specified in the constructor or in a call
+        to reset(). Always returns true if the length was not specified.
+    */
+    bool    eof() const { return fPos >= fStop; }
+
+    /** Read the specified number of bytes from the data pointer. If buffer is not
+        null, copy those bytes into buffer.
+    */
+    void    read(void* buffer, size_t size) { if (size) this->readNoSizeCheck(buffer, size); }
+    const void* skip(size_t size); // return start of skipped data
+    size_t  skipToAlign4();
+
+    void*       readPtr() { void* ptr; read(&ptr, sizeof(ptr)); return ptr; }
+    SkScalar    readScalar() { SkScalar x; read(&x, 4); return x; }
+    uint32_t    readU32() { uint32_t x; read(&x, 4); return x; }
+    int32_t     readS32() { int32_t x; read(&x, 4); return x; }
+    uint16_t    readU16() { uint16_t x; read(&x, 2); return x; }
+    int16_t     readS16() { int16_t x; read(&x, 2); return x; }
+    uint8_t     readU8() { uint8_t x; read(&x, 1); return x; }
+    bool        readBool() { return this->readU8() != 0; }
+
+protected:
+    void    readNoSizeCheck(void* buffer, size_t size);
+
+    const char* fData;
+    const char* fPos;
+    const char* fStop;
+};
+
+/** \class SkWBuffer
+
+    Light weight class for writing data to a memory block.
+    The WBuffer is given the buffer to write into, with either a specified size
+    or no size, in which case no range checking is performed. An empty WBuffer
+    is legal, in which case no data is ever written, but the relative pos()
+    is updated.
+*/
+class SkWBuffer : SkNoncopyable {
+public:
+    SkWBuffer() : fData(0), fPos(0), fStop(0) {}
+    SkWBuffer(void* data) { reset(data); }
+    SkWBuffer(void* data, size_t size) { reset(data, size); }
+
+    void reset(void* data)
+    {
+        fData = (char*)data;
+        fPos = (char*)data;
+        fStop = 0;  // no bounds checking
+    }
+    void reset(void* data, size_t size)
+    {
+        SkASSERT(data != 0 || size == 0);
+        fData = (char*)data;
+        fPos = (char*)data;
+        fStop = (char*)data + size;
+    }
+    
+    void*   data() const { return fData; }
+    size_t  pos() const { return fPos - fData; }
+    size_t  size() const { return fStop - fData; }
+    bool    eof() const { return fPos >= fStop; }
+    void*   skip(size_t size); // return start of skipped data
+    void    write(const void* buffer, size_t size) { if (size) this->writeNoSizeCheck(buffer, size); }
+    size_t  padToAlign4();
+
+    void    writePtr(const void* x) { this->writeNoSizeCheck(&x, sizeof(x)); }
+    void    writeScalar(SkScalar x) { this->writeNoSizeCheck(&x, 4); }
+    void    write32(int32_t x) { this->writeNoSizeCheck(&x, 4); }
+    void    write16(int16_t x) { this->writeNoSizeCheck(&x, 2); }
+    void    write8(int8_t x) { this->writeNoSizeCheck(&x, 1); }
+    void    writeBool(bool x) { this->write8(x); }
+
+protected:
+    void    writeNoSizeCheck(const void* buffer, size_t size);
+
+    char* fData;
+    char* fPos;
+    char* fStop;
+};
+
+#endif
+
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
new file mode 100644
index 0000000..a19a5ae
--- /dev/null
+++ b/include/core/SkCanvas.h
@@ -0,0 +1,794 @@
+/*
+ * 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 SkCanvas_DEFINED
+#define SkCanvas_DEFINED
+
+#include "SkTypes.h"
+#include "SkBitmap.h"
+#include "SkDeque.h"
+#include "SkPaint.h"
+#include "SkRefCnt.h"
+#include "SkPorterDuff.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkScalarCompare.h"
+
+class SkBounder;
+class SkDevice;
+class SkDraw;
+class SkDrawFilter;
+class SkPicture;
+
+/** \class SkCanvas
+
+    A Canvas encapsulates all of the state about drawing into a device (bitmap).
+    This includes a reference to the device itself, and a stack of matrix/clip
+    values. For any given draw call (e.g. drawRect), the geometry of the object
+    being drawn is transformed by the concatenation of all the matrices in the
+    stack. The transformed geometry is clipped by the intersection of all of
+    the clips in the stack.
+
+    While the Canvas holds the state of the drawing device, the state (style)
+    of the object being drawn is held by the Paint, which is provided as a
+    parameter to each of the draw() methods. The Paint holds attributes such as
+    color, typeface, textSize, strokeWidth, shader (e.g. gradients, patterns),
+    etc.
+*/
+class SkCanvas : public SkRefCnt {
+public:
+    /** Construct a canvas with the specified bitmap to draw into.
+        @param bitmap   Specifies a bitmap for the canvas to draw into. Its
+                        structure are copied to the canvas.
+    */
+    explicit SkCanvas(const SkBitmap& bitmap);
+    /** Construct a canvas with the specified device to draw into.
+        @param device   Specifies a device for the canvas to draw into. The
+                        device may be null.
+    */
+    explicit SkCanvas(SkDevice* device = NULL);
+    virtual ~SkCanvas();
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** If this subclass of SkCanvas supports GL viewports, return true and set
+        size (if not null) to the size of the viewport. If it is not supported,
+        ignore vp and return false.
+    */
+    virtual bool getViewport(SkIPoint* size) const;
+    
+    /** If this subclass of SkCanvas supports GL viewports, return true and set
+        the viewport to the specified x and y dimensions. If it is not
+        supported, ignore x and y and return false.
+    */
+    virtual bool setViewport(int x, int y);
+
+    /** Return the canvas' device object, which may be null. The device holds
+        the bitmap of the pixels that the canvas draws into. The reference count
+        of the returned device is not changed by this call.
+    */
+    SkDevice* getDevice() const;
+
+    /** Specify a device for this canvas to draw into. If it is not null, its
+        reference count is incremented. If the canvas was already holding a
+        device, its reference count is decremented. The new device is returned.
+    */
+    SkDevice* setDevice(SkDevice* device);
+    
+    /** Specify a bitmap for the canvas to draw into. This is a help method for
+        setDevice(), and it creates a device for the bitmap by calling
+        createDevice(). The structure of the bitmap is copied into the device.
+    */
+    virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap);
+
+    ///////////////////////////////////////////////////////////////////////////
+    
+    enum SaveFlags {
+        /** save the matrix state, restoring it on restore() */
+        kMatrix_SaveFlag            = 0x01,
+        /** save the clip state, restoring it on restore() */
+        kClip_SaveFlag              = 0x02,
+        /** the layer needs to support per-pixel alpha */
+        kHasAlphaLayer_SaveFlag     = 0x04,
+        /** the layer needs to support 8-bits per color component */
+        kFullColorLayer_SaveFlag    = 0x08,
+        /** the layer should clip against the bounds argument */
+        kClipToLayer_SaveFlag       = 0x10,
+
+        // helper masks for common choices
+        kMatrixClip_SaveFlag        = 0x03,
+        kARGB_NoClipLayer_SaveFlag  = 0x0F,
+        kARGB_ClipLayer_SaveFlag    = 0x1F
+    };
+
+    /** This call saves the current matrix and clip information, and pushes a
+        copy onto a private stack. Subsequent calls to translate, scale,
+        rotate, skew, concat or clipRect, clipPath all operate on this copy.
+        When the balancing call to restore() is made, this copy is deleted and
+        the previous matrix/clip state is restored.
+        @return The value to pass to restoreToCount() to balance this save()
+    */
+    virtual int save(SaveFlags flags = kMatrixClip_SaveFlag);
+
+    /** This behaves the same as save(), but in addition it allocates an
+        offscreen bitmap. All drawing calls are directed there, and only when
+        the balancing call to restore() is made is that offscreen transfered to
+        the canvas (or the previous layer). Subsequent calls to translate,
+        scale, rotate, skew, concat or clipRect, clipPath all operate on this
+        copy. When the balancing call to restore() is made, this copy is deleted
+        and the previous matrix/clip state is restored.
+        @param bounds (may be null) the maximum size the offscreen bitmap needs
+                      to be (in local coordinates)
+        @param paint (may be null) This is copied, and is applied to the
+                     offscreen when restore() is called
+        @param flags  LayerFlags
+        @return The value to pass to restoreToCount() to balance this save()
+    */
+    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags flags = kARGB_ClipLayer_SaveFlag);
+
+    /** This behaves the same as save(), but in addition it allocates an
+        offscreen bitmap. All drawing calls are directed there, and only when
+        the balancing call to restore() is made is that offscreen transfered to
+        the canvas (or the previous layer). Subsequent calls to translate,
+        scale, rotate, skew, concat or clipRect, clipPath all operate on this
+        copy. When the balancing call to restore() is made, this copy is deleted
+        and the previous matrix/clip state is restored.
+        @param bounds (may be null) the maximum size the offscreen bitmap needs
+                      to be (in local coordinates)
+        @param alpha  This is applied to the offscreen when restore() is called.
+        @param flags  LayerFlags
+        @return The value to pass to restoreToCount() to balance this save()
+    */
+    int saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
+                       SaveFlags flags = kARGB_ClipLayer_SaveFlag);
+
+    /** This call balances a previous call to save(), and is used to remove all
+        modifications to the matrix/clip state since the last save call. It is
+        an error to call restore() more times than save() was called.
+    */
+    virtual void restore();
+
+    /** Returns the number of matrix/clip states on the SkCanvas' private stack.
+        This will equal # save() calls - # restore() calls.
+    */
+    int getSaveCount() const;
+
+    /** Efficient way to pop any calls to save() that happened after the save
+        count reached saveCount. It is an error for saveCount to be less than
+        getSaveCount()
+        @param saveCount    The number of save() levels to restore from
+    */
+    void restoreToCount(int saveCount);
+
+    /** Preconcat the current matrix with the specified translation
+        @param dx   The distance to translate in X
+        @param dy   The distance to translate in Y
+        returns true if the operation succeeded (e.g. did not overflow)
+    */
+    virtual bool translate(SkScalar dx, SkScalar dy);
+
+    /** Preconcat the current matrix with the specified scale.
+        @param sx   The amount to scale in X
+        @param sy   The amount to scale in Y
+        returns true if the operation succeeded (e.g. did not overflow)
+    */
+    virtual bool scale(SkScalar sx, SkScalar sy);
+
+    /** Preconcat the current matrix with the specified rotation.
+        @param degrees  The amount to rotate, in degrees
+        returns true if the operation succeeded (e.g. did not overflow)
+    */
+    virtual bool rotate(SkScalar degrees);
+
+    /** Preconcat the current matrix with the specified skew.
+        @param sx   The amount to skew in X
+        @param sy   The amount to skew in Y
+        returns true if the operation succeeded (e.g. did not overflow)
+    */
+    virtual bool skew(SkScalar sx, SkScalar sy);
+
+    /** Preconcat the current matrix with the specified matrix.
+        @param matrix   The matrix to preconcatenate with the current matrix
+        @return true if the operation succeeded (e.g. did not overflow)
+    */
+    virtual bool concat(const SkMatrix& matrix);
+    
+    /** Replace the current matrix with a copy of the specified matrix.
+        @param matrix The matrix that will be copied into the current matrix.
+    */
+    virtual void setMatrix(const SkMatrix& matrix);
+    
+    /** Helper for setMatrix(identity). Sets the current matrix to identity.
+    */
+    void resetMatrix();
+
+    /** Modify the current clip with the specified rectangle.
+        @param rect The rect to intersect with the current clip
+        @param op The region op to apply to the current clip
+        @return true if the canvas' clip is non-empty
+    */
+    virtual bool clipRect(const SkRect& rect,
+                          SkRegion::Op op = SkRegion::kIntersect_Op);
+
+    /** Modify the current clip with the specified path.
+        @param path The path to apply to the current clip
+        @param op The region op to apply to the current clip
+        @return true if the canvas' new clip is non-empty
+    */
+    virtual bool clipPath(const SkPath& path,
+                          SkRegion::Op op = SkRegion::kIntersect_Op);
+
+    /** Modify the current clip with the specified region. Note that unlike
+        clipRect() and clipPath() which transform their arguments by the current
+        matrix, clipRegion() assumes its argument is already in device
+        coordinates, and so no transformation is performed.
+        @param deviceRgn    The region to apply to the current clip
+        @param op The region op to apply to the current clip
+        @return true if the canvas' new clip is non-empty
+    */
+    virtual bool clipRegion(const SkRegion& deviceRgn,
+                            SkRegion::Op op = SkRegion::kIntersect_Op);
+
+    /** Helper for clipRegion(rgn, kReplace_Op). Sets the current clip to the
+        specified region. This does not intersect or in any other way account
+        for the existing clip region.
+        @param deviceRgn The region to copy into the current clip.
+        @return true if the new clip region is non-empty
+    */
+    bool setClipRegion(const SkRegion& deviceRgn) {
+        return this->clipRegion(deviceRgn, SkRegion::kReplace_Op);
+    }
+
+    /** Enum describing how to treat edges when performing quick-reject tests
+        of a geometry against the current clip. Treating them as antialiased
+        (kAA_EdgeType) will take into account the extra pixels that may be drawn
+        if the edge does not lie exactly on a device pixel boundary (after being
+        transformed by the current matrix).
+    */
+    enum EdgeType {
+        /** Treat the edges as B&W (not antialiased) for the purposes of testing
+            against the current clip
+        */
+        kBW_EdgeType,
+        /** Treat the edges as antialiased for the purposes of testing
+            against the current clip
+        */
+        kAA_EdgeType
+    };
+
+    /** Return true if the specified rectangle, after being transformed by the
+        current matrix, would lie completely outside of the current clip. Call
+        this to check if an area you intend to draw into is clipped out (and
+        therefore you can skip making the draw calls).
+        @param rect the rect to compare with the current clip
+        @param et  specifies how to treat the edges (see EdgeType)
+        @return true if the rect (transformed by the canvas' matrix) does not
+                     intersect with the canvas' clip
+    */
+    bool quickReject(const SkRect& rect, EdgeType et) const;
+
+    /** Return true if the specified path, after being transformed by the
+        current matrix, would lie completely outside of the current clip. Call
+        this to check if an area you intend to draw into is clipped out (and
+        therefore you can skip making the draw calls). Note, for speed it may
+        return false even if the path itself might not intersect the clip
+        (i.e. the bounds of the path intersects, but the path does not).
+        @param path The path to compare with the current clip
+        @param et  specifies how to treat the edges (see EdgeType)
+        @return true if the path (transformed by the canvas' matrix) does not
+                     intersect with the canvas' clip
+    */
+    bool quickReject(const SkPath& path, EdgeType et) const;
+
+    /** Return true if the horizontal band specified by top and bottom is
+        completely clipped out. This is a conservative calculation, meaning
+        that it is possible that if the method returns false, the band may still
+        in fact be clipped out, but the converse is not true. If this method
+        returns true, then the band is guaranteed to be clipped out.
+        @param top  The top of the horizontal band to compare with the clip
+        @param bottom The bottom of the horizontal and to compare with the clip
+        @return true if the horizontal band is completely clipped out (i.e. does
+                     not intersect the current clip)
+    */
+    bool quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const;
+
+    /** Return the bounds of the current clip (in local coordinates) in the
+        bounds parameter, and return true if it is non-empty. This can be useful
+        in a way similar to quickReject, in that it tells you that drawing
+        outside of these bounds will be clipped out.
+    */
+    bool getClipBounds(SkRect* bounds, EdgeType et = kAA_EdgeType) const;
+
+    /** Fill the entire canvas' bitmap (restricted to the current clip) with the
+        specified ARGB color, using the specified PorterDuff mode.
+        @param a    the alpha component (0..255) of the color to fill the canvas
+        @param r    the red component (0..255) of the color to fill the canvas
+        @param g    the green component (0..255) of the color to fill the canvas
+        @param b    the blue component (0..255) of the color to fill the canvas
+        @param mode the mode to apply the color in (defaults to SrcOver)
+    */
+    void drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
+                  SkPorterDuff::Mode mode = SkPorterDuff::kSrcOver_Mode);
+
+    /** Fill the entire canvas' bitmap (restricted to the current clip) with the
+        specified color and porter-duff xfermode.
+        @param color    the color to draw with
+        @param mode the mode to apply the color in (defaults to SrcOver)
+    */
+    void drawColor(SkColor color,
+                   SkPorterDuff::Mode mode = SkPorterDuff::kSrcOver_Mode);
+
+    /** Fill the entire canvas' bitmap (restricted to the current clip) with the
+        specified paint.
+        @param paint    The paint used to fill the canvas
+    */
+    virtual void drawPaint(const SkPaint& paint);
+
+    enum PointMode {
+        /** drawPoints draws each point separately */
+        kPoints_PointMode,
+        /** drawPoints draws each pair of points as a line segment */
+        kLines_PointMode,
+        /** drawPoints draws the array of points as a polygon */
+        kPolygon_PointMode
+    };
+
+    /** Draw a series of points, interpreted based on the PointMode mode. For
+        all modes, the count parameter is interpreted as the total number of
+        points. For kLine mode, count/2 line segments are drawn.
+        For kPoint mode, each point is drawn centered at its coordinate, and its
+        size is specified by the paint's stroke-width. It draws as a square,
+        unless the paint's cap-type is round, in which the points are drawn as
+        circles.
+        For kLine mode, each pair of points is drawn as a line segment,
+        respecting the paint's settings for cap/join/width.
+        For kPolygon mode, the entire array is drawn as a series of connected
+        line segments.
+        Note that, while similar, kLine and kPolygon modes draw slightly
+        differently than the equivalent path built with a series of moveto,
+        lineto calls, in that the path will draw all of its contours at once,
+        with no interactions if contours intersect each other (think XOR
+        xfermode). drawPoints always draws each element one at a time.
+        @param mode     PointMode specifying how to draw the array of points.
+        @param count    The number of points in the array
+        @param pts      Array of points to draw
+        @param paint    The paint used to draw the points
+    */
+    virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                            const SkPaint& paint);
+
+    /** Helper method for drawing a single point. See drawPoints() for a more
+        details.
+    */
+    void drawPoint(SkScalar x, SkScalar y, const SkPaint& paint);
+    
+    /** Draws a single pixel in the specified color.
+        @param x        The X coordinate of which pixel to draw
+        @param y        The Y coordiante of which pixel to draw
+        @param color    The color to draw
+    */
+    void drawPoint(SkScalar x, SkScalar y, SkColor color);
+
+    /** Draw a line segment with the specified start and stop x,y coordinates,
+        using the specified paint. NOTE: since a line is always "framed", the
+        paint's Style is ignored.
+        @param x0    The x-coordinate of the start point of the line
+        @param y0    The y-coordinate of the start point of the line
+        @param x1    The x-coordinate of the end point of the line
+        @param y1    The y-coordinate of the end point of the line
+        @param paint The paint used to draw the line
+    */
+    void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
+                  const SkPaint& paint);
+
+    /** Draw the specified rectangle using the specified paint. The rectangle
+        will be filled or stroked based on the Style in the paint.
+        @param rect     The rect to be drawn
+        @param paint    The paint used to draw the rect
+    */
+    virtual void drawRect(const SkRect& rect, const SkPaint& paint);
+
+    /** Draw the specified rectangle using the specified paint. The rectangle
+        will be filled or framed based on the Style in the paint.
+        @param rect     The rect to be drawn
+        @param paint    The paint used to draw the rect
+    */
+    void drawIRect(const SkIRect& rect, const SkPaint& paint)
+    {
+        SkRect r;
+        r.set(rect);    // promotes the ints to scalars
+        this->drawRect(r, paint);
+    }
+    
+    /** Draw the specified rectangle using the specified paint. The rectangle
+        will be filled or framed based on the Style in the paint.
+        @param left     The left side of the rectangle to be drawn
+        @param top      The top side of the rectangle to be drawn
+        @param right    The right side of the rectangle to be drawn
+        @param bottom   The bottom side of the rectangle to be drawn
+        @param paint    The paint used to draw the rect
+    */
+    void drawRectCoords(SkScalar left, SkScalar top, SkScalar right,
+                        SkScalar bottom, const SkPaint& paint);
+
+    /** Draw the specified oval using the specified paint. The oval will be
+        filled or framed based on the Style in the paint.
+        @param oval     The rectangle bounds of the oval to be drawn
+        @param paint    The paint used to draw the oval
+    */
+    void drawOval(const SkRect& oval, const SkPaint&);
+
+    /** Draw the specified circle using the specified paint. If radius is <= 0,
+        then nothing will be drawn. The circle will be filled
+        or framed based on the Style in the paint.
+        @param cx       The x-coordinate of the center of the cirle to be drawn
+        @param cy       The y-coordinate of the center of the cirle to be drawn
+        @param radius   The radius of the cirle to be drawn
+        @param paint    The paint used to draw the circle
+    */
+    void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
+                    const SkPaint& paint);
+
+    /** Draw the specified arc, which will be scaled to fit inside the
+        specified oval. If the sweep angle is >= 360, then the oval is drawn
+        completely. Note that this differs slightly from SkPath::arcTo, which
+        treats the sweep angle mod 360.
+        @param oval The bounds of oval used to define the shape of the arc
+        @param startAngle Starting angle (in degrees) where the arc begins
+        @param sweepAngle Sweep angle (in degrees) measured clockwise
+        @param useCenter true means include the center of the oval. For filling
+                         this will draw a wedge. False means just use the arc.
+        @param paint    The paint used to draw the arc
+    */
+    void drawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+                 bool useCenter, const SkPaint& paint);
+
+    /** Draw the specified round-rect using the specified paint. The round-rect
+        will be filled or framed based on the Style in the paint.
+        @param rect     The rectangular bounds of the roundRect to be drawn
+        @param rx       The x-radius of the oval used to round the corners
+        @param ry       The y-radius of the oval used to round the corners
+        @param paint    The paint used to draw the roundRect
+    */
+    void drawRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
+                       const SkPaint& paint);
+
+    /** Draw the specified path using the specified paint. The path will be
+        filled or framed based on the Style in the paint.
+        @param path     The path to be drawn
+        @param paint    The paint used to draw the path
+    */
+    virtual void drawPath(const SkPath& path, const SkPaint& paint);
+
+    /** Draw the specified bitmap, with its top/left corner at (x,y), using the
+        specified paint, transformed by the current matrix. Note: if the paint
+        contains a maskfilter that generates a mask which extends beyond the
+        bitmap's original width/height, then the bitmap will be drawn as if it
+        were in a Shader with CLAMP mode. Thus the color outside of the original
+        width/height will be the edge color replicated.
+        @param bitmap   The bitmap to be drawn
+        @param left     The position of the left side of the bitmap being drawn
+        @param top      The position of the top side of the bitmap being drawn
+        @param paint    The paint used to draw the bitmap, or NULL
+    */
+    virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+                            const SkPaint* paint = NULL);
+
+    /** Draw the specified bitmap, with the specified matrix applied (before the
+        canvas' matrix is applied).
+        @param bitmap   The bitmap to be drawn
+        @param src      Optional: specify the subset of the bitmap to be drawn
+        @param dst      The destination rectangle where the scaled/translated
+                        image will be drawn
+        @param paint    The paint used to draw the bitmap, or NULL
+    */
+    virtual void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                                const SkRect& dst, const SkPaint* paint = NULL);
+
+    virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                  const SkPaint* paint = NULL);
+    
+    /** Draw the specified bitmap, with its top/left corner at (x,y),
+        NOT transformed by the current matrix. Note: if the paint
+        contains a maskfilter that generates a mask which extends beyond the
+        bitmap's original width/height, then the bitmap will be drawn as if it
+        were in a Shader with CLAMP mode. Thus the color outside of the original
+        width/height will be the edge color replicated.
+        @param bitmap   The bitmap to be drawn
+        @param left     The position of the left side of the bitmap being drawn
+        @param top      The position of the top side of the bitmap being drawn
+        @param paint    The paint used to draw the bitmap, or NULL
+    */
+    virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+                            const SkPaint* paint = NULL);
+
+    /** Draw the text, with origin at (x,y), using the specified paint.
+        The origin is interpreted based on the Align setting in the paint.
+        @param text The text to be drawn
+        @param byteLength   The number of bytes to read from the text parameter
+        @param x        The x-coordinate of the origin of the text being drawn
+        @param y        The y-coordinate of the origin of the text being drawn
+        @param paint    The paint used for the text (e.g. color, size, style)
+    */
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+                          SkScalar y, const SkPaint& paint);
+
+    /** Draw the text, with each character/glyph origin specified by the pos[]
+        array. The origin is interpreted by the Align setting in the paint. 
+        @param text The text to be drawn
+        @param byteLength   The number of bytes to read from the text parameter
+        @param pos      Array of positions, used to position each character
+        @param paint    The paint used for the text (e.g. color, size, style)
+        */
+    virtual void drawPosText(const void* text, size_t byteLength,
+                             const SkPoint pos[], const SkPaint& paint);
+    
+    /** Draw the text, with each character/glyph origin specified by the x
+        coordinate taken from the xpos[] array, and the y from the constY param.
+        The origin is interpreted by the Align setting in the paint. 
+        @param text The text to be drawn
+        @param byteLength   The number of bytes to read from the text parameter
+        @param xpos     Array of x-positions, used to position each character
+        @param constY   The shared Y coordinate for all of the positions
+        @param paint    The paint used for the text (e.g. color, size, style)
+        */
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                              const SkScalar xpos[], SkScalar constY,
+                              const SkPaint& paint);
+    
+    /** Draw the text, with origin at (x,y), using the specified paint, along
+        the specified path. The paint's Align setting determins where along the
+        path to start the text.
+        @param text The text to be drawn
+        @param byteLength   The number of bytes to read from the text parameter
+        @param path         The path the text should follow for its baseline
+        @param hOffset      The distance along the path to add to the text's
+                            starting position
+        @param vOffset      The distance above(-) or below(+) the path to
+                            position the text
+        @param paint        The paint used for the text
+    */
+    void drawTextOnPathHV(const void* text, size_t byteLength,
+                          const SkPath& path, SkScalar hOffset,
+                          SkScalar vOffset, const SkPaint& paint);
+
+    /** Draw the text, with origin at (x,y), using the specified paint, along
+        the specified path. The paint's Align setting determins where along the
+        path to start the text.
+        @param text The text to be drawn
+        @param byteLength   The number of bytes to read from the text parameter
+        @param path         The path the text should follow for its baseline
+        @param matrix       (may be null) Applied to the text before it is
+                            mapped onto the path
+        @param paint        The paint used for the text
+        */
+    virtual void drawTextOnPath(const void* text, size_t byteLength,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint);
+
+    /** Draw the picture into this canvas. This method effective brackets the
+        playback of the picture's draw calls with save/restore, so the state
+        of this canvas will be unchanged after this call. This contrasts with
+        the more immediate method SkPicture::draw(), which does not bracket
+        the canvas with save/restore, thus the canvas may be left in a changed
+        state after the call.
+        @param picture The recorded drawing commands to playback into this
+                       canvas.
+    */
+    virtual void drawPicture(SkPicture& picture);
+    
+    enum VertexMode {
+        kTriangles_VertexMode,
+        kTriangleStrip_VertexMode,
+        kTriangleFan_VertexMode
+    };
+    
+    /** Draw the array of vertices, interpreted as triangles (based on mode).
+        @param vmode How to interpret the array of vertices
+        @param vertexCount The number of points in the vertices array (and
+                    corresponding texs and colors arrays if non-null)
+        @param vertices Array of vertices for the mesh
+        @param texs May be null. If not null, specifies the coordinate
+                             in texture space for each vertex.
+        @param colors May be null. If not null, specifies a color for each
+                      vertex, to be interpolated across the triangle.
+        @param xmode Used if both texs and colors are present. In this
+                    case the colors are combined with the texture using mode,
+                    before being drawn using the paint. If mode is null, then
+                    the porter-duff MULTIPLY mode is used.
+        @param indices If not null, array of indices to reference into the
+                    vertex (texs, colors) array.
+        @param indexCount number of entries in the indices array (if not null)
+        @param paint Specifies the shader/texture if present. 
+    */
+    virtual void drawVertices(VertexMode vmode, int vertexCount,
+                              const SkPoint vertices[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint);
+
+    //////////////////////////////////////////////////////////////////////////
+    
+    /** Get the current bounder object.
+        The bounder's reference count is unchaged.
+        @return the canva's bounder (or NULL).
+    */
+    SkBounder*  getBounder() const { return fBounder; }
+
+    /** Set a new bounder (or NULL).
+        Pass NULL to clear any previous bounder.
+        As a convenience, the parameter passed is also returned.
+        If a previous bounder exists, its reference count is decremented.
+        If bounder is not NULL, its reference count is incremented.
+        @param bounder the new bounder (or NULL) to be installed in the canvas
+        @return the set bounder object
+    */
+    virtual SkBounder* setBounder(SkBounder* bounder);
+ 
+    /** Get the current filter object. The filter's reference count is not
+        affected. The filter is part of the state this is affected by
+        save/restore.
+        @return the canvas' filter (or NULL).
+    */
+    SkDrawFilter* getDrawFilter() const;
+    
+    /** Set the new filter (or NULL). Pass NULL to clear any existing filter.
+        As a convenience, the parameter is returned. If an existing filter
+        exists, its refcnt is decrement. If the new filter is not null, its
+        refcnt is incremented. The filter is part of the state this is affected
+        by save/restore.
+        @param filter the new filter (or NULL)
+        @return the new filter
+    */
+    virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter);
+
+    //////////////////////////////////////////////////////////////////////////
+
+    /** Return the current matrix on the canvas.
+        This does not account for the translate in any of the devices.
+        @return The current matrix on the canvas.
+    */
+    const SkMatrix& getTotalMatrix() const;
+
+    /** Return the current device clip (concatenation of all clip calls).
+        This does not account for the translate in any of the devices.
+        @return the current device clip (concatenation of all clip calls).
+    */
+    const SkRegion& getTotalClip() const;
+
+    /** May be overridden by subclasses. This returns a compatible device
+        for this canvas, with the specified config/width/height. If isOpaque
+        is true, then the underlying bitmap is optimized to assume that every
+        pixel will be drawn to, and thus it does not need to clear the alpha
+        channel ahead of time (assuming the specified config supports per-pixel
+        alpha.) If isOpaque is false, then the bitmap should clear its alpha
+        channel.
+    */
+    virtual SkDevice* createDevice(SkBitmap::Config, int width, int height,
+                                   bool isOpaque, bool isForLayer);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** After calling saveLayer(), there can be any number of devices that make
+        up the top-most drawing area. LayerIter can be used to iterate through
+        those devices. Note that the iterator is only valid until the next API
+        call made on the canvas. Ownership of all pointers in the iterator stays
+        with the canvas, so none of them should be modified or deleted.
+    */
+    class LayerIter /*: SkNoncopyable*/ {
+    public:
+        /** Initialize iterator with canvas, and set values for 1st device */
+        LayerIter(SkCanvas*, bool skipEmptyClips);
+        ~LayerIter();
+        
+        /** Return true if the iterator is done */
+        bool done() const { return fDone; }
+        /** Cycle to the next device */
+        void next();
+        
+        // These reflect the current device in the iterator
+
+        SkDevice*       device() const;
+        const SkMatrix& matrix() const;
+        const SkRegion& clip() const;
+        const SkPaint&  paint() const;
+        int             x() const;
+        int             y() const;
+        
+    private:
+        // used to embed the SkDrawIter object directly in our instance, w/o
+        // having to expose that class def to the public. There is an assert
+        // in our constructor to ensure that fStorage is large enough
+        // (though needs to be a compile-time-assert!). We use intptr_t to work
+        // safely with 32 and 64 bit machines (to ensure the storage is enough)
+        intptr_t          fStorage[12];
+        class SkDrawIter* fImpl;    // this points at fStorage
+        SkPaint           fDefaultPaint;
+        bool              fDone;
+    };
+
+protected:
+    // all of the drawBitmap variants call this guy
+    virtual void commonDrawBitmap(const SkBitmap&, const SkMatrix& m,
+                                  const SkPaint& paint);
+    
+private:
+    class MCRec;
+
+    SkDeque     fMCStack;
+    // points to top of stack
+    MCRec*      fMCRec;
+    // the first N recs that can fit here mean we won't call malloc
+    uint32_t    fMCRecStorage[32];
+
+    SkBounder*  fBounder;
+
+    void prepareForDeviceDraw(SkDevice*);
+    
+    bool fDeviceCMDirty;            // cleared by updateDeviceCMCache()
+    void updateDeviceCMCache();
+
+    friend class SkDrawIter;    // needs setupDrawForLayerDevice()
+
+    SkDevice* init(SkDevice*);
+    void internalDrawBitmap(const SkBitmap&, const SkMatrix& m,
+                                  const SkPaint* paint);
+    void drawDevice(SkDevice*, int x, int y, const SkPaint*);
+    // shared by save() and saveLayer()
+    int internalSave(SaveFlags flags);
+    void internalRestore();
+    
+    /*  These maintain a cache of the clip bounds in local coordinates,
+        (converted to 2s-compliment if floats are slow).
+     */
+    mutable SkRectCompareType fLocalBoundsCompareType;
+    mutable bool              fLocalBoundsCompareTypeDirty;
+
+    const SkRectCompareType& getLocalClipBoundsCompareType() const {
+        if (fLocalBoundsCompareTypeDirty) {
+            this->computeLocalClipBoundsCompareType();
+            fLocalBoundsCompareTypeDirty = false;
+        }
+        return fLocalBoundsCompareType;
+    }
+    void computeLocalClipBoundsCompareType() const;
+};
+
+/** Stack helper class to automatically call restoreToCount() on the canvas
+    when this object goes out of scope. Use this to guarantee that the canvas
+    is restored to a known state.
+*/
+class SkAutoCanvasRestore : SkNoncopyable {
+public:
+    SkAutoCanvasRestore(SkCanvas* canvas, bool doSave) : fCanvas(canvas) {
+        SkASSERT(canvas);
+        fSaveCount = canvas->getSaveCount();
+        if (doSave) {
+            canvas->save();
+        }
+    }
+    ~SkAutoCanvasRestore() {
+        fCanvas->restoreToCount(fSaveCount);
+    }
+
+private:
+    SkCanvas*   fCanvas;
+    int         fSaveCount;
+};
+
+#endif
+
diff --git a/include/core/SkChunkAlloc.h b/include/core/SkChunkAlloc.h
new file mode 100644
index 0000000..0e9ad18
--- /dev/null
+++ b/include/core/SkChunkAlloc.h
@@ -0,0 +1,61 @@
+/*
+ * 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 SkChunkAlloc_DEFINED
+#define SkChunkAlloc_DEFINED
+
+#include "SkTypes.h"
+
+class SkChunkAlloc : SkNoncopyable {
+public:
+    SkChunkAlloc(size_t minSize);
+    ~SkChunkAlloc();
+
+    /** Free up all allocated blocks. This invalidates all returned
+        pointers.
+    */
+    void reset();
+
+    /** Reuse all allocated blocks. This invalidates all returned
+        pointers (like reset) but doesn't necessarily free up all
+        of the privately allocated blocks. This is more efficient
+        if you plan to reuse the allocator multiple times.
+    */
+    void reuse();
+
+    enum AllocFailType {
+        kReturnNil_AllocFailType,
+        kThrow_AllocFailType
+    };
+    
+    void* alloc(size_t bytes, AllocFailType);
+    void* allocThrow(size_t bytes) {
+        return this->alloc(bytes, kThrow_AllocFailType);
+    }
+    
+    size_t totalCapacity() const { return fTotalCapacity; }
+    
+private:
+    struct Block;
+    Block*  fBlock;
+    size_t  fMinSize;
+    Block*  fPool;
+    size_t  fTotalCapacity;
+
+    Block* newBlock(size_t bytes, AllocFailType ftype);
+};
+
+#endif
diff --git a/include/core/SkColor.h b/include/core/SkColor.h
new file mode 100644
index 0000000..c97a8ec
--- /dev/null
+++ b/include/core/SkColor.h
@@ -0,0 +1,155 @@
+/*
+ * 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 SkColor_DEFINED
+#define SkColor_DEFINED
+
+#include "SkScalar.h"
+
+/** \file SkColor.h
+
+    Types and macros for colors
+*/
+
+/** 8-bit type for an alpha value. 0xFF is 100% opaque, 0x00 is 100% transparent.
+*/
+typedef uint8_t SkAlpha;
+/** 32 bit ARGB color value, not premultiplied. The color components are always in
+    a known order. This is different from SkPMColor, which has its bytes in a configuration
+    dependent order, to match the format of kARGB32 bitmaps. SkColor is the type used to
+    specify colors in SkPaint and in gradients.
+*/
+typedef uint32_t SkColor;
+
+/** Return a SkColor value from 8 bit component values
+*/
+static inline SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
+{
+    SkASSERT(a <= 255 && r <= 255 && g <= 255 && b <= 255);
+
+    return (a << 24) | (r << 16) | (g << 8) | (b << 0);
+}
+
+/** Return a SkColor value from 8 bit component values, with an implied value
+    of 0xFF for alpha (fully opaque)
+*/
+#define SkColorSetRGB(r, g, b)  SkColorSetARGB(0xFF, r, g, b)
+
+/** return the alpha byte from a SkColor value */
+#define SkColorGetA(color)      (((color) >> 24) & 0xFF)
+/** return the red byte from a SkColor value */
+#define SkColorGetR(color)      (((color) >> 16) & 0xFF)
+/** return the green byte from a SkColor value */
+#define SkColorGetG(color)      (((color) >>  8) & 0xFF)
+/** return the blue byte from a SkColor value */
+#define SkColorGetB(color)      (((color) >>  0) & 0xFF)
+
+static inline SkColor SkColorSetA(SkColor c, U8CPU a) {
+    return (c & 0x00FFFFFF) | (a << 24);
+}
+
+// common colors
+
+#define SK_ColorBLACK   0xFF000000  //!< black SkColor value
+#define SK_ColorDKGRAY  0xFF444444  //!< dark gray SkColor value
+#define SK_ColorGRAY    0xFF888888  //!< gray SkColor value
+#define SK_ColorLTGRAY  0xFFCCCCCC  //!< light gray SkColor value
+#define SK_ColorWHITE   0xFFFFFFFF  //!< white SkColor value
+
+#define SK_ColorRED     0xFFFF0000  //!< red SkColor value
+#define SK_ColorGREEN   0xFF00FF00  //!< green SkColor value
+#define SK_ColorBLUE    0xFF0000FF  //!< blue SkColor value
+#define SK_ColorYELLOW  0xFFFFFF00  //!< yellow SkColor value
+#define SK_ColorCYAN    0xFF00FFFF  //!< cyan SkColor value
+#define SK_ColorMAGENTA 0xFFFF00FF  //!< magenta SkColor value
+
+////////////////////////////////////////////////////////////////////////
+
+/** Convert RGB components to HSV.
+        hsv[0] is Hue [0 .. 360)
+        hsv[1] is Saturation [0...1]
+        hsv[2] is Value [0...1]
+    @param red  red component value [0..255]
+    @param green  green component value [0..255]
+    @param blue  blue component value [0..255]
+    @param hsv  3 element array which holds the resulting HSV components.
+*/
+void SkRGBToHSV(U8CPU red, U8CPU green, U8CPU blue, SkScalar hsv[3]);
+
+/** Convert the argb color to its HSV components.
+        hsv[0] is Hue [0 .. 360)
+        hsv[1] is Saturation [0...1]
+        hsv[2] is Value [0...1]
+    @param color the argb color to convert. Note: the alpha component is ignored.
+    @param hsv  3 element array which holds the resulting HSV components.
+*/
+static inline void SkColorToHSV(SkColor color, SkScalar hsv[3])
+{
+    SkRGBToHSV(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), hsv);
+}
+
+/** Convert HSV components to an ARGB color. The alpha component is passed through unchanged.
+        hsv[0] is Hue [0 .. 360)
+        hsv[1] is Saturation [0...1]
+        hsv[2] is Value [0...1]
+    If hsv values are out of range, they are pinned.
+    @param alpha the alpha component of the returned argb color.
+    @param hsv  3 element array which holds the input HSV components.
+    @return the resulting argb color
+*/
+SkColor SkHSVToColor(U8CPU alpha, const SkScalar hsv[3]);
+
+/** Convert HSV components to an ARGB color. The alpha component set to 0xFF.
+        hsv[0] is Hue [0 .. 360)
+        hsv[1] is Saturation [0...1]
+        hsv[2] is Value [0...1]
+    If hsv values are out of range, they are pinned.
+    @param hsv  3 element array which holds the input HSV components.
+    @return the resulting argb color
+*/
+static inline SkColor SkHSVToColor(const SkScalar hsv[3])
+{
+    return SkHSVToColor(0xFF, hsv);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+/** 32 bit ARGB color value, premultiplied. The byte order for this value is
+    configuration dependent, matching the format of kARGB32 bitmaps. This is different
+    from SkColor, which is nonpremultiplied, and is always in the same byte order.
+*/
+typedef uint32_t SkPMColor;
+
+/** Return a SkPMColor value from unpremultiplied 8 bit component values
+*/
+SkPMColor SkPreMultiplyARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
+/** Return a SkPMColor value from a SkColor value. This is done by multiplying the color
+    components by the color's alpha, and by arranging the bytes in a configuration
+    dependent order, to match the format of kARGB32 bitmaps.
+*/
+SkPMColor SkPreMultiplyColor(SkColor c);
+
+/** Define a function pointer type for combining two premultiplied colors
+*/
+typedef SkPMColor (*SkXfermodeProc)(SkPMColor src, SkPMColor dst);
+
+/** Define a function pointer type for combining a premultiplied src color
+    and a 16bit device color.
+*/
+typedef uint16_t (*SkXfermodeProc16)(SkPMColor src, uint16_t dst);
+
+#endif
+
diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
new file mode 100644
index 0000000..a1b3171
--- /dev/null
+++ b/include/core/SkColorFilter.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 SkColorFilter_DEFINED
+#define SkColorFilter_DEFINED
+
+#include "SkColor.h"
+#include "SkFlattenable.h"
+#include "SkPorterDuff.h"
+
+class SkColorFilter : public SkFlattenable {
+public:
+    /** Called with a scanline of colors, as if there was a shader installed.
+        The implementation writes out its filtered version into result[].
+        Note: shader and result may be the same buffer.
+        @param src      array of colors, possibly generated by a shader
+        @param count    the number of entries in the src[] and result[] arrays
+        @param result   written by the filter
+    */
+    virtual void filterSpan(const SkPMColor src[], int count,
+                            SkPMColor result[]) = 0;
+    /** Called with a scanline of colors, as if there was a shader installed.
+        The implementation writes out its filtered version into result[].
+        Note: shader and result may be the same buffer.
+        @param src      array of colors, possibly generated by a shader
+        @param count    the number of entries in the src[] and result[] arrays
+        @param result   written by the filter
+    */
+    virtual void filterSpan16(const uint16_t shader[], int count,
+                              uint16_t result[]);
+
+    enum Flags {
+        /** If set the filter methods will not change the alpha channel of the
+            colors.
+        */
+        kAlphaUnchanged_Flag = 0x01,
+        /** If set, this subclass implements filterSpan16(). If this flag is
+            set, then kAlphaUnchanged_Flag must also be set.
+        */
+        kHasFilter16_Flag    = 0x02
+    };
+
+    /** Returns the flags for this filter. Override in subclasses to return
+        custom flags.
+    */
+    virtual uint32_t getFlags() { return 0; }
+
+    /** Create a colorfilter that uses the specified color and porter-duff mode.
+        If porterDuffMode is DST, this function will return NULL (since that
+        mode will have no effect on the result).
+        @param srcColor The source color used with the specified mode
+        @param mode     The porter-duff mode that is applied to each color in
+                        the colorfilter's filterSpan[16,32] methods
+        @return colorfilter object that applies the src color and porter-duff
+                mode, or NULL if the mode will have no effect.
+    */
+    static SkColorFilter* CreatePorterDuffFilter(SkColor srcColor,
+                                                 SkPorterDuff::Mode mode);
+
+    /** Create a colorfilter that calls through to the specified procs to
+        filter the colors. The SkXfermodeProc parameter must be non-null, but
+        the SkXfermodeProc16 is optional, and may be null.
+    */
+    static SkColorFilter* CreatXfermodeProcFilter(SkColor srcColor,
+                                              SkXfermodeProc proc,
+                                              SkXfermodeProc16 proc16 = NULL);
+
+    /** Create a colorfilter that multiplies the RGB channels by one color, and
+        then adds a second color, pinning the result for each component to
+        [0..255]. The alpha components of the mul and add arguments
+        are ignored.
+    */
+    static SkColorFilter* CreateLightingFilter(SkColor mul, SkColor add);
+    
+protected:
+    SkColorFilter() {}
+    SkColorFilter(SkFlattenableReadBuffer& rb) : INHERITED(rb) {}
+    
+private:
+    typedef SkFlattenable INHERITED;
+};
+
+#include "SkShader.h"
+
+class SkFilterShader : public SkShader {
+public:
+    SkFilterShader(SkShader* shader, SkColorFilter* filter);
+    virtual ~SkFilterShader();
+
+    // override
+    virtual uint32_t getFlags();
+    virtual bool setContext(const SkBitmap& device, const SkPaint& paint,
+                            const SkMatrix& matrix);
+    virtual void shadeSpan(int x, int y, SkPMColor result[], int count);
+    virtual void shadeSpan16(int x, int y, uint16_t result[], int count);
+    virtual void beginSession();
+    virtual void endSession();
+    
+protected:
+    SkFilterShader(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { 
+        return SkNEW_ARGS(SkFilterShader, (buffer)); }
+    SkShader*       fShader;
+    SkColorFilter*  fFilter;
+    
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/include/core/SkColorPriv.h b/include/core/SkColorPriv.h
new file mode 100644
index 0000000..041c038
--- /dev/null
+++ b/include/core/SkColorPriv.h
@@ -0,0 +1,657 @@
+/*
+ * 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 SkColorPriv_DEFINED
+#define SkColorPriv_DEFINED
+
+// turn this own for extra debug checking when blending onto 565
+#ifdef SK_DEBUG
+    #define CHECK_FOR_565_OVERFLOW
+#endif
+
+#include "SkColor.h"
+#include "SkMath.h"
+
+/** Turn 0..255 into 0..256 by adding 1 at the half-way point. Used to turn a
+    byte into a scale value, so that we can say scale * value >> 8 instead of
+    alpha * value / 255.
+ 
+    In debugging, asserts that alpha is 0..255
+*/
+static inline unsigned SkAlpha255To256(U8CPU alpha) {
+    SkASSERT(SkToU8(alpha) == alpha);
+    return alpha + (alpha >> 7);
+}
+
+/** Multiplify value by 0..256, and shift the result down 8
+    (i.e. return (value * alpha256) >> 8)
+ */
+#define SkAlphaMul(value, alpha256)     (SkMulS16(value, alpha256) >> 8)
+
+//  The caller may want negative values, so keep all params signed (int)
+//  so we don't accidentally slip into unsigned math and lose the sign
+//  extension when we shift (in SkAlphaMul)
+inline int SkAlphaBlend(int src, int dst, int scale256) {
+    SkASSERT((unsigned)scale256 <= 256);
+    return dst + SkAlphaMul(src - dst, scale256);
+}
+
+#define SK_R16_BITS     5
+#define SK_G16_BITS     6
+#define SK_B16_BITS     5
+
+#define SK_R16_SHIFT    (SK_B16_BITS + SK_G16_BITS)
+#define SK_G16_SHIFT    (SK_B16_BITS)
+#define SK_B16_SHIFT    0
+
+#define SK_R16_MASK     ((1 << SK_R16_BITS) - 1)
+#define SK_G16_MASK     ((1 << SK_G16_BITS) - 1)
+#define SK_B16_MASK     ((1 << SK_B16_BITS) - 1)
+
+#define SkGetPackedR16(color)   (((unsigned)(color) >> SK_R16_SHIFT) & SK_R16_MASK)
+#define SkGetPackedG16(color)   (((unsigned)(color) >> SK_G16_SHIFT) & SK_G16_MASK)
+#define SkGetPackedB16(color)   (((unsigned)(color) >> SK_B16_SHIFT) & SK_B16_MASK)
+
+#define SkR16Assert(r)  SkASSERT((unsigned)(r) <= SK_R16_MASK)
+#define SkG16Assert(g)  SkASSERT((unsigned)(g) <= SK_G16_MASK)
+#define SkB16Assert(b)  SkASSERT((unsigned)(b) <= SK_B16_MASK)
+
+static inline uint16_t SkPackRGB16(unsigned r, unsigned g, unsigned b) {
+    SkASSERT(r <= SK_R16_MASK);
+    SkASSERT(g <= SK_G16_MASK);
+    SkASSERT(b <= SK_B16_MASK);
+
+    return SkToU16((r << SK_R16_SHIFT) | (g << SK_G16_SHIFT) | (b << SK_B16_SHIFT));
+}
+
+#define SK_R16_MASK_IN_PLACE        (SK_R16_MASK << SK_R16_SHIFT)
+#define SK_G16_MASK_IN_PLACE        (SK_G16_MASK << SK_G16_SHIFT)
+#define SK_B16_MASK_IN_PLACE        (SK_B16_MASK << SK_B16_SHIFT)
+
+/** Expand the 16bit color into a 32bit value that can be scaled all at once
+    by a value up to 32. Used in conjunction with SkCompact_rgb_16.
+*/
+static inline uint32_t SkExpand_rgb_16(U16CPU c) {
+    SkASSERT(c == (uint16_t)c);
+
+    return ((c & SK_G16_MASK_IN_PLACE) << 16) | (c & ~SK_G16_MASK_IN_PLACE);
+}
+
+/** Compress an expanded value (from SkExpand_rgb_16) back down to a 16bit
+    color value. The computation yields only 16bits of valid data, but we claim
+    to return 32bits, so that the compiler won't generate extra instructions to
+    "clean" the top 16bits. However, the top 16 can contain garbage, so it is
+    up to the caller to safely ignore them.
+*/
+static inline U16CPU SkCompact_rgb_16(uint32_t c) {
+    return ((c >> 16) & SK_G16_MASK_IN_PLACE) | (c & ~SK_G16_MASK_IN_PLACE);
+}
+
+/** Scale the 16bit color value by the 0..256 scale parameter.
+    The computation yields only 16bits of valid data, but we claim
+    to return 32bits, so that the compiler won't generate extra instructions to
+    "clean" the top 16bits.
+*/
+static inline U16CPU SkAlphaMulRGB16(U16CPU c, unsigned scale) {
+    return SkCompact_rgb_16(SkExpand_rgb_16(c) * (scale >> 3) >> 5);
+}
+
+// this helper explicitly returns a clean 16bit value (but slower)
+#define SkAlphaMulRGB16_ToU16(c, s)  (uint16_t)SkAlphaMulRGB16(c, s)
+
+/** Blend src and dst 16bit colors by the 0..256 scale parameter.
+    The computation yields only 16bits of valid data, but we claim
+    to return 32bits, so that the compiler won't generate extra instructions to
+    "clean" the top 16bits.
+*/
+static inline U16CPU SkBlendRGB16(U16CPU src, U16CPU dst, int srcScale) {
+    SkASSERT((unsigned)srcScale <= 256);
+
+    srcScale >>= 3;
+
+    uint32_t src32 = SkExpand_rgb_16(src);
+    uint32_t dst32 = SkExpand_rgb_16(dst);
+    return SkCompact_rgb_16(dst32 + ((src32 - dst32) * srcScale >> 5));
+}
+
+static inline void SkBlendRGB16(const uint16_t src[], uint16_t dst[],
+                                int srcScale, int count) {
+    SkASSERT(count > 0);
+    SkASSERT((unsigned)srcScale <= 256);
+ 
+    srcScale >>= 3;
+
+    do {
+        uint32_t src32 = SkExpand_rgb_16(*src++);
+        uint32_t dst32 = SkExpand_rgb_16(*dst);
+        *dst++ = SkCompact_rgb_16(dst32 + ((src32 - dst32) * srcScale >> 5));
+    } while (--count > 0);
+}
+
+#ifdef SK_DEBUG
+    static U16CPU SkRGB16Add(U16CPU a, U16CPU b) {
+        SkASSERT(SkGetPackedR16(a) + SkGetPackedR16(b) <= SK_R16_MASK);
+        SkASSERT(SkGetPackedG16(a) + SkGetPackedG16(b) <= SK_G16_MASK);
+        SkASSERT(SkGetPackedB16(a) + SkGetPackedB16(b) <= SK_B16_MASK);
+        
+        return a + b;
+    }
+#else
+    #define SkRGB16Add(a, b)  ((a) + (b))
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+#define SK_A32_BITS     8
+#define SK_R32_BITS     8
+#define SK_G32_BITS     8
+#define SK_B32_BITS     8
+
+/* 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)
+#define SK_B32_MASK     ((1 << SK_B32_BITS) - 1)
+
+#define SkGetPackedA32(packed)      ((uint32_t)((packed) << (24 - SK_A32_SHIFT)) >> 24)
+#define SkGetPackedR32(packed)      ((uint32_t)((packed) << (24 - SK_R32_SHIFT)) >> 24)
+#define SkGetPackedG32(packed)      ((uint32_t)((packed) << (24 - SK_G32_SHIFT)) >> 24)
+#define SkGetPackedB32(packed)      ((uint32_t)((packed) << (24 - SK_B32_SHIFT)) >> 24)
+
+#define SkA32Assert(a)  SkASSERT((unsigned)(a) <= SK_A32_MASK)
+#define SkR32Assert(r)  SkASSERT((unsigned)(r) <= SK_R32_MASK)
+#define SkG32Assert(g)  SkASSERT((unsigned)(g) <= SK_G32_MASK)
+#define SkB32Assert(b)  SkASSERT((unsigned)(b) <= SK_B32_MASK)
+
+#ifdef SK_DEBUG
+    inline void SkPMColorAssert(SkPMColor c) {
+        unsigned a = SkGetPackedA32(c);
+        unsigned r = SkGetPackedR32(c);
+        unsigned g = SkGetPackedG32(c);
+        unsigned b = SkGetPackedB32(c);
+        
+        SkA32Assert(a);
+        SkASSERT(r <= a);
+        SkASSERT(g <= a);
+        SkASSERT(b <= a);
+    }
+#else
+    #define SkPMColorAssert(c)
+#endif
+
+inline SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
+    SkA32Assert(a);
+    SkASSERT(r <= a);
+    SkASSERT(g <= a);
+    SkASSERT(b <= a);
+
+    return (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) |
+           (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT);
+}
+
+extern const uint32_t gMask_00FF00FF;
+
+inline uint32_t SkAlphaMulQ(uint32_t c, unsigned scale) {
+    uint32_t mask = gMask_00FF00FF;
+//    uint32_t mask = 0xFF00FF;
+
+    uint32_t rb = ((c & mask) * scale) >> 8;
+    uint32_t ag = ((c >> 8) & mask) * scale;
+    return (rb & mask) | (ag & ~mask);
+}
+
+inline SkPMColor SkPMSrcOver(SkPMColor src, SkPMColor dst) {
+    return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
+}
+
+inline SkPMColor SkBlendARGB32(SkPMColor src, SkPMColor dst, U8CPU aa) {
+    SkASSERT((unsigned)aa <= 255);
+
+    unsigned src_scale = SkAlpha255To256(aa);
+    unsigned dst_scale = SkAlpha255To256(255 - SkAlphaMul(SkGetPackedA32(src), src_scale));
+
+    return SkAlphaMulQ(src, src_scale) + SkAlphaMulQ(dst, dst_scale);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Convert a 32bit pixel to a 16bit pixel (no dither)
+
+#define SkR32ToR16_MACRO(r)   ((unsigned)(r) >> (SK_R32_BITS - SK_R16_BITS))
+#define SkG32ToG16_MACRO(g)   ((unsigned)(g) >> (SK_G32_BITS - SK_G16_BITS))
+#define SkB32ToB16_MACRO(b)   ((unsigned)(b) >> (SK_B32_BITS - SK_B16_BITS))
+
+#ifdef SK_DEBUG
+    inline unsigned SkR32ToR16(unsigned r)
+    {
+        SkR32Assert(r);
+        return SkR32ToR16_MACRO(r);
+    }
+    inline unsigned SkG32ToG16(unsigned g)
+    {
+        SkG32Assert(g);
+        return SkG32ToG16_MACRO(g);
+    }
+    inline unsigned SkB32ToB16(unsigned b)
+    {
+        SkB32Assert(b);
+        return SkB32ToB16_MACRO(b);
+    }
+#else
+    #define SkR32ToR16(r)   SkR32ToR16_MACRO(r)
+    #define SkG32ToG16(g)   SkG32ToG16_MACRO(g)
+    #define SkB32ToB16(b)   SkB32ToB16_MACRO(b)
+#endif
+
+#define SkPacked32ToR16(c)  (((unsigned)(c) >> (SK_R32_SHIFT + SK_R32_BITS - SK_R16_BITS)) & SK_R16_MASK)
+#define SkPacked32ToG16(c)  (((unsigned)(c) >> (SK_G32_SHIFT + SK_G32_BITS - SK_G16_BITS)) & SK_G16_MASK)
+#define SkPacked32ToB16(c)  (((unsigned)(c) >> (SK_B32_SHIFT + SK_B32_BITS - SK_B16_BITS)) & SK_B16_MASK)
+
+inline U16CPU SkPixel32ToPixel16(SkPMColor c)
+{
+    unsigned r = ((c >> (SK_R32_SHIFT + (8 - SK_R16_BITS))) & SK_R16_MASK) << SK_R16_SHIFT;
+    unsigned g = ((c >> (SK_G32_SHIFT + (8 - SK_G16_BITS))) & SK_G16_MASK) << SK_G16_SHIFT;
+    unsigned b = ((c >> (SK_B32_SHIFT + (8 - SK_B16_BITS))) & SK_B16_MASK) << SK_B16_SHIFT;
+    return r | g | b;
+}
+
+inline U16CPU SkPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b)
+{
+    return  (SkR32ToR16(r) << SK_R16_SHIFT) |
+            (SkG32ToG16(g) << SK_G16_SHIFT) |
+            (SkB32ToB16(b) << SK_B16_SHIFT);
+}
+
+#define SkPixel32ToPixel16_ToU16(src)   SkToU16(SkPixel32ToPixel16(src))
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Fast dither from 32->16
+
+#define SkShouldDitherXY(x, y)  (((x) ^ (y)) & 1)
+
+inline uint16_t SkDitherPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b)
+{
+    r = ((r << 1) - ((r >> (8 - SK_R16_BITS) << (8 - SK_R16_BITS)) | (r >> SK_R16_BITS))) >> (8 - SK_R16_BITS);
+    g = ((g << 1) - ((g >> (8 - SK_G16_BITS) << (8 - SK_G16_BITS)) | (g >> SK_G16_BITS))) >> (8 - SK_G16_BITS);
+    b = ((b << 1) - ((b >> (8 - SK_B16_BITS) << (8 - SK_B16_BITS)) | (b >> SK_B16_BITS))) >> (8 - SK_B16_BITS);
+    
+    return SkPackRGB16(r, g, b);
+}
+
+inline uint16_t SkDitherPixel32ToPixel16(SkPMColor c)
+{
+    return SkDitherPack888ToRGB16(SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c));
+}
+
+/*  Return c in expanded_rgb_16 format, but also scaled up by 32 (5 bits)
+    It is now suitable for combining with a scaled expanded_rgb_16 color
+    as in SkSrcOver32To16().
+    We must do this 565 high-bit replication, in order for the subsequent add
+    to saturate properly (and not overflow). If we take the 8 bits as is, it is
+    possible to overflow.
+*/
+static inline uint32_t SkPMColorToExpanded16x5(SkPMColor c)
+{
+    unsigned sr = SkPacked32ToR16(c);
+    unsigned sg = SkPacked32ToG16(c);
+    unsigned sb = SkPacked32ToB16(c);
+    
+    sr = (sr << 5) | sr;
+    sg = (sg << 5) | (sg >> 1);
+    sb = (sb << 5) | sb;
+    return (sr << 11) | (sg << 21) | (sb << 0);
+}
+
+/*  SrcOver the 32bit src color with the 16bit dst, returning a 16bit value
+    (with dirt in the high 16bits, so caller beware).
+*/
+static inline U16CPU SkSrcOver32To16(SkPMColor src, uint16_t dst) {
+    unsigned sr = SkGetPackedR32(src);
+    unsigned sg = SkGetPackedG32(src);
+    unsigned sb = SkGetPackedB32(src);
+    
+    unsigned dr = SkGetPackedR16(dst);
+    unsigned dg = SkGetPackedG16(dst);
+    unsigned db = SkGetPackedB16(dst);
+    
+    unsigned isa = 255 - SkGetPackedA32(src);
+    
+    dr = (sr + SkMul16ShiftRound(dr, isa, SK_R16_BITS)) >> (8 - SK_R16_BITS);
+    dg = (sg + SkMul16ShiftRound(dg, isa, SK_G16_BITS)) >> (8 - SK_G16_BITS);
+    db = (sb + SkMul16ShiftRound(db, isa, SK_B16_BITS)) >> (8 - SK_B16_BITS);
+    
+    return SkPackRGB16(dr, dg, db);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Convert a 16bit pixel to a 32bit pixel
+
+inline unsigned SkR16ToR32(unsigned r)
+{
+    return (r << (8 - SK_R16_BITS)) | (r >> (2 * SK_R16_BITS - 8));
+}
+inline unsigned SkG16ToG32(unsigned g)
+{
+    return (g << (8 - SK_G16_BITS)) | (g >> (2 * SK_G16_BITS - 8));
+}
+inline unsigned SkB16ToB32(unsigned b)
+{
+    return (b << (8 - SK_B16_BITS)) | (b >> (2 * SK_B16_BITS - 8));
+}
+
+#define SkPacked16ToR32(c)      SkR16ToR32(SkGetPackedR16(c))
+#define SkPacked16ToG32(c)      SkG16ToG32(SkGetPackedG16(c))
+#define SkPacked16ToB32(c)      SkB16ToB32(SkGetPackedB16(c))
+
+inline SkPMColor SkPixel16ToPixel32(U16CPU src)
+{
+    SkASSERT(src == SkToU16(src));
+
+    unsigned    r = SkPacked16ToR32(src);
+    unsigned    g = SkPacked16ToG32(src);
+    unsigned    b = SkPacked16ToB32(src);
+
+    SkASSERT((r >> (8 - SK_R16_BITS)) == SkGetPackedR16(src));
+    SkASSERT((g >> (8 - SK_G16_BITS)) == SkGetPackedG16(src));
+    SkASSERT((b >> (8 - SK_B16_BITS)) == SkGetPackedB16(src));
+
+    return SkPackARGB32(0xFF, r, g, b);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef uint16_t SkPMColor16;
+
+// Put in OpenGL order (r g b a)
+#define SK_A4444_SHIFT    0
+#define SK_R4444_SHIFT    12
+#define SK_G4444_SHIFT    8
+#define SK_B4444_SHIFT    4
+
+#define SkA32To4444(a)  ((unsigned)(a) >> 4)
+#define SkR32To4444(r)  ((unsigned)(r) >> 4)
+#define SkG32To4444(g)  ((unsigned)(g) >> 4)
+#define SkB32To4444(b)  ((unsigned)(b) >> 4)
+
+static U8CPU SkReplicateNibble(unsigned nib)
+{
+    SkASSERT(nib <= 0xF);
+    return (nib << 4) | nib;
+}
+
+#define SkA4444ToA32(a)     SkReplicateNibble(a)
+#define SkR4444ToR32(r)     SkReplicateNibble(r)
+#define SkG4444ToG32(g)     SkReplicateNibble(g)
+#define SkB4444ToB32(b)     SkReplicateNibble(b)
+
+#define SkGetPackedA4444(c)     (((unsigned)(c) >> SK_A4444_SHIFT) & 0xF)
+#define SkGetPackedR4444(c)     (((unsigned)(c) >> SK_R4444_SHIFT) & 0xF)
+#define SkGetPackedG4444(c)     (((unsigned)(c) >> SK_G4444_SHIFT) & 0xF)
+#define SkGetPackedB4444(c)     (((unsigned)(c) >> SK_B4444_SHIFT) & 0xF)
+
+#define SkPacked4444ToA32(c)    SkReplicateNibble(SkGetPackedA4444(c))
+#define SkPacked4444ToR32(c)    SkReplicateNibble(SkGetPackedR4444(c))
+#define SkPacked4444ToG32(c)    SkReplicateNibble(SkGetPackedG4444(c))
+#define SkPacked4444ToB32(c)    SkReplicateNibble(SkGetPackedB4444(c))
+
+#ifdef SK_DEBUG
+static inline void SkPMColor16Assert(U16CPU c)
+{
+    unsigned a = SkGetPackedA4444(c);
+    unsigned r = SkGetPackedR4444(c);
+    unsigned g = SkGetPackedG4444(c);
+    unsigned b = SkGetPackedB4444(c);
+    
+    SkASSERT(a <= 0xF);
+    SkASSERT(r <= a);
+    SkASSERT(g <= a);
+    SkASSERT(b <= a);
+}
+#else
+#define SkPMColor16Assert(c)
+#endif
+
+static inline unsigned SkAlpha15To16(unsigned a)
+{
+    SkASSERT(a <= 0xF);
+    return a + (a >> 3);
+}
+
+#ifdef SK_DEBUG
+    static inline int SkAlphaMul4(int value, int scale)
+    {
+        SkASSERT((unsigned)scale <= 0x10);
+        return value * scale >> 4;
+    }
+#else
+    #define SkAlphaMul4(value, scale)   ((value) * (scale) >> 4)
+#endif
+
+static inline unsigned SkR4444ToR565(unsigned r)
+{
+    SkASSERT(r <= 0xF);
+    return (r << (SK_R16_BITS - 4)) | (r >> (8 - SK_R16_BITS));
+}
+
+static inline unsigned SkG4444ToG565(unsigned g)
+{
+    SkASSERT(g <= 0xF);
+    return (g << (SK_G16_BITS - 4)) | (g >> (8 - SK_G16_BITS));
+}
+
+static inline unsigned SkB4444ToB565(unsigned b)
+{
+    SkASSERT(b <= 0xF);
+    return (b << (SK_B16_BITS - 4)) | (b >> (8 - SK_B16_BITS));
+}
+
+static inline SkPMColor16 SkPackARGB4444(unsigned a, unsigned r,
+                                         unsigned g, unsigned b)
+{
+    SkASSERT(a <= 0xF);
+    SkASSERT(r <= a);
+    SkASSERT(g <= a);
+    SkASSERT(b <= a);
+    
+    return (SkPMColor16)((a << SK_A4444_SHIFT) | (r << SK_R4444_SHIFT) |
+                         (g << SK_G4444_SHIFT) | (b << SK_B4444_SHIFT));
+}
+
+extern const uint16_t gMask_0F0F;
+
+inline U16CPU SkAlphaMulQ4(U16CPU c, unsigned scale)
+{
+    SkASSERT(scale <= 16);
+
+    const unsigned mask = 0xF0F;    //gMask_0F0F;
+    
+#if 0
+    unsigned rb = ((c & mask) * scale) >> 4;
+    unsigned ag = ((c >> 4) & mask) * scale;
+    return (rb & mask) | (ag & ~mask);
+#else
+    c = (c & mask) | ((c & (mask << 4)) << 12);
+    c = c * scale >> 4;
+    return (c & mask) | ((c >> 12) & (mask << 4));
+#endif
+}
+
+/** Expand the SkPMColor16 color into a 32bit value that can be scaled all at
+    once by a value up to 16. Used in conjunction with SkCompact_4444.
+*/
+inline uint32_t SkExpand_4444(U16CPU c)
+{
+    SkASSERT(c == (uint16_t)c);
+    
+    const unsigned mask = 0xF0F;    //gMask_0F0F;
+    return (c & mask) | ((c & ~mask) << 12);
+}
+
+/** Compress an expanded value (from SkExpand_4444) back down to a SkPMColor16.
+    NOTE: this explicitly does not clean the top 16 bits (which may be garbage).
+    It does this for speed, since if it is being written directly to 16bits of
+    memory, the top 16bits will be ignored. Casting the result to uint16_t here
+    would add 2 more instructions, slow us down. It is up to the caller to
+    perform the cast if needed.
+*/
+static inline U16CPU SkCompact_4444(uint32_t c)
+{
+    const unsigned mask = 0xF0F;    //gMask_0F0F;
+    return (c & mask) | ((c >> 12) & ~mask);
+}
+
+static inline uint16_t SkSrcOver4444To16(SkPMColor16 s, uint16_t d)
+{
+    unsigned sa = SkGetPackedA4444(s);
+    unsigned sr = SkR4444ToR565(SkGetPackedR4444(s));
+    unsigned sg = SkG4444ToG565(SkGetPackedG4444(s));
+    unsigned sb = SkB4444ToB565(SkGetPackedB4444(s));
+
+    // To avoid overflow, we have to clear the low bit of the synthetic sg
+    // if the src alpha is <= 7.
+    // to see why, try blending 0x4444 on top of 565-white and watch green
+    // overflow (sum == 64)
+    sg &= ~(~(sa >> 3) & 1);
+
+    unsigned scale = SkAlpha15To16(15 - sa);
+    unsigned dr = SkAlphaMul4(SkGetPackedR16(d), scale);
+    unsigned dg = SkAlphaMul4(SkGetPackedG16(d), scale);
+    unsigned db = SkAlphaMul4(SkGetPackedB16(d), scale);
+    
+#if 0
+    if (sg + dg > 63) {
+        SkDebugf("---- SkSrcOver4444To16 src=%x dst=%x scale=%d, sg=%d dg=%d\n", s, d, scale, sg, dg);
+    }
+#endif
+    return SkPackRGB16(sr + dr, sg + dg, sb + db);
+}
+
+static inline uint16_t SkBlend4444To16(SkPMColor16 src, uint16_t dst, int scale16)
+{
+    SkASSERT((unsigned)scale16 <= 16);
+    
+    return SkSrcOver4444To16(SkAlphaMulQ4(src, scale16), dst);
+}
+
+static inline uint16_t SkBlend4444(SkPMColor16 src, SkPMColor16 dst, int scale16)
+{
+    SkASSERT((unsigned)scale16 <= 16);
+    
+    uint32_t src32 = SkExpand_4444(src) * scale16;
+    // the scaled srcAlpha is the bottom byte
+#ifdef SK_DEBUG
+    {
+        unsigned srcA = SkGetPackedA4444(src) * scale16;
+        SkASSERT(srcA == (src32 & 0xFF));
+    }
+#endif
+    unsigned dstScale = SkAlpha255To256(255 - (src32 & 0xFF)) >> 4;
+    uint32_t dst32 = SkExpand_4444(dst) * dstScale;
+    return SkCompact_4444((src32 + dst32) >> 4);
+}
+
+static inline SkPMColor SkPixel4444ToPixel32(U16CPU c)
+{
+    uint32_t d = (SkGetPackedA4444(c) << SK_A32_SHIFT) |
+                 (SkGetPackedR4444(c) << SK_R32_SHIFT) |
+                 (SkGetPackedG4444(c) << SK_G32_SHIFT) |
+                 (SkGetPackedB4444(c) << SK_B32_SHIFT);
+    return d | (d << 4);
+}
+
+static inline SkPMColor16 SkPixel32ToPixel4444(SkPMColor c)
+{
+    return  (((c >> (SK_A32_SHIFT + 4)) & 0xF) << SK_A4444_SHIFT) |
+    (((c >> (SK_R32_SHIFT + 4)) & 0xF) << SK_R4444_SHIFT) |
+    (((c >> (SK_G32_SHIFT + 4)) & 0xF) << SK_G4444_SHIFT) |
+    (((c >> (SK_B32_SHIFT + 4)) & 0xF) << SK_B4444_SHIFT);
+}
+
+// cheap 2x2 dither
+static inline SkPMColor16 SkDitherARGB32To4444(U8CPU a, U8CPU r,
+                                               U8CPU g, U8CPU b)
+{
+    a = ((a << 1) - ((a >> 4 << 4) | (a >> 4))) >> 4;
+    r = ((r << 1) - ((r >> 4 << 4) | (r >> 4))) >> 4;
+    g = ((g << 1) - ((g >> 4 << 4) | (g >> 4))) >> 4;
+    b = ((b << 1) - ((b >> 4 << 4) | (b >> 4))) >> 4;
+    
+    return SkPackARGB4444(a, r, g, b);
+}
+
+static inline SkPMColor16 SkDitherPixel32To4444(SkPMColor c)
+{
+    return SkDitherARGB32To4444(SkGetPackedA32(c), SkGetPackedR32(c),
+                                SkGetPackedG32(c), SkGetPackedB32(c));
+}
+
+/*  Assumes 16bit is in standard RGBA order.
+    Transforms a normal ARGB_8888 into the same byte order as
+    expanded ARGB_4444, but keeps each component 8bits
+*/
+static uint32_t SkExpand_8888(SkPMColor c)
+{
+    return  (((c >> SK_R32_SHIFT) & 0xFF) << 24) |
+            (((c >> SK_G32_SHIFT) & 0xFF) <<  8) |
+            (((c >> SK_B32_SHIFT) & 0xFF) << 16) |
+            (((c >> SK_A32_SHIFT) & 0xFF) <<  0);
+}
+
+/*  Undo the operation of SkExpand_8888, turning the argument back into
+    a SkPMColor.
+*/
+static SkPMColor SkCompact_8888(uint32_t c)
+{
+    return  (((c >> 24) & 0xFF) << SK_R32_SHIFT) |
+            (((c >>  8) & 0xFF) << SK_G32_SHIFT) |
+            (((c >> 16) & 0xFF) << SK_B32_SHIFT) |
+            (((c >>  0) & 0xFF) << SK_A32_SHIFT);
+}
+
+/*  Like SkExpand_8888, this transforms a pmcolor into the expanded 4444 format,
+    but this routine just keeps the high 4bits of each component in the low
+    4bits of the result (just like a newly expanded PMColor16).
+*/
+static uint32_t SkExpand32_4444(SkPMColor c)
+{
+    return  (((c >> (SK_R32_SHIFT + 4)) & 0xF) << 24) |
+            (((c >> (SK_G32_SHIFT + 4)) & 0xF) <<  8) |
+            (((c >> (SK_B32_SHIFT + 4)) & 0xF) << 16) |
+            (((c >> (SK_A32_SHIFT + 4)) & 0xF) <<  0);
+}
+
+// takes two values and alternamtes them as part of a memset16
+// used for cheap 2x2 dithering when the colors are opaque
+void sk_dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int n);
+
+#endif
+
diff --git a/include/core/SkColorShader.h b/include/core/SkColorShader.h
new file mode 100644
index 0000000..f9c3dc3
--- /dev/null
+++ b/include/core/SkColorShader.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2007 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 SkColorShader_DEFINED
+#define SkColorShader_DEFINED
+
+#include "SkShader.h"
+
+/** \class SkColorShader
+    A Shader that represents a single color. In general, this effect can be
+    accomplished by just using the color field on the paint, but if an
+    actual shader object is needed, this provides that feature.
+*/
+class SkColorShader : public SkShader {
+public:
+    /** Create a ColorShader that will inherit its color from the Paint
+        at draw time.
+    */
+    SkColorShader() : fInheritColor(true) {}
+    /** Create a ColorShader that ignores the color in the paint, and uses the
+        specified color. Note: like all shaders, at draw time the paint's alpha
+        will be respected, and is applied to the specified color.
+    */
+    SkColorShader(SkColor c) : fColor(c), fInheritColor(false) {}
+    
+    virtual uint32_t getFlags();
+    virtual uint8_t getSpan16Alpha() const;
+    virtual bool setContext(const SkBitmap& device, const SkPaint& paint,
+                            const SkMatrix& matrix);
+    virtual void shadeSpan(int x, int y, SkPMColor span[], int count);
+    virtual void shadeSpan16(int x, int y, uint16_t span[], int count);
+    virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count);
+
+protected:
+    SkColorShader(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { 
+        return SkNEW_ARGS(SkColorShader, (buffer)); 
+    }
+    SkColor     fColor;         // ignored if fInheritColor is true
+    SkPMColor   fPMColor;       // cached after setContext()
+    uint16_t    fColor16;       // cached after setContext()
+    SkBool8     fInheritColor;
+
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/include/core/SkComposeShader.h b/include/core/SkComposeShader.h
new file mode 100644
index 0000000..0b198f6
--- /dev/null
+++ b/include/core/SkComposeShader.h
@@ -0,0 +1,66 @@
+/*
+ * 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 SkComposeShader_DEFINED
+#define SkComposeShader_DEFINED
+
+#include "SkShader.h"
+
+class SkXfermode;
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+/** \class SkComposeShader
+    This subclass of shader returns the coposition of two other shaders, combined by
+    a xfermode.
+*/
+class SkComposeShader : public SkShader {
+public:
+    /** Create a new compose shader, given shaders A, B, and a combining xfermode mode.
+        When the xfermode is called, it will be given the result from shader A as its
+        "dst", and the result of from shader B as its "src".
+        mode->xfer32(sA_result, sB_result, ...)
+        @param shaderA  The colors from this shader are seen as the "dst" by the xfermode
+        @param shaderB  The colors from this shader are seen as the "src" by the xfermode
+        @param mode     The xfermode that combines the colors from the two shaders. If mode
+                        is null, then SRC_OVER is assumed.
+    */
+    SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode = NULL);
+    virtual ~SkComposeShader();
+    
+    // override
+    virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix);
+    virtual void shadeSpan(int x, int y, SkPMColor result[], int count);
+    virtual void beginSession();
+    virtual void endSession();
+
+protected:
+    SkComposeShader(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { 
+        return SkNEW_ARGS(SkComposeShader, (buffer)); }
+
+    SkShader*   fShaderA;
+    SkShader*   fShaderB;
+    SkXfermode* fMode;
+
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/include/core/SkDeque.h b/include/core/SkDeque.h
new file mode 100644
index 0000000..cbed930
--- /dev/null
+++ b/include/core/SkDeque.h
@@ -0,0 +1,74 @@
+/*
+ * 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 SkDeque_DEFINED
+#define SkDeque_DEFINED
+
+#include "SkTypes.h"
+
+class SkDeque : SkNoncopyable {
+public:
+    explicit SkDeque(size_t elemSize);
+    SkDeque(size_t elemSize, void* storage, size_t storageSize);
+    ~SkDeque();
+
+    bool    empty() const { return 0 == fCount; }
+    int     count() const { return fCount; }
+    size_t  elemSize() const { return fElemSize; }
+
+    const void* front() const;
+    const void* back() const;
+
+    void* front() {
+        return (void*)((const SkDeque*)this)->front();
+    }
+
+    void* back() {
+        return (void*)((const SkDeque*)this)->back();
+    }
+
+    void* push_front();
+    void* push_back();
+    
+    void pop_front();
+    void pop_back();
+
+private:
+    struct Head;
+
+public:
+    class Iter {
+    public:
+        Iter(const SkDeque& d);
+        void* next();
+
+    private:
+        SkDeque::Head*  fHead;
+        char*           fPos;
+        size_t          fElemSize;
+    };
+
+private:
+    Head*   fFront;
+    Head*   fBack;
+    size_t  fElemSize;
+    void*   fInitialStorage;
+    int     fCount;
+    
+    friend class Iter;
+};
+
+#endif
diff --git a/include/core/SkDescriptor.h b/include/core/SkDescriptor.h
new file mode 100644
index 0000000..8074cff
--- /dev/null
+++ b/include/core/SkDescriptor.h
@@ -0,0 +1,187 @@
+/*
+ * 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 SkDescriptor_DEFINED
+#define SkDescriptor_DEFINED
+
+#include "SkTypes.h"
+
+class SkDescriptor : SkNoncopyable {
+public:
+    static size_t ComputeOverhead(int entryCount)
+    {
+        SkASSERT(entryCount >= 0);
+        return sizeof(SkDescriptor) + entryCount * sizeof(Entry);
+    }
+
+    static SkDescriptor* Alloc(size_t length)
+    {
+        SkASSERT(SkAlign4(length) == length);
+        SkDescriptor* desc = (SkDescriptor*)sk_malloc_throw(length);
+        return desc;
+    }
+
+    static void Free(SkDescriptor* desc)
+    {
+        sk_free(desc);
+    }
+
+    void init()
+    {
+        fLength = sizeof(SkDescriptor);
+        fCount  = 0;
+    }
+
+    uint32_t getLength() const { return fLength; }
+
+    void* addEntry(uint32_t tag, uint32_t length, const void* data = NULL)
+    {
+        SkASSERT(tag);
+        SkASSERT(SkAlign4(length) == length);
+        SkASSERT(this->findEntry(tag, NULL) == NULL);
+
+        Entry*  entry = (Entry*)((char*)this + fLength);
+        entry->fTag = tag;
+        entry->fLen = length;
+        if (data)
+            memcpy(entry + 1, data, length);
+
+        fCount += 1;
+        fLength += sizeof(Entry) + length;
+        return (entry + 1); // return its data
+    }
+
+    void computeChecksum()
+    {
+        fChecksum = SkDescriptor::ComputeChecksum(this);
+    }
+
+#ifdef SK_DEBUG
+    void assertChecksum() const
+    {
+        SkASSERT(fChecksum == SkDescriptor::ComputeChecksum(this));
+    }
+#endif
+
+    const void* findEntry(uint32_t tag, uint32_t* length) const
+    {
+        const Entry* entry = (const Entry*)(this + 1);
+        int          count = fCount;
+
+        while (--count >= 0)
+        {
+            if (entry->fTag == tag)
+            {
+                if (length)
+                    *length = entry->fLen;
+                return entry + 1;
+            }
+            entry = (const Entry*)((const char*)(entry + 1) + entry->fLen);
+        }
+        return NULL;
+    }
+
+    SkDescriptor* copy() const
+    {
+        SkDescriptor* desc = SkDescriptor::Alloc(fLength);
+        memcpy(desc, this, fLength);
+        return desc;
+    }
+
+    bool equals(const SkDescriptor& other) const
+    {
+        // probe to see if we have a good checksum algo
+//        SkASSERT(a.fChecksum != b.fChecksum || memcmp(&a, &b, a.fLength) == 0);
+
+        // the first value we should look at is the checksum, so this loop
+        // should terminate early if they descriptors are different.
+        // NOTE: if we wrote a sentinel value at the end of each, we chould
+        //       remove the aa < stop test in the loop...
+        const uint32_t* aa = (const uint32_t*)this;
+        const uint32_t* bb = (const uint32_t*)&other;
+        const uint32_t* stop = (const uint32_t*)((const char*)aa + fLength);
+        do {
+            if (*aa++ != *bb++)
+                return false;
+        } while (aa < stop);
+        return true;
+    }
+
+    struct Entry {
+        uint32_t fTag;
+        uint32_t fLen;
+    };
+
+#ifdef SK_DEBUG
+    uint32_t getChecksum() const { return fChecksum; }
+    uint32_t getCount() const { return fCount; }
+#endif
+
+private:
+    uint32_t fChecksum;  // must be first
+    uint32_t fLength;    // must be second
+    uint32_t fCount;
+
+    static uint32_t ComputeChecksum(const SkDescriptor* desc)
+    {
+        const uint32_t*  ptr = (const uint32_t*)desc + 1; // skip the checksum field
+        const uint32_t*  stop = (const uint32_t*)((const char*)desc + desc->fLength);
+        uint32_t         sum = 0;
+
+        SkASSERT(ptr < stop);
+        do {
+            sum = (sum << 1) | (sum >> 31);
+            sum ^= *ptr++;
+        } while (ptr < stop);
+
+        return sum;
+    }
+    
+    // private so no one can create one except our factories
+    SkDescriptor() {}
+};
+
+#include "SkScalerContext.h"
+
+class SkAutoDescriptor : SkNoncopyable {
+public:
+    SkAutoDescriptor(size_t size)
+    {
+        if (size <= sizeof(fStorage))
+            fDesc = (SkDescriptor*)(void*)fStorage;
+        else
+            fDesc = SkDescriptor::Alloc(size);
+    }
+    ~SkAutoDescriptor()
+    {
+        if (fDesc != (SkDescriptor*)(void*)fStorage)
+            SkDescriptor::Free(fDesc);
+    }
+    SkDescriptor* getDesc() const { return fDesc; }
+private:
+    enum {
+        kStorageSize =  sizeof(SkDescriptor)
+                        + sizeof(SkDescriptor::Entry) + sizeof(SkScalerContext::Rec)    // for rec
+                        + sizeof(SkDescriptor::Entry) + sizeof(void*)                   // for typeface
+                        + 32   // slop for occational small extras
+    };
+    SkDescriptor*   fDesc;
+    uint32_t        fStorage[(kStorageSize + 3) >> 2];
+};
+
+
+#endif
+
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
new file mode 100644
index 0000000..4d678c6
--- /dev/null
+++ b/include/core/SkDevice.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkDevice_DEFINED
+#define SkDevice_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+
+class SkDraw;
+struct SkIRect;
+class SkMatrix;
+class SkRegion;
+
+class SkDevice : public SkRefCnt {
+public:
+    SkDevice();
+    /** Construct a new device, extracting the width/height/config/isOpaque values from
+        the bitmap. If transferPixelOwnership is true, and the bitmap claims to own its
+        own pixels (getOwnsPixels() == true), then transfer this responsibility to the
+        device, and call setOwnsPixels(false) on the bitmap.
+        
+        Subclasses may override the destructor, which is virtual, even though this class
+        doesn't have one. SkRefCnt does.
+    
+        @param bitmap   A copy of this bitmap is made and stored in the device
+    */
+    SkDevice(const SkBitmap& bitmap);
+
+    /** Return the width of the device (in pixels).
+    */
+    int width() const { return fBitmap.width(); }
+    /** Return the height of the device (in pixels).
+    */
+    int height() const { return fBitmap.height(); }
+    /** Return the bitmap config of the device's pixels
+    */
+    SkBitmap::Config config() const { return fBitmap.getConfig(); }
+    /** Returns true if the device's bitmap's config treats every pixels as
+        implicitly opaque.
+    */
+    bool isOpaque() const { return fBitmap.isOpaque(); }
+    
+    /** Return the bounds of the device
+    */
+    void getBounds(SkIRect* bounds) const;
+    
+    /** Return true if the specified rectangle intersects the bounds of the
+        device. If sect is not NULL and there is an intersection, sect returns
+        the intersection.
+    */
+    bool intersects(const SkIRect& r, SkIRect* sect = NULL) const;
+
+    /** Return the bitmap associated with this device. Call this each time you need
+        to access the bitmap, as it notifies the subclass to perform any flushing
+        etc. before you examine the pixels.
+        @param changePixels set to true if the caller plans to change the pixels
+        @return the device's bitmap
+    */
+    const SkBitmap& accessBitmap(bool changePixels);
+
+    /** Helper to erase the entire device to the specified color (including
+        alpha).
+    */
+    void eraseColor(SkColor eraseColor);
+
+    /** Called when this device is installed into a Canvas. Balanaced by a call
+        to unlockPixels() when the device is removed from a Canvas.
+    */
+    virtual void lockPixels();
+    virtual void unlockPixels();
+
+    /** Called with the correct matrix and clip before this device is drawn
+        to using those settings. If your subclass overrides this, be sure to
+        call through to the base class as well.
+    */
+    virtual void setMatrixClip(const SkMatrix&, const SkRegion&);
+
+    /** Called when this device gains focus (i.e becomes the current device
+        for drawing).
+    */
+    virtual void gainFocus(SkCanvas*) {}
+
+    /** 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& path,
+                          const SkPaint& paint);
+    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+                            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&);
+
+protected:
+    /** Update as needed the pixel value in the bitmap, so that the caller can access
+        the pixels directly. Note: only the pixels field should be altered. The config/width/height/rowbytes
+        must remain unchanged.
+    */
+    virtual void onAccessBitmap(SkBitmap*);
+
+private:
+    SkBitmap fBitmap;
+};
+
+#endif
diff --git a/include/core/SkDither.h b/include/core/SkDither.h
new file mode 100644
index 0000000..5b2552d
--- /dev/null
+++ b/include/core/SkDither.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkDither_DEFINED
+#define SkDither_DEFINED
+
+#include "SkColorPriv.h"
+
+#define SK_DitherValueMax4444   15
+#define SK_DitherValueMax565    7
+
+/*  need to use macros for bit-counts for each component, and then
+    move these into SkColorPriv.h
+*/
+
+#define SkDITHER_R32_FOR_565_MACRO(r, d)    (r + d - (r >> 5))
+#define SkDITHER_G32_FOR_565_MACRO(g, d)    (g + (d >> 1) - (g >> 6))
+#define SkDITHER_B32_FOR_565_MACRO(b, d)    (b + d - (b >> 5))
+
+#define SkDITHER_A32_FOR_4444_MACRO(a, d)    (a + 15 - (a >> 4))
+#define SkDITHER_R32_FOR_4444_MACRO(r, d)    (r + d - (r >> 4))
+#define SkDITHER_G32_FOR_4444_MACRO(g, d)    (g + d - (g >> 4))
+#define SkDITHER_B32_FOR_4444_MACRO(b, d)    (b + d - (b >> 4))
+
+#ifdef SK_DEBUG
+    inline unsigned SkDITHER_R32_FOR_565(unsigned r, unsigned d)
+    {
+        SkASSERT(d <= SK_DitherValueMax565);
+        SkA32Assert(r);
+        r = SkDITHER_R32_FOR_565_MACRO(r, d);
+        SkA32Assert(r);
+        return r;
+    }
+    inline unsigned SkDITHER_G32_FOR_565(unsigned g, unsigned d)
+    {
+        SkASSERT(d <= SK_DitherValueMax565);
+        SkG32Assert(g);
+        g = SkDITHER_G32_FOR_565_MACRO(g, d);
+        SkG32Assert(g);
+        return g;
+    }
+    inline unsigned SkDITHER_B32_FOR_565(unsigned b, unsigned d)
+    {
+        SkASSERT(d <= SK_DitherValueMax565);
+        SkB32Assert(b);
+        b = SkDITHER_B32_FOR_565_MACRO(b, d);
+        SkB32Assert(b);
+        return b;
+    }
+#else
+    #define SkDITHER_R32_FOR_565(r, d)  SkDITHER_R32_FOR_565_MACRO(r, d)
+    #define SkDITHER_G32_FOR_565(g, d)  SkDITHER_G32_FOR_565_MACRO(g, d)
+    #define SkDITHER_B32_FOR_565(b, d)  SkDITHER_B32_FOR_565_MACRO(b, d)
+#endif
+
+#define SkDITHER_R32To565(r, d)  SkR32ToR16(SkDITHER_R32_FOR_565(r, d))
+#define SkDITHER_G32To565(g, d)  SkG32ToG16(SkDITHER_G32_FOR_565(g, d))
+#define SkDITHER_B32To565(b, d)  SkB32ToB16(SkDITHER_B32_FOR_565(b, d))
+
+#define SkDITHER_A32To4444(a, d)  SkA32To4444(SkDITHER_A32_FOR_4444_MACRO(a, d))
+#define SkDITHER_R32To4444(r, d)  SkR32To4444(SkDITHER_R32_FOR_4444_MACRO(r, d))
+#define SkDITHER_G32To4444(g, d)  SkG32To4444(SkDITHER_G32_FOR_4444_MACRO(g, d))
+#define SkDITHER_B32To4444(b, d)  SkB32To4444(SkDITHER_B32_FOR_4444_MACRO(b, d))
+
+static inline SkPMColor SkDitherARGB32For565(SkPMColor c, unsigned dither)
+{
+    SkASSERT(dither <= SK_DitherValueMax565);
+    
+    unsigned sa = SkGetPackedA32(c);
+    dither = SkAlphaMul(dither, SkAlpha255To256(sa));
+
+    unsigned sr = SkGetPackedR32(c);
+    unsigned sg = SkGetPackedG32(c);
+    unsigned sb = SkGetPackedB32(c);
+    sr = SkDITHER_R32_FOR_565(sr, dither);
+    sg = SkDITHER_G32_FOR_565(sg, dither);
+    sb = SkDITHER_B32_FOR_565(sb, dither);
+    
+    return SkPackARGB32(sa, sr, sg, sb);
+}
+
+static inline SkPMColor SkDitherRGB32For565(SkPMColor c, unsigned dither)
+{
+    SkASSERT(dither <= SK_DitherValueMax565);
+    
+    unsigned sr = SkGetPackedR32(c);
+    unsigned sg = SkGetPackedG32(c);
+    unsigned sb = SkGetPackedB32(c);
+    sr = SkDITHER_R32_FOR_565(sr, dither);
+    sg = SkDITHER_G32_FOR_565(sg, dither);
+    sb = SkDITHER_B32_FOR_565(sb, dither);
+    
+    return SkPackARGB32(0xFF, sr, sg, sb);
+}
+
+static inline uint16_t SkDitherRGBTo565(U8CPU r, U8CPU g, U8CPU b,
+                                              unsigned dither)
+{
+    SkASSERT(dither <= SK_DitherValueMax565);
+    r = SkDITHER_R32To565(r, dither);
+    g = SkDITHER_G32To565(g, dither);
+    b = SkDITHER_B32To565(b, dither);
+    return SkPackRGB16(r, g, b);
+}
+
+static inline uint16_t SkDitherRGB32To565(SkPMColor c, unsigned dither)
+{
+    SkASSERT(dither <= SK_DitherValueMax565);
+    
+    unsigned sr = SkGetPackedR32(c);
+    unsigned sg = SkGetPackedG32(c);
+    unsigned sb = SkGetPackedB32(c);
+    sr = SkDITHER_R32To565(sr, dither);
+    sg = SkDITHER_G32To565(sg, dither);
+    sb = SkDITHER_B32To565(sb, dither);
+    
+    return SkPackRGB16(sr, sg, sb);
+}
+
+static inline uint16_t SkDitherARGB32To565(U8CPU sa, SkPMColor c, unsigned dither)
+{
+    SkASSERT(dither <= SK_DitherValueMax565);    
+    dither = SkAlphaMul(dither, SkAlpha255To256(sa));
+    
+    unsigned sr = SkGetPackedR32(c);
+    unsigned sg = SkGetPackedG32(c);
+    unsigned sb = SkGetPackedB32(c);
+    sr = SkDITHER_R32To565(sr, dither);
+    sg = SkDITHER_G32To565(sg, dither);
+    sb = SkDITHER_B32To565(sb, dither);
+    
+    return SkPackRGB16(sr, sg, sb);
+}
+
+///////////////////////// 4444
+
+static inline SkPMColor16 SkDitherARGB32To4444(U8CPU a, U8CPU r, U8CPU g,
+                                               U8CPU b, unsigned dither)
+{
+    dither = SkAlphaMul(dither, SkAlpha255To256(a));
+
+    a = SkDITHER_A32To4444(a, dither);
+    r = SkDITHER_R32To4444(r, dither);
+    g = SkDITHER_G32To4444(g, dither);
+    b = SkDITHER_B32To4444(b, dither);
+    
+    return SkPackARGB4444(a, r, g, b);
+}
+
+static inline SkPMColor16 SkDitherARGB32To4444(SkPMColor c, unsigned dither)
+{
+    unsigned a = SkGetPackedA32(c);
+    unsigned r = SkGetPackedR32(c);
+    unsigned g = SkGetPackedG32(c);
+    unsigned b = SkGetPackedB32(c);
+
+    dither = SkAlphaMul(dither, SkAlpha255To256(a));
+
+    a = SkDITHER_A32To4444(a, dither);
+    r = SkDITHER_R32To4444(r, dither);
+    g = SkDITHER_G32To4444(g, dither);
+    b = SkDITHER_B32To4444(b, dither);
+    
+    return SkPackARGB4444(a, r, g, b);
+}
+
+// TODO: need dither routines for 565 -> 4444
+
+// this toggles between a 4x4 and a 1x4 array
+//#define ENABLE_DITHER_MATRIX_4X4
+
+#ifdef ENABLE_DITHER_MATRIX_4X4
+    extern const uint8_t gDitherMatrix_4Bit_4X4[4][4];
+    extern const uint8_t gDitherMatrix_3Bit_4X4[4][4];
+
+    #define DITHER_4444_SCAN(y) const uint8_t* dither_scan = gDitherMatrix_4Bit_4X4[(y) & 3]
+    #define DITHER_565_SCAN(y)  const uint8_t* dither_scan = gDitherMatrix_3Bit_4X4[(y) & 3]
+
+    #define DITHER_VALUE(x) dither_scan[(x) & 3]
+#else
+    extern const uint16_t gDitherMatrix_4Bit_16[4];
+    extern const uint16_t gDitherMatrix_3Bit_16[4];
+
+    #define DITHER_4444_SCAN(y) const uint16_t dither_scan = gDitherMatrix_4Bit_16[(y) & 3]
+    #define DITHER_565_SCAN(y)  const uint16_t dither_scan = gDitherMatrix_3Bit_16[(y) & 3]
+
+    #define DITHER_VALUE(x) ((dither_scan >> (((x) & 3) << 2)) & 0xF)
+#endif
+
+#define DITHER_INC_X(x) ++(x)
+
+#endif
diff --git a/include/core/SkDraw.h b/include/core/SkDraw.h
new file mode 100644
index 0000000..2d775a4
--- /dev/null
+++ b/include/core/SkDraw.h
@@ -0,0 +1,124 @@
+/*
+ * 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 SkDraw_DEFINED
+#define SkDraw_DEFINED
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkMask.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkRect.h"
+#include "SkAutoKern.h"
+
+class SkBounder;
+class SkDevice;
+class SkPath;
+class SkRegion;
+struct SkDrawProcs;
+
+class SkDraw {
+public:
+    SkDraw() : fDevice(NULL), fBounder(NULL), fProcs(NULL) {}
+    SkDraw(const SkDraw& src);
+
+    void    drawPaint(const SkPaint&) const;
+    void    drawPoints(SkCanvas::PointMode, size_t count, const SkPoint[],
+                       const SkPaint&) const;
+    void    drawRect(const SkRect&, const SkPaint&) const;
+    /*  To save on mallocs, we allow a flag that tells us that srcPath is
+        mutable, so that we don't have to make copies of it as we transform it.
+    */
+    void    drawPath(const SkPath& srcPath, const SkPaint&,
+                     const SkMatrix* prePathMatrix, bool pathIsMutable) const;
+    void    drawBitmap(const SkBitmap&, const SkMatrix&, const SkPaint&) const;
+    void    drawSprite(const SkBitmap&, int x, int y, const SkPaint&) const;
+    void    drawText(const char text[], size_t byteLength, SkScalar x,
+                     SkScalar y, const SkPaint& paint) const;
+    void    drawPosText(const char text[], size_t byteLength,
+                        const SkScalar pos[], SkScalar constY,
+                        int scalarsPerPosition, const SkPaint& paint) const;
+    void    drawTextOnPath(const char text[], size_t byteLength,
+                        const SkPath&, const SkMatrix*, const SkPaint&) const;
+    void    drawVertices(SkCanvas::VertexMode mode, int count,
+                         const SkPoint vertices[], const SkPoint textures[],
+                         const SkColor colors[], SkXfermode* xmode,
+                         const uint16_t indices[], int ptCount,
+                         const SkPaint& paint) const;
+        
+    void drawPath(const SkPath& src, const SkPaint& paint) const {
+        this->drawPath(src, paint, NULL, false);
+    }
+
+    /** Helper function that creates a mask from a path and an optional maskfilter.
+        Note however, that the resulting mask will not have been actually filtered,
+        that must be done afterwards (by calling filterMask). The maskfilter is provided
+        solely to assist in computing the mask's bounds (if the mode requests that).
+    */
+    static bool DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
+                           SkMaskFilter* filter, const SkMatrix* filterMatrix,
+                           SkMask* mask, SkMask::CreateMode mode);
+
+private:
+    void    drawText_asPaths(const char text[], size_t byteLength,
+                             SkScalar x, SkScalar y, const SkPaint&) const;
+    void    drawDevMask(const SkMask& mask, const SkPaint&) const;
+    void    drawBitmapAsMask(const SkBitmap&, const SkPaint&) const;
+
+public:
+    const SkBitmap* fBitmap;        // required
+    const SkMatrix* fMatrix;        // required
+    const SkRegion* fClip;          // required
+    SkDevice*       fDevice;        // optional
+    SkBounder*      fBounder;       // optional
+    SkDrawProcs*    fProcs;         // optional
+
+#ifdef SK_DEBUG
+    void    validate() const;
+#endif
+};
+
+#include "SkGlyphCache.h"
+
+class SkTextToPathIter {
+public:
+    SkTextToPathIter(const char text[], size_t length, const SkPaint&,
+                     bool applyStrokeAndPathEffects, bool forceLinearTextOn);
+    ~SkTextToPathIter();
+
+    const SkPaint&  getPaint() const { return fPaint; }
+    SkScalar        getPathScale() const { return fScale; }
+
+    const SkPath*   next(SkScalar* xpos);   //!< returns nil when there are no more paths
+
+private:
+    SkGlyphCache*   fCache;
+    SkPaint         fPaint;
+    SkScalar        fScale;
+    SkFixed         fPrevAdvance;
+    const char*     fText;
+    const char*     fStop;
+    SkMeasureCacheProc fGlyphCacheProc;
+
+    const SkPath*   fPath;      // returned in next
+    SkScalar        fXPos;      // accumulated xpos, returned in next
+    SkAutoKern      fAutoKern;
+};
+
+#endif
+
+
diff --git a/include/core/SkDrawFilter.h b/include/core/SkDrawFilter.h
new file mode 100644
index 0000000..db5a685
--- /dev/null
+++ b/include/core/SkDrawFilter.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkDrawFilter_DEFINED
+#define SkDrawFilter_DEFINED
+
+#include "SkRefCnt.h"
+
+////////////////// EXPERIMENTAL //////////////////////////
+
+class SkCanvas;
+class SkPaint;
+
+/** Right before something is being draw, filter() is called with the
+    current canvas and paint. If it returns true, then drawing proceeds
+    with the (possibly modified) canvas/paint, and then restore() is called
+    to restore the canvas/paint to their state before filter() was called.
+    If filter returns false, canvas/paint should not have been changed, and
+    restore() will not be called.
+*/
+class SkDrawFilter : public SkRefCnt {
+public:
+    enum Type {
+        kPaint_Type,
+        kPoint_Type,
+        kLine_Type,
+        kBitmap_Type,
+        kRect_Type,
+        kPath_Type,
+        kText_Type
+    };
+
+    /** Return true to allow the draw to continue (with possibly modified
+        canvas/paint). If true is returned, then restore() will be called.
+    */
+    virtual bool filter(SkCanvas*, SkPaint*, Type) = 0;
+    /** If filter() returned true, then restore() will be called to restore the
+        canvas/paint to their previous states
+    */
+    virtual void restore(SkCanvas*, SkPaint*, Type) = 0;
+};
+
+#endif
diff --git a/include/core/SkDrawLooper.h b/include/core/SkDrawLooper.h
new file mode 100644
index 0000000..333fb41
--- /dev/null
+++ b/include/core/SkDrawLooper.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkDrawLooper_DEFINED
+#define SkDrawLooper_DEFINED
+
+#include "SkFlattenable.h"
+
+////////////////// EXPERIMENTAL //////////////////////////
+
+class SkCanvas;
+class SkPaint;
+
+/** \class SkDrawLooper
+    Subclasses of SkDrawLooper can be attached to a SkPaint. Where they are,
+    and something is drawn to a canvas with that paint, the looper subclass will
+    be called, allowing it to modify the canvas and/or paint for that draw call.
+    More than that, via the next() method, the looper can modify the draw to be
+    invoked multiple times (hence the name loop-er), allow it to perform effects
+    like shadows or frame/fills, that require more than one pass.
+*/
+class SkDrawLooper : public SkFlattenable {
+public:
+    /** Called right before something is being drawn to the specified canvas
+        with the specified paint. Subclass that want to modify either parameter
+        can do so now.
+    */
+    virtual void init(SkCanvas*, SkPaint*) {}
+    /** Called in a loop (after init()). Each time true is returned, the object
+        is drawn (possibly with a modified canvas and/or paint). When false is
+        finally returned, drawing for the object stops.
+    */
+    virtual bool next() { return false; }
+    /** Called after the looper has finally returned false from next(), allowing
+        the looper to restore the canvas/paint to their original states.
+        is this required, since the subclass knows when it is done???
+        should we pass the canvas/paint here, and/or to the next call
+        so that subclasses don't need to retain pointers to them during the 
+        loop?
+    */
+    virtual void restore() {}
+    
+protected:
+    SkDrawLooper() {}
+    SkDrawLooper(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
diff --git a/include/core/SkEndian.h b/include/core/SkEndian.h
new file mode 100644
index 0000000..f08a9a9
--- /dev/null
+++ b/include/core/SkEndian.h
@@ -0,0 +1,98 @@
+/*
+ * 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 SkEndian_DEFINED
+#define SkEndian_DEFINED
+
+#include "SkTypes.h"
+
+/** \file SkEndian.h
+
+    Macros and helper functions for handling 16 and 32 bit values in
+    big and little endian formats.
+*/
+
+#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN)
+    #error "can't have both LENDIAN and BENDIAN defined"
+#endif
+
+#if !defined(SK_CPU_LENDIAN) && !defined(SK_CPU_BENDIAN)
+    #error "need either LENDIAN or BENDIAN defined"
+#endif
+
+/** Swap the two bytes in the low 16bits of the parameters.
+    e.g. 0x1234 -> 0x3412
+*/
+inline uint16_t SkEndianSwap16(U16CPU value)
+{
+    SkASSERT(value == (uint16_t)value);
+    return (uint16_t)((value >> 8) | (value << 8));
+}
+
+/** Vector version of SkEndianSwap16(), which swaps the
+    low two bytes of each value in the array.
+*/
+inline void SkEndianSwap16s(uint16_t array[], int count)
+{
+    SkASSERT(count == 0 || array != NULL);
+
+    while (--count >= 0)
+    {
+        *array = SkEndianSwap16(*array);
+        array += 1;
+    }
+}
+
+/** Reverse all 4 bytes in a 32bit value.
+    e.g. 0x12345678 -> 0x78563412
+*/
+inline uint32_t SkEndianSwap32(uint32_t value)
+{
+    return  ((value & 0xFF) << 24) |
+            ((value & 0xFF00) << 8) |
+            ((value & 0xFF0000) >> 8) |
+            (value >> 24);
+}
+
+/** Vector version of SkEndianSwap16(), which swaps the
+    bytes of each value in the array.
+*/
+inline void SkEndianSwap32s(uint32_t array[], int count)
+{
+    SkASSERT(count == 0 || array != NULL);
+
+    while (--count >= 0)
+    {
+        *array = SkEndianSwap32(*array);
+        array += 1;
+    }
+}
+
+#ifdef SK_CPU_LENDIAN
+    #define SkEndian_SwapBE16(n)    SkEndianSwap16(n)
+    #define SkEndian_SwapBE32(n)    SkEndianSwap32(n)
+    #define SkEndian_SwapLE16(n)    (n)
+    #define SkEndian_SwapLE32(n)    (n)
+#else   // SK_CPU_BENDIAN
+    #define SkEndian_SwapBE16(n)    (n)
+    #define SkEndian_SwapBE32(n)    (n)
+    #define SkEndian_SwapLE16(n)    SkEndianSwap16(n)
+    #define SkEndian_SwapLE32(n)    SkEndianSwap32(n)
+#endif
+
+
+#endif
+
diff --git a/include/core/SkFDot6.h b/include/core/SkFDot6.h
new file mode 100644
index 0000000..a4b9cf7
--- /dev/null
+++ b/include/core/SkFDot6.h
@@ -0,0 +1,67 @@
+/*
+ * 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 SkFDot6_DEFINED
+#define SkFDot6_DEFINED
+
+#include "SkMath.h"
+
+typedef int32_t SkFDot6;
+
+#define SK_FDot61           (64)
+#define SK_FDot6Half        (32)
+
+#ifdef SK_DEBUG
+    inline SkFDot6 SkIntToFDot6(S16CPU x)
+    {
+        SkASSERT(SkToS16(x) == x);
+        return x << 6;
+    }
+#else
+    #define SkIntToFDot6(x) ((x) << 6)
+#endif
+
+#define SkFDot6Floor(x)     ((x) >> 6)
+#define SkFDot6Ceil(x)      (((x) + 63) >> 6)
+#define SkFDot6Round(x)     (((x) + 32) >> 6)
+
+#define SkFixedToFDot6(x)   ((x) >> 10)
+
+inline SkFixed SkFDot6ToFixed(SkFDot6 x)
+{
+    SkASSERT((x << 10 >> 10) == x);
+
+    return x << 10;
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define SkScalarToFDot6(x)  (SkFDot6)((x) * 64)
+#else
+    #define SkScalarToFDot6(x)  ((x) >> 10)
+#endif
+
+inline SkFixed SkFDot6Div(SkFDot6 a, SkFDot6 b)
+{
+    SkASSERT(b != 0);
+
+    if (a == (int16_t)a)
+        return (a << 16) / b;
+    else
+        return SkFixedDiv(a, b);
+}
+
+#endif
+
diff --git a/include/core/SkFixed.h b/include/core/SkFixed.h
new file mode 100644
index 0000000..be4bf99
--- /dev/null
+++ b/include/core/SkFixed.h
@@ -0,0 +1,253 @@
+/*
+ * 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 SkFixed_DEFINED
+#define SkFixed_DEFINED
+
+#include "SkTypes.h"
+
+/** \file SkFixed.h
+
+    Types and macros for 16.16 fixed point
+*/
+
+/** 32 bit signed integer used to represent fractions values with 16 bits to the right of the decimal point
+*/
+typedef int32_t             SkFixed;
+#define SK_Fixed1           (1 << 16)
+#define SK_FixedHalf        (1 << 15)
+#define SK_FixedMax         (0x7FFFFFFF)
+#define SK_FixedMin         (0x1)
+#define SK_FixedNaN         ((int) 0x80000000)
+#define SK_FixedPI          (0x3243F)
+#define SK_FixedSqrt2       (92682)
+#define SK_FixedTanPIOver8  (0x6A0A)
+#define SK_FixedRoot2Over2  (0xB505)
+
+#ifdef SK_CAN_USE_FLOAT
+    #define SkFixedToFloat(x)   ((x) * 1.5258789e-5f)
+    #define SkFloatToFixed(x)   ((SkFixed)((x) * SK_Fixed1))
+
+    #define SkFixedToDouble(x)  ((x) * 1.5258789e-5)
+    #define SkDoubleToFixed(x)  ((SkFixed)((x) * SK_Fixed1))
+#endif
+
+/** 32 bit signed integer used to represent fractions values with 30 bits to the right of the decimal point
+*/
+typedef int32_t             SkFract;
+#define SK_Fract1           (1 << 30)
+#define Sk_FracHalf         (1 << 29)
+#define SK_FractPIOver180   (0x11DF46A)
+
+#ifdef SK_CAN_USE_FLOAT
+    #define SkFractToFloat(x)   ((float)(x) * 0.00000000093132257f)
+    #define SkFloatToFract(x)   ((SkFract)((x) * SK_Fract1))
+#endif
+
+/** Converts an integer to a SkFixed, asserting that the result does not overflow
+    a 32 bit signed integer
+*/
+#ifdef SK_DEBUG
+    inline SkFixed SkIntToFixed(int n)
+    {
+        SkASSERT(n >= -32768 && n <= 32767);
+        return n << 16;
+    }
+#else
+    //  force the cast to SkFixed to ensure that the answer is signed (like the debug version)
+    #define SkIntToFixed(n)     (SkFixed)((n) << 16)
+#endif
+
+/** Converts a SkFixed to a SkFract, asserting that the result does not overflow
+    a 32 bit signed integer
+*/
+#ifdef SK_DEBUG
+    inline SkFract SkFixedToFract(SkFixed x)
+    {
+        SkASSERT(x >= (-2 << 16) && x <= (2 << 16) - 1);
+        return x << 14;
+    }
+#else
+    #define SkFixedToFract(x)   ((x) << 14)
+#endif
+
+/** Returns the signed fraction of a SkFixed
+*/
+inline SkFixed SkFixedFraction(SkFixed x)
+{
+    SkFixed mask = x >> 31 << 16;
+    return (x & 0xFFFF) | mask;
+}
+
+/** Converts a SkFract to a SkFixed
+*/
+#define SkFractToFixed(x)   ((x) >> 14)
+/** Round a SkFixed to an integer
+*/
+#define SkFixedRound(x)     (((x) + SK_FixedHalf) >> 16)
+#define SkFixedCeil(x)      (((x) + SK_Fixed1 - 1) >> 16)
+#define SkFixedFloor(x)     ((x) >> 16)
+#define SkFixedAbs(x)       SkAbs32(x)
+#define SkFixedAve(a, b)    (((a) + (b)) >> 1)
+
+SkFixed SkFixedMul_portable(SkFixed, SkFixed);
+SkFract SkFractMul_portable(SkFract, SkFract);
+inline SkFixed SkFixedSquare_portable(SkFixed value)
+{
+    uint32_t a = SkAbs32(value);
+    uint32_t ah = a >> 16;
+    uint32_t al = a & 0xFFFF;
+    return ah * a + al * ah + (al * al >> 16);
+}
+
+#define SkFixedDiv(numer, denom)    SkDivBits(numer, denom, 16)
+SkFixed SkFixedDivInt(int32_t numer, int32_t denom);
+SkFixed SkFixedMod(SkFixed numer, SkFixed denom);
+#define SkFixedInvert(n)            SkDivBits(SK_Fixed1, n, 16)
+SkFixed SkFixedFastInvert(SkFixed n);
+#define SkFixedSqrt(n)              SkSqrtBits(n, 23)
+SkFixed SkFixedMean(SkFixed a, SkFixed b);  //*< returns sqrt(x*y)
+int SkFixedMulCommon(SkFixed, int , int bias);  // internal used by SkFixedMulFloor, SkFixedMulCeil, SkFixedMulRound
+
+#define SkFractDiv(numer, denom)    SkDivBits(numer, denom, 30)
+#define SkFractSqrt(n)              SkSqrtBits(n, 30)
+
+SkFixed SkFixedSinCos(SkFixed radians, SkFixed* cosValueOrNull);
+#define SkFixedSin(radians)         SkFixedSinCos(radians, NULL)
+inline SkFixed SkFixedCos(SkFixed radians)
+{
+    SkFixed cosValue;
+    (void)SkFixedSinCos(radians, &cosValue);
+    return cosValue;
+}
+SkFixed SkFixedTan(SkFixed radians);
+SkFixed SkFixedASin(SkFixed);
+SkFixed SkFixedACos(SkFixed);
+SkFixed SkFixedATan2(SkFixed y, SkFixed x);
+SkFixed SkFixedExp(SkFixed);
+SkFixed SkFixedLog(SkFixed);
+
+#define SK_FixedNearlyZero          (SK_Fixed1 >> 12)
+
+inline bool SkFixedNearlyZero(SkFixed x, SkFixed tolerance = SK_FixedNearlyZero)
+{
+    SkASSERT(tolerance > 0);
+    return SkAbs32(x) < tolerance;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+// Now look for ASM overrides for our portable versions (should consider putting this in its own file)
+
+#ifdef SkLONGLONG
+    inline SkFixed SkFixedMul_longlong(SkFixed a, SkFixed b)
+    {
+        return (SkFixed)((SkLONGLONG)a * b >> 16);
+    }
+    inline SkFract SkFractMul_longlong(SkFract a, SkFract b)
+    {
+        return (SkFixed)((SkLONGLONG)a * b >> 30);
+    }
+    inline SkFixed SkFixedSquare_longlong(SkFixed value)
+    {
+        return (SkFixed)((SkLONGLONG)value * value >> 16);
+    }
+    #define SkFixedMul(a,b)     SkFixedMul_longlong(a,b)
+    #define SkFractMul(a,b)     SkFractMul_longlong(a,b)
+    #define SkFixedSquare(a)    SkFixedSquare_longlong(a)
+#endif
+
+#if defined(__arm__) && !defined(__thumb__)
+    /* This guy does not handle NaN or other obscurities, but is faster than
+       than (int)(x*65536) when we only have software floats
+    */
+    inline SkFixed SkFloatToFixed_arm(float x)
+    {
+        register int32_t y, z;
+        asm("movs    %1, %3, lsl #1         \n"
+            "mov     %2, #0x8E              \n"
+            "sub     %1, %2, %1, lsr #24    \n"
+            "mov     %2, %3, lsl #8         \n"
+            "orr     %2, %2, #0x80000000    \n"
+            "mov     %1, %2, lsr %1         \n"
+            "rsbcs   %1, %1, #0             \n"
+            : "=r"(x), "=&r"(y), "=&r"(z)
+            : "r"(x)
+            : "cc"
+            );
+        return y;
+    }
+    inline SkFixed SkFixedMul_arm(SkFixed x, SkFixed y)
+    {
+        register int32_t t;
+        asm("smull  %0, %2, %1, %3          \n"
+            "mov    %0, %0, lsr #16         \n"
+            "orr    %0, %0, %2, lsl #16     \n"
+            : "=r"(x), "=&r"(y), "=r"(t)
+            : "r"(x), "1"(y)
+            :
+            );
+        return x;
+    }
+    inline SkFixed SkFixedMulAdd_arm(SkFixed x, SkFixed y, SkFixed a)
+    {
+        register int32_t t;
+        asm("smull  %0, %3, %1, %4          \n"
+            "add    %0, %2, %0, lsr #16     \n"
+            "add    %0, %0, %3, lsl #16     \n"
+            : "=r"(x), "=&r"(y), "=&r"(a), "=r"(t)
+            : "%r"(x), "1"(y), "2"(a)
+            :
+            );
+        return x;
+    }
+    inline SkFixed SkFractMul_arm(SkFixed x, SkFixed y)
+    {
+        register int32_t t;
+        asm("smull  %0, %2, %1, %3          \n"
+            "mov    %0, %0, lsr #30         \n"
+            "orr    %0, %0, %2, lsl #2      \n"
+            : "=r"(x), "=&r"(y), "=r"(t)
+            : "r"(x), "1"(y)
+            :
+            );
+        return x;
+    }
+    #undef SkFixedMul
+    #undef SkFractMul
+    #define SkFixedMul(x, y)        SkFixedMul_arm(x, y)
+    #define SkFractMul(x, y)        SkFractMul_arm(x, y)
+    #define SkFixedMulAdd(x, y, a)  SkFixedMulAdd_arm(x, y, a)
+
+    #undef SkFloatToFixed
+    #define SkFloatToFixed(x)  SkFloatToFixed_arm(x)
+#endif
+
+/////////////////////// Now define our macros to the portable versions if they weren't overridden
+
+#ifndef SkFixedSquare
+    #define SkFixedSquare(x)    SkFixedSquare_portable(x)
+#endif
+#ifndef SkFixedMul
+    #define SkFixedMul(x, y)    SkFixedMul_portable(x, y)
+#endif
+#ifndef SkFractMul
+    #define SkFractMul(x, y)    SkFractMul_portable(x, y)
+#endif
+#ifndef SkFixedMulAdd
+    #define SkFixedMulAdd(x, y, a)  (SkFixedMul(x, y) + (a))
+#endif
+
+#endif
diff --git a/include/core/SkFlattenable.h b/include/core/SkFlattenable.h
new file mode 100644
index 0000000..6958462
--- /dev/null
+++ b/include/core/SkFlattenable.h
@@ -0,0 +1,207 @@
+/*
+ * 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 SkFlattenable_DEFINED
+#define SkFlattenable_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkBitmap.h"
+#include "SkReader32.h"
+#include "SkTDArray.h"
+#include "SkWriter32.h"
+
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+class SkString;
+
+/** \class SkFlattenable
+ 
+ SkFlattenable is the base class for objects that need to be flattened
+ into a data stream for either transport or as part of the key to the
+ font cache.
+ */
+class SkFlattenable : public SkRefCnt {
+public:
+    typedef SkFlattenable* (*Factory)(SkFlattenableReadBuffer&);
+    
+    SkFlattenable() {}
+    
+    /** Implement this to return a factory function pointer that can be called
+     to recreate your class given a buffer (previously written to by your
+     override of flatten().
+     */
+    virtual Factory getFactory() = 0;
+    /** Override this to write data specific to your subclass into the buffer,
+     being sure to call your super-class' version first. This data will later
+     be passed to your Factory function, returned by getFactory().
+     */
+    virtual void flatten(SkFlattenableWriteBuffer&);
+    
+    /** Set the string to describe the sublass and return true. If this is not
+        overridden, ignore the string param and return false.
+     */
+    virtual bool toDumpString(SkString*) const;
+
+    static Factory NameToFactory(const char name[]);
+    static const char* FactoryToName(Factory);
+    static void Register(const char name[], Factory);
+    
+    class Registrar {
+    public:
+        Registrar(const char name[], Factory factory) {
+            SkFlattenable::Register(name, factory);
+        }
+    };
+    
+protected:
+    SkFlattenable(SkFlattenableReadBuffer&) {}
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class SkTypeface;
+
+class SkFlattenableReadBuffer : public SkReader32 {
+public:
+    SkFlattenableReadBuffer();
+    explicit SkFlattenableReadBuffer(const void* data);
+    SkFlattenableReadBuffer(const void* data, size_t size);
+    
+    void setRefCntArray(SkRefCnt* array[], int count) {
+        fRCArray = array;
+        fRCCount = count;
+    }
+    
+    void setTypefaceArray(SkTypeface* array[], int count) {
+        fTFArray = array;
+        fTFCount = count;
+    }
+    
+    void setFactoryPlayback(SkFlattenable::Factory array[], int count) {
+        fFactoryArray = array;
+        fFactoryCount = count;
+    }
+    
+    SkTypeface* readTypeface();
+    SkRefCnt* readRefCnt();
+    void* readFunctionPtr();
+    SkFlattenable* readFlattenable();
+    
+private:
+    SkRefCnt** fRCArray;
+    int        fRCCount;
+    
+    SkTypeface** fTFArray;
+    int        fTFCount;
+    
+    SkFlattenable::Factory* fFactoryArray;
+    int                     fFactoryCount;
+    
+    typedef SkReader32 INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPtrRecorder.h"
+
+class SkRefCntRecorder : public SkPtrRecorder {
+public:
+    virtual ~SkRefCntRecorder();
+    
+    /** Add a refcnt object to the set and ref it if not already present,
+        or if it is already present, do nothing. Either way, returns 0 if obj
+        is null, or a base-1 index if obj is not null.
+    */
+    uint32_t record(SkRefCnt* ref) {
+        return this->recordPtr(ref);
+    }
+
+    // This does not change the owner counts on the objects
+    void get(SkRefCnt* array[]) const {
+        this->getPtrs((void**)array);
+    }
+
+protected:
+    // overrides
+    virtual void incPtr(void*);
+    virtual void decPtr(void*);
+
+private:
+    typedef SkPtrRecorder INHERITED;
+};
+
+class SkFactoryRecorder : public SkPtrRecorder {
+public:
+    /** Add a factory to the set. If it is null return 0, otherwise return a
+        base-1 index for the factory.
+    */
+    uint32_t record(SkFlattenable::Factory fact) {
+        return this->recordPtr((void*)fact);
+    }
+    
+    void get(SkFlattenable::Factory array[]) const {
+        this->getPtrs((void**)array);
+    }
+    
+private:
+    typedef SkPtrRecorder INHERITED;
+};
+
+class SkFlattenableWriteBuffer : public SkWriter32 {
+public:
+    SkFlattenableWriteBuffer(size_t minSize);
+    virtual ~SkFlattenableWriteBuffer();
+
+    void writeTypeface(SkTypeface*);
+    void writeRefCnt(SkRefCnt*);
+    void writeFunctionPtr(void*);
+    void writeFlattenable(SkFlattenable* flattenable);
+    
+    SkRefCntRecorder* getTypefaceRecorder() const { return fTFRecorder; }
+    SkRefCntRecorder* setTypefaceRecorder(SkRefCntRecorder*);
+    
+    SkRefCntRecorder* getRefCntRecorder() const { return fRCRecorder; }
+    SkRefCntRecorder* setRefCntRecorder(SkRefCntRecorder*);
+    
+    SkFactoryRecorder* getFactoryRecorder() const { return fFactoryRecorder; }
+    SkFactoryRecorder* setFactoryRecorder(SkFactoryRecorder*);
+
+    enum Flags {
+        kCrossProcess_Flag      = 0x01
+    };
+    Flags getFlags() const { return fFlags; }
+    void setFlags(Flags flags) { fFlags = flags; }
+    
+    bool isCrossProcess() const { return (fFlags & kCrossProcess_Flag) != 0; }
+
+    bool persistBitmapPixels() const {
+        return (fFlags & kCrossProcess_Flag) != 0;
+    }
+    
+    bool persistTypeface() const { return (fFlags & kCrossProcess_Flag) != 0; }
+
+private:
+    Flags               fFlags;
+    SkRefCntRecorder*   fTFRecorder;
+    SkRefCntRecorder*   fRCRecorder;
+    SkFactoryRecorder*  fFactoryRecorder;
+    
+    typedef SkWriter32 INHERITED;
+};
+
+#endif
+
diff --git a/include/core/SkFloatBits.h b/include/core/SkFloatBits.h
new file mode 100644
index 0000000..1628f6e
--- /dev/null
+++ b/include/core/SkFloatBits.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkFloatBits_DEFINED
+#define SkFloatBits_DEFINED
+
+#include "SkTypes.h"
+
+/** Convert a sign-bit int (i.e. float interpreted as int) into a 2s compliement
+    int. This also converts -0 (0x80000000) to 0. Doing this to a float allows
+    it to be compared using normal C operators (<, <=, etc.)
+*/
+static inline int32_t SkSignBitTo2sCompliment(int32_t x) {
+    if (x < 0) {
+        x &= 0x7FFFFFFF;
+        x = -x;
+    }
+    return x;
+}
+
+/** Convert a 2s compliment int to a sign-bit (i.e. int interpreted as float).
+    This undoes the result of SkSignBitTo2sCompliment().
+ */
+static inline int32_t Sk2sComplimentToSignBit(int32_t x) {
+    int sign = x >> 31;
+    // make x positive
+    x = (x ^ sign) - sign;
+    // set the sign bit as needed
+    x |= sign << 31;
+    return x;
+}
+
+/** Given the bit representation of a float, return its value cast to an int.
+    If the value is out of range, or NaN, return return +/- SK_MaxS32
+*/
+int32_t SkFloatBits_toIntCast(int32_t floatBits);
+
+/** Given the bit representation of a float, return its floor as an int.
+    If the value is out of range, or NaN, return return +/- SK_MaxS32
+ */
+int32_t SkFloatBits_toIntFloor(int32_t floatBits);
+
+/** Given the bit representation of a float, return it rounded to an int.
+    If the value is out of range, or NaN, return return +/- SK_MaxS32
+ */
+int32_t SkFloatBits_toIntRound(int32_t floatBits);
+
+/** Given the bit representation of a float, return its ceiling as an int.
+    If the value is out of range, or NaN, return return +/- SK_MaxS32
+ */
+int32_t SkFloatBits_toIntCeil(int32_t floatBits);
+
+
+#ifdef SK_CAN_USE_FLOAT
+
+union SkFloatIntUnion {
+    float   fFloat;
+    int32_t fSignBitInt;
+};
+
+// Helper to see a float as its bit pattern (w/o aliasing warnings)
+static inline int32_t SkFloat2Bits(float x) {
+    SkFloatIntUnion data;
+    data.fFloat = x;
+    return data.fSignBitInt;
+}
+
+// Helper to see a bit pattern as a float (w/o aliasing warnings)
+static inline float SkBits2Float(int32_t floatAsBits) {
+    SkFloatIntUnion data;
+    data.fSignBitInt = floatAsBits;
+    return data.fFloat;
+}
+
+/** Return the float as a 2s compliment int. Just to be used to compare floats
+    to each other or against positive float-bit-constants (like 0). This does
+    not return the int equivalent of the float, just something cheaper for
+    compares-only.
+ */
+static inline int32_t SkFloatAs2sCompliment(float x) {
+    return SkSignBitTo2sCompliment(SkFloat2Bits(x));
+}
+
+/** Return the 2s compliment int as a float. This undos the result of
+    SkFloatAs2sCompliment
+ */
+static inline float Sk2sComplimentAsFloat(int32_t x) {
+    return SkBits2Float(Sk2sComplimentToSignBit(x));
+}
+
+/** Return x cast to a float (i.e. (float)x)
+*/
+float SkIntToFloatCast(int x);
+float SkIntToFloatCast_NoOverflowCheck(int x);
+
+/** Return the float cast to an int.
+    If the value is out of range, or NaN, return +/- SK_MaxS32
+*/
+static inline int32_t SkFloatToIntCast(float x) {
+    return SkFloatBits_toIntCast(SkFloat2Bits(x));
+}
+
+/** Return the floor of the float as an int.
+    If the value is out of range, or NaN, return +/- SK_MaxS32
+*/
+static inline int32_t SkFloatToIntFloor(float x) {
+    return SkFloatBits_toIntFloor(SkFloat2Bits(x));
+}
+
+/** Return the float rounded to an int.
+    If the value is out of range, or NaN, return +/- SK_MaxS32
+*/
+static inline int32_t SkFloatToIntRound(float x) {
+    return SkFloatBits_toIntRound(SkFloat2Bits(x));
+}
+
+/** Return the ceiling of the float as an int.
+    If the value is out of range, or NaN, return +/- SK_MaxS32
+*/
+static inline int32_t SkFloatToIntCeil(float x) {
+    return SkFloatBits_toIntCeil(SkFloat2Bits(x));
+}
+
+#endif
+
+//  Scalar wrappers for float-bit routines
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define SkScalarAs2sCompliment(x)    SkFloatAs2sCompliment(x)
+    #define Sk2sComplimentAsScalar(x)    Sk2sComplimentAsFloat(x)
+#else
+    #define SkScalarAs2sCompliment(x)    (x)
+    #define Sk2sComplimentAsScalar(x)    (x)
+#endif
+
+#endif
+
diff --git a/include/core/SkFloatingPoint.h b/include/core/SkFloatingPoint.h
new file mode 100644
index 0000000..d3a6fc5
--- /dev/null
+++ b/include/core/SkFloatingPoint.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 SkFloatingPoint_DEFINED
+#define SkFloatingPoint_DEFINED
+
+#include "SkTypes.h"
+
+#ifdef SK_CAN_USE_FLOAT
+
+#include <math.h>
+#include <float.h>
+#include "SkFloatBits.h"
+
+#ifdef SK_BUILD_FOR_WINCE
+    #define sk_float_sqrt(x)        (float)::sqrt(x)
+    #define sk_float_sin(x)         (float)::sin(x)
+    #define sk_float_cos(x)         (float)::cos(x)
+    #define sk_float_tan(x)         (float)::tan(x)
+    #define sk_float_acos(x)        (float)::acos(x)
+    #define sk_float_asin(x)        (float)::asin(x)
+    #define sk_float_atan2(y,x)     (float)::atan2(y,x)
+    #define sk_float_abs(x)         (float)::fabs(x)
+    #define sk_float_mod(x,y)       (float)::fmod(x,y)
+    #define sk_float_exp(x)         (float)::exp(x)
+    #define sk_float_log(x)         (float)::log(x)
+    #define sk_float_floor(x)       (float)::floor(x)
+    #define sk_float_ceil(x)        (float)::ceil(x)
+#else
+    #define sk_float_sqrt(x)        sqrtf(x)
+    #define sk_float_sin(x)         sinf(x)
+    #define sk_float_cos(x)         cosf(x)
+    #define sk_float_tan(x)         tanf(x)
+    #define sk_float_floor(x)       floorf(x)
+    #define sk_float_ceil(x)        ceilf(x)
+#ifdef SK_BUILD_FOR_MAC
+    #define sk_float_acos(x)        acos(x)
+    #define sk_float_asin(x)        asin(x)
+#else
+    #define sk_float_acos(x)        acosf(x)
+    #define sk_float_asin(x)        asinf(x)
+#endif
+    #define sk_float_atan2(y,x) atan2f(y,x)
+    #define sk_float_abs(x)         fabsf(x)
+    #define sk_float_mod(x,y)       fmodf(x,y)
+    #define sk_float_exp(x)         expf(x)
+    #define sk_float_log(x)         logf(x)
+    #define sk_float_isNaN(x)       _isnan(x)
+#endif
+
+#ifdef SK_USE_FLOATBITS
+    #define sk_float_floor2int(x)   SkFloatToIntFloor(x)
+    #define sk_float_round2int(x)   SkFloatToIntRound(x)
+    #define sk_float_ceil2int(x)    SkFloatToIntCeil(x)
+#else
+    #define sk_float_floor2int(x)   (int)sk_float_floor(x)
+    #define sk_float_round2int(x)   (int)sk_float_floor((x) + 0.5f)
+    #define sk_float_ceil2int(x)    (int)sk_float_ceil(x)
+#endif
+
+#endif
+#endif
diff --git a/include/core/SkFontHost.h b/include/core/SkFontHost.h
new file mode 100644
index 0000000..ede40b7
--- /dev/null
+++ b/include/core/SkFontHost.h
@@ -0,0 +1,122 @@
+/*
+ * 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 SkFontHost_DEFINED
+#define SkFontHost_DEFINED
+
+#include "SkScalerContext.h"
+#include "SkTypeface.h"
+
+class SkDescriptor;
+class SkStream;
+class SkWStream;
+
+/** \class SkFontHost
+
+    This class is ported to each environment. It is responsible for bridging the gap
+    between SkTypeface and the resulting platform-specific instance of SkScalerContext.
+*/
+class SkFontHost {
+public:
+    /** Return the closest matching typeface given either an existing family
+        (specified by a typeface in that family) or by a familyName, and a
+        requested style.
+        1) If familyFace is null, use famillyName.
+        2) If famillyName is null, use familyFace.
+        3) If both are null, return the default font that best matches style
+
+        NOTE: this does not return a new typeface, nor does it affect the
+        owner count of an existing one, so the caller is free to ignore the
+        return result, or just compare it against null.
+     */
+    static SkTypeface* FindTypeface(const SkTypeface* familyFace,
+                                    const char famillyName[],
+                                    SkTypeface::Style style);
+
+    /** Return the typeface associated with the uniqueID, or null if that ID
+        does not match any faces.
+
+        NOTE: this does not return a new typeface, nor does it affect the
+        owner count of an existing one, so the caller is free to ignore the
+        return result, or just compare it against null.
+    */
+    static SkTypeface* ResolveTypeface(uint32_t uniqueID);
+    
+    /** Return a new stream to read the font data, or null if the uniqueID does
+        not match an existing typeface. The caller must call CloseStream() when
+        it is finished reading the stream.
+    */
+    static SkStream* OpenStream(uint32_t uniqueID);
+    
+    /** Call this when finished reading from the stream returned by OpenStream.
+        The caller should NOT try to delete the stream.
+     */
+    static void CloseStream(uint32_t uniqueID, SkStream*);
+
+    /** Return a new typeface given the data buffer (owned by the caller).
+        If the data does not represent a valid font, return null. The caller is
+        responsible for unref-ing the returned typeface (if it is not null).
+    */
+    static SkTypeface* CreateTypeface(SkStream*);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** Write a unique identifier to the stream, so that the same typeface can
+        be retrieved with Deserialize().
+    */
+    static void Serialize(const SkTypeface*, SkWStream*);
+
+    /** Given a stream created by Serialize(), return the corresponding typeface
+        or null if no match is found.
+
+        NOTE: this does not return a new typeface, nor does it affect the
+        owner count of an existing one, so the caller is free to ignore the
+        return result, or just compare it against null.
+     */
+    static SkTypeface* Deserialize(SkStream*);
+
+    ///////////////////////////////////////////////////////////////////////////
+    
+    /** Return a subclass of SkScalarContext
+    */
+    static SkScalerContext* CreateScalerContext(const SkDescriptor* desc);
+
+    /** Return a scalercontext using the "fallback" font. If there is no designated
+        fallback, return null.
+    */
+    static SkScalerContext* CreateFallbackScalerContext(const SkScalerContext::Rec&);
+
+    /** Return the number of bytes (approx) that should be purged from the font
+        cache. The input parameter is the cache's estimate of how much as been
+        allocated by the cache so far.
+        To purge (basically) everything, return the input parameter.
+        To purge nothing, return 0
+    */
+    static size_t ShouldPurgeFontCache(size_t sizeAllocatedSoFar);
+
+    /** Return SkScalerContext gamma flag, or 0, based on the paint that will be
+        used to draw something with antialiasing.
+    */
+    static int ComputeGammaFlag(const SkPaint& paint);
+
+    /** Return NULL or a pointer to 256 bytes for the black (table[0]) and
+        white (table[1]) gamma tables.
+    */
+    static void GetGammaTables(const uint8_t* tables[2]);
+};
+
+#endif
+
diff --git a/include/core/SkGlobals.h b/include/core/SkGlobals.h
new file mode 100644
index 0000000..8e28290
--- /dev/null
+++ b/include/core/SkGlobals.h
@@ -0,0 +1,67 @@
+/*
+ * 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 SkGlobals_DEFINED
+#define SkGlobals_DEFINED
+
+#include "SkThread.h"
+
+class SkGlobals {
+public:
+    class Rec {
+    public:
+        virtual ~Rec();
+    private:
+        Rec*        fNext;
+        uint32_t    fTag;
+
+        friend class SkGlobals;
+    };
+
+    /** Look for a matching Rec for the specified tag. If one is found, return it.
+        If one is not found, if create_proc is null, return null, else
+        call the proc, and if it returns a Rec, add it to the global list
+        and return it.
+
+        create_proc can NOT call back into SkGlobals::Find (it would deadlock)
+    */
+    static Rec* Find(uint32_t tag, Rec* (*create_proc)());
+    /** Helper for Find, when you want to assert that the Rec is already in the list
+    */
+    static Rec* Get(uint32_t tag)
+    {
+        Rec* rec = SkGlobals::Find(tag, NULL);
+        SkASSERT(rec);
+        return rec;
+    }
+
+    // used by porting layer
+    struct BootStrap {
+        SkMutex fMutex;
+        Rec*    fHead;
+    };
+
+private:
+    static void Init();
+    static void Term();
+    friend class SkGraphics;
+
+    //  This last function is implemented in the porting layer
+    static BootStrap& GetBootStrap();
+};
+
+#endif
+
diff --git a/include/core/SkGraphics.h b/include/core/SkGraphics.h
new file mode 100644
index 0000000..cb06128
--- /dev/null
+++ b/include/core/SkGraphics.h
@@ -0,0 +1,46 @@
+/*
+ * 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 SkGraphics_DEFINED
+#define SkGraphics_DEFINED
+
+#include "SkTypes.h"
+
+class SkGraphics {
+public:
+    static void Init(bool runUnitTests);
+    static void Term();
+
+    /** Return the (approximate) number of bytes used by the font cache.
+    */
+    static size_t GetFontCacheUsed();
+    
+    /** Attempt to purge the font cache until <= the specified amount remains
+        in the cache. Specifying 0 will attempt to purge the entire cache.
+        Returns true if some amount was purged from the font cache.
+    */
+    static bool SetFontCacheUsed(size_t usageInBytes);
+    
+private:
+    /** This is automatically called by SkGraphics::Init(), and must be
+        implemented by the host OS. This allows the host OS to register a callback
+        with the C++ runtime to call SkGraphics::FreeCaches()
+    */
+    static void InstallNewHandler();
+};
+
+#endif
+
diff --git a/include/core/SkMMapStream.h b/include/core/SkMMapStream.h
new file mode 100644
index 0000000..600c621
--- /dev/null
+++ b/include/core/SkMMapStream.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkMMapStream_DEFINED
+#define SkMMapStream_DEFINED
+
+#include "SkStream.h"
+
+class SkMMAPStream : public SkMemoryStream {
+public:
+    SkMMAPStream(const char filename[]);
+    virtual ~SkMMAPStream();
+
+    virtual void setMemory(const void* data, size_t length);
+private:
+    int     fFildes;
+    void*   fAddr;
+    size_t  fSize;
+    
+    void closeMMap();
+    
+    typedef SkMemoryStream INHERITED;
+};
+
+#endif
diff --git a/include/core/SkMallocPixelRef.h b/include/core/SkMallocPixelRef.h
new file mode 100644
index 0000000..b6a013d
--- /dev/null
+++ b/include/core/SkMallocPixelRef.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkMallocPixelRef_DEFINED
+#define SkMallocPixelRef_DEFINED
+
+#include "SkPixelRef.h"
+
+/** We explicitly use the same allocator for our pixels that SkMask does,
+    so that we can freely assign memory allocated by one class to the other.
+*/
+class SkMallocPixelRef : public SkPixelRef {
+public:
+    /** Allocate the specified buffer for pixels. The memory is freed when the
+        last owner of this pixelref is gone.
+     */
+    SkMallocPixelRef(void* addr, size_t size, SkColorTable* ctable);
+    virtual ~SkMallocPixelRef();
+    
+    //! Return the allocation size for the pixels
+    size_t getSize() const { return fSize; }
+
+    // overrides from SkPixelRef
+    virtual void flatten(SkFlattenableWriteBuffer&) const;
+    virtual Factory getFactory() const {
+        return Create;
+    }
+    static SkPixelRef* Create(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkMallocPixelRef, (buffer));
+    }
+
+protected:
+    // overrides from SkPixelRef
+    virtual void* onLockPixels(SkColorTable**);
+    virtual void onUnlockPixels();
+
+    SkMallocPixelRef(SkFlattenableReadBuffer& buffer);
+
+private:
+    void*           fStorage;
+    size_t          fSize;
+    SkColorTable*   fCTable;
+
+    typedef SkPixelRef INHERITED;
+};
+
+#endif
diff --git a/include/core/SkMask.h b/include/core/SkMask.h
new file mode 100644
index 0000000..764ead6
--- /dev/null
+++ b/include/core/SkMask.h
@@ -0,0 +1,86 @@
+/*
+ * 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 SkMask_DEFINED
+#define SkMask_DEFINED
+
+#include "SkRect.h"
+
+/** \class SkMask
+    SkMask is used to describe alpha bitmaps, either 1bit, 8bit, or
+    the 3-channel 3D format. These are passed to SkMaskFilter objects.
+*/
+struct SkMask {
+    enum Format {
+        kBW_Format, //!< 1bit per pixel mask (e.g. monochrome)
+        kA8_Format, //!< 8bits per pixel mask (e.g. antialiasing)
+        k3D_Format, //!< 3 8bit per pixl planes: alpha, mul, add
+        kLCD_Format //!< 3 bytes/pixel: r/g/b
+    };
+    
+    enum {
+        kCountMaskFormats = kLCD_Format + 1
+    };
+
+    uint8_t*    fImage;
+    SkIRect     fBounds;
+    uint16_t    fRowBytes;
+    uint8_t     fFormat;    // Format
+
+    /** Return the byte size of the mask, assuming only 1 plane.
+        Does not account for k3D_Format. For that, use computeFormatImageSize()
+    */
+    size_t computeImageSize() const;
+    /** Return the byte size of the mask, taking into account
+        any extra planes (e.g. k3D_Format).
+    */
+    size_t computeTotalImageSize() const;
+
+    /** Returns the address of the byte that holds the specified bit.
+        Asserts that the mask is kBW_Format, and that x,y are in range.
+        x,y are in the same coordiate space as fBounds.
+    */
+    uint8_t* getAddr1(int x, int y) const
+    {
+        SkASSERT(fFormat == kBW_Format);
+        SkASSERT(fBounds.contains(x, y));
+        SkASSERT(fImage != NULL);
+        return fImage + ((x - fBounds.fLeft) >> 3) + (y - fBounds.fTop) * fRowBytes;
+    }
+    /** Returns the address of the specified byte.
+        Asserts that the mask is kA8_Format, and that x,y are in range.
+        x,y are in the same coordiate space as fBounds.
+    */
+    uint8_t* getAddr(int x, int y) const
+    {
+        SkASSERT(fFormat != kBW_Format);
+        SkASSERT(fBounds.contains(x, y));
+        SkASSERT(fImage != NULL);
+        return fImage + x - fBounds.fLeft + (y - fBounds.fTop) * fRowBytes;
+    }
+
+    static uint8_t* AllocImage(size_t bytes);
+    static void FreeImage(void* image);
+    
+    enum CreateMode {
+        kJustComputeBounds_CreateMode,      //!< compute bounds and return
+        kJustRenderImage_CreateMode,        //!< render into preallocate mask
+        kComputeBoundsAndRenderImage_CreateMode  //!< compute bounds, alloc image and render into it
+    };
+};
+
+#endif
+
diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h
new file mode 100644
index 0000000..749a73a
--- /dev/null
+++ b/include/core/SkMaskFilter.h
@@ -0,0 +1,100 @@
+/*
+ * 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 SkMaskFilter_DEFINED
+#define SkMaskFilter_DEFINED
+
+#include "SkFlattenable.h"
+#include "SkMask.h"
+
+class SkBlitter;
+class SkBounder;
+class SkMatrix;
+class SkPath;
+class SkRegion;
+
+/** \class SkMaskFilter
+
+    SkMaskFilter is the base class for object that perform transformations on
+    an alpha-channel mask before drawing it. A subclass of SkMaskFilter may be
+    installed into a SkPaint. Once there, each time a primitive is drawn, it
+    is first scan converted into a SkMask::kA8_Format mask, and handed to the
+    filter, calling its filterMask() method. If this returns true, then the
+    new mask is used to render into the device.
+
+    Blur and emboss are implemented as subclasses of SkMaskFilter.
+*/
+class SkMaskFilter : public SkFlattenable {
+public:
+    SkMaskFilter() {}
+
+    /** Returns the format of the resulting mask that this subclass will return
+        when its filterMask() method is called.
+    */
+    virtual SkMask::Format  getFormat() = 0;
+
+    /** Create a new mask by filter the src mask.
+        If src.fImage == null, then do not allocate or create the dst image
+        but do fill out the other fields in dstMask.
+        If you do allocate a dst image, use SkMask::AllocImage()
+        If this returns false, dst mask is ignored.
+        @param  dst the result of the filter. If src.fImage == null, dst should not allocate its image
+        @param src the original image to be filtered.
+        @param matrix the CTM
+        @param margin   if not null, return the buffer dx/dy need when calculating the effect. Used when
+                        drawing a clipped object to know how much larger to allocate the src before
+                        applying the filter. If returning false, ignore this parameter.
+        @return true if the dst mask was correctly created.
+    */
+    virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, SkIPoint* margin);
+
+    /** Helper method that, given a path in device space, will rasterize it into a kA8_Format mask
+        and then call filterMask(). If this returns true, the specified blitter will be called
+        to render that mask. Returns false if filterMask() returned false.
+        This method is not exported to java.
+    */
+    bool filterPath(const SkPath& devPath, const SkMatrix& devMatrix,
+                    const SkRegion& devClip, SkBounder*, SkBlitter* blitter);
+
+    virtual void flatten(SkFlattenableWriteBuffer& ) {}
+protected:
+    // empty for now, but lets get our subclass to remember to init us for the future
+    SkMaskFilter(SkFlattenableReadBuffer&) {}
+};
+
+/** \class SkAutoMaskImage
+
+    Stack class used to manage the fImage buffer in a SkMask.
+    When this object loses scope, the buffer is freed with SkMask::FreeImage().
+*/
+class SkAutoMaskImage {
+public:
+    SkAutoMaskImage(SkMask* mask, bool alloc)
+    {
+        if (alloc)
+            mask->fImage = SkMask::AllocImage(mask->computeImageSize());
+        fImage = mask->fImage;
+    }
+    ~SkAutoMaskImage()
+    {
+        SkMask::FreeImage(fImage);
+    }
+private:
+    uint8_t*    fImage;
+};
+
+#endif
+
diff --git a/include/core/SkMath.h b/include/core/SkMath.h
new file mode 100644
index 0000000..5c2b475
--- /dev/null
+++ b/include/core/SkMath.h
@@ -0,0 +1,230 @@
+/*
+ * 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 SkMath_DEFINED
+#define SkMath_DEFINED
+
+#include "SkTypes.h"
+
+//! Returns the number of leading zero bits (0...32)
+int SkCLZ_portable(uint32_t);
+
+/** Computes the 64bit product of a * b, and then shifts the answer down by
+    shift bits, returning the low 32bits. shift must be [0..63]
+    e.g. to perform a fixedmul, call SkMulShift(a, b, 16)
+*/
+int32_t SkMulShift(int32_t a, int32_t b, unsigned shift);
+
+/** Computes numer1 * numer2 / denom in full 64 intermediate precision.
+    It is an error for denom to be 0. There is no special handling if
+    the result overflows 32bits.
+*/
+int32_t SkMulDiv(int32_t numer1, int32_t numer2, int32_t denom);
+
+/** Computes (numer1 << shift) / denom in full 64 intermediate precision.
+    It is an error for denom to be 0. There is no special handling if
+    the result overflows 32bits.
+*/
+int32_t SkDivBits(int32_t numer, int32_t denom, int shift);
+
+/** Return the integer square root of value, with a bias of bitBias
+*/
+int32_t SkSqrtBits(int32_t value, int bitBias);
+
+/** Return the integer square root of n, treated as a SkFixed (16.16)
+*/
+#define SkSqrt32(n)         SkSqrtBits(n, 15)
+
+/** Return the integer cube root of value, with a bias of bitBias
+ */
+int32_t SkCubeRootBits(int32_t value, int bitBias);
+
+/** Returns -1 if n < 0, else returns 0
+*/
+#define SkExtractSign(n)    ((int32_t)(n) >> 31)
+
+/** If sign == -1, returns -n, else sign must be 0, and returns n.
+    Typically used in conjunction with SkExtractSign().
+*/
+static inline int32_t SkApplySign(int32_t n, int32_t sign) {
+    SkASSERT(sign == 0 || sign == -1);
+    return (n ^ sign) - sign;
+}
+
+/** Returns (value < 0 ? 0 : value) efficiently (i.e. no compares or branches)
+*/
+static inline int SkClampPos(int value) {
+    return value & ~(value >> 31);
+}
+
+/** Given an integer and a positive (max) integer, return the value
+    pinned against 0 and max, inclusive.
+    Note: only works as long as max - value doesn't wrap around
+    @param value    The value we want returned pinned between [0...max]
+    @param max      The positive max value
+    @return 0 if value < 0, max if value > max, else value
+*/
+static inline int SkClampMax(int value, int max) {
+    // ensure that max is positive
+    SkASSERT(max >= 0);
+    // ensure that if value is negative, max - value doesn't wrap around
+    SkASSERT(value >= 0 || max - value > 0);
+
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (value < 0) {
+        value = 0;
+    }
+    if (value > max) {
+        value = max;
+    }
+    return value;
+#else
+
+    int diff = max - value;
+    // clear diff if diff is positive
+    diff &= diff >> 31;
+
+    // clear the result if value < 0
+    return (value + diff) & ~(value >> 31);
+#endif
+}
+
+/** Given a positive value and a positive max, return the value
+    pinned against max.
+    Note: only works as long as max - value doesn't wrap around
+    @return max if value >= max, else value
+*/
+static inline unsigned SkClampUMax(unsigned value, unsigned max) {
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (value > max) {
+        value = max;
+    }
+    return value;
+#else
+    int diff = max - value;
+    // clear diff if diff is positive
+    diff &= diff >> 31;
+
+    return value + diff;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined(__arm__) && !defined(__thumb__)
+    #define SkCLZ(x)    __builtin_clz(x)
+#endif
+
+#ifndef SkCLZ
+    #define SkCLZ(x)    SkCLZ_portable(x)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Returns the smallest power-of-2 that is >= the specified value. If value
+    is already a power of 2, then it is returned unchanged. It is undefined
+    if value is <= 0.
+*/
+static inline int SkNextPow2(int value) {
+    SkASSERT(value > 0);
+    return 1 << (32 - SkCLZ(value - 1));
+}
+
+/** Returns the log2 of the specified value, were that value to be rounded up
+    to the next power of 2. It is undefined to pass 0. Examples:
+         SkNextLog2(1) -> 0
+         SkNextLog2(2) -> 1
+         SkNextLog2(3) -> 2
+         SkNextLog2(4) -> 2
+         SkNextLog2(5) -> 3
+*/
+static inline int SkNextLog2(uint32_t value) {
+    SkASSERT(value != 0);
+    return 32 - SkCLZ(value - 1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** SkMulS16(a, b) multiplies a * b, but requires that a and b are both int16_t.
+    With this requirement, we can generate faster instructions on some
+    architectures.
+*/
+#if defined(__arm__) && !defined(__thumb__)
+    static inline int32_t SkMulS16(S16CPU x, S16CPU y) {
+        SkASSERT((int16_t)x == x);
+        SkASSERT((int16_t)y == y);
+        int32_t product;
+        asm("smulbb %0, %1, %2 \n"
+            : "=r"(product)
+            : "r"(x), "r"(y)
+            :
+            );
+        return product;
+    }
+#else
+    #ifdef SK_DEBUG
+        static inline int32_t SkMulS16(S16CPU x, S16CPU y) {
+            SkASSERT((int16_t)x == x);
+            SkASSERT((int16_t)y == y);
+            return x * y;
+        }
+    #else
+        #define SkMulS16(x, y)  ((x) * (y))
+    #endif
+#endif
+
+/** Return a*b/255, truncating away any fractional bits. Only valid if both
+    a and b are 0..255
+*/
+static inline U8CPU SkMulDiv255Trunc(U8CPU a, U8CPU b) {
+    SkASSERT((uint8_t)a == a);
+    SkASSERT((uint8_t)b == b);
+    unsigned prod = SkMulS16(a, b) + 1;
+    return (prod + (prod >> 8)) >> 8;
+}
+
+/** Return a*b/255, rounding any fractional bits. Only valid if both
+    a and b are 0..255
+ */
+static inline U8CPU SkMulDiv255Round(U8CPU a, U8CPU b) {
+    SkASSERT((uint8_t)a == a);
+    SkASSERT((uint8_t)b == b);
+    unsigned prod = SkMulS16(a, b) + 128;
+    return (prod + (prod >> 8)) >> 8;
+}
+
+/** Return a*b/((1 << shift) - 1), rounding any fractional bits.
+    Only valid if a and b are unsigned and <= 32767 and shift is > 0 and <= 8
+*/
+static inline unsigned SkMul16ShiftRound(unsigned a, unsigned b, int shift) {
+    SkASSERT(a <= 32767);
+    SkASSERT(b <= 32767);
+    SkASSERT(shift > 0 && shift <= 8);
+    unsigned prod = SkMulS16(a, b) + (1 << (shift - 1));
+    return (prod + (prod >> shift)) >> shift;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+    class SkMath {
+    public:
+        static void UnitTest();
+    };
+#endif
+
+#endif
+
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
new file mode 100644
index 0000000..2b25dca
--- /dev/null
+++ b/include/core/SkMatrix.h
@@ -0,0 +1,479 @@
+/*
+ * 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 SkMatrix_DEFINED
+#define SkMatrix_DEFINED
+
+#include "SkRect.h"
+
+class SkString;
+
+/** \class SkMatrix
+
+    The SkMatrix class holds a 3x3 matrix for transforming coordinates.
+    SkMatrix does not have a constructor, so it must be explicitly initialized
+    using either reset() - to construct an identity matrix, or one of the set
+    functions (e.g. setTranslate, setRotate, etc.).
+*/
+class SkMatrix {
+public:
+    /** Enum of bit fields for the mask return by getType().
+        Use this to identify the complexity of the matrix.
+    */
+    enum TypeMask {
+        kIdentity_Mask      = 0,
+        kTranslate_Mask     = 0x01,  //!< set if the matrix has translation
+        kScale_Mask         = 0x02,  //!< set if the matrix has X or Y scale
+        kAffine_Mask        = 0x04,  //!< set if the matrix skews or rotates
+        kPerspective_Mask   = 0x08   //!< set if the matrix is in perspective
+    };
+
+    /** Returns a mask bitfield describing the types of transformations
+        that the matrix will perform. This information is used by routines
+        like mapPoints, to optimize its inner loops to only perform as much
+        arithmetic as is necessary.
+    */
+    TypeMask getType() const {
+        if (fTypeMask & kUnknown_Mask) {
+            fTypeMask = this->computeTypeMask();
+        }
+        // only return the public masks
+        return (TypeMask)(fTypeMask & 0xF);
+    }
+
+    /** Returns true if the matrix is identity.
+    */
+    bool isIdentity() const {
+        return this->getType() == 0;
+    }
+
+    /** Returns true if will map a rectangle to another rectangle. This can be
+        true if the matrix is identity, scale-only, or rotates a multiple of
+        90 degrees.
+    */
+    bool rectStaysRect() const {
+        if (fTypeMask & kUnknown_Mask) {
+            fTypeMask = this->computeTypeMask();
+        }
+        return (fTypeMask & kRectStaysRect_Mask) != 0;
+    }
+
+    enum {
+        kMScaleX,
+        kMSkewX,
+        kMTransX,
+        kMSkewY,
+        kMScaleY,
+        kMTransY,
+        kMPersp0,
+        kMPersp1,
+        kMPersp2
+    };
+    
+    SkScalar operator[](int index) const {
+        SkASSERT((unsigned)index < 9);
+        return fMat[index];
+    }
+    
+    SkScalar get(int index) const {
+        SkASSERT((unsigned)index < 9);
+        return fMat[index];
+    }
+    
+    SkScalar getScaleX() const { return fMat[kMScaleX]; }
+    SkScalar getScaleY() const { return fMat[kMScaleY]; }
+    SkScalar getSkewY() const { return fMat[kMSkewY]; }
+    SkScalar getSkewX() const { return fMat[kMSkewX]; }
+    SkScalar getTranslateX() const { return fMat[kMTransX]; }
+    SkScalar getTranslateY() const { return fMat[kMTransY]; }
+    SkScalar getPerspX() const { return fMat[kMPersp0]; }
+    SkScalar getPerspY() const { return fMat[kMPersp1]; }
+
+    void set(int index, SkScalar value) {
+        SkASSERT((unsigned)index < 9);
+        fMat[index] = value;
+        this->setTypeMask(kUnknown_Mask);
+    }
+
+    void setScaleX(SkScalar v) { this->set(kMScaleX, v); }
+    void setScaleY(SkScalar v) { this->set(kMScaleY, v); }
+    void setSkewY(SkScalar v) { this->set(kMSkewY, v); }
+    void setSkewX(SkScalar v) { this->set(kMSkewX, v); }
+    void setTranslateX(SkScalar v) { this->set(kMTransX, v); }
+    void setTranslateY(SkScalar v) { this->set(kMTransY, v); }
+    void setPerspX(SkScalar v) { this->set(kMPersp0, v); }
+    void setPerspY(SkScalar v) { this->set(kMPersp1, v); }
+
+    /** Set the matrix to identity
+    */
+    void reset();
+    
+    /** Set the matrix to translate by (dx, dy).
+    */
+    void setTranslate(SkScalar dx, SkScalar dy);
+    /** Set the matrix to scale by sx and sy, with a pivot point at (px, py).
+        The pivot point is the coordinate that should remain unchanged by the
+        specified transformation.
+    */
+    void setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
+    /** Set the matrix to scale by sx and sy.
+    */
+    void setScale(SkScalar sx, SkScalar sy);
+    /** Set the matrix to rotate by the specified number of degrees, with a
+        pivot point at (px, py). The pivot point is the coordinate that should
+        remain unchanged by the specified transformation.
+    */
+    void setRotate(SkScalar degrees, SkScalar px, SkScalar py);
+    /** Set the matrix to rotate about (0,0) by the specified number of degrees.
+    */
+    void setRotate(SkScalar degrees);
+    /** Set the matrix to rotate by the specified sine and cosine values, with
+        a pivot point at (px, py). The pivot point is the coordinate that
+        should remain unchanged by the specified transformation.
+    */
+    void setSinCos(SkScalar sinValue, SkScalar cosValue,
+                   SkScalar px, SkScalar py);
+    /** Set the matrix to rotate by the specified sine and cosine values.
+    */
+    void setSinCos(SkScalar sinValue, SkScalar cosValue);
+    /** Set the matrix to skew by sx and sy, with a pivot point at (px, py).
+        The pivot point is the coordinate that should remain unchanged by the
+        specified transformation.
+    */
+    void setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
+    /** Set the matrix to skew by sx and sy.
+    */
+    void setSkew(SkScalar kx, SkScalar ky);
+    /** Set the matrix to the concatenation of the two specified matrices,
+        returning true if the the result can be represented. Either of the
+        two matrices may also be the target matrix. *this = a * b;
+    */
+    bool setConcat(const SkMatrix& a, const SkMatrix& b);
+
+    /** Preconcats the matrix with the specified translation.
+        M' = M * T(dx, dy)
+    */
+    bool preTranslate(SkScalar dx, SkScalar dy);
+    /** Preconcats the matrix with the specified scale.
+        M' = M * S(sx, sy, px, py)
+    */
+    bool preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
+    /** Preconcats the matrix with the specified scale.
+        M' = M * S(sx, sy)
+    */
+    bool preScale(SkScalar sx, SkScalar sy);
+    /** Preconcats the matrix with the specified rotation.
+        M' = M * R(degrees, px, py)
+    */
+    bool preRotate(SkScalar degrees, SkScalar px, SkScalar py);
+    /** Preconcats the matrix with the specified rotation.
+        M' = M * R(degrees)
+    */
+    bool preRotate(SkScalar degrees);
+    /** Preconcats the matrix with the specified skew.
+        M' = M * K(kx, ky, px, py)
+    */
+    bool preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
+    /** Preconcats the matrix with the specified skew.
+        M' = M * K(kx, ky)
+    */
+    bool preSkew(SkScalar kx, SkScalar ky);
+    /** Preconcats the matrix with the specified matrix.
+        M' = M * other
+    */
+    bool preConcat(const SkMatrix& other);
+
+    /** Postconcats the matrix with the specified translation.
+        M' = T(dx, dy) * M
+    */
+    bool postTranslate(SkScalar dx, SkScalar dy);
+    /** Postconcats the matrix with the specified scale.
+        M' = S(sx, sy, px, py) * M
+    */
+    bool postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
+    /** Postconcats the matrix with the specified scale.
+        M' = S(sx, sy) * M
+    */
+    bool postScale(SkScalar sx, SkScalar sy);
+    /** Postconcats the matrix by dividing it by the specified integers.
+        M' = S(1/divx, 1/divy, 0, 0) * M
+    */
+    bool postIDiv(int divx, int divy);
+    /** Postconcats the matrix with the specified rotation.
+        M' = R(degrees, px, py) * M
+    */
+    bool postRotate(SkScalar degrees, SkScalar px, SkScalar py);
+    /** Postconcats the matrix with the specified rotation.
+        M' = R(degrees) * M
+    */
+    bool postRotate(SkScalar degrees);
+    /** Postconcats the matrix with the specified skew.
+        M' = K(kx, ky, px, py) * M
+    */
+    bool postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
+    /** Postconcats the matrix with the specified skew.
+        M' = K(kx, ky) * M
+    */
+    bool postSkew(SkScalar kx, SkScalar ky);
+    /** Postconcats the matrix with the specified matrix.
+        M' = other * M
+    */
+    bool postConcat(const SkMatrix& other);
+
+    enum ScaleToFit {
+        /**
+         * Scale in X and Y independently, so that src matches dst exactly.
+         * This may change the aspect ratio of the src.
+         */
+        kFill_ScaleToFit,
+        /**
+         * Compute a scale that will maintain the original src aspect ratio,
+         * but will also ensure that src fits entirely inside dst. At least one
+         * axis (X or Y) will fit exactly. kStart aligns the result to the
+         * left and top edges of dst.
+         */
+        kStart_ScaleToFit,
+        /**
+         * Compute a scale that will maintain the original src aspect ratio,
+         * but will also ensure that src fits entirely inside dst. At least one
+         * axis (X or Y) will fit exactly. The result is centered inside dst.
+         */
+        kCenter_ScaleToFit,
+        /**
+         * Compute a scale that will maintain the original src aspect ratio,
+         * but will also ensure that src fits entirely inside dst. At least one
+         * axis (X or Y) will fit exactly. kEnd aligns the result to the
+         * right and bottom edges of dst.
+         */
+        kEnd_ScaleToFit
+    };
+
+    /** Set the matrix to the scale and translate values that map the source
+        rectangle to the destination rectangle, returning true if the the result
+        can be represented.
+        @param src the source rectangle to map from.
+        @param dst the destination rectangle to map to.
+        @param stf the ScaleToFit option
+        @return true if the matrix can be represented by the rectangle mapping.
+    */
+    bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf);
+    
+    /** Set the matrix such that the specified src points would map to the
+        specified dst points. count must be within [0..4].
+        @param src  The array of src points
+        @param dst  The array of dst points
+        @param count The number of points to use for the transformation
+        @return true if the matrix was set to the specified transformation
+    */
+    bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count);
+
+    /** If this matrix can be inverted, return true and if inverse is not null,
+        set inverse to be the inverse of this matrix. If this matrix cannot be
+        inverted, ignore inverse and return false
+    */
+    bool invert(SkMatrix* inverse) const;
+
+    /** Apply this matrix to the array of points specified by src, and write
+        the transformed points into the array of points specified by dst.
+        dst[] = M * src[]
+        @param dst  Where the transformed coordinates are written. It must
+                    contain at least count entries
+        @param src  The original coordinates that are to be transformed. It
+                    must contain at least count entries
+        @param count The number of points in src to read, and then transform
+                     into dst.
+    */
+    void mapPoints(SkPoint dst[], const SkPoint src[], int count) const;
+
+    /** Apply this matrix to the array of points, overwriting it with the
+        transformed values.
+        dst[] = M * pts[]
+        @param pts  The points to be transformed. It must contain at least
+                    count entries
+        @param count The number of points in pts.
+    */
+    void mapPoints(SkPoint pts[], int count) const {
+        this->mapPoints(pts, pts, count);
+    }
+    
+    void mapXY(SkScalar x, SkScalar y, SkPoint* result) const {
+        SkASSERT(result);
+        this->getMapXYProc()(*this, x, y, result);
+    }
+
+    /** Apply this matrix to the array of vectors specified by src, and write
+        the transformed vectors into the array of vectors specified by dst.
+        This is similar to mapPoints, but ignores any translation in the matrix.
+        @param dst  Where the transformed coordinates are written. It must
+                    contain at least count entries
+        @param src  The original coordinates that are to be transformed. It
+                    must contain at least count entries
+        @param count The number of vectors in src to read, and then transform
+                     into dst.
+    */
+    void mapVectors(SkVector dst[], const SkVector src[], int count) const;
+
+    /** Apply this matrix to the array of vectors specified by src, and write
+        the transformed vectors into the array of vectors specified by dst.
+        This is similar to mapPoints, but ignores any translation in the matrix.
+        @param vecs The vectors to be transformed. It must contain at least
+                    count entries
+        @param count The number of vectors in vecs.
+    */
+    void mapVectors(SkVector vecs[], int count) const {
+        this->mapVectors(vecs, vecs, count);
+    }
+
+    /** Apply this matrix to the src rectangle, and write the transformed
+        rectangle into dst. This is accomplished by transforming the 4 corners
+        of src, and then setting dst to the bounds of those points.
+        @param dst  Where the transformed rectangle is written.
+        @param src  The original rectangle to be transformed.
+        @return the result of calling rectStaysRect()
+    */
+    bool mapRect(SkRect* dst, const SkRect& src) const;
+
+    /** Apply this matrix to the rectangle, and write the transformed rectangle
+        back into it. This is accomplished by transforming the 4 corners of
+        rect, and then setting it to the bounds of those points
+        @param rect The rectangle to transform.
+        @return the result of calling rectStaysRect()
+    */
+    bool mapRect(SkRect* rect) const {
+        return this->mapRect(rect, *rect);
+    }
+
+    /** Return the mean radius of a circle after it has been mapped by
+        this matrix. NOTE: in perspective this value assumes the circle
+        has its center at the origin.
+    */
+    SkScalar mapRadius(SkScalar radius) const;
+
+    typedef void (*MapXYProc)(const SkMatrix& mat, SkScalar x, SkScalar y,
+                                 SkPoint* result);
+
+    static MapXYProc GetMapXYProc(TypeMask mask) {
+        SkASSERT((mask & ~kAllMasks) == 0);
+        return gMapXYProcs[mask & kAllMasks];
+    }
+    
+    MapXYProc getMapXYProc() const {
+        return GetMapXYProc(this->getType());
+    }
+
+    typedef void (*MapPtsProc)(const SkMatrix& mat, SkPoint dst[],
+                                  const SkPoint src[], int count);
+
+    static MapPtsProc GetMapPtsProc(TypeMask mask) {
+        SkASSERT((mask & ~kAllMasks) == 0);
+        return gMapPtsProcs[mask & kAllMasks];
+    }
+    
+    MapPtsProc getMapPtsProc() const {
+        return GetMapPtsProc(this->getType());
+    }
+
+    /** If the matrix can be stepped in X (not complex perspective)
+        then return true and if step[XY] is not null, return the step[XY] value.
+        If it cannot, return false and ignore step.
+    */
+    bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const;
+
+    friend bool operator==(const SkMatrix& a, const SkMatrix& b) {
+        return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) == 0;
+    }
+
+    friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
+        return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) != 0;
+    }
+    
+    void dump() const;
+    void toDumpString(SkString*) const;
+
+#ifdef SK_DEBUG
+  /** @cond UNIT_TEST */
+
+    static void UnitTest();
+  /** @endcond */
+#endif
+
+private:
+    enum {
+        /** Set if the matrix will map a rectangle to another rectangle. This
+            can be true if the matrix is scale-only, or rotates a multiple of
+            90 degrees. This bit is not set if the matrix is identity.
+             
+            This bit will be set on identity matrices
+        */
+        kRectStaysRect_Mask = 0x10,
+
+        kUnknown_Mask = 0x80,
+        
+        kAllMasks = kTranslate_Mask |
+                    kScale_Mask |
+                    kAffine_Mask |
+                    kPerspective_Mask |
+                    kRectStaysRect_Mask
+    };
+
+    SkScalar        fMat[9];
+    mutable uint8_t fTypeMask;
+
+    uint8_t computeTypeMask() const;
+
+    void setTypeMask(int mask) {
+        // allow kUnknown or a valid mask
+        SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask);
+        fTypeMask = SkToU8(mask);
+    }
+    
+    void clearTypeMask(int mask) {
+        // only allow a valid mask
+        SkASSERT((mask & kAllMasks) == mask);
+        fTypeMask &= ~mask;
+    }
+    
+    static bool Poly2Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
+    static bool Poly3Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
+    static bool Poly4Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
+
+    static void Identity_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
+    static void Trans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
+    static void Scale_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
+    static void ScaleTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
+    static void Rot_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
+    static void RotTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
+    static void Persp_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
+    
+    static const MapXYProc gMapXYProcs[];
+    
+    static void Identity_pts(const SkMatrix&, SkPoint[], const SkPoint[], int);
+    static void Trans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
+    static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
+    static void ScaleTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
+                               int count);
+    static void Rot_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
+    static void RotTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
+                             int count);
+    static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
+    
+    static const MapPtsProc gMapPtsProcs[];
+
+    friend class SkPerspIter;
+};
+
+#endif
+
diff --git a/include/core/SkPackBits.h b/include/core/SkPackBits.h
new file mode 100644
index 0000000..c11231b
--- /dev/null
+++ b/include/core/SkPackBits.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPackBits_DEFINED
+#define SkPackBits_DEFINED
+
+#include "SkTypes.h"
+
+class SkPackBits {
+public:
+    /** Given the number of 16bit values that will be passed to Pack16,
+        returns the worst-case size needed for the dst[] buffer.
+    */
+    static size_t ComputeMaxSize16(int count);
+
+    /** Given the number of 8bit values that will be passed to Pack8,
+        returns the worst-case size needed for the dst[] buffer.
+    */
+    static size_t ComputeMaxSize8(int count);
+
+    /** Write the src array into a packed format. The packing process may end
+        up writing more bytes than it read, so dst[] must be large enough.
+        @param src      Input array of 16bit values
+        @param count    Number of entries in src[]
+        @param dst      Buffer (allocated by caller) to write the packed data
+                        into
+        @return the number of bytes written to dst[]
+    */
+    static size_t Pack16(const uint16_t src[], int count, uint8_t dst[]);
+
+    /** Write the src array into a packed format. The packing process may end
+        up writing more bytes than it read, so dst[] must be large enough.
+        @param src      Input array of 8bit values
+        @param count    Number of entries in src[]
+        @param dst      Buffer (allocated by caller) to write the packed data
+                        into
+        @return the number of bytes written to dst[]
+    */
+    static size_t Pack8(const uint8_t src[], int count, uint8_t dst[]);
+
+    /** Unpack the data in src[], and expand it into dst[]. The src[] data was
+        written by a previous call to Pack16.
+        @param src  Input data to unpack, previously created by Pack16.
+        @param srcSize  Number of bytes of src to unpack
+        @param dst  Buffer (allocated by caller) to expand the src[] into.
+        @return the number of dst elements (not bytes) written into dst.
+    */
+    static int Unpack16(const uint8_t src[], size_t srcSize, uint16_t dst[]);
+
+    /** Unpack the data in src[], and expand it into dst[]. The src[] data was
+        written by a previous call to Pack8.
+        @param src      Input data to unpack, previously created by Pack8.
+        @param srcSize  Number of bytes of src to unpack
+        @param dst      Buffer (allocated by caller) to expand the src[] into.
+        @return the number of bytes written into dst.
+    */
+    static int Unpack8(const uint8_t src[], size_t srcSize, uint8_t dst[]);
+
+    /** Unpack the data from src[], skip the first dstSkip bytes, then write
+        dstWrite bytes into dst[]. The src[] data was written by a previous
+        call to Pack8. Return the number of bytes actually writtten into dst[]
+        @param src      Input data to unpack, previously created by Pack8.
+        @param dst      Buffer (allocated by caller) to expand the src[] into.
+        @param dstSkip  Number of bytes of unpacked src to skip before writing
+                        into dst
+        @param dstWrite Number of bytes of unpacked src to write into dst (after
+                        skipping dstSkip bytes)
+    */
+    static void Unpack8(uint8_t dst[], size_t dstSkip, size_t dstWrite,
+                        const uint8_t src[]);
+};
+
+#endif
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
new file mode 100644
index 0000000..fc390ab
--- /dev/null
+++ b/include/core/SkPaint.h
@@ -0,0 +1,814 @@
+/*
+ * 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 SkPaint_DEFINED
+#define SkPaint_DEFINED
+
+#include "SkColor.h"
+#include "SkMath.h"
+#include "SkPorterDuff.h"
+
+class SkAutoGlyphCache;
+class SkColorFilter;
+class SkDescriptor;
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+struct SkGlyph;
+struct SkRect;
+class SkGlyphCache;
+class SkMaskFilter;
+class SkMatrix;
+class SkPath;
+class SkPathEffect;
+class SkRasterizer;
+class SkShader;
+class SkDrawLooper;
+class SkTypeface;
+class SkXfermode;
+
+typedef const SkGlyph& (*SkDrawCacheProc)(SkGlyphCache*, const char**,
+                                           SkFixed x, SkFixed y);
+
+typedef const SkGlyph& (*SkMeasureCacheProc)(SkGlyphCache*, const char**);
+
+/** \class SkPaint
+
+    The SkPaint class holds the style and color information about how to draw
+    geometries, text and bitmaps.
+*/
+class SkPaint {
+public:
+    SkPaint();
+    SkPaint(const SkPaint& paint);
+    ~SkPaint();
+
+    SkPaint& operator=(const SkPaint&);
+
+    friend int operator==(const SkPaint& a, const SkPaint& b);
+    friend int operator!=(const SkPaint& a, const SkPaint& b)
+    {
+        return !(a == b);
+    }
+    
+    void flatten(SkFlattenableWriteBuffer&) const;
+    void unflatten(SkFlattenableReadBuffer&);
+
+    /** Restores the paint to its initial settings.
+    */
+    void reset();
+
+    /** Specifies the bit values that are stored in the paint's flags.
+    */
+    enum Flags {
+        kAntiAlias_Flag       = 0x01,   //!< mask to enable antialiasing
+        kFilterBitmap_Flag    = 0x02,   //!< mask to enable bitmap filtering
+        kDither_Flag          = 0x04,   //!< mask to enable dithering
+        kUnderlineText_Flag   = 0x08,   //!< mask to enable underline text
+        kStrikeThruText_Flag  = 0x10,   //!< mask to enable strike-thru text
+        kFakeBoldText_Flag    = 0x20,   //!< mask to enable fake-bold text
+        kLinearText_Flag      = 0x40,   //!< mask to enable linear-text
+        kSubpixelText_Flag    = 0x80,   //!< mask to enable subpixel-text
+        kDevKernText_Flag     = 0x100,  //!< mask to enable device kerning text
+
+        kAllFlags = 0x1FF
+    };
+
+    /** Return the paint's flags. Use the Flag enum to test flag values.
+        @return the paint's flags (see enums ending in _Flag for bit masks)
+    */
+    uint32_t getFlags() const { return fFlags; }
+
+    /** Set the paint's flags. Use the Flag enum to specific flag values.
+        @param flags    The new flag bits for the paint (see Flags enum)
+    */
+    void setFlags(uint32_t flags);
+
+    /** Helper for getFlags(), returning true if kAntiAlias_Flag bit is set
+        @return true if the antialias bit is set in the paint's flags.
+        */
+    bool isAntiAlias() const
+    {
+        return SkToBool(this->getFlags() & kAntiAlias_Flag);
+    }
+    
+    /** Helper for setFlags(), setting or clearing the kAntiAlias_Flag bit
+        @param aa   true to enable antialiasing, false to disable it
+        */
+    void setAntiAlias(bool aa);
+    
+    /** Helper for getFlags(), returning true if kDither_Flag bit is set
+        @return true if the dithering bit is set in the paint's flags.
+        */
+    bool isDither() const
+    {
+        return SkToBool(this->getFlags() & kDither_Flag);
+    }
+    
+    /** Helper for setFlags(), setting or clearing the kDither_Flag bit
+        @param dither   true to enable dithering, false to disable it
+        */
+    void setDither(bool dither);
+    
+    /** Helper for getFlags(), returning true if kLinearText_Flag bit is set
+        @return true if the lineartext bit is set in the paint's flags
+    */
+    bool isLinearText() const
+    {
+        return SkToBool(this->getFlags() & kLinearText_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kLinearText_Flag bit
+        @param linearText true to set the linearText bit in the paint's flags,
+                          false to clear it.
+    */
+    void setLinearText(bool linearText);
+
+    /** Helper for getFlags(), returning true if kSubpixelText_Flag bit is set
+        @return true if the lineartext bit is set in the paint's flags
+    */
+    bool isSubpixelText() const
+    {
+        return SkToBool(this->getFlags() & kSubpixelText_Flag);
+    }
+    
+    /** Helper for setFlags(), setting or clearing the kSubpixelText_Flag bit
+        @param subpixelText true to set the subpixelText bit in the paint's
+                            flags, false to clear it.
+    */
+    void setSubpixelText(bool subpixelText);
+    
+    /** Helper for getFlags(), returning true if kUnderlineText_Flag bit is set
+        @return true if the underlineText bit is set in the paint's flags.
+    */
+    bool isUnderlineText() const
+    {
+        return SkToBool(this->getFlags() & kUnderlineText_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kUnderlineText_Flag bit
+        @param underlineText true to set the underlineText bit in the paint's
+                             flags, false to clear it.
+    */
+    void setUnderlineText(bool underlineText);
+
+    /** Helper for getFlags(), returns true if kStrikeThruText_Flag bit is set
+        @return true if the strikeThruText bit is set in the paint's flags.
+    */
+    bool    isStrikeThruText() const
+    {
+        return SkToBool(this->getFlags() & kStrikeThruText_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kStrikeThruText_Flag bit
+        @param strikeThruText   true to set the strikeThruText bit in the
+                                paint's flags, false to clear it.
+    */
+    void setStrikeThruText(bool strikeThruText);
+
+    /** Helper for getFlags(), returns true if kFakeBoldText_Flag bit is set
+        @return true if the kFakeBoldText_Flag bit is set in the paint's flags.
+    */
+    bool isFakeBoldText() const
+    {
+        return SkToBool(this->getFlags() & kFakeBoldText_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kFakeBoldText_Flag bit
+        @param fakeBoldText true to set the kFakeBoldText_Flag bit in the paint's
+                            flags, false to clear it.
+    */
+    void setFakeBoldText(bool fakeBoldText);
+
+    /** Helper for getFlags(), returns true if kDevKernText_Flag bit is set
+        @return true if the kernText bit is set in the paint's flags.
+    */
+    bool isDevKernText() const
+    {
+        return SkToBool(this->getFlags() & kDevKernText_Flag);
+    }
+
+    /** Helper for setFlags(), setting or clearing the kKernText_Flag bit
+        @param kernText true to set the kKernText_Flag bit in the paint's
+                            flags, false to clear it.
+    */
+    void setDevKernText(bool devKernText);
+
+    bool isFilterBitmap() const
+    {
+        return SkToBool(this->getFlags() & kFilterBitmap_Flag);
+    }
+    
+    void setFilterBitmap(bool filterBitmap);
+
+    /** Styles apply to rect, oval, path, and text.
+        Bitmaps are always drawn in "fill", and lines are always drawn in
+        "stroke".
+    */
+    enum Style {
+        kFill_Style,            //!< fill with the paint's color
+        kStroke_Style,          //!< stroke with the paint's color
+        kStrokeAndFill_Style,   //!< fill and stroke with the paint's color
+
+        kStyleCount,
+    };
+
+    /** Return the paint's style, used for controlling how primitives'
+        geometries are interpreted (except for drawBitmap, which always assumes
+        kFill_Style).
+        @return the paint's Style
+    */
+    Style getStyle() const { return (Style)fStyle; }
+
+    /** Set the paint's style, used for controlling how primitives'
+        geometries are interpreted (except for drawBitmap, which always assumes
+        Fill).
+        @param style    The new style to set in the paint
+    */
+    void setStyle(Style style);
+
+    /** Return the paint's color. Note that the color is a 32bit value
+        containing alpha as well as r,g,b. This 32bit value is not
+        premultiplied, meaning that its alpha can be any value, regardless of
+        the values of r,g,b.
+        @return the paint's color (and alpha).
+    */
+    SkColor getColor() const { return fColor; }
+
+    /** Set the paint's color. Note that the color is a 32bit value containing
+        alpha as well as r,g,b. This 32bit value is not premultiplied, meaning
+        that its alpha can be any value, regardless of the values of r,g,b.
+        @param color    The new color (including alpha) to set in the paint.
+    */
+    void setColor(SkColor color);
+
+    /** Helper to getColor() that just returns the color's alpha value.
+        @return the alpha component of the paint's color.
+        */
+    uint8_t getAlpha() const { return SkToU8(SkColorGetA(fColor)); }
+    
+    /** Helper to setColor(), that only assigns the color's alpha value,
+        leaving its r,g,b values unchanged.
+        @param a    set the alpha component (0..255) of the paint's color.
+    */
+    void setAlpha(U8CPU a);
+
+    /** Helper to setColor(), that takes a,r,g,b and constructs the color value
+        using SkColorSetARGB()
+        @param a    The new alpha component (0..255) of the paint's color.
+        @param r    The new red component (0..255) of the paint's color.
+        @param g    The new green component (0..255) of the paint's color.
+        @param b    The new blue component (0..255) of the paint's color.
+    */
+    void setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
+
+    /** Return the width for stroking. 
+        <p />
+        A value of 0 strokes in hairline mode.
+        Hairlines always draw 1-pixel wide, regardless of the matrix.
+        @return the paint's stroke width, used whenever the paint's style is
+                Stroke or StrokeAndFill.
+    */
+    SkScalar getStrokeWidth() const { return fWidth; }
+
+    /** Set the width for stroking. 
+        Pass 0 to stroke in hairline mode.
+        Hairlines always draw 1-pixel wide, regardless of the matrix.
+        @param width set the paint's stroke width, used whenever the paint's
+                     style is Stroke or StrokeAndFill.
+    */
+    void setStrokeWidth(SkScalar width);
+
+    /** Return the paint's stroke miter value. This is used to control the
+        behavior of miter joins when the joins angle is sharp.
+        @return the paint's miter limit, used whenever the paint's style is
+                Stroke or StrokeAndFill.
+    */
+    SkScalar getStrokeMiter() const { return fMiterLimit; }
+
+    /** Set the paint's stroke miter value. This is used to control the
+        behavior of miter joins when the joins angle is sharp. This value must
+        be >= 0.
+        @param miter    set the miter limit on the paint, used whenever the
+                        paint's style is Stroke or StrokeAndFill.
+    */
+    void setStrokeMiter(SkScalar miter);
+
+    /** Cap enum specifies the settings for the paint's strokecap. This is the
+        treatment that is applied to the beginning and end of each non-closed
+        contour (e.g. lines).
+    */
+    enum Cap {
+        kButt_Cap,      //!< begin/end contours with no extension
+        kRound_Cap,     //!< begin/end contours with a semi-circle extension
+        kSquare_Cap,    //!< begin/end contours with a half square extension
+
+        kCapCount,
+        kDefault_Cap = kButt_Cap
+    };
+
+    /** Join enum specifies the settings for the paint's strokejoin. This is
+        the treatment that is applied to corners in paths and rectangles.
+    */
+    enum Join {
+        kMiter_Join,    //!< connect path segments with a sharp join
+        kRound_Join,    //!< connect path segments with a round join
+        kBevel_Join,    //!< connect path segments with a flat bevel join
+
+        kJoinCount,
+        kDefault_Join = kMiter_Join
+    };
+
+    /** Return the paint's stroke cap type, controlling how the start and end
+        of stroked lines and paths are treated.
+        @return the line cap style for the paint, used whenever the paint's
+                style is Stroke or StrokeAndFill.
+    */
+    Cap getStrokeCap() const { return (Cap)fCapType; }
+
+    /** Set the paint's stroke cap type.
+        @param cap  set the paint's line cap style, used whenever the paint's
+                    style is Stroke or StrokeAndFill.
+    */
+    void setStrokeCap(Cap cap);
+
+    /** Return the paint's stroke join type.
+        @return the paint's line join style, used whenever the paint's style is
+                Stroke or StrokeAndFill.
+    */
+    Join getStrokeJoin() const { return (Join)fJoinType; }
+
+    /** Set the paint's stroke join type.
+        @param join set the paint's line join style, used whenever the paint's
+                    style is Stroke or StrokeAndFill.
+    */
+    void setStrokeJoin(Join join);
+
+    /** Applies any/all effects (patheffect, stroking) to src, returning the
+        result in dst. The result is that drawing src with this paint will be
+        the same as drawing dst with a default paint (at least from the
+        geometric perspective).
+        @param src  input path
+        @param dst  output path (may be the same as src)
+        @return     true if the path should be filled, or false if it should be
+                    drawn with a hairline (width == 0)
+    */
+    bool getFillPath(const SkPath& src, SkPath* dst) const;
+
+    /** Returns true if the current paint settings allow for fast computation of
+        bounds (i.e. there is nothing complex like a patheffect that would make
+        the bounds computation expensive.
+    */
+    bool canComputeFastBounds() const;
+    
+    /** Only call this if canComputeFastBounds() returned true. This takes a
+        raw rectangle (the raw bounds of a shape), and adjusts it for stylistic
+        effects in the paint (e.g. stroking). If needed, it uses the storage
+        rect parameter. It returns the adjusted bounds that can then be used
+        for quickReject tests.
+     
+        The returned rect will either be orig or storage, thus the caller
+        should not rely on storage being set to the result, but should always
+        use the retured value. It is legal for orig and storage to be the same
+        rect.
+        
+        e.g.
+        if (paint.canComputeFastBounds()) {
+            SkRect r, storage;
+            path.computeBounds(&r, SkPath::kFast_BoundsType);
+            const SkRect& fastR = paint.computeFastBounds(r, &storage);
+            if (canvas->quickReject(fastR, ...)) {
+                // don't draw the path
+            }
+        }
+    */
+    const SkRect& computeFastBounds(const SkRect& orig, SkRect* storage) const;
+
+    /** Get the paint's shader object.
+        <p />
+      The shader's reference count is not affected.
+        @return the paint's shader (or NULL)
+    */
+    SkShader* getShader() const { return fShader; }
+
+    /** Set or clear the shader object.
+        <p />
+        Pass NULL to clear any previous shader.
+        As a convenience, the parameter passed is also returned.
+        If a previous shader exists, its reference count is decremented.
+        If shader is not NULL, its reference count is incremented.
+        @param shader   May be NULL. The shader to be installed in the paint
+        @return         shader
+    */
+    SkShader* setShader(SkShader* shader);
+    
+    /** Get the paint's colorfilter. If there is a colorfilter, its reference
+        count is not changed.
+        @return the paint's colorfilter (or NULL)
+    */
+    SkColorFilter* getColorFilter() const { return fColorFilter; }
+
+    /** Set or clear the paint's colorfilter, returning the parameter.
+        <p />
+        If the paint already has a filter, its reference count is decremented.
+        If filter is not NULL, its reference count is incremented.
+        @param filter   May be NULL. The filter to be installed in the paint
+        @return         filter
+    */
+    SkColorFilter* setColorFilter(SkColorFilter* filter);
+
+    /** Get the paint's xfermode object.
+        <p />
+      The xfermode's reference count is not affected.
+        @return the paint's xfermode (or NULL)
+    */
+    SkXfermode* getXfermode() const { return fXfermode; }
+
+    /** Set or clear the xfermode object.
+        <p />
+        Pass NULL to clear any previous xfermode.
+        As a convenience, the parameter passed is also returned.
+        If a previous xfermode exists, its reference count is decremented.
+        If xfermode is not NULL, its reference count is incremented.
+        @param xfermode May be NULL. The new xfermode to be installed in the
+                        paint
+        @return         xfermode
+    */
+    SkXfermode* setXfermode(SkXfermode* xfermode);
+    
+    /** Helper for setXfermode, passing the corresponding xfermode object
+        returned from the PorterDuff factory.
+        @param mode The porter-duff mode used to create an xfermode for the
+                    paint.
+        @return     the resulting xfermode object (or NULL if the mode is
+                    SrcOver)
+    */
+    SkXfermode* setPorterDuffXfermode(SkPorterDuff::Mode mode);
+
+    /** Get the paint's patheffect object.
+        <p />
+      The patheffect reference count is not affected.
+        @return the paint's patheffect (or NULL)
+    */
+    SkPathEffect* getPathEffect() const { return fPathEffect; }
+
+    /** Set or clear the patheffect object.
+        <p />
+        Pass NULL to clear any previous patheffect.
+        As a convenience, the parameter passed is also returned.
+        If a previous patheffect exists, its reference count is decremented.
+        If patheffect is not NULL, its reference count is incremented.
+        @param effect   May be NULL. The new patheffect to be installed in the
+                        paint
+        @return         effect
+    */
+    SkPathEffect* setPathEffect(SkPathEffect* effect);
+
+    /** Get the paint's maskfilter object.
+        <p />
+      The maskfilter reference count is not affected.
+        @return the paint's maskfilter (or NULL)
+    */
+    SkMaskFilter* getMaskFilter() const { return fMaskFilter; }
+
+    /** Set or clear the maskfilter object.
+        <p />
+        Pass NULL to clear any previous maskfilter.
+        As a convenience, the parameter passed is also returned.
+        If a previous maskfilter exists, its reference count is decremented.
+        If maskfilter is not NULL, its reference count is incremented.
+        @param maskfilter   May be NULL. The new maskfilter to be installed in
+                            the paint
+        @return             maskfilter
+    */
+    SkMaskFilter* setMaskFilter(SkMaskFilter* maskfilter);
+
+    // These attributes are for text/fonts
+
+    /** Get the paint's typeface object.
+        <p />
+        The typeface object identifies which font to use when drawing or
+        measuring text. The typeface reference count is not affected.
+        @return the paint's typeface (or NULL)
+    */
+    SkTypeface* getTypeface() const { return fTypeface; }
+
+    /** Set or clear the typeface object.
+        <p />
+        Pass NULL to clear any previous typeface.
+        As a convenience, the parameter passed is also returned.
+        If a previous typeface exists, its reference count is decremented.
+        If typeface is not NULL, its reference count is incremented.
+        @param typeface May be NULL. The new typeface to be installed in the
+                        paint
+        @return         typeface
+    */
+    SkTypeface* setTypeface(SkTypeface* typeface);
+
+    /** Get the paint's rasterizer (or NULL).
+        <p />
+        The raster controls how paths/text are turned into alpha masks.
+        @return the paint's rasterizer (or NULL)
+    */
+    SkRasterizer* getRasterizer() const { return fRasterizer; }
+
+    /** Set or clear the rasterizer object.
+        <p />
+        Pass NULL to clear any previous rasterizer.
+        As a convenience, the parameter passed is also returned.
+        If a previous rasterizer exists in the paint, its reference count is
+        decremented. If rasterizer is not NULL, its reference count is
+        incremented.
+        @param rasterizer May be NULL. The new rasterizer to be installed in
+                          the paint.
+        @return           rasterizer
+    */
+    SkRasterizer* setRasterizer(SkRasterizer* rasterizer);
+
+    SkDrawLooper* getLooper() const { return fLooper; }
+    SkDrawLooper* setLooper(SkDrawLooper*);
+
+    enum Align {
+        kLeft_Align,
+        kCenter_Align,
+        kRight_Align,
+
+        kAlignCount
+    };
+    /** Return the paint's Align value for drawing text.
+        @return the paint's Align value for drawing text.
+    */
+    Align   getTextAlign() const { return (Align)fTextAlign; }
+    /** Set the paint's text alignment.
+        @param align set the paint's Align value for drawing text.
+    */
+    void    setTextAlign(Align align);
+
+    /** Return the paint's text size.
+        @return the paint's text size.
+    */
+    SkScalar getTextSize() const { return fTextSize; }
+
+    /** Set the paint's text size. This value must be > 0
+        @param textSize set the paint's text size.
+    */
+    void setTextSize(SkScalar textSize);
+
+    /** Return the paint's horizontal scale factor for text. The default value
+        is 1.0.
+        @return the paint's scale factor in X for drawing/measuring text
+    */
+    SkScalar getTextScaleX() const { return fTextScaleX; }
+
+    /** Set the paint's horizontal scale factor for text. The default value
+        is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
+        stretch the text narrower.
+        @param scaleX   set the paint's scale factor in X for drawing/measuring
+                        text.
+    */
+    void setTextScaleX(SkScalar scaleX);
+
+    /** Return the paint's horizontal skew factor for text. The default value
+        is 0.
+        @return the paint's skew factor in X for drawing text.
+    */
+    SkScalar getTextSkewX() const { return fTextSkewX; }
+
+    /** Set the paint's horizontal skew factor for text. The default value
+        is 0. For approximating oblique text, use values around -0.25.
+        @param skewX set the paint's skew factor in X for drawing text.
+    */
+    void setTextSkewX(SkScalar skewX);
+
+    /** Describes how to interpret the text parameters that are passed to paint
+        methods like measureText() and getTextWidths().
+    */
+    enum TextEncoding {
+        kUTF8_TextEncoding,     //!< the text parameters are UTF8
+        kUTF16_TextEncoding,    //!< the text parameters are UTF16
+        kGlyphID_TextEncoding   //!< the text parameters are glyph indices
+    };
+    
+    TextEncoding getTextEncoding() const
+    {
+        return (TextEncoding)fTextEncoding;
+    }
+
+    void setTextEncoding(TextEncoding encoding);
+
+    struct FontMetrics {
+        SkScalar    fTop;       //!< The greatest distance above the baseline for any glyph (will be <= 0)
+        SkScalar    fAscent;    //!< The recommended distance above the baseline (will be <= 0)
+        SkScalar    fDescent;   //!< The recommended distance below the baseline (will be >= 0)
+        SkScalar    fBottom;    //!< The greatest distance below the baseline for any glyph (will be >= 0)
+        SkScalar    fLeading;   //!< The recommended distance to add between lines of text (will be >= 0)
+    };
+    
+    /** Return the recommend spacing between lines (which will be
+        fDescent - fAscent + fLeading).
+        If metrics is not null, return in it the font metrics for the
+        typeface/pointsize/etc. currently set in the paint. 
+        @param metrics      If not null, returns the font metrics for the
+                            current typeface/pointsize/etc setting in this
+                            paint.
+        @param scale        If not 0, return width as if the canvas were scaled
+                            by this value
+        @param return the recommended spacing between lines
+    */
+    SkScalar getFontMetrics(FontMetrics* metrics, SkScalar scale = 0) const;
+    
+    /** Return the recommend line spacing. This will be
+        fDescent - fAscent + fLeading
+    */
+    SkScalar getFontSpacing() const { return this->getFontMetrics(NULL, 0); }
+
+    /** Convert the specified text into glyph IDs, returning the number of
+        glyphs ID written. If glyphs is NULL, it is ignore and only the count
+        is returned.
+    */
+    int textToGlyphs(const void* text, size_t byteLength,
+                     uint16_t glyphs[]) const;
+
+    /** Return the number of drawable units in the specified text buffer.
+        This looks at the current TextEncoding field of the paint. If you also
+        want to have the text converted into glyph IDs, call textToGlyphs
+        instead.
+    */
+    int countText(const void* text, size_t byteLength) const
+    {
+        return this->textToGlyphs(text, byteLength, NULL);
+    }
+
+    /** Return the width of the text.
+        @param text         The text to be measured
+        @param length       Number of bytes of text to measure
+        @param bounds       If not NULL, returns the bounds of the text,
+                            relative to (0, 0).
+        @param scale        If not 0, return width as if the canvas were scaled
+                            by this value
+        @return             The advance width of the text
+    */
+    SkScalar    measureText(const void* text, size_t length,
+                            SkRect* bounds, SkScalar scale = 0) const;
+
+    /** Return the width of the text.
+        @param text         Address of the text
+        @param length       Number of bytes of text to measure
+        @return The width of the text
+    */
+    SkScalar measureText(const void* text, size_t length) const
+    {
+        return this->measureText(text, length, NULL, 0);
+    }
+    
+    /** Specify the direction the text buffer should be processed in breakText()
+    */
+    enum TextBufferDirection {
+        /** When measuring text for breakText(), begin at the start of the text
+            buffer and proceed forward through the data. This is the default.
+        */
+        kForward_TextBufferDirection,
+        /** When measuring text for breakText(), begin at the end of the text
+            buffer and proceed backwards through the data.
+        */
+        kBackward_TextBufferDirection
+    };
+
+    /** Return the width of the text.
+        @param text     The text to be measured
+        @param length   Number of bytes of text to measure
+        @param maxWidth Maximum width. Only the subset of text whose accumulated
+                        widths are <= maxWidth are measured.
+        @param measuredWidth Optional. If non-null, this returns the actual
+                        width of the measured text.
+        @param tbd      Optional. The direction the text buffer should be
+                        traversed during measuring.
+        @return         The number of bytes of text that were measured. Will be
+                        <= length.
+    */
+    size_t  breakText(const void* text, size_t length, SkScalar maxWidth,
+                      SkScalar* measuredWidth = NULL,
+                      TextBufferDirection tbd = kForward_TextBufferDirection)
+                      const;
+
+    /** Return the advance widths for the characters in the string.
+        @param text         the text
+        @param byteLength   number of bytes to of text
+        @param widths       If not null, returns the array of advance widths of
+                            the glyphs. If not NULL, must be at least a large
+                            as the number of unichars in the specified text.
+        @param bounds       If not null, returns the bounds for each of
+                            character, relative to (0, 0)
+        @return the number of unichars in the specified text.
+    */
+    int getTextWidths(const void* text, size_t byteLength, SkScalar widths[],
+                      SkRect bounds[] = NULL) const;
+
+    /** Return the path (outline) for the specified text.
+        Note: just like SkCanvas::drawText, this will respect the Align setting
+              in the paint.
+    */
+    void getTextPath(const void* text, size_t length, SkScalar x, SkScalar y,
+                     SkPath* path) const;
+
+private:
+    SkTypeface*     fTypeface;
+    SkScalar        fTextSize;
+    SkScalar        fTextScaleX;
+    SkScalar        fTextSkewX;
+
+    SkPathEffect*   fPathEffect;
+    SkShader*       fShader;
+    SkXfermode*     fXfermode;
+    SkMaskFilter*   fMaskFilter;
+    SkColorFilter*  fColorFilter;
+    SkRasterizer*   fRasterizer;
+    SkDrawLooper*   fLooper;
+
+    SkColor         fColor;
+    SkScalar        fWidth;
+    SkScalar        fMiterLimit;
+    unsigned        fFlags : 9;
+    unsigned        fTextAlign : 2;
+    unsigned        fCapType : 2;
+    unsigned        fJoinType : 2;
+    unsigned        fStyle : 2;
+    unsigned        fTextEncoding : 2;  // 3 values
+
+    SkDrawCacheProc    getDrawCacheProc() const;
+    SkMeasureCacheProc getMeasureCacheProc(TextBufferDirection dir,
+                                           bool needFullMetrics) const;
+
+    SkScalar measure_text(SkGlyphCache*, const char* text, size_t length,
+                          int* count, SkRect* bounds) const;
+
+    SkGlyphCache*   detachCache(const SkMatrix*) const;
+
+    void descriptorProc(const SkMatrix* deviceMatrix,
+                        void (*proc)(const SkDescriptor*, void*),
+                        void* context) const;
+        
+    enum {
+        kCanonicalTextSizeForPaths = 64
+    };
+    friend class SkCanvas;
+    friend class SkDraw;
+    friend class SkAutoGlyphCache;
+    friend class SkTextToPathIter;
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+#include "SkPathEffect.h"
+
+/** \class SkStrokePathEffect
+
+    SkStrokePathEffect simulates stroking inside a patheffect, allowing the
+    caller to have explicit control of when to stroke a path. Typically this is
+    used if the caller wants to stroke before another patheffect is applied
+    (using SkComposePathEffect or SkSumPathEffect).
+*/
+class SkStrokePathEffect : public SkPathEffect {
+public:
+    SkStrokePathEffect(const SkPaint&);
+    SkStrokePathEffect(SkScalar width, SkPaint::Style, SkPaint::Join,
+                       SkPaint::Cap, SkScalar miterLimit = -1);
+
+    // overrides
+    // This method is not exported to java.
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+    // overrides for SkFlattenable
+    // This method is not exported to java.
+    virtual void flatten(SkFlattenableWriteBuffer&);
+    // This method is not exported to java.
+    virtual Factory getFactory();
+
+private:
+    SkScalar    fWidth, fMiter;
+    uint8_t     fStyle, fJoin, fCap;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+    SkStrokePathEffect(SkFlattenableReadBuffer&);
+
+    typedef SkPathEffect INHERITED;
+
+    // illegal
+    SkStrokePathEffect(const SkStrokePathEffect&);
+    SkStrokePathEffect& operator=(const SkStrokePathEffect&);
+};
+
+#endif
+
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
new file mode 100644
index 0000000..e2409ad
--- /dev/null
+++ b/include/core/SkPath.h
@@ -0,0 +1,588 @@
+/*
+ * 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 SkPath_DEFINED
+#define SkPath_DEFINED
+
+#include "SkMatrix.h"
+#include "SkTDArray.h"
+
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+class SkAutoPathBoundsUpdate;
+class SkString;
+
+/** \class SkPath
+
+    The SkPath class encapsulates compound (multiple contour) geometric paths
+    consisting of straight line segments, quadratic curves, and cubic curves.
+*/
+class SkPath {
+public:
+    SkPath();
+    SkPath(const SkPath&);
+    ~SkPath();
+
+    SkPath& operator=(const SkPath&);
+
+    enum FillType {
+        /** Specifies that "inside" is computed by a non-zero sum of signed
+            edge crossings
+        */
+        kWinding_FillType,
+        /** Specifies that "inside" is computed by an odd number of edge
+            crossings
+        */
+        kEvenOdd_FillType,
+        /** Same as Winding, but draws outside of the path, rather than inside
+        */
+        kInverseWinding_FillType,
+        /** Same as EvenOdd, but draws outside of the path, rather than inside
+         */
+        kInverseEvenOdd_FillType
+    };
+
+    /** Return the path's fill type. This is used to define how "inside" is
+        computed. The default value is kWinding_FillType.
+
+        @return the path's fill type
+    */
+    FillType getFillType() const { return (FillType)fFillType; }
+
+    /** Set the path's fill type. This is used to define how "inside" is
+        computed. The default value is kWinding_FillType.
+     
+        @param ft The new fill type for this path
+    */
+    void setFillType(FillType ft) { fFillType = SkToU8(ft); }
+
+    /** Returns true if the filltype is one of the Inverse variants */
+    bool isInverseFillType() const { return (fFillType & 2) != 0; }
+
+    /** Toggle between inverse and normal filltypes. This reverse the return
+        value of isInverseFillType()
+    */
+    void toggleInverseFillType() { fFillType ^= 2; }
+
+    /** Clear any lines and curves from the path, making it empty. This frees up
+        internal storage associated with those segments.
+        This does NOT change the fill-type setting.
+    */
+    void reset();
+    
+    /** Similar to reset(), in that all lines and curves are removed from the
+        path. However, any internal storage for those lines/curves is retained,
+        making reuse of the path potentially faster.
+        This does NOT change the fill-type setting.
+    */
+    void rewind();
+
+    /** Returns true if the path is empty (contains no lines or curves)
+
+        @return true if the path is empty (contains no lines or curves)
+    */
+    bool isEmpty() const;
+
+    /** Returns true if the path specifies a rectangle. If so, and if rect is
+        not null, set rect to the bounds of the path. If the path does not
+        specify a rectangle, return false and ignore rect.
+     
+        @param rect If not null, returns the bounds of the path if it specifies
+                    a rectangle
+        @return true if the path specifies a rectangle
+    */
+    bool isRect(SkRect* rect) const;
+
+    /** Returns the number of points in the path. Up to max points are copied.
+     
+        @param points If not null, receives up to max points
+        @param max The maximum number of points to copy into points
+        @return the actual number of points in the path
+    */
+    int getPoints(SkPoint points[], int max) const;
+
+    //! Swap contents of this and other. Guaranteed not to throw
+    void swap(SkPath& other);
+
+    enum BoundsType {
+        /** compute the bounds of the path's control points, may be larger than
+            with kExact_BoundsType, but may be faster to compute
+        */
+        kFast_BoundsType,
+        /** compute the exact bounds of the path, may be smaller than with
+            kFast_BoundsType, but may be slower to compute
+        */
+        kExact_BoundsType
+    };
+
+    /** Compute the bounds of the path, and write the answer into bounds. If the
+        path contains 0 or 1 points, the bounds is set to (0,0,0,0)
+     
+        @param bounds   Returns the computed bounds of the path
+        @param btype    Specifies if the computed bounds should be exact
+                        (slower) or approximate (faster)
+    */
+    void computeBounds(SkRect* bounds, BoundsType btype) const;
+
+    /** Calling this will, if the internal cache of the bounds is out of date,
+        update it so that subsequent calls to computeBounds will be instanteous.
+        This also means that any copies or simple transformations of the path
+        will inherit the cached bounds.
+    */
+    void updateBoundsCache() const;
+
+    //  Construction methods
+
+    /** Hint to the path to prepare for adding more points. This can allow the
+        path to more efficiently grow its storage.
+    
+        @param extraPtCount The number of extra points the path should
+                            preallocate for.
+    */
+    void incReserve(unsigned extraPtCount);
+
+    /** Set the beginning of the next contour to the point (x,y).
+     
+        @param x    The x-coordinate of the start of a new contour
+        @param y    The y-coordinate of the start of a new contour
+    */
+    void moveTo(SkScalar x, SkScalar y);
+
+    /** Set the beginning of the next contour to the point
+     
+        @param p    The start of a new contour
+    */
+    void moveTo(const SkPoint& p) {
+        this->moveTo(p.fX, p.fY);
+    }
+
+    /** Set the beginning of the next contour relative to the last point on the
+        previous contour. If there is no previous contour, this is treated the
+        same as moveTo().
+     
+        @param dx   The amount to add to the x-coordinate of the end of the
+                    previous contour, to specify the start of a new contour
+        @param dy   The amount to add to the y-coordinate of the end of the
+                    previous contour, to specify the start of a new contour
+    */
+    void rMoveTo(SkScalar dx, SkScalar dy);
+
+    /** Add a line from the last point to the specified point (x,y). If no
+        moveTo() call has been made for this contour, the first point is
+        automatically set to (0,0).
+
+        @param x    The x-coordinate of the end of a line
+        @param y    The y-coordinate of the end of a line
+    */
+    void lineTo(SkScalar x, SkScalar y);
+
+    /** Add a line from the last point to the specified point. If no moveTo()
+        call has been made for this contour, the first point is automatically
+        set to (0,0).
+
+        @param p    The end of a line
+    */
+    void lineTo(const SkPoint& p) {
+        this->lineTo(p.fX, p.fY);
+    }
+
+    /** Same as lineTo, but the coordinates are considered relative to the last
+        point on this contour. If there is no previous point, then a moveTo(0,0)
+        is inserted automatically.
+     
+        @param dx   The amount to add to the x-coordinate of the previous point
+                    on this contour, to specify a line
+        @param dy   The amount to add to the y-coordinate of the previous point
+                    on this contour, to specify a line
+    */
+    void rLineTo(SkScalar dx, SkScalar dy);
+
+    /** Add a quadratic bezier from the last point, approaching control point
+        (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
+        this contour, the first point is automatically set to (0,0).
+     
+        @param x1   The x-coordinate of the control point on a quadratic curve
+        @param y1   The y-coordinate of the control point on a quadratic curve
+        @param x2   The x-coordinate of the end point on a quadratic curve
+        @param y2   The y-coordinate of the end point on a quadratic curve
+    */
+    void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2);
+
+    /** Add a quadratic bezier from the last point, approaching control point
+        p1, and ending at p2. If no moveTo() call has been made for this
+        contour, the first point is automatically set to (0,0).
+     
+        @param p1   The control point on a quadratic curve
+        @param p2   The end point on a quadratic curve
+    */
+    void quadTo(const SkPoint& p1, const SkPoint& p2) {
+        this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY);
+    }
+
+    /** Same as quadTo, but the coordinates are considered relative to the last
+        point on this contour. If there is no previous point, then a moveTo(0,0)
+        is inserted automatically.
+
+        @param dx1   The amount to add to the x-coordinate of the last point on
+                this contour, to specify the control point of a quadratic curve
+        @param dy1   The amount to add to the y-coordinate of the last point on
+                this contour, to specify the control point of a quadratic curve
+        @param dx2   The amount to add to the x-coordinate of the last point on
+                     this contour, to specify the end point of a quadratic curve
+        @param dy2   The amount to add to the y-coordinate of the last point on
+                     this contour, to specify the end point of a quadratic curve
+    */
+    void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
+
+    /** Add a cubic bezier from the last point, approaching control points
+        (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
+        made for this contour, the first point is automatically set to (0,0).
+     
+        @param x1   The x-coordinate of the 1st control point on a cubic curve
+        @param y1   The y-coordinate of the 1st control point on a cubic curve
+        @param x2   The x-coordinate of the 2nd control point on a cubic curve
+        @param y2   The y-coordinate of the 2nd control point on a cubic curve
+        @param x3   The x-coordinate of the end point on a cubic curve
+        @param y3   The y-coordinate of the end point on a cubic curve
+    */
+    void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                 SkScalar x3, SkScalar y3);
+
+    /** Add a cubic bezier from the last point, approaching control points p1
+        and p2, and ending at p3. If no moveTo() call has been made for this
+        contour, the first point is automatically set to (0,0).
+     
+        @param p1   The 1st control point on a cubic curve
+        @param p2   The 2nd control point on a cubic curve
+        @param p3   The end point on a cubic curve
+    */
+    void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) {
+        this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY);
+    }
+
+    /** Same as cubicTo, but the coordinates are considered relative to the
+        current point on this contour. If there is no previous point, then a
+        moveTo(0,0) is inserted automatically.
+     
+        @param dx1   The amount to add to the x-coordinate of the last point on
+                this contour, to specify the 1st control point of a cubic curve
+        @param dy1   The amount to add to the y-coordinate of the last point on
+                this contour, to specify the 1st control point of a cubic curve
+        @param dx2   The amount to add to the x-coordinate of the last point on
+                this contour, to specify the 2nd control point of a cubic curve
+        @param dy2   The amount to add to the y-coordinate of the last point on
+                this contour, to specify the 2nd control point of a cubic curve
+        @param dx3   The amount to add to the x-coordinate of the last point on
+                     this contour, to specify the end point of a cubic curve
+        @param dy3   The amount to add to the y-coordinate of the last point on
+                     this contour, to specify the end point of a cubic curve
+    */
+    void    rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                     SkScalar x3, SkScalar y3);
+
+    /** Append the specified arc to the path as a new contour. If the start of
+        the path is different from the path's current last point, then an
+        automatic lineTo() is added to connect the current contour to the start
+        of the arc. However, if the path is empty, then we call moveTo() with
+        the first point of the arc. The sweep angle is treated mod 360.
+     
+        @param oval The bounding oval defining the shape and size of the arc
+        @param startAngle Starting angle (in degrees) where the arc begins
+        @param sweepAngle Sweep angle (in degrees) measured clockwise. This is
+                          treated mod 360.
+        @param forceMoveTo If true, always begin a new contour with the arc
+    */
+    void    arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+                  bool forceMoveTo);
+
+    /** Append a line and arc to the current path. This is the same as the
+        PostScript call "arct".
+    */
+    void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+               SkScalar radius);
+
+    /** Append a line and arc to the current path. This is the same as the
+        PostScript call "arct".
+    */
+    void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) {
+        this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius);
+    }
+
+    /** Close the current contour. If the current point is not equal to the
+        first point of the contour, a line segment is automatically added.
+    */
+    void close();
+
+    enum Direction {
+        /** clockwise direction for adding closed contours */
+        kCW_Direction,
+        /** counter-clockwise direction for adding closed contours */
+        kCCW_Direction
+    };
+
+    /** Add a closed rectangle contour to the path
+        @param rect The rectangle to add as a closed contour to the path
+        @param dir  The direction to wind the rectangle's contour
+    */
+    void    addRect(const SkRect& rect, Direction dir = kCW_Direction);
+
+    /** Add a closed rectangle contour to the path
+
+        @param left     The left side of a rectangle to add as a closed contour
+                        to the path
+        @param top      The top of a rectangle to add as a closed contour to the
+                        path
+        @param right    The right side of a rectangle to add as a closed contour
+                        to the path
+        @param bottom   The bottom of a rectangle to add as a closed contour to
+                        the path
+        @param dir      The direction to wind the rectangle's contour
+    */
+    void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
+                 Direction dir = kCW_Direction);
+
+    /** Add a closed oval contour to the path
+
+        @param oval The bounding oval to add as a closed contour to the path
+        @param dir  The direction to wind the oval's contour
+    */
+    void addOval(const SkRect& oval, Direction dir = kCW_Direction);
+
+    /** Add a closed circle contour to the path
+
+        @param x        The x-coordinate of the center of a circle to add as a
+                        closed contour to the path
+        @param y        The y-coordinate of the center of a circle to add as a
+                        closed contour to the path
+        @param radius   The radius of a circle to add as a closed contour to the
+                        path
+        @param dir      The direction to wind the circle's contour
+    */
+    void addCircle(SkScalar x, SkScalar y, SkScalar radius,
+                   Direction dir = kCW_Direction);
+
+    /** Add the specified arc to the path as a new contour.
+     
+        @param oval The bounds of oval used to define the size of the arc
+        @param startAngle Starting angle (in degrees) where the arc begins
+        @param sweepAngle Sweep angle (in degrees) measured clockwise
+    */
+    void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle);
+
+    /** Add a closed round-rectangle contour to the path
+        @param rect The bounds of a round-rectangle to add as a closed contour
+        @param rx   The x-radius of the rounded corners on the round-rectangle
+        @param ry   The y-radius of the rounded corners on the round-rectangle
+        @param dir  The direction to wind the round-rectangle's contour
+    */
+    void    addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
+                         Direction dir = kCW_Direction);
+
+    /** Add a closed round-rectangle contour to the path. Each corner receives
+        two radius values [X, Y]. The corners are ordered top-left, top-right,
+        bottom-right, bottom-left.
+        @param rect The bounds of a round-rectangle to add as a closed contour
+        @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner
+        @param dir  The direction to wind the round-rectangle's contour
+        */
+    void addRoundRect(const SkRect& rect, const SkScalar radii[],
+                      Direction dir = kCW_Direction);
+
+    /** Add a copy of src to the path, offset by (dx,dy)
+        @param src  The path to add as a new contour
+        @param dx   The amount to translate the path in X as it is added
+        @param dx   The amount to translate the path in Y as it is added
+    */
+    void    addPath(const SkPath& src, SkScalar dx, SkScalar dy);
+
+    /** Add a copy of src to the path
+    */
+    void addPath(const SkPath& src) {
+        SkMatrix m;
+        m.reset();
+        this->addPath(src, m);
+    }
+
+    /** Add a copy of src to the path, transformed by matrix
+        @param src  The path to add as a new contour
+    */
+    void addPath(const SkPath& src, const SkMatrix& matrix);
+
+    /** Offset the path by (dx,dy), returning true on success
+     
+        @param dx   The amount in the X direction to offset the entire path 
+        @param dy   The amount in the Y direction to offset the entire path 
+        @param dst  The translated path is written here
+    */
+    void offset(SkScalar dx, SkScalar dy, SkPath* dst) const;
+
+    /** Offset the path by (dx,dy), returning true on success
+     
+        @param dx   The amount in the X direction to offset the entire path 
+        @param dy   The amount in the Y direction to offset the entire path 
+    */
+    void offset(SkScalar dx, SkScalar dy) {
+        this->offset(dx, dy, this);
+    }
+
+    /** Transform the points in this path by matrix, and write the answer into
+        dst.
+     
+        @param matrix   The matrix to apply to the path
+        @param dst      The transformed path is written here
+    */
+    void transform(const SkMatrix& matrix, SkPath* dst) const;
+
+    /** Transform the points in this path by matrix
+
+        @param matrix The matrix to apply to the path
+    */
+    void transform(const SkMatrix& matrix) {
+        this->transform(matrix, this);
+    }
+
+    /** Return the last point on the path. If no points have been added, (0,0)
+        is returned.
+     
+        @param lastPt   The last point on the path is returned here
+    */
+    void getLastPt(SkPoint* lastPt) const;
+
+    /** Set the last point on the path. If no points have been added,
+        moveTo(x,y) is automatically called.
+     
+        @param x    The new x-coordinate for the last point
+        @param y    The new y-coordinate for the last point
+    */
+    void setLastPt(SkScalar x, SkScalar y);
+
+    /** Set the last point on the path. If no points have been added, moveTo(p)
+        is automatically called.
+
+        @param p    The new location for the last point
+    */
+    void setLastPt(const SkPoint& p) {
+        this->setLastPt(p.fX, p.fY);
+    }
+
+    enum Verb {
+        kMove_Verb,     //!< iter.next returns 1 point
+        kLine_Verb,     //!< iter.next returns 2 points
+        kQuad_Verb,     //!< iter.next returns 3 points
+        kCubic_Verb,    //!< iter.next returns 4 points
+        kClose_Verb,    //!< iter.next returns 1 point (the last point)
+        kDone_Verb      //!< iter.next returns 0 points
+    };
+
+    /** Iterate through all of the segments (lines, quadratics, cubics) of
+        each contours in a path.
+    */
+    class Iter {
+    public:
+                Iter();
+                Iter(const SkPath&, bool forceClose);
+
+        void setPath(const SkPath&, bool forceClose);
+
+        /** Return the next verb in this iteration of the path. When all
+            segments have been visited, return kDone_Verb.
+         
+            @param  pts The points representing the current verb and/or segment
+            @return The verb for the current segment
+        */
+        Verb next(SkPoint pts[4]);
+
+        /** If next() returns kLine_Verb, then this query returns true if the
+            line was the result of a close() command (i.e. the end point is the
+            initial moveto for this contour). If next() returned a different
+            verb, this returns an undefined value.
+         
+            @return If the last call to next() returned kLine_Verb, return true
+                    if it was the result of an explicit close command.
+        */
+        bool isCloseLine() const { return SkToBool(fCloseLine); }
+        
+        /** Returns true if the current contour is closed (has a kClose_Verb)
+            @return true if the current contour is closed (has a kClose_Verb)
+        */
+        bool isClosedContour() const;
+
+    private:
+        const SkPoint*  fPts;
+        const uint8_t*  fVerbs;
+        const uint8_t*  fVerbStop;
+        SkPoint         fMoveTo;
+        SkPoint         fLastPt;
+        SkBool8         fForceClose;
+        SkBool8         fNeedClose;
+        SkBool8         fNeedMoveTo;
+        SkBool8         fCloseLine;
+
+        bool cons_moveTo(SkPoint pts[1]);
+        Verb autoClose(SkPoint pts[2]);
+    };
+
+#ifdef SK_DEBUG
+  /** @cond UNIT_TEST */
+    void dump(bool forceClose, const char title[] = NULL) const;
+    static void UnitTest();
+  /** @endcond */
+#endif
+
+    void flatten(SkFlattenableWriteBuffer&) const;
+    void unflatten(SkFlattenableReadBuffer&);
+
+    /** Subdivide the path so that no segment is longer that dist.
+        If bendLines is true, then turn all line segments into curves.
+        If dst == null, then the original path itself is modified (not const!)
+    */
+    void subdivide(SkScalar dist, bool bendLines, SkPath* dst = NULL) const;
+
+    /** Return an SVG-compatible string of the path.
+    */
+    void toString(SkString*) const;
+
+    SkDEBUGCODE(void validate() const;)
+
+private:
+    SkTDArray<SkPoint>  fPts;
+    SkTDArray<uint8_t>  fVerbs;
+    mutable SkRect      fFastBounds;
+    mutable uint8_t     fFastBoundsIsDirty;
+    uint8_t             fFillType;
+
+    friend class Iter;
+    void cons_moveto();
+
+    friend class SkPathStroker;
+    /*  Append the first contour of path, ignoring path's initial point. If no
+        moveTo() call has been made for this contour, the first point is
+        automatically set to (0,0).
+    */
+    void pathTo(const SkPath& path);
+
+    /*  Append, in reverse order, the first contour of path, ignoring path's
+        last point. If no moveTo() call has been made for this contour, the
+        first point is automatically set to (0,0).
+    */
+    void reversePathTo(const SkPath&);
+
+    friend const SkPoint* sk_get_path_points(const SkPath&, int index);
+    friend class SkAutoPathBoundsUpdate;
+};
+
+#endif
+
diff --git a/include/core/SkPathEffect.h b/include/core/SkPathEffect.h
new file mode 100644
index 0000000..f97adb7
--- /dev/null
+++ b/include/core/SkPathEffect.h
@@ -0,0 +1,144 @@
+/*
+ * 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 SkPathEffect_DEFINED
+#define SkPathEffect_DEFINED
+
+#include "SkFlattenable.h"
+
+class SkPath;
+
+/** \class SkPathEffect
+
+    SkPathEffect is the base class for objects in the SkPaint that affect
+    the geometry of a drawing primitive before it is transformed by the
+    canvas' matrix and drawn.
+
+    Dashing is implemented as a subclass of SkPathEffect.
+*/
+class SkPathEffect : public SkFlattenable {
+public:
+    //  This method is not exported to java.
+    SkPathEffect() {}
+
+    /** Given a src path and a width value, return true if the patheffect
+        has produced a new path (dst) and a new width value. If false is returned,
+        ignore dst and width.
+        On input, width >= 0 means the src should be stroked
+        On output, width >= 0 means the dst should be stroked
+    */
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) = 0;
+
+private:
+    // illegal
+    SkPathEffect(const SkPathEffect&);
+    SkPathEffect& operator=(const SkPathEffect&);
+};
+
+/** \class SkPairPathEffect
+
+    Common baseclass for Compose and Sum. This subclass manages two pathEffects,
+    including flattening them. It does nothing in filterPath, and is only useful
+    for managing the lifetimes of its two arguments.
+*/
+class SkPairPathEffect : public SkPathEffect {
+public:
+    SkPairPathEffect(SkPathEffect* pe0, SkPathEffect* pe1);
+    virtual ~SkPairPathEffect();
+
+protected:
+    SkPairPathEffect(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&);
+    // these are visible to our subclasses
+    SkPathEffect* fPE0, *fPE1;
+    
+private:
+    typedef SkPathEffect INHERITED;
+};
+
+/** \class SkComposePathEffect
+
+    This subclass of SkPathEffect composes its two arguments, to create
+    a compound pathEffect.
+*/
+class SkComposePathEffect : public SkPairPathEffect {
+public:
+    /** Construct a pathEffect whose effect is to apply first the inner pathEffect
+        and the the outer pathEffect (e.g. outer(inner(path)))
+        The reference counts for outer and inner are both incremented in the constructor,
+        and decremented in the destructor.
+    */
+    SkComposePathEffect(SkPathEffect* outer, SkPathEffect* inner)
+        : INHERITED(outer, inner) {}
+
+    // overrides
+    
+    //  This method is not exported to java.
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkComposePathEffect, (buffer));
+    }
+    SkComposePathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+    // illegal
+    SkComposePathEffect(const SkComposePathEffect&);
+    SkComposePathEffect& operator=(const SkComposePathEffect&);
+    
+    typedef SkPairPathEffect INHERITED;
+};
+
+/** \class SkSumPathEffect
+
+    This subclass of SkPathEffect applies two pathEffects, one after the other.
+    Its filterPath() returns true if either of the effects succeeded.
+*/
+class SkSumPathEffect : public SkPairPathEffect {
+public:
+    /** Construct a pathEffect whose effect is to apply two effects, in sequence.
+        (e.g. first(path) + second(path))
+        The reference counts for first and second are both incremented in the constructor,
+        and decremented in the destructor.
+    */
+    SkSumPathEffect(SkPathEffect* first, SkPathEffect* second)
+        : INHERITED(first, second) {}
+
+    // overrides
+    //  This method is not exported to java.
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)  {
+        return SkNEW_ARGS(SkSumPathEffect, (buffer));
+    }
+    SkSumPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+    // illegal
+    SkSumPathEffect(const SkSumPathEffect&);
+    SkSumPathEffect& operator=(const SkSumPathEffect&);
+
+    typedef SkPairPathEffect INHERITED;
+};
+
+#endif
+
diff --git a/include/core/SkPathMeasure.h b/include/core/SkPathMeasure.h
new file mode 100644
index 0000000..5ab97ca
--- /dev/null
+++ b/include/core/SkPathMeasure.h
@@ -0,0 +1,115 @@
+/*
+ * 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 SkPathMeasure_DEFINED
+#define SkPathMeasure_DEFINED
+
+#include "SkPath.h"
+#include "SkTDArray.h"
+
+class SkPathMeasure : SkNoncopyable {
+public:
+    SkPathMeasure();
+    /** Initialize the pathmeasure with the specified path. The path must remain valid
+        for the lifetime of the measure object, or until setPath() is called with
+        a different path (or null), since the measure object keeps a pointer to the
+        path object (does not copy its data).
+    */
+    SkPathMeasure(const SkPath& path, bool forceClosed);
+    ~SkPathMeasure();
+
+    /** Reset the pathmeasure with the specified path. The path must remain valid
+        for the lifetime of the measure object, or until setPath() is called with
+        a different path (or null), since the measure object keeps a pointer to the
+        path object (does not copy its data).
+    */
+    void    setPath(const SkPath*, bool forceClosed);
+
+    /** Return the total length of the current contour, or 0 if no path
+        is associated (e.g. resetPath(null))
+    */
+    SkScalar getLength();
+
+    /** Pins distance to 0 <= distance <= getLength(), and then computes
+        the corresponding position and tangent.
+        Returns false if there is no path, or a zero-length path was specified, in which case
+        position and tangent are unchanged.
+    */
+    bool getPosTan(SkScalar distance, SkPoint* position, SkVector* tangent);
+
+    enum MatrixFlags {
+        kGetPosition_MatrixFlag     = 0x01,
+        kGetTangent_MatrixFlag      = 0x02,
+        kGetPosAndTan_MatrixFlag    = kGetPosition_MatrixFlag | kGetTangent_MatrixFlag
+    };
+    /** Pins distance to 0 <= distance <= getLength(), and then computes
+        the corresponding matrix (by calling getPosTan).
+        Returns false if there is no path, or a zero-length path was specified, in which case
+        matrix is unchanged.
+    */
+    bool getMatrix(SkScalar distance, SkMatrix* matrix, MatrixFlags flags = kGetPosAndTan_MatrixFlag);
+    /** Given a start and stop distance, return in dst the intervening segment(s).
+        If the segment is zero-length, return false, else return true.
+        startD and stopD are pinned to legal values (0..getLength()). If startD <= stopD
+        then return false (and leave dst untouched).
+        Begin the segment with a moveTo if startWithMoveTo is true
+    */
+    bool getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, bool startWithMoveTo);
+
+    /** Return true if the current contour is closed()
+    */
+    bool isClosed();
+
+    /** Move to the next contour in the path. Return true if one exists, or false if
+        we're done with the path.
+    */
+    bool nextContour();
+
+#ifdef SK_DEBUG
+    void    dump();
+    static void UnitTest();
+#endif
+
+private:
+    SkPath::Iter    fIter;
+    const SkPath*   fPath;
+    SkScalar        fLength;            // relative to the current contour
+    int             fFirstPtIndex;      // relative to the current contour
+    bool            fIsClosed;          // relative to the current contour
+    bool            fForceClosed;
+
+    struct Segment {
+        SkScalar    fDistance;  // total distance up to this point
+        unsigned    fPtIndex : 15;
+        unsigned    fTValue : 15;
+        unsigned    fType : 2;
+
+        SkScalar getScalarT() const;
+    };
+    SkTDArray<Segment>  fSegments;
+
+    static const Segment* NextSegment(const Segment*);
+
+    void     buildSegments();
+    SkScalar compute_quad_segs(const SkPoint pts[3], SkScalar distance,
+                                int mint, int maxt, int ptIndex);
+    SkScalar compute_cubic_segs(const SkPoint pts[3], SkScalar distance,
+                                int mint, int maxt, int ptIndex);
+    const Segment* distanceToSegment(SkScalar distance, SkScalar* t);
+};
+
+#endif
+
diff --git a/include/core/SkPerspIter.h b/include/core/SkPerspIter.h
new file mode 100644
index 0000000..81ce7c8
--- /dev/null
+++ b/include/core/SkPerspIter.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 SkPerspIter_DEFINED
+#define SkPerspIter_DEFINED
+
+#include "SkMatrix.h"
+
+class SkPerspIter {
+public:
+    /** Iterate a line through the matrix [x,y] ... [x+count-1, y].
+        @param m    The matrix we will be iterating a line through
+        @param x    The initial X coordinate to be mapped through the matrix
+        @param y    The initial Y coordinate to be mapped through the matrix
+        @param count The number of points (x,y) (x+1,y) (x+2,y) ... we will eventually map
+    */
+    SkPerspIter(const SkMatrix& m, SkScalar x, SkScalar y, int count);
+    
+    /** Return the buffer of [x,y] fixed point values we will be filling.
+        This always returns the same value, so it can be saved across calls to
+        next().
+    */
+    const SkFixed* getXY() const { return fStorage; }
+
+    /** Return the number of [x,y] pairs that have been filled in the getXY() buffer.
+        When this returns 0, the iterator is finished.
+    */
+    int next();
+    
+private:
+    enum {
+        kShift  = 4,
+        kCount  = (1 << kShift)
+    };
+    const SkMatrix& fMatrix;
+    SkFixed         fStorage[kCount * 2];
+    SkFixed         fX, fY;
+    SkScalar        fSX, fSY;
+    int             fCount;
+};
+
+#endif
diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h
new file mode 100644
index 0000000..fa614c9
--- /dev/null
+++ b/include/core/SkPicture.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2007 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 SkPicture_DEFINED
+#define SkPicture_DEFINED
+
+#include "SkRefCnt.h"
+
+class SkCanvas;
+class SkPicturePlayback;
+class SkPictureRecord;
+class SkStream;
+class SkWStream;
+
+/** \class SkPicture
+
+    The SkPicture class records the drawing commands made to a canvas, to
+    be played back at a later time.
+*/
+class SkPicture : public SkRefCnt {
+public:
+    /** The constructor prepares the picture to record.
+        @param width the width of the virtual device the picture records.
+        @param height the height of the virtual device the picture records.
+    */
+    SkPicture();
+    /** Make a copy of the contents of src. If src records more drawing after
+        this call, those elements will not appear in this picture.
+    */
+    SkPicture(const SkPicture& src);
+    explicit SkPicture(SkStream*);
+    virtual ~SkPicture();
+    
+    /**
+     *  Swap the contents of the two pictures. Guaranteed to succeed.
+     */
+    void swap(SkPicture& other);
+    
+    /** Returns the canvas that records the drawing commands.
+        @return the picture canvas.
+    */
+    SkCanvas* beginRecording(int width, int height);
+    /** Returns the recording canvas if one is active, or NULL if recording is
+        not active. This does not alter the refcnt on the canvas (if present).
+    */
+    SkCanvas* getRecordingCanvas() const;
+    /** Signal that the caller is done recording. This invalidates the canvas
+        returned by beginRecording/getRecordingCanvas, and prepares the picture
+        for drawing. Note: this happens implicitly the first time the picture
+        is drawn.
+    */
+    void endRecording();
+    
+    /** Replays the drawing commands on the specified canvas. This internally
+        calls endRecording() if that has not already been called.
+        @param surface the canvas receiving the drawing commands.
+    */
+    void draw(SkCanvas* surface);
+    
+    /** Return the width of the picture's recording canvas. This
+        value reflects what was passed to setSize(), and does not necessarily
+        reflect the bounds of what has been recorded into the picture.
+        @return the width of the picture's recording canvas
+    */
+    int width() const { return fWidth; }
+
+    /** Return the height of the picture's recording canvas. This
+        value reflects what was passed to setSize(), and does not necessarily
+        reflect the bounds of what has been recorded into the picture.
+        @return the height of the picture's recording canvas
+    */
+    int height() const { return fHeight; }
+
+    void serialize(SkWStream*) const;
+
+    /** Signals that the caller is prematurely done replaying the drawing
+        commands. This can be called from a canvas virtual while the picture
+        is drawing. Has no effect if the picture is not drawing. 
+    */
+    void abortPlayback();
+    
+private:
+    int fWidth, fHeight;
+    SkPictureRecord* fRecord;
+    SkPicturePlayback* fPlayback;
+
+    friend class SkFlatPicture;
+    friend class SkPicturePlayback;
+};
+
+class SkAutoPictureRecord : SkNoncopyable {
+public:
+    SkAutoPictureRecord(SkPicture* pict, int width, int height) {
+        fPicture = pict;
+        fCanvas = pict->beginRecording(width, height);
+    }
+    ~SkAutoPictureRecord() {
+        fPicture->endRecording();
+    }
+    
+    /** Return the canvas to draw into for recording into the picture.
+    */
+    SkCanvas* getRecordingCanvas() const { return fCanvas; }
+    
+private:
+    SkPicture*  fPicture;
+    SkCanvas*   fCanvas;
+};
+
+
+#endif
diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h
new file mode 100644
index 0000000..82e5ca7
--- /dev/null
+++ b/include/core/SkPixelRef.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPixelRef_DEFINED
+#define SkPixelRef_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkString.h"
+
+class SkColorTable;
+class SkMutex;
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+
+/** \class SkPixelRef
+
+    This class is the smart container for pixel memory, and is used with
+    SkBitmap. A pixelref is installed into a bitmap, and then the bitmap can
+    access the actual pixel memory by calling lockPixels/unlockPixels.
+
+    This class can be shared/accessed between multiple threads.
+*/
+class SkPixelRef : public SkRefCnt {
+public:
+    explicit SkPixelRef(SkMutex* mutex = NULL);
+    
+    /** Return the pixel memory returned from lockPixels, or null if the
+        lockCount is 0.
+    */
+    void* pixels() const { return fPixels; }
+
+    /** Return the current colorTable (if any) if pixels are locked, or null.
+    */
+    SkColorTable* colorTable() const { return fColorTable; }
+
+    /** Return the current lockcount (defaults to 0)
+    */
+    int getLockCount() const { return fLockCount; }
+
+    /** Call to access the pixel memory, which is returned. Balance with a call
+        to unlockPixels().
+    */
+    void lockPixels();
+    /** Call to balanace a previous call to lockPixels(). Returns the pixels
+        (or null) after the unlock. NOTE: lock calls can be nested, but the
+        matching number of unlock calls must be made in order to free the
+        memory (if the subclass implements caching/deferred-decoding.)
+    */
+    void unlockPixels();
+    
+    /** Returns a non-zero, unique value corresponding to the pixels in this
+        pixelref. Each time the pixels are changed (and notifyPixelsChanged is
+        called), a different generation ID will be returned.
+    */
+    uint32_t getGenerationID() const;
+    
+    /** Call this if you have changed the contents of the pixels. This will in-
+        turn cause a different generation ID value to be returned from
+        getGenerationID().
+    */
+    void notifyPixelsChanged();
+
+    /** Returns true if this pixelref is marked as immutable, meaning that the
+        contents of its pixels will not change for the lifetime of the pixelref.
+    */
+    bool isImmutable() const { return fIsImmutable; }
+    
+    /** Marks this pixelref is immutable, meaning that the contents of its
+        pixels will not change for the lifetime of the pixelref. This state can
+        be set on a pixelref, but it cannot be cleared once it is set.
+    */
+    void setImmutable();
+
+    /** Return the optional URI string associated with this pixelref. May be
+        null.
+    */
+    const char* getURI() const { return fURI.size() ? fURI.c_str() : NULL; }
+
+    /** Copy a URI string to this pixelref, or clear the URI if the uri is null
+     */
+    void setURI(const char uri[]) {
+        fURI.set(uri);
+    }
+    
+    /** Copy a URI string to this pixelref
+     */
+    void setURI(const char uri[], size_t len) {
+        fURI.set(uri, len);
+    }
+    
+    /** Assign a URI string to this pixelref.
+    */
+    void setURI(const SkString& uri) { fURI = uri; }
+
+    // serialization
+
+    typedef SkPixelRef* (*Factory)(SkFlattenableReadBuffer&);
+
+    virtual Factory getFactory() const { return NULL; }
+    virtual void flatten(SkFlattenableWriteBuffer&) const;
+
+    static Factory NameToFactory(const char name[]);
+    static const char* FactoryToName(Factory);
+    static void Register(const char name[], Factory);
+    
+    class Registrar {
+    public:
+        Registrar(const char name[], Factory factory) {
+            SkPixelRef::Register(name, factory);
+        }
+    };
+
+protected:
+    /** Called when the lockCount goes from 0 to 1. The caller will have already
+        acquire a mutex for thread safety, so this method need not do that.
+    */
+    virtual void* onLockPixels(SkColorTable**) = 0;
+    /** Called when the lock count goes from 1 to 0. The caller will have
+        already acquire a mutex for thread safety, so this method need not do
+        that.
+    */
+    virtual void onUnlockPixels() = 0;
+
+    /** Return the mutex associated with this pixelref. This value is assigned
+        in the constructor, and cannot change during the lifetime of the object.
+    */
+    SkMutex* mutex() const { return fMutex; }
+
+    SkPixelRef(SkFlattenableReadBuffer&, SkMutex*);
+
+private:
+    SkMutex*        fMutex; // must remain in scope for the life of this object
+    void*           fPixels;
+    SkColorTable*   fColorTable;    // we do not track ownership, subclass does
+    int             fLockCount;
+    
+    mutable uint32_t fGenerationID;
+    
+    SkString    fURI;
+
+    // can go from false to true, but never from true to false
+    bool    fIsImmutable;
+};
+
+#endif
diff --git a/include/core/SkPoint.h b/include/core/SkPoint.h
new file mode 100644
index 0000000..f038afb
--- /dev/null
+++ b/include/core/SkPoint.h
@@ -0,0 +1,288 @@
+/*
+ * 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 SkPoint_DEFINED
+#define SkPoint_DEFINED
+
+#include "SkMath.h"
+#include "SkScalar.h"
+
+/** \struct SkIPoint
+
+    SkIPoint holds two 32 bit integer coordinates
+*/
+struct SkIPoint {
+    int32_t fX, fY;
+
+    /** Set the x and y values of the point. */
+    void set(int32_t x, int32_t y) { fX = x; fY = y; }
+
+    /** Rotate the point clockwise, writing the new point into dst
+        It is legal for dst == this
+    */
+    void rotateCW(SkIPoint* dst) const;
+
+    /** Rotate the point clockwise, writing the new point back into the point
+    */
+
+    void rotateCW() { this->rotateCW(this); }
+
+    /** Rotate the point counter-clockwise, writing the new point into dst.
+        It is legal for dst == this
+    */
+    void rotateCCW(SkIPoint* dst) const;
+
+    /** Rotate the point counter-clockwise, writing the new point back into
+        the point
+    */
+    void rotateCCW() { this->rotateCCW(this); }
+    
+    /** Negate the X and Y coordinates of the point.
+    */
+    void negate() { fX = -fX; fY = -fY; }
+    
+    /** Return a new point whose X and Y coordinates are the negative of the
+        original point's
+    */
+    SkIPoint operator-() const {
+        SkIPoint neg;
+        neg.fX = -fX;
+        neg.fY = -fY;
+        return neg;
+    }
+
+    /** Add v's coordinates to this point's */
+    void operator+=(const SkIPoint& v) {
+        fX += v.fX;
+        fY += v.fY;
+    }
+    
+    /** Subtract v's coordinates from this point's */
+    void operator-=(const SkIPoint& v) {
+        fX -= v.fX;
+        fY -= v.fY;
+    }
+
+    /** Returns true if the point's coordinates equal (x,y) */
+    bool equals(int32_t x, int32_t y) const {
+        return fX == x && fY == y;
+    }
+
+    friend bool operator==(const SkIPoint& a, const SkIPoint& b) {
+        return a.fX == b.fX && a.fY == b.fY;
+    }
+    
+    friend bool operator!=(const SkIPoint& a, const SkIPoint& b) {
+        return a.fX != b.fX || a.fY != b.fY;
+    }
+
+    /** Returns a new point whose coordinates are the difference between
+        a and b (i.e. a - b)
+    */
+    friend SkIPoint operator-(const SkIPoint& a, const SkIPoint& b) {
+        SkIPoint v;
+        v.set(a.fX - b.fX, a.fY - b.fY);
+        return v;
+    }
+
+    /** Returns a new point whose coordinates are the sum of a and b (a + b)
+    */
+    friend SkIPoint operator+(const SkIPoint& a, const SkIPoint& b) {
+        SkIPoint v;
+        v.set(a.fX + b.fX, a.fY + b.fY);
+        return v;
+    }
+    
+    /** Returns the dot product of a and b, treating them as 2D vectors
+    */
+    static int32_t DotProduct(const SkIPoint& a, const SkIPoint& b) {
+        return a.fX * b.fX + a.fY * b.fY;
+    }
+
+    /** Returns the cross product of a and b, treating them as 2D vectors
+    */
+    static int32_t CrossProduct(const SkIPoint& a, const SkIPoint& b) {
+        return a.fX * b.fY - a.fY * b.fX;
+    }
+};
+
+struct SkPoint {
+    SkScalar    fX, fY;
+
+    /** Set the point's X and Y coordinates */
+    void set(SkScalar x, SkScalar y) { fX = x; fY = y; }
+    
+    /** Set the point's X and Y coordinates by automatically promoting (x,y) to
+        SkScalar values.
+    */
+    void iset(int32_t x, int32_t y) {
+        fX = SkIntToScalar(x);
+        fY = SkIntToScalar(y);
+    }
+    
+    /** Set the point's X and Y coordinates by automatically promoting p's
+        coordinates to SkScalar values.
+    */
+    void iset(const SkIPoint& p) {
+        fX = SkIntToScalar(p.fX);
+        fY = SkIntToScalar(p.fY);
+    }
+
+    /** Return the euclidian distance from (0,0) to the point
+    */
+    SkScalar length() const { return SkPoint::Length(fX, fY); }
+
+    /** Set the point (vector) to be unit-length in the same direction as it
+        currently is, and return its old length. If the old length is
+        degenerately small (nearly zero), do nothing and return false, otherwise
+        return true.
+    */
+    bool normalize();
+    
+    /** Set the point (vector) to be unit-length in the same direction as the
+        x,y params. If the vector (x,y) has a degenerate length (i.e. nearly 0)
+        then return false and do nothing, otherwise return true.
+    */
+    bool setNormalize(SkScalar x, SkScalar y);
+    
+    /** Scale the point (vector) to have the specified length, and return that
+        length. If the original length is degenerately small (nearly zero),
+        do nothing and return false, otherwise return true.
+    */
+    bool setLength(SkScalar length);
+    
+    /** Set the point (vector) to have the specified length in the same
+     direction as (x,y). If the vector (x,y) has a degenerate length
+     (i.e. nearly 0) then return false and do nothing, otherwise return true.
+    */
+    bool setLength(SkScalar x, SkScalar y, SkScalar length);
+
+    /** Scale the point's coordinates by scale, writing the answer into dst.
+        It is legal for dst == this.
+    */
+    void scale(SkScalar scale, SkPoint* dst) const;
+    
+    /** Scale the point's coordinates by scale, writing the answer back into
+        the point.
+    */
+    void scale(SkScalar scale) { this->scale(scale, this); }
+
+    /** Rotate the point clockwise by 90 degrees, writing the answer into dst.
+        It is legal for dst == this.
+    */
+    void rotateCW(SkPoint* dst) const;
+    
+    /** Rotate the point clockwise by 90 degrees, writing the answer back into
+        the point.
+    */
+    void rotateCW() { this->rotateCW(this); }
+    
+    /** Rotate the point counter-clockwise by 90 degrees, writing the answer
+        into dst. It is legal for dst == this.
+    */
+    void rotateCCW(SkPoint* dst) const;
+    
+    /** Rotate the point counter-clockwise by 90 degrees, writing the answer
+        back into the point.
+    */
+    void rotateCCW() { this->rotateCCW(this); }
+    
+    /** Negate the point's coordinates
+    */
+    void negate() {
+        fX = -fX;
+        fY = -fY;
+    }
+    
+    /** Returns a new point whose coordinates are the negative of the point's
+    */
+    SkPoint operator-() const {
+        SkPoint neg;
+        neg.fX = -fX;
+        neg.fY = -fY;
+        return neg;
+    }
+
+    /** Add v's coordinates to the point's
+    */
+    void operator+=(const SkPoint& v) {
+        fX += v.fX;
+        fY += v.fY;
+    }
+    
+    /** Subtract v's coordinates from the point's
+    */
+    void operator-=(const SkPoint& v) {
+        fX -= v.fX;
+        fY -= v.fY;
+    }
+
+    /** Returns true if the point's coordinates equal (x,y)
+    */
+    bool equals(SkScalar x, SkScalar y) const { return fX == x && fY == y; }
+
+    friend bool operator==(const SkPoint& a, const SkPoint& b) {
+        return a.fX == b.fX && a.fY == b.fY;
+    }
+    
+    friend bool operator!=(const SkPoint& a, const SkPoint& b) {
+        return a.fX != b.fX || a.fY != b.fY;
+    }
+
+    /** Returns a new point whose coordinates are the difference between
+        a's and b's (a - b)
+    */
+    friend SkPoint operator-(const SkPoint& a, const SkPoint& b) {
+        SkPoint v;
+        v.set(a.fX - b.fX, a.fY - b.fY);
+        return v;
+    }
+
+    /** Returns a new point whose coordinates are the sum of a's and b's (a + b)
+    */
+    friend SkPoint operator+(const SkPoint& a, const SkPoint& b) {
+        SkPoint v;
+        v.set(a.fX + b.fX, a.fY + b.fY);
+        return v;
+    }
+
+    /** Returns the euclidian distance from (0,0) to (x,y)
+    */
+    static SkScalar Length(SkScalar x, SkScalar y);
+    
+    /** Returns the euclidian distance between a and b
+    */
+    static SkScalar Distance(const SkPoint& a, const SkPoint& b) {
+        return Length(a.fX - b.fX, a.fY - b.fY);
+    }
+
+    /** Returns the dot product of a and b, treating them as 2D vectors
+    */
+    static SkScalar DotProduct(const SkPoint& a, const SkPoint& b) {
+        return SkScalarMul(a.fX, b.fX) + SkScalarMul(a.fY, b.fY);
+    }
+
+    /** Returns the cross product of a and b, treating them as 2D vectors
+    */
+    static SkScalar CrossProduct(const SkPoint& a, const SkPoint& b) {
+        return SkScalarMul(a.fX, b.fY) - SkScalarMul(a.fY, b.fX);
+    }
+};
+
+typedef SkPoint SkVector;
+
+#endif
+
diff --git a/include/core/SkPorterDuff.h b/include/core/SkPorterDuff.h
new file mode 100644
index 0000000..f03e9cb
--- /dev/null
+++ b/include/core/SkPorterDuff.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 SkPorterDuff_DEFINED
+#define SkPorterDuff_DEFINED
+
+#include "SkColor.h"
+
+class SkXfermode;
+
+class SkPorterDuff {
+public:
+    /** List of predefined xfermodes. In general, the algebra for the modes
+        uses the following symbols:
+        Sa, Sc  - source alpha and color
+        Da, Dc - destination alpha and color (before compositing)
+        [a, c] - Resulting (alpha, color) values
+        For these equations, the colors are in premultiplied state.
+        If no xfermode is specified, kSrcOver is assumed.
+    */
+    enum Mode {
+        kClear_Mode,    //!< [0, 0]
+        kSrc_Mode,      //!< [Sa, Sc]
+        kDst_Mode,      //!< [Da, Dc]
+        kSrcOver_Mode,  //!< [Sa + Da - Sa*Da, Rc = Sc + (1 - Sa)*Dc]
+        kDstOver_Mode,  //!< [Sa + Da - Sa*Da, Rc = Dc + (1 - Da)*Sc]
+        kSrcIn_Mode,    //!< [Sa * Da, Sc * Da]
+        kDstIn_Mode,    //!< [Sa * Da, Sa * Dc]
+        kSrcOut_Mode,   //!< [Sa * (1 - Da), Sc * (1 - Da)]
+        kDstOut_Mode,   //!< [Da * (1 - Sa), Dc * (1 - Sa)]
+        kSrcATop_Mode,  //!< [Da, Sc * Da + (1 - Sa) * Dc]
+        kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
+        kXor_Mode,      //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
+        kDarken_Mode,   //!< [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)]
+        kLighten_Mode,  //!< [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)]
+        kMultiply_Mode, //!< [Sa * Da, Sc * Dc]
+        kScreen_Mode,   //!< [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc]
+
+        kModeCount
+    };
+    /** Return an SkXfermode object for the specified mode.
+    */
+    static SkXfermode* CreateXfermode(Mode mode);
+    
+    /** Return a function pointer to a routine that applies the specified
+        porter-duff transfer mode.
+    */
+    static SkXfermodeProc GetXfermodeProc(Mode mode);
+    
+    /** Return a function pointer to a routine that applies the specified
+        porter-duff transfer mode and srcColor to a 16bit device color. Note,
+        if the mode+srcColor might return a non-opaque color, then there is not
+        16bit proc, and this will return NULL.
+    */
+    static SkXfermodeProc16 GetXfermodeProc16(Mode mode, SkColor srcColor);
+    
+    /** If the specified xfermode advertises itself as one of the porterduff
+        modes (via SkXfermode::Coeff), return true and if not null, set mode
+        to the corresponding porterduff mode. If it is not recognized as a one,
+        return false and ignore the mode parameter.
+    */
+    static bool IsMode(SkXfermode*, Mode* mode);
+};
+
+#endif
+
diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h
new file mode 100644
index 0000000..e32d6a1
--- /dev/null
+++ b/include/core/SkPostConfig.h
@@ -0,0 +1,216 @@
+/*
+ * 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 SkPostConfig_DEFINED
+#define SkPostConfig_DEFINED
+
+#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_WINCE)
+    #define SK_BUILD_FOR_WIN
+#endif
+
+#if defined(SK_DEBUG) && defined(SK_RELEASE)
+    #error "cannot define both SK_DEBUG and SK_RELEASE"
+#elif !defined(SK_DEBUG) && !defined(SK_RELEASE)
+    #error "must define either SK_DEBUG or SK_RELEASE"
+#endif
+
+#if defined SK_SUPPORT_UNITTEST && !defined(SK_DEBUG)
+    #error "can't have unittests without debug"
+#endif
+
+#if defined(SK_SCALAR_IS_FIXED) && defined(SK_SCALAR_IS_FLOAT)
+    #error "cannot define both SK_SCALAR_IS_FIXED and SK_SCALAR_IS_FLOAT"
+#elif !defined(SK_SCALAR_IS_FIXED) && !defined(SK_SCALAR_IS_FLOAT)
+    #ifdef SK_CAN_USE_FLOAT
+        #define SK_SCALAR_IS_FLOAT
+    #else
+        #define SK_SCALAR_IS_FIXED
+    #endif
+#endif
+
+#if defined(SK_SCALAR_IS_FLOAT) && !defined(SK_CAN_USE_FLOAT)
+    #define SK_CAN_USE_FLOAT
+    // we do nothing in the else case: fixed-scalars can have floats or not
+#endif
+
+#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN)
+    #error "cannot define both SK_CPU_LENDIAN and SK_CPU_BENDIAN"
+#elif !defined(SK_CPU_LENDIAN) && !defined(SK_CPU_BENDIAN)
+    #error "must define either SK_CPU_LENDIAN or SK_CPU_BENDIAN"
+#endif
+
+// ensure the port has defined all of these, or none of them
+#ifdef SK_A32_SHIFT
+    #if !defined(SK_R32_SHIFT) || !defined(SK_G32_SHIFT) || !defined(SK_B32_SHIFT)
+        #error "all or none of the 32bit SHIFT amounts must be defined"
+    #endif
+#else
+    #if defined(SK_R32_SHIFT) || defined(SK_G32_SHIFT) || defined(SK_B32_SHIFT)
+        #error "all or none of the 32bit SHIFT amounts must be defined"
+    #endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef SkNEW
+    #define SkNEW(type_name)                new type_name
+    #define SkNEW_ARGS(type_name, args)     new type_name args
+    #define SkNEW_ARRAY(type_name, count)   new type_name[count]
+    #define SkDELETE(obj)                   delete obj
+    #define SkDELETE_ARRAY(array)           delete[] array
+#endif
+
+#ifndef SK_CRASH
+#if 1   // set to 0 for infinite loop, which can help connecting gdb
+    #define SK_CRASH() *(int *)(uintptr_t)0xbbadbeef = 0
+#else
+    #define SK_CRASH()  do {} while (true)
+#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined(SK_SOFTWARE_FLOAT) && defined(SK_SCALAR_IS_FLOAT)
+    // if this is defined, we convert floats to 2scompliment ints for compares
+    #ifndef SK_SCALAR_SLOW_COMPARES
+        #define SK_SCALAR_SLOW_COMPARES
+    #endif
+#endif
+
+#ifdef SK_BUILD_FOR_WIN
+    #define WIN32_LEAN_AND_MEAN
+    #include <windows.h>
+    #undef WIN32_LEAN_AND_MEAN
+
+    #ifndef SK_DEBUGBREAK
+        #define SK_DEBUGBREAK(cond)     do { if (!(cond)) DebugBreak(); } while (false)
+    #endif
+
+    #ifdef SK_BUILD_FOR_WIN32
+        #define strcasecmp(a, b)        stricmp(a, b)
+        #define strncasecmp(a, b, c)    strnicmp(a, b, c)
+    #elif defined(SK_BUILD_FOR_WINCE)
+        #define strcasecmp(a, b)        _stricmp(a, b)
+        #define strncasecmp(a, b, c)    _strnicmp(a, b, c)
+    #endif
+#elif defined(SK_BUILD_FOR_MAC)
+    #ifndef SK_DEBUGBREAK
+        #define SK_DEBUGBREAK(cond)     do { if (!(cond)) SK_CRASH(); } while (false)
+    #endif
+#else
+    #ifdef SK_DEBUG
+        #include <stdio.h>
+        #ifndef SK_DEBUGBREAK
+            #define SK_DEBUGBREAK(cond) do { if (cond) break; \
+                SkDebugf("%s:%d: failed assertion \"%s\"\n", \
+                __FILE__, __LINE__, #cond); SK_CRASH(); } while (false)
+        #endif
+    #endif
+#endif
+
+//  stdlib macros
+
+#if 0
+#if !defined(strlen) && defined(SK_DEBUG)
+    extern size_t sk_strlen(const char*);
+    #define strlen(s)   sk_strlen(s)
+#endif
+#ifndef sk_strcpy
+    #define sk_strcpy(dst, src)     strcpy(dst, src)
+#endif
+#ifndef sk_strchr
+    #define sk_strchr(s, c)         strchr(s, c)
+#endif
+#ifndef sk_strrchr
+    #define sk_strrchr(s, c)        strrchr(s, c)
+#endif
+#ifndef sk_strcmp
+    #define sk_strcmp(s, t)         strcmp(s, t)
+#endif
+#ifndef sk_strncmp
+    #define sk_strncmp(s, t, n)     strncmp(s, t, n)
+#endif
+#ifndef sk_memcpy
+    #define sk_memcpy(dst, src, n)  memcpy(dst, src, n)
+#endif
+#ifndef memmove
+    #define memmove(dst, src, n)    memmove(dst, src, n)
+#endif
+#ifndef sk_memset
+    #define sk_memset(dst, val, n)  memset(dst, val, n)
+#endif
+#ifndef sk_memcmp
+    #define sk_memcmp(s, t, n)      memcmp(s, t, n)
+#endif
+
+#define sk_strequal(s, t)           (!sk_strcmp(s, t))
+#define sk_strnequal(s, t, n)       (!sk_strncmp(s, t, n))
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+#ifndef SK_BUILD_FOR_WINCE
+#include <string.h>
+#include <stdlib.h>
+#else
+#define _CMNINTRIN_DECLARE_ONLY
+#include "cmnintrin.h"
+#endif
+
+#if defined SK_DEBUG && defined SK_BUILD_FOR_WIN32
+//#define _CRTDBG_MAP_ALLOC
+#ifdef free
+#undef free
+#endif
+#include <crtdbg.h>
+#undef free
+
+#ifdef SK_DEBUGx
+#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(__cplusplus)
+    void * operator new(
+        size_t cb,
+        int nBlockUse,
+        const char * szFileName,
+        int nLine,
+        int foo
+        );
+    void * operator new[](
+        size_t cb,
+        int nBlockUse,
+        const char * szFileName,
+        int nLine,
+        int foo
+        );
+    void operator delete(
+        void *pUserData,
+        int, const char*, int, int
+        );
+    void operator delete(
+        void *pUserData
+        );
+    void operator delete[]( void * p );
+    #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__, 0)
+#else
+    #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
+#endif
+    #define new DEBUG_CLIENTBLOCK
+#else
+#define DEBUG_CLIENTBLOCK
+#endif // _DEBUG
+
+#endif
+
+#endif
+
diff --git a/include/core/SkPreConfig.h b/include/core/SkPreConfig.h
new file mode 100644
index 0000000..04f1987
--- /dev/null
+++ b/include/core/SkPreConfig.h
@@ -0,0 +1,110 @@
+/*
+ * 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 SkPreConfig_DEFINED
+#define SkPreConfig_DEFINED
+
+#ifdef ANDROID
+    #define SK_BUILD_FOR_UNIX
+    #define SkLONGLONG  int64_t
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(SK_BUILD_FOR_PALM) && !defined(SK_BUILD_FOR_WINCE) && !defined(SK_BUILD_FOR_WIN32) && !defined(SK_BUILD_FOR_SYMBIAN) && !defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC)
+
+    #if defined(PALMOS_SDK_VERSION)
+        #define SK_BUILD_FOR_PALM
+    #elif defined(UNDER_CE)
+        #define SK_BUILD_FOR_WINCE
+    #elif defined(WIN32)
+        #define SK_BUILD_FOR_WIN32
+    #elif defined(__SYMBIAN32__)
+        #define SK_BUILD_FOR_WIN32
+    #elif defined(linux)
+        #define SK_BUILD_FOR_UNIX
+    #else
+        #define SK_BUILD_FOR_MAC
+    #endif
+
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(SK_DEBUG) && !defined(SK_RELEASE)
+    #ifdef NDEBUG
+        #define SK_RELEASE
+    #else
+        #define SK_DEBUG
+    #endif
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+// define to blank or change this in SkUserConfig.h as needed
+#define SK_RESTRICT __restrict__
+
+//////////////////////////////////////////////////////////////////////
+
+#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_MAC)
+    #ifndef SK_CAN_USE_FLOAT
+        #define SK_CAN_USE_FLOAT
+    #endif
+    #if !defined(SK_SCALAR_IS_FIXED) && !defined(SK_SCALAR_IS_FLOAT)
+        #define SK_SCALAR_IS_FIXED
+    #endif
+
+    #ifndef SkLONGLONG
+        #ifdef SK_BUILD_FOR_WIN32
+            #define SkLONGLONG  __int64
+        #else
+            #define SkLONGLONG  long long
+        #endif
+    #endif
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(SK_CPU_BENDIAN) && !defined(SK_CPU_LENDIAN)
+    #if defined (__ppc__) || defined(__ppc64__)
+        #define SK_CPU_BENDIAN
+    #else
+        #define SK_CPU_LENDIAN
+    #endif
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#if (defined(__arm__) && !defined(__thumb__)) || defined(SK_BUILD_FOR_BREW) || defined(SK_BUILD_FOR_WINCE) || (defined(SK_BUILD_FOR_SYMBIAN) && !defined(__MARM_THUMB__))
+    /* e.g. the ARM instructions have conditional execution, making tiny branches cheap */
+    #define SK_CPU_HAS_CONDITIONAL_INSTR
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Conditional features based on build target
+
+#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)
+    #ifndef SK_BUILD_NO_IMAGE_ENCODE
+        #define SK_SUPPORT_IMAGE_ENCODE
+    #endif
+#endif
+
+#ifdef SK_BUILD_FOR_SYMBIAN
+    #define SK_USE_RUNTIME_GLOBALS
+#endif
+
+#endif
+
diff --git a/include/core/SkPtrRecorder.h b/include/core/SkPtrRecorder.h
new file mode 100644
index 0000000..ff1e14d
--- /dev/null
+++ b/include/core/SkPtrRecorder.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPtrRecorder_DEFINED
+#define SkPtrRecorder_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkTDArray.h"
+
+class SkPtrRecorder : public SkRefCnt {
+public:
+    uint32_t recordPtr(void*);
+    
+    int count() const { return fList.count(); }
+    void getPtrs(void* array[]) const;
+
+    void reset();
+
+protected:
+    virtual void incPtr(void* ptr) {}
+    virtual void decPtr(void* ptr) {}
+
+private:
+    struct Pair {
+        void*       fPtr;
+        uint32_t    fIndex;
+    };
+    SkTDArray<Pair>  fList;
+    
+    static int Cmp(const Pair& a, const Pair& b);
+    
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/core/SkRandom.h b/include/core/SkRandom.h
new file mode 100644
index 0000000..33b563a
--- /dev/null
+++ b/include/core/SkRandom.h
@@ -0,0 +1,110 @@
+/*
+ * 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 SkRandom_DEFINED
+#define SkRandom_DEFINED
+
+#include "Sk64.h"
+#include "SkScalar.h"
+
+/** \class SkRandom
+
+    Utility class that implements pseudo random 32bit numbers using a fast
+    linear equation. Unlike rand(), this class holds its own seed (initially
+    set to 0), so that multiple instances can be used with no side-effects.
+*/
+class SkRandom {
+public:
+    SkRandom() : fSeed(0) {}
+    SkRandom(uint32_t seed) : fSeed(seed) {}
+
+    /** Return the next pseudo random number as an unsigned 32bit value.
+    */
+    uint32_t nextU() { uint32_t r = fSeed * kMul + kAdd; fSeed = r; return r; }
+
+    /** Return the next pseudo random number as a signed 32bit value.
+    */
+    int32_t nextS() { return (int32_t)this->nextU(); }
+
+    /** Return the next pseudo random number as an unsigned 16bit value.
+    */
+    U16CPU nextU16() { return this->nextU() >> 16; }
+
+    /** Return the next pseudo random number as a signed 16bit value.
+    */
+    S16CPU nextS16() { return this->nextS() >> 16; }
+
+    /** Return the next pseudo random number, as an unsigned value of
+        at most bitCount bits.
+        @param bitCount The maximum number of bits to be returned
+    */
+    uint32_t nextBits(unsigned bitCount) {
+        SkASSERT(bitCount > 0 && bitCount <= 32);
+        return this->nextU() >> (32 - bitCount);
+    }
+
+    /** Return the next pseudo random unsigned number, mapped to lie within
+        [min, max] inclusive.
+    */
+    uint32_t nextRangeU(uint32_t min, uint32_t max) {
+        SkASSERT(min <= max);
+        return min + this->nextU() % (max - min + 1);
+    }
+
+    /** Return the next pseudo random number expressed as an unsigned SkFixed
+        in the range [0..SK_Fixed1).
+    */
+    SkFixed nextUFixed1() { return this->nextU() >> 16; }
+
+    /** Return the next pseudo random number expressed as a signed SkFixed
+        in the range (-SK_Fixed1..SK_Fixed1).
+    */
+    SkFixed nextSFixed1() { return this->nextS() >> 15; }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+        in the range [0..SK_Scalar1).
+    */
+    SkScalar nextUScalar1() { return SkFixedToScalar(this->nextUFixed1()); }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+        in the range (-SK_Scalar1..SK_Scalar1).
+    */
+    SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); }
+
+    /** Return the next pseudo random number as a signed 64bit value.
+    */
+    void next64(Sk64* a) {
+        SkASSERT(a);
+        a->set(this->nextS(), this->nextU());
+    }
+
+    /** Set the seed of the random object. The seed is initialized to 0 when the
+        object is first created, and is updated each time the next pseudo random
+        number is requested.
+    */
+    void setSeed(int32_t seed) { fSeed = (uint32_t)seed; }
+
+private:
+    //  See "Numerical Recipes in C", 1992 page 284 for these constants
+    enum {
+        kMul = 1664525,
+        kAdd = 1013904223
+    };
+    uint32_t fSeed;
+};
+
+#endif
+
diff --git a/include/core/SkRasterizer.h b/include/core/SkRasterizer.h
new file mode 100644
index 0000000..d81e613
--- /dev/null
+++ b/include/core/SkRasterizer.h
@@ -0,0 +1,50 @@
+/*
+ * 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 SkRasterizer_DEFINED
+#define SkRasterizer_DEFINED
+
+#include "SkFlattenable.h"
+#include "SkMask.h"
+
+class SkMaskFilter;
+class SkMatrix;
+class SkPath;
+struct SkIRect;
+
+class SkRasterizer : public SkFlattenable {
+public:
+    SkRasterizer() {}
+
+    /** Turn the path into a mask, respecting the specified local->device matrix.
+    */
+    bool rasterize(const SkPath& path, const SkMatrix& matrix,
+                   const SkIRect* clipBounds, SkMaskFilter* filter,
+                   SkMask* mask, SkMask::CreateMode mode);
+
+    virtual void flatten(SkFlattenableWriteBuffer& ) {}
+protected:
+    SkRasterizer(SkFlattenableReadBuffer&);
+
+    virtual bool onRasterize(const SkPath& path, const SkMatrix& matrix,
+                             const SkIRect* clipBounds,
+                             SkMask* mask, SkMask::CreateMode mode);
+
+private:
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
diff --git a/include/core/SkReader32.h b/include/core/SkReader32.h
new file mode 100644
index 0000000..1c72a87
--- /dev/null
+++ b/include/core/SkReader32.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkReader32_DEFINED
+#define SkReader32_DEFINED
+
+#include "SkTypes.h"
+
+#include "SkScalar.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+
+class SkReader32 : SkNoncopyable {
+public:
+    SkReader32() : fCurr(NULL), fStop(NULL), fBase(NULL) {}
+    SkReader32(const void* data, size_t size)  {
+        this->setMemory(data, size);
+    }
+
+    void setMemory(const void* data, size_t size) {
+        SkASSERT(ptr_align_4(data));
+        SkASSERT(SkAlign4(size) == size);
+        
+        fBase = fCurr = (const char*)data;
+        fStop = (const char*)data + size;
+    }
+    
+    uint32_t size() const { return fStop - fBase; }
+    uint32_t offset() const { return fCurr - fBase; }
+    bool eof() const { return fCurr >= fStop; }
+    const void* base() const { return fBase; }
+    const void* peek() const { return fCurr; }
+    void rewind() { fCurr = fBase; }
+
+    void setOffset(size_t offset) {
+        SkASSERT(SkAlign4(offset) == offset);
+        SkASSERT(offset <= this->size());
+        fCurr = fBase + offset;
+    }
+    
+    bool readBool() { return this->readInt() != 0; }
+    
+    int32_t readInt() {
+        SkASSERT(ptr_align_4(fCurr));
+        int32_t value = *(const int32_t*)fCurr;
+        fCurr += sizeof(value);
+        SkASSERT(fCurr <= fStop);
+        return value;
+    }
+    
+    SkScalar readScalar() {
+        SkASSERT(ptr_align_4(fCurr));
+        SkScalar value = *(const SkScalar*)fCurr;
+        fCurr += sizeof(value);
+        SkASSERT(fCurr <= fStop);
+        return value;
+    }
+    
+    const SkPoint* skipPoint() {
+        return (const SkPoint*)this->skip(sizeof(SkPoint));
+    }
+    
+    const SkRect* skipRect() {
+        return (const SkRect*)this->skip(sizeof(SkRect));
+    }
+
+    const void* skip(size_t size) {
+        SkASSERT(ptr_align_4(fCurr));
+        const void* addr = fCurr;
+        fCurr += SkAlign4(size);
+        SkASSERT(fCurr <= fStop);
+        return addr;
+    }
+    
+    void read(void* dst, size_t size) {
+        SkASSERT(dst != NULL);
+        SkASSERT(ptr_align_4(fCurr));
+        memcpy(dst, fCurr, size);
+        fCurr += SkAlign4(size);
+        SkASSERT(fCurr <= fStop);
+    }
+    
+    uint8_t readU8() { return (uint8_t)this->readInt(); }
+    uint16_t readU16() { return (uint16_t)this->readInt(); }
+    int32_t readS32() { return this->readInt(); }
+    uint32_t readU32() { return this->readInt(); }
+    
+private:
+    // these are always 4-byte aligned
+    const char* fCurr;  // current position within buffer
+    const char* fStop;  // end of buffer
+    const char* fBase;  // beginning of buffer
+    
+#ifdef SK_DEBUG
+    static bool ptr_align_4(const void* ptr)
+    {
+        return (((const char*)ptr - (const char*)NULL) & 3) == 0;
+    }
+#endif
+};
+
+#endif
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
new file mode 100644
index 0000000..a9f25aa
--- /dev/null
+++ b/include/core/SkRect.h
@@ -0,0 +1,435 @@
+/*
+ * 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 SkRect_DEFINED
+#define SkRect_DEFINED
+
+#include "SkPoint.h"
+
+/** \struct SkIRect
+
+    SkIRect holds four 32 bit integer coordinates for a rectangle
+*/
+struct SkIRect {
+    int32_t fLeft, fTop, fRight, fBottom;
+
+    /** Return true if the rectangle's width or height are <= 0
+    */
+    bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
+
+    /** Returns the rectangle's width. This does not check for a valid rectangle (i.e. left <= right)
+        so the result may be negative.
+    */
+    int width() const { return fRight - fLeft; }
+
+    /** Returns the rectangle's height. This does not check for a valid rectangle (i.e. top <= bottom)
+        so the result may be negative.
+    */
+    int height() const { return fBottom - fTop; }
+
+    friend int operator==(const SkIRect& a, const SkIRect& b)
+    {
+        return !memcmp(&a, &b, sizeof(a));
+    }
+    friend int operator!=(const SkIRect& a, const SkIRect& b)
+    {
+        return memcmp(&a, &b, sizeof(a));
+    }
+
+    /** Set the rectangle to (0,0,0,0)
+    */
+    void setEmpty() { memset(this, 0, sizeof(*this)); }
+
+    void set(int32_t left, int32_t top, int32_t right, int32_t bottom)
+    {
+        fLeft   = left;
+        fTop    = top;
+        fRight  = right;
+        fBottom = bottom;
+    }
+
+    /** Offset set the rectangle by adding dx to its left and right,
+        and adding dy to its top and bottom.
+    */
+    void offset(int32_t dx, int32_t dy)
+    {
+        fLeft   += dx;
+        fTop    += dy;
+        fRight  += dx;
+        fBottom += dy;
+    }
+
+    /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards,
+        making the rectangle narrower. If dx is negative, then the sides are moved outwards,
+        making the rectangle wider. The same hods true for dy and the top and bottom.
+    */
+    void inset(int32_t dx, int32_t dy)
+    {
+        fLeft   += dx;
+        fTop    += dy;
+        fRight  -= dx;
+        fBottom -= dy;
+    }
+    /** Returns true if (x,y) is inside the rectangle and the rectangle is not
+        empty. The left and top are considered to be inside, while the right
+        and bottom are not. Thus for the rectangle (0, 0, 5, 10), the
+        points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not.
+    */
+    bool contains(int32_t x, int32_t y) const
+    {
+        return  (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) &&
+                (unsigned)(y - fTop) < (unsigned)(fBottom - fTop);
+    }
+
+    /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle.
+        If either rectangle is empty, contains() returns false.
+    */
+    bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const
+    {
+        return  left < right && top < bottom && !this->isEmpty() && // check for empties
+                fLeft <= left && fTop <= top &&
+                fRight >= right && fBottom >= bottom;
+    }
+
+    /** Returns true if the specified rectangle r is inside or equal to this rectangle.
+    */
+    bool contains(const SkIRect& r) const
+    {
+        return  !r.isEmpty() && !this->isEmpty() &&     // check for empties
+                fLeft <= r.fLeft && fTop <= r.fTop &&
+                fRight >= r.fRight && fBottom >= r.fBottom;
+    }
+
+    /** Return true if this rectangle contains the specified rectangle.
+		For speed, this method does not check if either this or the specified
+		rectangles are empty, and if either is, its return value is undefined.
+		In the debugging build however, we assert that both this and the
+		specified rectangles are non-empty.
+    */
+    bool containsNoEmptyCheck(int32_t left, int32_t top,
+							  int32_t right, int32_t bottom) const
+    {
+		SkASSERT(fLeft < fRight && fTop < fBottom);
+        SkASSERT(left < right && top < bottom);
+
+        return fLeft <= left && fTop <= top &&
+			   fRight >= right && fBottom >= bottom;
+    }
+    
+    /** If r intersects this rectangle, return true and set this rectangle to that
+        intersection, otherwise return false and do not change this rectangle.
+        If either rectangle is empty, do nothing and return false.
+    */
+    bool intersect(const SkIRect& r)
+    {
+        SkASSERT(&r);
+        return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
+    }
+
+    /** If rectangles a and b intersect, return true and set this rectangle to
+        that intersection, otherwise return false and do not change this
+        rectangle. If either rectangle is empty, do nothing and return false.
+    */
+    bool intersect(const SkIRect& a, const SkIRect& b)
+    {
+        SkASSERT(&a && &b);
+        
+        if (!a.isEmpty() && !b.isEmpty() &&
+            a.fLeft < b.fRight && b.fLeft < a.fRight &&
+            a.fTop < b.fBottom && b.fTop < a.fBottom)
+        {
+            fLeft   = SkMax32(a.fLeft,   b.fLeft);
+            fTop    = SkMax32(a.fTop,    b.fTop);
+            fRight  = SkMin32(a.fRight,  b.fRight);
+            fBottom = SkMin32(a.fBottom, b.fBottom);
+            return true;
+        }
+        return false;
+    }
+    
+    /** If rectangles a and b intersect, return true and set this rectangle to
+        that intersection, otherwise return false and do not change this
+        rectangle. For speed, no check to see if a or b are empty is performed.
+        If either is, then the return result is undefined. In the debug build,
+        we assert that both rectangles are non-empty.
+    */
+    bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b)
+    {
+        SkASSERT(&a && &b);
+        SkASSERT(!a.isEmpty() && !b.isEmpty());
+        
+        if (a.fLeft < b.fRight && b.fLeft < a.fRight &&
+            a.fTop < b.fBottom && b.fTop < a.fBottom)
+        {
+            fLeft   = SkMax32(a.fLeft,   b.fLeft);
+            fTop    = SkMax32(a.fTop,    b.fTop);
+            fRight  = SkMin32(a.fRight,  b.fRight);
+            fBottom = SkMin32(a.fBottom, b.fBottom);
+            return true;
+        }
+        return false;
+    }
+
+    /** If the rectangle specified by left,top,right,bottom intersects this rectangle,
+        return true and set this rectangle to that intersection,
+        otherwise return false and do not change this rectangle.
+        If either rectangle is empty, do nothing and return false.
+    */
+    bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom)
+    {
+        if (left < right && top < bottom && !this->isEmpty() &&
+            fLeft < right && left < fRight && fTop < bottom && top < fBottom)
+        {
+            if (fLeft < left) fLeft = left;
+            if (fTop < top) fTop = top;
+            if (fRight > right) fRight = right;
+            if (fBottom > bottom) fBottom = bottom;
+            return true;
+        }
+        return false;
+    }
+    
+    /** Returns true if a and b are not empty, and they intersect
+    */
+    static bool Intersects(const SkIRect& a, const SkIRect& b)
+    {
+        return  !a.isEmpty() && !b.isEmpty() &&              // check for empties
+                a.fLeft < b.fRight && b.fLeft < a.fRight &&
+                a.fTop < b.fBottom && b.fTop < a.fBottom;
+    }
+    
+    /** Update this rectangle to enclose itself and the specified rectangle.
+        If this rectangle is empty, just set it to the specified rectangle. If the specified
+        rectangle is empty, do nothing.
+    */
+    void join(int32_t left, int32_t top, int32_t right, int32_t bottom);
+
+    /** Update this rectangle to enclose itself and the specified rectangle.
+        If this rectangle is empty, just set it to the specified rectangle. If the specified
+        rectangle is empty, do nothing.
+    */
+    void join(const SkIRect& r)
+    {
+        this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
+    }
+
+    /** Swap top/bottom or left/right if there are flipped.
+        This can be called if the edges are computed separately,
+        and may have crossed over each other.
+        When this returns, left <= right && top <= bottom
+    */
+    void sort();
+};
+
+/** \struct SkRect
+*/
+struct SkRect {
+    SkScalar    fLeft, fTop, fRight, fBottom;
+
+    /** Return true if the rectangle's width or height are <= 0
+    */
+    bool        isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
+    SkScalar    width() const { return fRight - fLeft; }
+    SkScalar    height() const { return fBottom - fTop; }
+    SkScalar    centerX() const { return SkScalarHalf(fLeft + fRight); }
+    SkScalar    centerY() const { return SkScalarHalf(fTop + fBottom); }
+
+    friend int operator==(const SkRect& a, const SkRect& b)
+    {
+        return !memcmp(&a, &b, sizeof(a));
+    }
+    friend int operator!=(const SkRect& a, const SkRect& b)
+    {
+        return memcmp(&a, &b, sizeof(a));
+    }
+
+    /** return the 4 points that enclose the rectangle
+    */
+    void toQuad(SkPoint quad[4]) const;
+
+    /** Set this rectangle to the empty rectangle (0,0,0,0)
+    */
+    void setEmpty() { memset(this, 0, sizeof(*this)); }
+
+    void set(const SkIRect& src)
+    {
+        fLeft   = SkIntToScalar(src.fLeft);
+        fTop    = SkIntToScalar(src.fTop);
+        fRight  = SkIntToScalar(src.fRight);
+        fBottom = SkIntToScalar(src.fBottom);
+    }
+
+    void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
+    {
+        fLeft   = left;
+        fTop    = top;
+        fRight  = right;
+        fBottom = bottom;
+    }
+    
+    /** Initialize the rect with the 4 specified integers. The routine handles
+        converting them to scalars (by calling SkIntToScalar)
+     */
+    void iset(int left, int top, int right, int bottom) {
+        fLeft   = SkIntToScalar(left);
+        fTop    = SkIntToScalar(top);
+        fRight  = SkIntToScalar(right);
+        fBottom = SkIntToScalar(bottom);
+    }
+
+    /** Set this rectangle to be the bounds of the array of points.
+        If the array is empty (count == 0), then set this rectangle
+        to the empty rectangle (0,0,0,0)
+    */
+    void set(const SkPoint pts[], int count);
+
+    /** Offset set the rectangle by adding dx to its left and right,
+        and adding dy to its top and bottom.
+    */
+    void offset(SkScalar dx, SkScalar dy)
+    {
+        fLeft   += dx;
+        fTop    += dy;
+        fRight  += dx;
+        fBottom += dy;
+    }   
+
+    /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards,
+        making the rectangle narrower. If dx is negative, then the sides are moved outwards,
+        making the rectangle wider. The same hods true for dy and the top and bottom.
+    */
+    void inset(SkScalar dx, SkScalar dy)
+    {
+        fLeft   += dx;
+        fTop    += dy;
+        fRight  -= dx;
+        fBottom -= dy;
+    }
+
+    /** If this rectangle intersects r, return true and set this rectangle to that
+        intersection, otherwise return false and do not change this rectangle.
+        If either rectangle is empty, do nothing and return false.
+    */
+    bool intersect(const SkRect& r);
+
+    /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
+        return true and set this rectangle to that intersection, otherwise return false
+        and do not change this rectangle.
+        If either rectangle is empty, do nothing and return false.
+    */
+    bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
+
+    /** Return true if this rectangle is not empty, and the specified sides of
+        a rectangle are not empty, and they intersect.
+    */
+    bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const
+    {
+        return // first check that both are not empty
+               left < right && top < bottom &&
+               fLeft < fRight && fTop < fBottom &&
+               // now check for intersection
+               fLeft < right && left < fRight &&
+               fTop < bottom && top < fBottom;
+    }
+    
+    /** Return true if rectangles a and b are not empty and intersect.
+        */
+    static bool Intersects(const SkRect& a, const SkRect& b)
+    {
+        return  !a.isEmpty() && !b.isEmpty() &&             // check for empties
+        a.fLeft < b.fRight && b.fLeft < a.fRight &&
+        a.fTop < b.fBottom && b.fTop < a.fBottom;
+    }
+    
+    /** Update this rectangle to enclose itself and the specified rectangle.
+        If this rectangle is empty, just set it to the specified rectangle. If the specified
+        rectangle is empty, do nothing.
+    */
+    void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
+
+    /** Update this rectangle to enclose itself and the specified rectangle.
+        If this rectangle is empty, just set it to the specified rectangle. If the specified
+        rectangle is empty, do nothing.
+    */
+    void join(const SkRect& r)
+    {
+        this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
+    }
+    
+    /** Returns true if (p.fX,p.fY) is inside the rectangle. The left and top coordinates of
+        the rectangle are considered to be inside, while the right and bottom coordinates
+        are not. Thus for the rectangle (0, 0, 5, 10), the points (0,0) and (0,9) are inside,
+        while (-1,0) and (5,9) are not.
+        If this rectangle is empty, return false.
+    */
+    bool contains(const SkPoint& p) const
+    {
+        return  !this->isEmpty() &&
+                fLeft <= p.fX && p.fX < fRight &&
+                fTop <= p.fY && p.fY < fBottom;
+    }
+
+    /** Returns true if (x,y) is inside the rectangle. The left and top coordinates of
+        the rectangle are considered to be inside, while the right and bottom coordinates
+        are not. Thus for the rectangle (0, 0, 5, 10), the points (0,0) and (0,9) are inside,
+        while (-1,0) and (5,9) are not.
+        If this rectangle is empty, return false.
+    */
+    bool contains(SkScalar x, SkScalar y) const
+    {
+        return  !this->isEmpty() &&
+                fLeft <= x && x < fRight &&
+                fTop <= y && y < fBottom;
+    }
+
+    /** Return true if this rectangle contains r.
+        If either rectangle is empty, return false.
+    */
+    bool contains(const SkRect& r) const
+    {
+        return  !r.isEmpty() && !this->isEmpty() &&     // check for empties
+                fLeft <= r.fLeft && fTop <= r.fTop &&
+                fRight >= r.fRight && fBottom >= r.fBottom;
+    }
+
+    /** Set the dst integer rectangle by rounding this rectangle's coordinates
+        to their nearest integer values.
+    */
+    void round(SkIRect* dst) const
+    {
+        SkASSERT(dst);
+        dst->set(SkScalarRound(fLeft), SkScalarRound(fTop), SkScalarRound(fRight), SkScalarRound(fBottom));
+    }
+
+    /** Set the dst integer rectangle by rounding "out" this rectangle, choosing the floor of top and left,
+        and the ceiling of right and bototm.
+    */
+    void roundOut(SkIRect* dst) const
+    {
+        SkASSERT(dst);
+        dst->set(SkScalarFloor(fLeft), SkScalarFloor(fTop), SkScalarCeil(fRight), SkScalarCeil(fBottom));
+    }
+
+    /** Swap top/bottom or left/right if there are flipped.
+        This can be called if the edges are computed separately,
+        and may have crossed over each other.
+        When this returns, left <= right && top <= bottom
+    */
+    void sort();
+};
+
+#endif
+
diff --git a/include/core/SkRefCnt.h b/include/core/SkRefCnt.h
new file mode 100644
index 0000000..adb59dd
--- /dev/null
+++ b/include/core/SkRefCnt.h
@@ -0,0 +1,134 @@
+/*
+ * 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 SkRefCnt_DEFINED
+#define SkRefCnt_DEFINED
+
+#include "SkThread.h"
+
+/** \class SkRefCnt
+
+    SkRefCnt is the base class for objects that may be shared by multiple
+    objects. When a new owner wants a reference, it calls ref(). When an owner
+    wants to release its reference, it calls unref(). When the shared object's
+    reference count goes to zero as the result of an unref() call, its (virtual)
+    destructor is called. It is an error for the destructor to be called
+    explicitly (or via the object going out of scope on the stack or calling
+    delete) if getRefCnt() > 1.
+*/
+class SkRefCnt : SkNoncopyable {
+public:
+    /** Default construct, initializing the reference count to 1.
+    */
+    SkRefCnt() : fRefCnt(1) {}
+
+    /**  Destruct, asserting that the reference count is 1.
+    */
+    virtual ~SkRefCnt() { SkASSERT(fRefCnt == 1); }
+
+    /** Return the reference count.
+    */
+    int32_t getRefCnt() const { return fRefCnt; }
+
+    /** Increment the reference count. Must be balanced by a call to unref().
+    */
+    void ref() const {
+        SkASSERT(fRefCnt > 0);
+        sk_atomic_inc(&fRefCnt);
+    }
+
+    /** Decrement the reference count. If the reference count is 1 before the
+        decrement, then call delete on the object. Note that if this is the
+        case, then the object needs to have been allocated via new, and not on
+        the stack.
+    */
+    void unref() const {
+        SkASSERT(fRefCnt > 0);
+        if (sk_atomic_dec(&fRefCnt) == 1) {
+            fRefCnt = 1;    // so our destructor won't complain
+            SkDELETE(this);
+        }
+    }
+    
+    /** Helper version of ref(), that first checks to see if this is not null.
+        If this is null, then do nothing.
+    */
+    void safeRef() const {
+        if (this) {
+            this->ref();
+        }
+    }
+
+    /** Helper version of unref(), that first checks to see if this is not null.
+        If this is null, then do nothing.
+    */
+    void safeUnref() const {
+        if (this) {
+            this->unref();
+        }
+    }
+
+private:
+    mutable int32_t fRefCnt;
+};
+
+/** \class SkAutoUnref
+
+    SkAutoUnref is a stack-helper class that will automatically call unref() on
+    the object it points to when the SkAutoUnref object goes out of scope.
+    If obj is null, do nothing.
+*/
+class SkAutoUnref : SkNoncopyable {
+public:
+    SkAutoUnref(SkRefCnt* obj) : fObj(obj) {}
+    ~SkAutoUnref();
+
+    SkRefCnt*   get() const { return fObj; }
+
+    /** If the hosted object is null, do nothing and return false, else call
+        ref() on it and return true
+    */
+    bool        ref();
+
+    /** If the hosted object is null, do nothing and return false, else call
+        unref() on it, set its reference to null, and return true
+    */
+    bool        unref();
+
+    /** If the hosted object is null, do nothing and return NULL, else call
+        unref() on it, set its reference to null, and return the object
+    */
+    SkRefCnt*   detach();
+
+private:
+    SkRefCnt*   fObj;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Helper macro to safely assign one SkRefCnt[TS]* to another, checking for
+    null in on each side of the assignment, and ensuring that ref() is called
+    before unref(), in case the two pointers point to the same object.
+*/
+#define SkRefCnt_SafeAssign(dst, src)   \
+    do {                                \
+        if (src) src->ref();            \
+        if (dst) dst->unref();          \
+        dst = src;                      \
+    } while (0)
+
+#endif
+
diff --git a/include/core/SkRegion.h b/include/core/SkRegion.h
new file mode 100644
index 0000000..8b15a89
--- /dev/null
+++ b/include/core/SkRegion.h
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2005 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 SkRegion_DEFINED
+#define SkRegion_DEFINED
+
+#include "SkRect.h"
+
+class SkPath;
+class SkRgnBuilder;
+
+namespace android {
+    class Region;
+}
+
+#define SkRegion_gEmptyRunHeadPtr   ((SkRegion::RunHead*)-1)
+#define SkRegion_gRectRunHeadPtr    0
+
+/** \class SkRegion
+
+    The SkRegion class encapsulates the geometric region used to specify
+    clipping areas for drawing.
+*/
+class SkRegion {
+public:
+    typedef int32_t RunType;
+    enum {
+        kRunTypeSentinel = 0x7FFFFFFF
+    };
+    
+    SkRegion();
+    SkRegion(const SkRegion&);
+    explicit SkRegion(const SkIRect&);
+    ~SkRegion();
+
+    SkRegion& operator=(const SkRegion&);
+    
+    friend int operator==(const SkRegion& a, const SkRegion& b);
+    friend int operator!=(const SkRegion& a, const SkRegion& b) {
+        return !(a == b);
+    }
+    
+    /** Replace this region with the specified region, and return true if the
+        resulting region is non-empty.
+    */
+    bool set(const SkRegion& src) {
+        SkASSERT(&src);
+        *this = src;
+        return !this->isEmpty();
+    }
+
+    /** Swap the contents of this and the specified region. This operation
+        is gauarenteed to never fail.
+    */
+    void    swap(SkRegion&);
+
+    /** Return true if this region is empty */
+    bool    isEmpty() const { return fRunHead == SkRegion_gEmptyRunHeadPtr; }
+    /** Return true if this region is a single, non-empty rectangle */
+    bool    isRect() const { return fRunHead == SkRegion_gRectRunHeadPtr; }
+    /** Return true if this region consists of more than 1 rectangular area */
+    bool    isComplex() const { return !this->isEmpty() && !this->isRect(); }
+    /** Return the bounds of this region. If the region is empty, returns an
+        empty rectangle.
+    */
+    const SkIRect& getBounds() const { return fBounds; }
+
+    /** Returns true if the region is non-empty, and if so, sets the specified
+        path to the boundary(s) of the region.
+    */
+    bool getBoundaryPath(SkPath* path) const;
+
+    /** Set the region to be empty, and return false, since the resulting
+        region is empty
+    */
+    bool    setEmpty();
+
+    /** If rect is non-empty, set this region to that rectangle and return true,
+        otherwise set this region to empty and return false.
+    */
+    bool    setRect(const SkIRect&);
+
+    /** If left < right and top < bottom, set this region to that rectangle and
+        return true, otherwise set this region to empty and return false.
+    */
+    bool    setRect(int32_t left, int32_t top, int32_t right, int32_t bottom);
+
+    /** Set this region to the specified region, and return true if it is
+        non-empty. */
+    bool    setRegion(const SkRegion&);
+
+    /** Set this region to the area described by the path, clipped.
+        Return true if the resulting region is non-empty.
+        This produces a region that is identical to the pixels that would be
+        drawn by the path (with no antialiasing) with the specified clip.
+    */
+    bool    setPath(const SkPath&, const SkRegion& clip);
+    
+    /** Returns true if the specified rectangle has a non-empty intersection
+        with this region.
+    */
+    bool    intersects(const SkIRect&) const;
+    
+    /** Returns true if the specified region has a non-empty intersection
+        with this region.
+    */
+    bool    intersects(const SkRegion&) const;
+
+    /** Return true if the specified x,y coordinate is inside the region.
+    */
+    bool    contains(int32_t x, int32_t y) const;
+
+    /** Return true if the specified rectangle is completely inside the region.
+        This works for simple (rectangular) and complex regions, and always
+        returns the correct result. Note: if either this region or the rectangle
+        is empty, contains() returns false.
+    */
+    bool    contains(const SkIRect&) const;
+
+    /** Return true if the specified region is completely inside the region.
+        This works for simple (rectangular) and complex regions, and always
+        returns the correct result. Note: if either region is empty, contains()
+        returns false.
+    */
+    bool    contains(const SkRegion&) const;
+
+    /** Return true if this region is a single rectangle (not complex) and the
+        specified rectangle is contained by this region. Returning false is not
+        a guarantee that the rectangle is not contained by this region, but
+        return true is a guarantee that the rectangle is contained by this region.
+    */
+    bool quickContains(const SkIRect& r) const {
+        return this->quickContains(r.fLeft, r.fTop, r.fRight, r.fBottom);
+    }
+
+    /** Return true if this region is a single rectangle (not complex) and the
+        specified rectangle is contained by this region. Returning false is not
+        a guarantee that the rectangle is not contained by this region, but
+        return true is a guarantee that the rectangle is contained by this
+        region.
+    */
+    bool quickContains(int32_t left, int32_t top, int32_t right,
+                       int32_t bottom) const {
+        SkASSERT(this->isEmpty() == fBounds.isEmpty()); // valid region
+        
+        return left < right && top < bottom &&
+               fRunHead == SkRegion_gRectRunHeadPtr &&  // this->isRect()
+               /* fBounds.contains(left, top, right, bottom); */
+               fBounds.fLeft <= left && fBounds.fTop <= top &&
+               fBounds.fRight >= right && fBounds.fBottom >= bottom;
+    }
+    
+    /** Return true if this region is empty, or if the specified rectangle does
+        not intersect the region. Returning false is not a guarantee that they
+        intersect, but returning true is a guarantee that they do not.
+    */
+    bool quickReject(const SkIRect& rect) const
+    {
+        return this->isEmpty() || rect.isEmpty() ||
+                !SkIRect::Intersects(fBounds, rect);
+    }
+
+    /** Return true if this region, or rgn, is empty, or if their bounds do not
+        intersect. Returning false is not a guarantee that they intersect, but
+        returning true is a guarantee that they do not.
+    */
+    bool quickReject(const SkRegion& rgn) const {
+        return this->isEmpty() || rgn.isEmpty() ||
+               !SkIRect::Intersects(fBounds, rgn.fBounds);
+    }
+
+    /** Translate the region by the specified (dx, dy) amount.
+    */
+    void translate(int dx, int dy) { this->translate(dx, dy, this); }
+
+    /** Translate the region by the specified (dx, dy) amount, writing the
+        resulting region into dst. Note: it is legal to pass this region as the
+        dst parameter, effectively translating the region in place. If dst is
+        null, nothing happens.
+    */
+    void translate(int dx, int dy, SkRegion* dst) const;
+
+    /** The logical operations that can be performed when combining two regions.
+    */
+    enum Op {
+        kDifference_Op, //!< subtract the op region from the first region
+        kIntersect_Op,  //!< intersect the two regions
+        kUnion_Op,      //!< union (inclusive-or) the two regions
+        kXOR_Op,        //!< exclusive-or the two regions
+        /** subtract the first region from the op region */
+        kReverseDifference_Op,
+        kReplace_Op     //!< replace the dst region with the op region
+    };
+    
+    /** Set this region to the result of applying the Op to this region and the
+        specified rectangle: this = (this op rect).
+        Return true if the resulting region is non-empty.
+        */
+    bool op(const SkIRect& rect, Op op) { return this->op(*this, rect, op); }
+    
+    /** Set this region to the result of applying the Op to this region and the
+        specified rectangle: this = (this op rect).
+        Return true if the resulting region is non-empty.
+    */
+    bool op(int left, int top, int right, int bottom, Op op) {
+        SkIRect rect;
+        rect.set(left, top, right, bottom);
+        return this->op(*this, rect, op);
+    }
+    
+    /** Set this region to the result of applying the Op to this region and the
+        specified region: this = (this op rgn).
+        Return true if the resulting region is non-empty.
+    */
+    bool op(const SkRegion& rgn, Op op) { return this->op(*this, rgn, op); }
+    /** Set this region to the result of applying the Op to the specified
+        rectangle and region: this = (rect op rgn).
+        Return true if the resulting region is non-empty.
+    */
+    bool op(const SkIRect& rect, const SkRegion& rgn, Op);
+    /** Set this region to the result of applying the Op to the specified
+        region and rectangle: this = (rgn op rect).
+        Return true if the resulting region is non-empty.
+    */
+    bool op(const SkRegion& rgn, const SkIRect& rect, Op);
+    /** Set this region to the result of applying the Op to the specified
+        regions: this = (rgna op rgnb).
+        Return true if the resulting region is non-empty.
+    */
+    bool op(const SkRegion& rgna, const SkRegion& rgnb, Op op);
+
+    /** Returns the sequence of rectangles, sorted in Y and X, that make up
+        this region.
+    */
+    class Iterator {
+    public:
+        Iterator() : fRgn(NULL), fDone(true) {}
+        Iterator(const SkRegion&);
+        // if we have a region, reset to it and return true, else return false
+        bool rewind();
+        // reset the iterator, using the new region
+        void reset(const SkRegion&);
+        bool done() { return fDone; }
+        void next();
+        const SkIRect& rect() const { return fRect; }
+
+    private:
+        const SkRegion* fRgn;
+        const RunType*  fRuns;
+        SkIRect         fRect;
+        bool            fDone;
+    };
+
+    /** Returns the sequence of rectangles, sorted in Y and X, that make up
+        this region intersected with the specified clip rectangle.
+    */
+    class Cliperator {
+    public:
+        Cliperator(const SkRegion&, const SkIRect& clip);
+        bool            done() { return fDone; }
+        void            next();
+        const SkIRect& rect() const { return fRect; }
+
+    private:
+        Iterator    fIter;
+        SkIRect     fClip;
+        SkIRect     fRect;
+        bool        fDone;
+    };
+
+    /** Returns the sequence of runs that make up this region for the specified
+        Y scanline, clipped to the specified left and right X values.
+    */
+    class Spanerator {
+    public:
+        Spanerator(const SkRegion&, int y, int left, int right);
+        bool    next(int* left, int* right);
+
+    private:
+        const SkRegion::RunType* fRuns;
+        int     fLeft, fRight;
+        bool    fDone;
+    };
+
+    /** Write the region to the buffer, and return the number of bytes written.
+        If buffer is NULL, it still returns the number of bytes.
+    */
+    uint32_t flatten(void* buffer) const;
+    /** Initialized the region from the buffer, returning the number
+        of bytes actually read.
+    */
+    uint32_t unflatten(const void* buffer);
+    
+    SkDEBUGCODE(void dump() const;)
+    SkDEBUGCODE(void validate() const;)
+    SkDEBUGCODE(static void UnitTest();)
+
+    // expose this to allow for regression test on complex regions
+    SkDEBUGCODE(bool debugSetRuns(const RunType runs[], int count);)
+
+private:
+    enum {
+        kOpCount = kReplace_Op + 1
+    };
+
+    enum {
+        kRectRegionRuns = 6 // need to store a region of a rect [T B L R S S]        
+    };
+
+    friend class android::Region;    // needed for marshalling efficiently
+    void allocateRuns(int count); // allocate space for count runs
+
+    struct RunHead;
+
+    SkIRect     fBounds;
+    RunHead*    fRunHead;
+
+    void            freeRuns();
+    const RunType*  getRuns(RunType tmpStorage[], int* count) const;
+    bool            setRuns(RunType runs[], int count);
+
+    int count_runtype_values(int* itop, int* ibot) const;
+    
+    static void BuildRectRuns(const SkIRect& bounds,
+                              RunType runs[kRectRegionRuns]);
+    // returns true if runs are just a rect
+    static bool ComputeRunBounds(const RunType runs[], int count,
+                                 SkIRect* bounds);
+
+    friend struct RunHead;
+    friend class Iterator;
+    friend class Spanerator;
+    friend class SkRgnBuilder;
+    friend class SkFlatRegion;
+};
+
+
+#endif
+
diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h
new file mode 100644
index 0000000..86341eb
--- /dev/null
+++ b/include/core/SkScalar.h
@@ -0,0 +1,254 @@
+/*
+ * 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 SkScalar_DEFINED
+#define SkScalar_DEFINED
+
+#include "SkFixed.h"
+
+/** \file SkScalar.h
+
+    Types and macros for the data type SkScalar. This is the fractional numeric type
+    that, depending on the compile-time flag SK_SCALAR_IS_FLOAT, may be implemented
+    either as an IEEE float, or as a 16.16 SkFixed. The macros in this file are written
+    to allow the calling code to manipulate SkScalar values without knowing which representation
+    is in effect.
+*/
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #include "SkFloatingPoint.h"
+
+    /** SkScalar is our type for fractional values and coordinates. Depending on
+        compile configurations, it is either represented as an IEEE float, or
+        as a 16.16 fixed point integer.
+    */
+    typedef float   SkScalar;
+    extern const uint32_t gIEEENotANumber;
+    extern const uint32_t gIEEEInfinity;
+
+    /** SK_Scalar1 is defined to be 1.0 represented as an SkScalar
+    */
+    #define SK_Scalar1              (1.0f)
+    /** SK_Scalar1 is defined to be 1/2 represented as an SkScalar
+    */
+    #define SK_ScalarHalf           (0.5f)
+    /** SK_ScalarInfinity is defined to be infinity as an SkScalar
+    */
+    #define SK_ScalarInfinity           (*(const float*)&gIEEEInfinity)
+    /** SK_ScalarMax is defined to be the largest value representable as an SkScalar
+    */
+    #define SK_ScalarMax            (3.4028235e+38f)
+    /** SK_ScalarMin is defined to be the smallest value representable as an SkScalar
+    */
+    #define SK_ScalarMin            (1.1754944e-38f)
+    /** SK_ScalarNaN is defined to be 'Not a Number' as an SkScalar
+    */
+    #define SK_ScalarNaN      (*(const float*)(const void*)&gIEEENotANumber)
+    /** SkScalarIsNaN(n) returns true if argument is not a number
+    */
+    static inline bool SkScalarIsNaN(float x) { return x != x; }
+    /** SkIntToScalar(n) returns its integer argument as an SkScalar
+    */
+    #define SkIntToScalar(n)        ((float)(n))
+    /** SkFixedToScalar(n) returns its SkFixed argument as an SkScalar
+    */
+    #define SkFixedToScalar(x)      SkFixedToFloat(x)
+    /** SkScalarToFixed(n) returns its SkScalar argument as an SkFixed
+    */
+    #define SkScalarToFixed(x)      SkFloatToFixed(x)
+
+    #define SkScalarToFloat(n)      (n)
+    #define SkFloatToScalar(n)      (n)
+
+    #define SkScalarToDouble(n)      (double)(n)
+    #define SkDoubleToScalar(n)      (float)(n)
+
+    /** SkScalarFraction(x) returns the signed fractional part of the argument
+    */
+    #define SkScalarFraction(x)     sk_float_mod(x, 1.0f)
+    /** Rounds the SkScalar to the nearest integer value
+    */
+    #define SkScalarRound(x)        sk_float_round2int(x)
+    /** Returns the smallest integer that is >= the specified SkScalar
+    */
+    #define SkScalarCeil(x)         sk_float_ceil2int(x)
+    /** Returns the largest integer that is <= the specified SkScalar
+    */
+    #define SkScalarFloor(x)        sk_float_floor2int(x)
+    /** Returns the absolute value of the specified SkScalar
+    */
+    #define SkScalarAbs(x)          sk_float_abs(x)
+    /** Returns the value pinned between 0 and max inclusive
+    */
+    inline SkScalar SkScalarClampMax(SkScalar x, SkScalar max) {
+        return x < 0 ? 0 : x > max ? max : x;
+    }
+    /** Returns the value pinned between min and max inclusive
+    */
+    inline SkScalar SkScalarPin(SkScalar x, SkScalar min, SkScalar max) {
+        return x < min ? min : x > max ? max : x;
+    }
+    /** Returns the specified SkScalar squared (x*x)
+    */
+    inline SkScalar SkScalarSquare(SkScalar x) { return x * x; }
+    /** Returns the product of two SkScalars
+    */
+    #define SkScalarMul(a, b)       ((float)(a) * (b))
+    /** Returns the product of two SkScalars plus a third SkScalar
+    */
+    #define SkScalarMulAdd(a, b, c) ((float)(a) * (b) + (c))
+    /** Returns the product of a SkScalar and an int rounded to the nearest integer value
+    */
+    #define SkScalarMulRound(a, b) SkScalarRound((float)(a) * (b))
+    /** Returns the product of a SkScalar and an int promoted to the next larger int
+    */
+    #define SkScalarMulCeil(a, b) SkScalarCeil((float)(a) * (b))
+    /** Returns the product of a SkScalar and an int truncated to the next smaller int
+    */
+    #define SkScalarMulFloor(a, b) SkScalarFloor((float)(a) * (b))
+    /** Returns the quotient of two SkScalars (a/b)
+    */
+    #define SkScalarDiv(a, b)       ((float)(a) / (b))
+    /** Returns the mod of two SkScalars (a mod b)
+    */
+    #define SkScalarMod(x,y)        sk_float_mod(x,y)
+    /** Returns the product of the first two arguments, divided by the third argument
+    */
+    #define SkScalarMulDiv(a, b, c) ((float)(a) * (b) / (c))
+    /** Returns the multiplicative inverse of the SkScalar (1/x)
+    */
+    #define SkScalarInvert(x)       (SK_Scalar1 / (x))
+    #define SkScalarFastInvert(x)   (SK_Scalar1 / (x))
+    /** Returns the square root of the SkScalar
+    */
+    #define SkScalarSqrt(x)         sk_float_sqrt(x)
+    /** Returns the average of two SkScalars (a+b)/2
+    */
+    #define SkScalarAve(a, b)       (((a) + (b)) * 0.5f)
+    /** Returns the geometric mean of two SkScalars
+    */
+    #define SkScalarMean(a, b)      sk_float_sqrt((float)(a) * (b))
+    /** Returns one half of the specified SkScalar
+    */
+    #define SkScalarHalf(a)         ((a) * 0.5f)
+
+    #define SK_ScalarSqrt2          1.41421356f
+    #define SK_ScalarPI             3.14159265f
+    #define SK_ScalarTanPIOver8     0.414213562f
+    #define SK_ScalarRoot2Over2     0.707106781f
+
+    #define SkDegreesToRadians(degrees) ((degrees) * (SK_ScalarPI / 180))
+    float SkScalarSinCos(SkScalar radians, SkScalar* cosValue);
+    #define SkScalarSin(radians)    (float)sk_float_sin(radians)
+    #define SkScalarCos(radians)    (float)sk_float_cos(radians)
+    #define SkScalarTan(radians)    (float)sk_float_tan(radians)
+    #define SkScalarASin(val)   (float)sk_float_asin(val)
+    #define SkScalarACos(val)   (float)sk_float_acos(val)
+    #define SkScalarATan2(y, x) (float)sk_float_atan2(y,x)
+    #define SkScalarExp(x)  (float)sk_float_exp(x)
+    #define SkScalarLog(x)  (float)sk_float_log(x)
+
+    inline SkScalar SkMaxScalar(SkScalar a, SkScalar b) { return a > b ? a : b; }
+    inline SkScalar SkMinScalar(SkScalar a, SkScalar b) { return a < b ? a : b; }
+
+#else
+    typedef SkFixed SkScalar;
+
+    #define SK_Scalar1              SK_Fixed1
+    #define SK_ScalarHalf           SK_FixedHalf
+    #define SK_ScalarInfinity   SK_FixedMax
+    #define SK_ScalarMax            SK_FixedMax
+    #define SK_ScalarMin            SK_FixedMin
+    #define SK_ScalarNaN            SK_FixedNaN
+    #define SkScalarIsNaN(x)        ((x) == SK_FixedNaN)
+    #define SkIntToScalar(n)        SkIntToFixed(n)
+    #define SkFixedToScalar(x)      (x)
+    #define SkScalarToFixed(x)      (x)
+    #ifdef SK_CAN_USE_FLOAT
+        #define SkScalarToFloat(n)  SkFixedToFloat(n)
+        #define SkFloatToScalar(n)  SkFloatToFixed(n)
+
+        #define SkScalarToDouble(n) SkFixedToDouble(n)
+        #define SkDoubleToScalar(n) SkDoubleToFixed(n)
+    #endif
+    #define SkScalarFraction(x)     SkFixedFraction(x)
+    #define SkScalarRound(x)        SkFixedRound(x)
+    #define SkScalarCeil(x)         SkFixedCeil(x)
+    #define SkScalarFloor(x)        SkFixedFloor(x)
+    #define SkScalarAbs(x)          SkFixedAbs(x)
+    #define SkScalarClampMax(x, max) SkClampMax(x, max)
+    #define SkScalarPin(x, min, max) SkPin32(x, min, max)
+    #define SkScalarSquare(x)       SkFixedSquare(x)
+    #define SkScalarMul(a, b)       SkFixedMul(a, b)
+    #define SkScalarMulAdd(a, b, c) SkFixedMulAdd(a, b, c)
+    #define SkScalarMulRound(a, b)  SkFixedMulCommon(a, b, SK_FixedHalf)
+    #define SkScalarMulCeil(a, b)   SkFixedMulCommon(a, b, SK_Fixed1 - 1)
+    #define SkScalarMulFloor(a, b)  SkFixedMulCommon(a, b, 0)
+    #define SkScalarDiv(a, b)       SkFixedDiv(a, b)
+    #define SkScalarMod(a, b)       SkFixedMod(a, b)
+    #define SkScalarMulDiv(a, b, c) SkMulDiv(a, b, c)
+    #define SkScalarInvert(x)       SkFixedInvert(x)
+    #define SkScalarFastInvert(x)   SkFixedFastInvert(x)
+    #define SkScalarSqrt(x)         SkFixedSqrt(x)
+    #define SkScalarAve(a, b)       SkFixedAve(a, b)
+    #define SkScalarMean(a, b)      SkFixedMean(a, b)
+    #define SkScalarHalf(a)         ((a) >> 1)
+
+    #define SK_ScalarSqrt2          SK_FixedSqrt2
+    #define SK_ScalarPI             SK_FixedPI
+    #define SK_ScalarTanPIOver8     SK_FixedTanPIOver8
+    #define SK_ScalarRoot2Over2     SK_FixedRoot2Over2
+
+    #define SkDegreesToRadians(degrees)     SkFractMul(degrees, SK_FractPIOver180)
+    #define SkScalarSinCos(radians, cosPtr) SkFixedSinCos(radians, cosPtr)
+    #define SkScalarSin(radians)    SkFixedSin(radians)
+    #define SkScalarCos(radians)    SkFixedCos(radians)
+    #define SkScalarTan(val)        SkFixedTan(val)
+    #define SkScalarASin(val)       SkFixedASin(val)
+    #define SkScalarACos(val)       SkFixedACos(val)
+    #define SkScalarATan2(y, x)     SkFixedATan2(y,x)
+    #define SkScalarExp(x)          SkFixedExp(x)
+    #define SkScalarLog(x)          SkFixedLog(x)
+
+    #define SkMaxScalar(a, b)       SkMax32(a, b)
+    #define SkMinScalar(a, b)       SkMin32(a, b)
+#endif
+
+#define SK_ScalarNearlyZero         (SK_Scalar1 / (1 << 12))
+
+/*  <= is slower than < for floats, so we use < for our tolerance test
+*/
+
+inline bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance = SK_ScalarNearlyZero)
+{
+    SkASSERT(tolerance > 0);
+    return SkScalarAbs(x) < tolerance;
+}
+
+/** Linearly interpolate between A and B, based on t.
+    If t is 0, return A
+    If t is 1, return B
+    else interpolate.
+    t must be [0..SK_Scalar1]
+*/
+inline SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t)
+{
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+    return A + SkScalarMul(B - A, t);
+}
+
+#endif
+
diff --git a/include/core/SkScalarCompare.h b/include/core/SkScalarCompare.h
new file mode 100644
index 0000000..fee554c
--- /dev/null
+++ b/include/core/SkScalarCompare.h
@@ -0,0 +1,34 @@
+/*
+ * 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 SkScalarCompare_DEFINED
+#define SkScalarCompare_DEFINED
+
+#include "SkFloatBits.h"
+#include "SkRect.h"
+
+#ifdef SK_SCALAR_SLOW_COMPARES
+    typedef int32_t SkScalarCompareType;
+    typedef SkIRect SkRectCompareType;
+    #define SkScalarToCompareType(x)    SkScalarAs2sCompliment(x)
+#else
+    typedef SkScalar SkScalarCompareType;
+    typedef SkRect SkRectCompareType;
+    #define SkScalarToCompareType(x)    (x)
+#endif
+
+#endif
+
diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h
new file mode 100644
index 0000000..ec2cd0d
--- /dev/null
+++ b/include/core/SkScalerContext.h
@@ -0,0 +1,219 @@
+/*
+ * 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 SkScalerContext_DEFINED
+#define SkScalerContext_DEFINED
+
+#include "SkMask.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+
+class SkDescriptor;
+class SkMaskFilter;
+class SkPathEffect;
+class SkRasterizer;
+
+// needs to be != to any valid SkMask::Format
+#define MASK_FORMAT_JUST_ADVANCE    (0xFF)
+
+struct SkGlyph {
+    void*       fImage;
+    SkPath*     fPath;
+    SkFixed     fAdvanceX, fAdvanceY;
+
+    uint32_t    fID;
+    uint16_t    fWidth, fHeight;
+    int16_t     fTop, fLeft;
+
+    uint8_t     fMaskFormat;
+    int8_t      fRsbDelta, fLsbDelta;  // used by auto-kerning
+    
+    unsigned rowBytes() const {
+        unsigned rb = fWidth;
+        if (SkMask::kBW_Format == fMaskFormat) {
+            rb = (rb + 7) >> 3;
+        } else {
+            rb = SkAlign4(rb);
+        }
+        return rb;
+    }
+    
+    bool isJustAdvance() const {
+        return MASK_FORMAT_JUST_ADVANCE == fMaskFormat;
+    }
+    
+    bool isFullMetrics() const {
+        return MASK_FORMAT_JUST_ADVANCE != fMaskFormat;
+    }
+    
+    uint16_t getGlyphID() const {
+        return ID2Code(fID);
+    }
+
+    unsigned getGlyphID(unsigned baseGlyphCount) const {
+        unsigned code = ID2Code(fID);
+        SkASSERT(code >= baseGlyphCount);
+        return code - baseGlyphCount;
+    }
+    
+    unsigned getSubX() const {
+        return ID2SubX(fID);
+    }
+    
+    SkFixed getSubXFixed() const {
+        return SubToFixed(ID2SubX(fID));
+    }
+    
+    SkFixed getSubYFixed() const {
+        return SubToFixed(ID2SubY(fID));
+    }
+    
+    size_t computeImageSize() const;
+    
+    enum {
+        kSubBits = 2,
+        kSubMask = ((1 << kSubBits) - 1),
+        kSubShift = 24, // must be large enough for glyphs and unichars
+        kCodeMask = ((1 << kSubShift) - 1),
+        // relative offsets for X and Y subpixel bits
+        kSubShiftX = kSubBits,
+        kSubShiftY = 0
+    };
+
+    static unsigned ID2Code(uint32_t id) {
+        return id & kCodeMask;
+    }
+    
+    static unsigned ID2SubX(uint32_t id) {
+        return id >> (kSubShift + kSubShiftX);
+    }
+    
+    static unsigned ID2SubY(uint32_t id) {
+        return (id >> (kSubShift + kSubShiftY)) & kSubMask;
+    }
+    
+    static unsigned FixedToSub(SkFixed n) {
+        return (n >> (16 - kSubBits)) & kSubMask;
+    }
+    
+    static SkFixed SubToFixed(unsigned sub) {
+        SkASSERT(sub <= kSubMask);
+        return sub << (16 - kSubBits);
+    }
+    
+    static uint32_t MakeID(unsigned code) {
+        return code;
+    }
+    
+    static uint32_t MakeID(unsigned code, SkFixed x, SkFixed y) {
+        SkASSERT(code <= kCodeMask);
+        x = FixedToSub(x);
+        y = FixedToSub(y);
+        return (x << (kSubShift + kSubShiftX)) |
+               (y << (kSubShift + kSubShiftY)) |
+               code;
+    }
+    
+    void toMask(SkMask* mask) const;
+};
+
+class SkScalerContext {
+public:
+    enum Hints {
+        kNo_Hints,
+        kSubpixel_Hints,
+        kNormal_Hints
+    };
+    enum Flags {
+        kFrameAndFill_Flag  = 0x01,
+        kDevKernText_Flag   = 0x02,
+        kGammaForBlack_Flag = 0x04, // illegal to set both Gamma flags
+        kGammaForWhite_Flag = 0x08  // illegal to set both Gamma flags
+    };
+    struct Rec {
+        uint32_t    fFontID;
+        SkScalar    fTextSize, fPreScaleX, fPreSkewX;
+        SkScalar    fPost2x2[2][2];
+        SkScalar    fFrameWidth, fMiterLimit;
+        uint8_t     fHints;
+        uint8_t     fMaskFormat;
+        uint8_t     fStrokeJoin;
+        uint8_t     fFlags;
+        
+        void    getMatrixFrom2x2(SkMatrix*) const;
+        void    getLocalMatrix(SkMatrix*) const;
+        void    getSingleMatrix(SkMatrix*) const;
+    };
+
+    SkScalerContext(const SkDescriptor* desc);
+    virtual ~SkScalerContext();
+
+    void setBaseGlyphCount(unsigned baseGlyphCount) {
+        fBaseGlyphCount = baseGlyphCount;
+    }
+
+    uint16_t    charToGlyphID(SkUnichar uni);
+
+    unsigned    getGlyphCount() const { return this->generateGlyphCount(); }
+    void        getAdvance(SkGlyph*);
+    void        getMetrics(SkGlyph*);
+    void        getImage(const SkGlyph&);
+    void        getPath(const SkGlyph&, SkPath*);
+    void        getFontMetrics(SkPaint::FontMetrics* mX,
+                               SkPaint::FontMetrics* mY);
+
+    static inline void MakeRec(const SkPaint&, const SkMatrix*, Rec* rec);
+    static SkScalerContext* Create(const SkDescriptor*);
+
+protected:
+    Rec         fRec;
+    unsigned    fBaseGlyphCount;
+
+    virtual unsigned generateGlyphCount() const = 0;
+    virtual uint16_t generateCharToGlyph(SkUnichar) = 0;
+    virtual void generateAdvance(SkGlyph*) = 0;
+    virtual void generateMetrics(SkGlyph*) = 0;
+    virtual void generateImage(const SkGlyph&) = 0;
+    virtual void generatePath(const SkGlyph&, SkPath*) = 0;
+    virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
+                                     SkPaint::FontMetrics* mY) = 0;
+
+private:
+    SkPathEffect*   fPathEffect;
+    SkMaskFilter*   fMaskFilter;
+    SkRasterizer*   fRasterizer;
+    SkScalar        fDevFrameWidth;
+
+    void internalGetPath(const SkGlyph& glyph, SkPath* fillPath,
+                         SkPath* devPath, SkMatrix* fillToDevMatrix);
+
+    mutable SkScalerContext* fAuxScalerContext;
+
+    SkScalerContext* getGlyphContext(const SkGlyph& glyph) const;
+    
+    // return loaded fAuxScalerContext or NULL
+    SkScalerContext* loadAuxContext() const;
+};
+
+#define kRec_SkDescriptorTag            SkSetFourByteTag('s', 'r', 'e', 'c')
+#define kPathEffect_SkDescriptorTag     SkSetFourByteTag('p', 't', 'h', 'e')
+#define kMaskFilter_SkDescriptorTag     SkSetFourByteTag('m', 's', 'k', 'f')
+#define kRasterizer_SkDescriptorTag     SkSetFourByteTag('r', 'a', 's', 't')
+
+#endif
+
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
new file mode 100644
index 0000000..7c13e3d
--- /dev/null
+++ b/include/core/SkShader.h
@@ -0,0 +1,184 @@
+/*
+ * 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 SkShader_DEFINED
+#define SkShader_DEFINED
+
+#include "SkBitmap.h"
+#include "SkFlattenable.h"
+#include "SkMask.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+
+class SkPath;
+
+/** \class SkShader
+
+    SkShader is the based class for objects that return horizontal spans of colors during drawing.
+    A subclass of SkShader is installed in a SkPaint calling paint.setShader(shader). After that
+    any object (other than a bitmap) that is drawn with that paint will get its color(s) from the
+    shader.
+*/
+class SkShader : public SkFlattenable {
+public:
+            SkShader();
+    virtual ~SkShader();
+
+    /** Return true if the shader has a non-identity local matrix.
+        @param localM   Optional: If not null, return the shader's local matrix
+        @return true if the shader has a non-identity local matrix.
+    */
+    bool getLocalMatrix(SkMatrix* localM) const;
+    /** Set the shader's local matrix.
+        @param localM   The shader's new local matrix.
+    */
+    void setLocalMatrix(const SkMatrix& localM);
+    /** Reset the shader's local matrix to identity.
+    */
+    void resetLocalMatrix();
+
+    enum TileMode {
+        kClamp_TileMode,    //!< replicate the edge color if the shader draws outside of its original bounds
+        kRepeat_TileMode,   //!< repeat the shader's image horizontally and vertically
+        kMirror_TileMode,   //!< repeat the shader's image horizontally and vertically, alternating mirror images so that adjacent images always seam
+
+        kTileModeCount
+    };
+
+    // override these in your subclass
+
+    enum Flags {
+        //!< set if all of the colors will be opaque
+        kOpaqueAlpha_Flag   = 0x01,
+        //! set if this shader's shadeSpan16() method can be called
+        kHasSpan16_Flag     = 0x02,
+        /** Set this bit if the shader's native data type is instrinsically 16
+            bit, meaning that calling the 32bit shadeSpan() entry point will
+            mean the the impl has to up-sample 16bit data into 32bit. Used as a
+            a means of clearing a dither request if the it will have no effect
+        */
+        kIntrinsicly16_Flag = 0x04
+    };
+
+    /** Called sometimes before drawing with this shader.
+        Return the type of alpha your shader will return.
+        The default implementation returns 0. Your subclass should override if it can
+        (even sometimes) report a non-zero value, since that will enable various blitters
+        to perform faster.
+    */
+    virtual uint32_t getFlags() { return 0; }
+
+    /** Return the alpha associated with the data returned by shadeSpan16(). If
+        kHasSpan16_Flag is not set, this value is meaningless.
+    */
+    virtual uint8_t getSpan16Alpha() const { return fPaintAlpha; }
+    
+    /** Called once before drawing, with the current paint and
+        device matrix. Return true if your shader supports these
+        parameters, or false if not. If false is returned, nothing
+        will be drawn.
+    */
+    virtual bool    setContext( const SkBitmap& device,
+                                const SkPaint& paint,
+                                const SkMatrix& matrix);
+
+    /** Called for each span of the object being drawn. Your subclass
+        should set the appropriate colors (with premultiplied alpha) that
+        correspond to the specified device coordinates.
+    */
+    virtual void    shadeSpan(int x, int y, SkPMColor[], int count) = 0;
+    /** Called only for 16bit devices when getFlags() returns kOpaqueAlphaFlag | kHasSpan16_Flag
+    */
+    virtual void    shadeSpan16(int x, int y, uint16_t[], int count);
+    /** Similar to shadeSpan, but only returns the alpha-channel for a span.
+        The default implementation calls shadeSpan() and then extracts the alpha
+        values from the returned colors.
+    */
+    virtual void    shadeSpanAlpha(int x, int y, uint8_t alpha[], int count);
+
+    /** Helper function that returns true if this shader's shadeSpan16() method can
+        be called.
+    */
+    bool canCallShadeSpan16()
+    {
+        return SkShader::CanCallShadeSpan16(this->getFlags());
+    }
+
+    /** Helper to check the flags to know if it is legal to call shadeSpan16()
+    */
+    static bool CanCallShadeSpan16(uint32_t flags) {
+        return (flags & kHasSpan16_Flag) != 0;
+    }
+
+    /** Called before a session using the shader begins. Some shaders override
+        this to defer some of their work (like calling bitmap.lockPixels()).
+        Must be balanced by a call to endSession.
+    */
+    virtual void beginSession();
+    virtual void endSession();
+    
+    /** Optional methods for shaders that can pretend to be a bitmap/texture
+        to play along with opengl. Default just returns false and ignores
+        the out parameters.
+    */
+    virtual bool asABitmap(SkBitmap* outTexture, SkMatrix* outMatrix,
+                           TileMode xy[2]);
+
+    //////////////////////////////////////////////////////////////////////////
+    //  Factory methods for stock shaders
+
+    /** Call this to create a new shader that will draw with the specified bitmap.
+        @param src  The bitmap to use inside the shader
+        @param tmx  The tiling mode to use when sampling the bitmap in the x-direction.
+        @param tmy  The tiling mode to use when sampling the bitmap in the y-direction.
+        @return     Returns a new shader object. Note: this function never returns null.
+    */
+    static SkShader* CreateBitmapShader(const SkBitmap& src,
+                                        TileMode tmx, TileMode tmy);
+
+    virtual void flatten(SkFlattenableWriteBuffer& );
+protected:
+    enum MatrixClass {
+        kLinear_MatrixClass,            // no perspective
+        kFixedStepInX_MatrixClass,      // fast perspective, need to call fixedStepInX() each scanline
+        kPerspective_MatrixClass        // slow perspective, need to mappoints each pixel
+    };
+    static MatrixClass ComputeMatrixClass(const SkMatrix&);
+
+    // These can be called by your subclass after setContext() has been called
+    uint8_t             getPaintAlpha() const { return fPaintAlpha; }
+    SkBitmap::Config    getDeviceConfig() const { return (SkBitmap::Config)fDeviceConfig; }
+    const SkMatrix&     getTotalInverse() const { return fTotalInverse; }
+    MatrixClass         getInverseClass() const { return (MatrixClass)fTotalInverseClass; }
+
+    SkShader(SkFlattenableReadBuffer& );
+private:
+    SkMatrix*           fLocalMatrix;
+    SkMatrix            fTotalInverse;
+    uint8_t             fPaintAlpha;
+    uint8_t             fDeviceConfig;
+    uint8_t             fTotalInverseClass;
+    SkDEBUGCODE(SkBool8 fInSession;)
+
+    static SkShader* CreateBitmapShader(const SkBitmap& src,
+                                        TileMode, TileMode,
+                                        void* storage, size_t storageSize);
+    friend class SkAutoBitmapShaderInstall;
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
+
diff --git a/include/core/SkStream.h b/include/core/SkStream.h
new file mode 100644
index 0000000..26ef43f
--- /dev/null
+++ b/include/core/SkStream.h
@@ -0,0 +1,315 @@
+/*
+ * 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 SkStream_DEFINED
+#define SkStream_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+
+class SkStream : public SkRefCnt {
+public:
+    virtual ~SkStream();
+    /** Called to rewind to the beginning of the stream. If this cannot be
+        done, return false.
+    */
+    virtual bool rewind() = 0;
+    /** If this stream represents a file, this method returns the file's name.
+        If it does not, it returns NULL (the default behavior).
+    */
+    virtual const char* getFileName();
+    /** Called to read or skip size number of bytes.
+        If buffer is NULL and size > 0, skip that many bytes, returning how many were skipped.
+        If buffer is NULL and size == 0, return the total length of the stream.
+        If buffer != NULL, copy the requested number of bytes into buffer, returning how many were copied.
+        @param buffer   If buffer is NULL, ignore and just skip size bytes, otherwise copy size bytes into buffer
+        @param size The number of bytes to skip or copy
+        @return bytes read on success
+    */
+    virtual size_t read(void* buffer, size_t size) = 0;
+
+    /** Return the total length of the stream.
+    */
+    size_t getLength() { return this->read(NULL, 0); }
+    
+    /** Skip the specified number of bytes, returning the actual number
+        of bytes that could be skipped.
+    */
+    size_t skip(size_t bytes);
+
+    /** If the stream is backed by RAM, this method returns the starting
+        address for the data. If not (i.e. it is backed by a file or other
+        structure), this method returns NULL.
+        The default implementation returns NULL.
+    */
+    virtual const void* getMemoryBase();
+
+    int8_t   readS8();
+    int16_t  readS16();
+    int32_t  readS32();
+
+    uint8_t  readU8() { return (uint8_t)this->readS8(); }
+    uint16_t readU16() { return (uint16_t)this->readS16(); }
+    uint32_t readU32() { return (uint32_t)this->readS32(); }
+
+    bool     readBool() { return this->readU8() != 0; }
+    SkScalar readScalar();
+    size_t   readPackedUInt();
+
+    static void UnitTest();
+};
+
+class SkWStream : SkNoncopyable {
+public:
+    virtual ~SkWStream();
+
+    /** Called to write bytes to a SkWStream. Returns true on success
+        @param buffer the address of at least size bytes to be written to the stream
+        @param size The number of bytes in buffer to write to the stream
+        @return true on success
+    */
+    virtual bool write(const void* buffer, size_t size) = 0;
+    virtual void newline();
+    virtual void flush();
+
+    // helpers
+    
+    bool    write8(U8CPU);
+    bool    write16(U16CPU);
+    bool    write32(uint32_t);
+
+    bool    writeText(const char text[]);
+    bool    writeDecAsText(int32_t);
+    bool    writeHexAsText(uint32_t, int minDigits = 0);
+    bool    writeScalarAsText(SkScalar);
+    
+    bool    writeBool(bool v) { return this->write8(v); }
+    bool    writeScalar(SkScalar);
+    bool    writePackedUInt(size_t);
+    
+    bool writeStream(SkStream* input, size_t length);
+
+    static void UnitTest();
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkString.h"
+
+struct SkFILE;
+
+/** A stream that reads from a FILE*, which is opened in the constructor and
+    closed in the destructor
+ */
+class SkFILEStream : public SkStream {
+public:
+    /** Initialize the stream by calling fopen on the specified path. Will be
+        closed in the destructor.
+     */
+    explicit SkFILEStream(const char path[] = NULL);
+    virtual ~SkFILEStream();
+
+    /** Returns true if the current path could be opened.
+    */
+    bool isValid() const { return fFILE != NULL; }
+    /** Close the current file, and open a new file with the specified
+        path. If path is NULL, just close the current file.
+    */
+    void setPath(const char path[]);
+
+    virtual bool rewind();
+    virtual size_t read(void* buffer, size_t size);
+    virtual const char* getFileName();
+
+private:
+    SkFILE*     fFILE;
+    SkString    fName;
+};
+
+/** A stream that reads from a file descriptor
+ */
+class SkFDStream : public SkStream {
+public:
+    /** Initialize the stream with a dup() of the specified file descriptor.
+        If closeWhenDone is true, then the descriptor will be closed in the
+        destructor.
+     */
+    SkFDStream(int fileDesc, bool closeWhenDone);
+    virtual ~SkFDStream();
+    
+    /** Returns true if the current path could be opened.
+     */
+    bool isValid() const { return fFD >= 0; }
+    
+    virtual bool rewind();
+    virtual size_t read(void* buffer, size_t size);
+    virtual const char* getFileName() { return NULL; }
+    
+private:
+    int     fFD;
+    bool    fCloseWhenDone;
+};
+
+class SkMemoryStream : public SkStream {
+public:
+    SkMemoryStream();
+    /** We allocate (and free) the memory. Write to it via getMemoryBase()
+    */
+    SkMemoryStream(size_t length);
+    /** if copyData is true, the stream makes a private copy of the data
+    */
+    SkMemoryStream(const void* data, size_t length, bool copyData = false);
+    virtual ~SkMemoryStream();
+
+    /** Resets the stream to the specified data and length,
+        just like the constructor.
+        if copyData is true, the stream makes a private copy of the data
+    */
+    virtual void setMemory(const void* data, size_t length,
+                           bool copyData = false);
+    void skipToAlign4();
+    virtual bool rewind();
+    virtual size_t read(void* buffer, size_t size);
+    virtual const void* getMemoryBase();
+    const void* getAtPos();
+    size_t seek(size_t offset);
+    size_t peek() const { return fOffset; }
+    
+private:
+    const void* fSrc;
+    size_t fSize, fOffset;
+    SkBool8 fWeOwnTheData;
+};
+
+/** \class SkBufferStream
+    This is a wrapper class that adds buffering to another stream.
+    The caller can provide the buffer, or ask SkBufferStream to allocated/free
+    it automatically.
+*/
+class SkBufferStream : public SkStream {
+public:
+    /** Provide the stream to be buffered (proxy), and the size of the buffer that
+        should be used. This will be allocated and freed automatically. If bufferSize is 0,
+        a default buffer size will be used.
+        The proxy stream is referenced, and will be unreferenced in when the
+        bufferstream is destroyed.
+    */
+    SkBufferStream(SkStream* proxy, size_t bufferSize = 0);
+    /** Provide the stream to be buffered (proxy), and a buffer and size to be used.
+        This buffer is owned by the caller, and must be at least bufferSize bytes big.
+        Passing NULL for buffer will cause the buffer to be allocated/freed automatically.
+        If buffer is not NULL, it is an error for bufferSize to be 0.
+     The proxy stream is referenced, and will be unreferenced in when the
+     bufferstream is destroyed.
+    */
+    SkBufferStream(SkStream* proxy, void* buffer, size_t bufferSize);
+    virtual ~SkBufferStream();
+
+    virtual bool        rewind();
+    virtual const char* getFileName();
+    virtual size_t      read(void* buffer, size_t size);
+    virtual const void* getMemoryBase();
+
+private:
+    enum {
+        kDefaultBufferSize  = 128
+    };
+    // illegal
+    SkBufferStream(const SkBufferStream&);
+    SkBufferStream& operator=(const SkBufferStream&);
+
+    SkStream*   fProxy;
+    char*       fBuffer;
+    size_t      fOrigBufferSize, fBufferSize, fBufferOffset;
+    bool        fWeOwnTheBuffer;
+
+    void    init(void*, size_t);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkFILEWStream : public SkWStream {
+public:
+            SkFILEWStream(const char path[]);
+    virtual ~SkFILEWStream();
+
+    /** Returns true if the current path could be opened.
+    */
+    bool isValid() const { return fFILE != NULL; }
+
+    virtual bool write(const void* buffer, size_t size);
+    virtual void flush();
+private:
+    SkFILE* fFILE;
+};
+
+class SkMemoryWStream : public SkWStream {
+public:
+    SkMemoryWStream(void* buffer, size_t size);
+    virtual bool write(const void* buffer, size_t size);
+    
+private:
+    char*   fBuffer;
+    size_t  fMaxLength;
+    size_t  fBytesWritten;
+};
+
+class SkDynamicMemoryWStream : public SkWStream {
+public:
+    SkDynamicMemoryWStream();
+    virtual ~SkDynamicMemoryWStream();
+    virtual bool write(const void* buffer, size_t size);
+    // random access write
+    // modifies stream and returns true if offset + size is less than or equal to getOffset()
+    bool write(const void* buffer, size_t offset, size_t size);
+    bool read(void* buffer, size_t offset, size_t size);
+    size_t getOffset() { return fBytesWritten; }
+
+    // copy what has been written to the stream into dst
+    void    copyTo(void* dst) const;
+    /*  return a cache of the flattened data returned by copyTo().
+        This copy is only valid until the next call to write().
+        The memory is managed by the stream class.
+    */
+    const char* getStream() const;
+
+    // same as getStream, but additionally detach the flattened datat
+    const char* detach();
+    
+    // reset the stream to its original state
+    void reset();
+    void padToAlign4();
+private:
+    struct Block;
+    Block*  fHead;
+    Block*  fTail;
+    size_t  fBytesWritten;
+    mutable char*   fCopyToCache;
+};
+
+
+class SkDebugWStream : public SkWStream {
+public:
+    // overrides
+    virtual bool write(const void* buffer, size_t size);
+    virtual void newline();
+};
+
+// for now
+typedef SkFILEStream SkURLStream;
+
+#endif
+
diff --git a/include/core/SkString.h b/include/core/SkString.h
new file mode 100644
index 0000000..743b093
--- /dev/null
+++ b/include/core/SkString.h
@@ -0,0 +1,170 @@
+/*
+ * 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 SkString_DEFINED
+#define SkString_DEFINED
+
+#include "SkScalar.h"
+
+/*  Some helper functions for C strings
+*/
+
+bool SkStrStartsWith(const char string[], const char prefix[]);
+bool SkStrEndsWith(const char string[], const char suffix[]);
+int SkStrStartsWithOneOf(const char string[], const char prefixes[]);
+
+#define SkStrAppendS32_MaxSize  11
+char*   SkStrAppendS32(char buffer[], int32_t);
+#define SkStrAppendScalar_MaxSize  11
+char*   SkStrAppendScalar(char buffer[], SkScalar);
+
+/** \class SkString
+
+    Light weight class for managing strings. Uses reference
+    counting to make string assignments and copies very fast
+    with no extra RAM cost. Assumes UTF8 encoding.
+*/
+class SkString {
+public:
+                SkString();
+    explicit    SkString(size_t len);
+    explicit    SkString(const char text[]);
+                SkString(const char text[], size_t len);
+    explicit    SkString(const SkString&);
+                ~SkString();
+
+    bool        isEmpty() const { return fRec->fLength == 0; }
+    size_t      size() const { return (size_t) fRec->fLength; }
+    const char* c_str() const { return fRec->data(); }
+
+    bool    equals(const SkString&) const;
+    bool    equals(const char text[]) const;
+    bool    equals(const char text[], size_t len) const;
+
+    bool    startsWith(const char prefix[]) const
+    {
+        return SkStrStartsWith(fRec->data(), prefix);
+    }
+    bool    endsWith(const char suffix[]) const
+    {
+        return SkStrEndsWith(fRec->data(), suffix);
+    }
+
+    friend int operator==(const SkString& a, const SkString& b)
+    {
+        return a.equals(b);
+    }
+    friend int operator!=(const SkString& a, const SkString& b)
+    {
+        return !a.equals(b);
+    }
+
+    // these methods edit the string
+
+    SkString&   operator=(const SkString&);
+
+    char*   writable_str();
+
+    void    reset();
+    void    resize(size_t len) { this->set(NULL, len); }
+    void    set(const SkString& src) { *this = src; }
+    void    set(const char text[]);
+    void    set(const char text[], size_t len);
+    void    setUTF16(const uint16_t[]);
+    void    setUTF16(const uint16_t[], size_t len);
+
+    void    insert(size_t offset, const SkString& src) { this->insert(offset, src.c_str(), src.size()); }
+    void    insert(size_t offset, const char text[]);
+    void    insert(size_t offset, const char text[], size_t len);
+    void    insertUnichar(size_t offset, SkUnichar);
+    void    insertS32(size_t offset, int32_t value);
+    void    insertHex(size_t offset, uint32_t value, int minDigits = 0);
+    void    insertScalar(size_t offset, SkScalar);
+
+    void    append(const SkString& str) { this->insert((size_t)-1, str); }
+    void    append(const char text[]) { this->insert((size_t)-1, text); }
+    void    append(const char text[], size_t len) { this->insert((size_t)-1, text, len); }
+    void    appendUnichar(SkUnichar uni) { this->insertUnichar((size_t)-1, uni); }
+    void    appendS32(int32_t value) { this->insertS32((size_t)-1, value); }
+    void    appendHex(uint32_t value, int minDigits = 0) { this->insertHex((size_t)-1, value, minDigits); }
+    void    appendScalar(SkScalar value) { this->insertScalar((size_t)-1, value); }
+
+    void    prepend(const SkString& str) { this->insert(0, str); }
+    void    prepend(const char text[]) { this->insert(0, text); }
+    void    prepend(const char text[], size_t len) { this->insert(0, text, len); }
+    void    prependUnichar(SkUnichar uni) { this->insertUnichar(0, uni); }
+    void    prependS32(int32_t value) { this->insertS32(0, value); }
+    void    prependHex(uint32_t value, int minDigits = 0) { this->insertHex(0, value, minDigits); }
+    void    prependScalar(SkScalar value) { this->insertScalar((size_t)-1, value); }
+
+    void    printf(const char format[], ...);
+    void    appendf(const char format[], ...);
+    void    prependf(const char format[], ...);
+
+    void    remove(size_t offset, size_t length);
+
+    /** Swap contents between this and other. This function is guaranteed
+        to never fail or throw.
+    */
+    void    swap(SkString& other);
+
+  /** @cond UNIT_TEST */
+    SkDEBUGCODE(static void UnitTest();)
+  /** @endcond */
+    
+private:
+    struct Rec {
+    public:
+        uint16_t    fLength;
+        uint16_t    fRefCnt;
+        char        fBeginningOfData;
+
+        char* data() { return &fBeginningOfData; }
+        const char* data() const { return &fBeginningOfData; }
+    };
+    Rec* fRec;
+
+#ifdef SK_DEBUG
+    const char* fStr;
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+    static const Rec gEmptyRec;
+    static Rec* AllocRec(const char text[], U16CPU len);
+    static Rec* RefRec(Rec*);
+};
+
+class SkAutoUCS2 {
+public:
+    SkAutoUCS2(const char utf8[]);
+    ~SkAutoUCS2();
+
+    /** This returns the number of ucs2 characters
+    */
+    int count() const { return fCount; }
+    /** This returns a null terminated ucs2 string
+    */
+    const uint16_t* getUCS2() const { return fUCS2; }
+
+private:
+    int         fCount;
+    uint16_t*   fUCS2;
+};
+
+#endif
+
diff --git a/include/core/SkStroke.h b/include/core/SkStroke.h
new file mode 100644
index 0000000..b593b69
--- /dev/null
+++ b/include/core/SkStroke.h
@@ -0,0 +1,71 @@
+/*
+ * 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 SkStroke_DEFINED
+#define SkStroke_DEFINED
+
+#include "SkPoint.h"
+#include "SkPaint.h"
+
+struct SkRect;
+class SkPath;
+
+#define SK_DefaultStrokeWidth       SK_Scalar1
+#define SK_DefaultMiterLimit        SkIntToScalar(4)
+
+
+/** \class SkStroke
+    SkStroke is the utility class that constructs paths by stroking
+    geometries (lines, rects, ovals, roundrects, paths). This is
+    invoked when a geometry or text is drawn in a canvas with the
+    kStroke_Mask bit set in the paint.
+*/
+class SkStroke {
+public:
+    SkStroke();
+    SkStroke(const SkPaint&);
+    SkStroke(const SkPaint&, SkScalar width);   // width overrides paint.getStrokeWidth()
+
+    SkPaint::Cap    getCap() const { return (SkPaint::Cap)fCap; }
+    void        setCap(SkPaint::Cap);
+
+    SkPaint::Join   getJoin() const { return (SkPaint::Join)fJoin; }
+    void        setJoin(SkPaint::Join);
+
+    void    setMiterLimit(SkScalar);
+    void    setWidth(SkScalar);
+
+    bool    getDoFill() const { return SkToBool(fDoFill); }
+    void    setDoFill(bool doFill) { fDoFill = SkToU8(doFill); }
+
+    void    strokeLine(const SkPoint& start, const SkPoint& end, SkPath*) const;
+    void    strokeRect(const SkRect& rect, SkPath*) const;
+    void    strokeOval(const SkRect& oval, SkPath*) const;
+    void    strokeRRect(const SkRect& rect, SkScalar rx, SkScalar ry, SkPath*) const;
+    void    strokePath(const SkPath& path, SkPath*) const;
+
+    ////////////////////////////////////////////////////////////////
+
+private:
+    SkScalar    fWidth, fMiterLimit;
+    uint8_t     fCap, fJoin;
+    SkBool8     fDoFill;
+
+    friend class SkPaint;
+};
+
+#endif
+
diff --git a/include/core/SkTDArray.h b/include/core/SkTDArray.h
new file mode 100644
index 0000000..4d2d7f7
--- /dev/null
+++ b/include/core/SkTDArray.h
@@ -0,0 +1,293 @@
+/*
+ * 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 SkTDArray_DEFINED
+#define SkTDArray_DEFINED
+
+#include "SkTypes.h"
+
+template <typename T> class SkTDArray {
+public:
+    SkTDArray() {
+        fReserve = fCount = 0;
+        fArray = NULL;
+#ifdef SK_DEBUG
+        fData = NULL;
+#endif
+    }
+    SkTDArray(const T src[], size_t count) {
+        SkASSERT(src || count == 0);
+
+        fReserve = fCount = 0;
+        fArray = NULL;
+#ifdef SK_DEBUG
+        fData = NULL;
+#endif
+        if (count) {
+            fArray = (T*)sk_malloc_throw(count * sizeof(T));
+#ifdef SK_DEBUG
+            fData = (ArrayT*)fArray;
+#endif
+            memcpy(fArray, src, sizeof(T) * count);
+            fReserve = fCount = count;
+        }
+    }
+    SkTDArray(const SkTDArray<T>& src) {
+        fReserve = fCount = 0;
+        fArray = NULL;
+#ifdef SK_DEBUG
+        fData = NULL;
+#endif
+        SkTDArray<T> tmp(src.fArray, src.fCount);
+        this->swap(tmp);
+    }
+    ~SkTDArray() {
+        sk_free(fArray);
+    }
+
+    SkTDArray<T>& operator=(const SkTDArray<T>& src) {
+        if (this != &src) {
+            if (src.fCount > fReserve) {
+                SkTDArray<T> tmp(src.fArray, src.fCount);
+                this->swap(tmp);
+            } else {
+                memcpy(fArray, src.fArray, sizeof(T) * src.fCount);
+                fCount = src.fCount;
+            }
+        }
+        return *this;
+    }
+
+    friend int operator==(const SkTDArray<T>& a, const SkTDArray<T>& b) {
+        return  a.fCount == b.fCount &&
+                (a.fCount == 0 ||
+                 !memcmp(a.fArray, b.fArray, a.fCount * sizeof(T)));
+    }
+
+    void swap(SkTDArray<T>& other) {
+        SkTSwap(fArray, other.fArray);
+#ifdef SK_DEBUG
+        SkTSwap(fData, other.fData);
+#endif
+        SkTSwap(fReserve, other.fReserve);
+        SkTSwap(fCount, other.fCount);
+    }
+
+    bool isEmpty() const { return fCount == 0; }
+    int count() const { return fCount; }
+    T*  begin() const { return fArray; }
+    T*  end() const { return fArray ? fArray + fCount : NULL; }
+    T&  operator[](int index) const {
+        SkASSERT((unsigned)index < fCount);
+        return fArray[index];
+    }
+
+    void reset() {
+        if (fArray) {
+            sk_free(fArray);
+            fArray = NULL;
+#ifdef SK_DEBUG
+            fData = NULL;
+#endif
+            fReserve = fCount = 0;
+        } else {
+            SkASSERT(fReserve == 0 && fCount == 0);
+        }
+    }
+    
+    void rewind() {
+        // same as setCount(0)
+        fCount = 0;
+    }
+
+    void setCount(size_t count) {
+        if (count > fReserve) {
+            this->growBy(count - fCount);
+        } else {
+            fCount = count;
+        }
+    }
+
+    void setReserve(size_t reserve) {
+        if (reserve > fReserve) {
+            SkASSERT(reserve > fCount);
+            size_t count = fCount;
+            this->growBy(reserve - fCount);
+            fCount = count;
+        }
+    }
+
+    T* prepend() {
+        this->growBy(1);
+        memmove(fArray + 1, fArray, (fCount - 1) * sizeof(T));
+        return fArray;
+    }
+
+    T* append() {
+        return this->append(1, NULL);
+    }
+    T* append(size_t count, const T* src = NULL) {
+        unsigned oldCount = fCount;
+        if (count)  {
+            SkASSERT(src == NULL || fArray == NULL ||
+                    src + count <= fArray || fArray + oldCount <= src);
+
+            this->growBy(count);
+            if (src) {
+                memcpy(fArray + oldCount, src, sizeof(T) * count);
+            }
+        }
+        return fArray + oldCount;
+    }
+    
+    T* appendClear() {
+        T* result = this->append(); 
+        *result = 0;
+        return result;
+    }
+
+    T* insert(size_t index) {
+        return this->insert(index, 1, NULL);
+    }
+    T* insert(size_t index, size_t count, const T* src = NULL) {
+        SkASSERT(count);
+        SkASSERT(index <= fCount);
+        int oldCount = fCount;
+        this->growBy(count);
+        T* dst = fArray + index;
+        memmove(dst + count, dst, sizeof(T) * (oldCount - index));
+        if (src) {
+            memcpy(dst, src, sizeof(T) * count);
+        }
+        return dst;
+    }
+
+    void remove(size_t index, size_t count = 1) {
+        SkASSERT(index + count <= fCount);
+        fCount = fCount - count;
+        memmove(fArray + index, fArray + index + count, sizeof(T) * (fCount - index));
+    }
+
+    void removeShuffle(size_t index) {
+        SkASSERT(index < fCount);
+        unsigned newCount = fCount - 1;
+        fCount = newCount;
+        if (index != newCount) {
+            memcpy(fArray + index, fArray + newCount, sizeof(T));
+        }
+    }
+
+    int find(const T& elem) const {
+        const T* iter = fArray;
+        const T* stop = fArray + fCount;
+
+        for (; iter < stop; iter++) {
+            if (*iter == elem) {
+                return (int) (iter - fArray);
+            }
+        }
+        return -1;
+    }
+
+    int rfind(const T& elem) const {
+        const T* iter = fArray + fCount;
+        const T* stop = fArray;
+
+        while (iter > stop) {
+            if (*--iter == elem) {
+                return iter - stop;
+            }
+        }
+        return -1;
+    }
+
+    // routines to treat the array like a stack
+    T*          push() { return this->append(); }
+    void        push(const T& elem) { *this->append() = elem; }
+    const T&    top() const { return (*this)[fCount - 1]; }
+    T&          top() { return (*this)[fCount - 1]; }
+    void        pop(T* elem) { if (elem) *elem = (*this)[fCount - 1]; --fCount; }
+    void        pop() { --fCount; }
+
+    void deleteAll() {
+        T*  iter = fArray;
+        T*  stop = fArray + fCount;
+        while (iter < stop) {
+            delete (*iter);
+            iter += 1;
+        }
+        this->reset();
+    }
+
+    void freeAll() {
+        T*  iter = fArray;
+        T*  stop = fArray + fCount;
+        while (iter < stop) {
+            sk_free(*iter);
+            iter += 1;
+        }
+        this->reset();
+    }
+
+    void unrefAll() {
+        T*  iter = fArray;
+        T*  stop = fArray + fCount;
+        while (iter < stop) {
+            (*iter)->unref();
+            iter += 1;
+        }
+        this->reset();
+    }
+    
+#ifdef SK_DEBUG
+    void validate() const {
+        SkASSERT((fReserve == 0 && fArray == NULL) ||
+                 (fReserve > 0 && fArray != NULL));
+        SkASSERT(fCount <= fReserve);
+        SkASSERT(fData == (ArrayT*)fArray);
+    }
+#endif
+
+private:
+#ifdef SK_DEBUG
+    enum {
+        kDebugArraySize = 16
+    };
+    typedef T ArrayT[kDebugArraySize];
+    ArrayT* fData;
+#endif
+    T*      fArray;
+    size_t  fReserve, fCount;
+
+    void growBy(size_t extra) {
+        SkASSERT(extra);
+
+        if (fCount + extra > fReserve) {
+            size_t size = fCount + extra + 4;
+            size += size >> 2;
+
+            fArray = (T*)sk_realloc_throw(fArray, size * sizeof(T));
+#ifdef SK_DEBUG
+            fData = (ArrayT*)fArray;
+#endif
+            fReserve = size;
+        }
+        fCount += extra;
+    }
+};
+
+#endif
+
diff --git a/include/core/SkTDStack.h b/include/core/SkTDStack.h
new file mode 100644
index 0000000..5bc10ee
--- /dev/null
+++ b/include/core/SkTDStack.h
@@ -0,0 +1,120 @@
+/*
+ * 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 SkTDStack_DEFINED
+#define SkTDStack_DEFINED
+
+#include "SkTypes.h"
+
+template <typename T> class SkTDStack : SkNoncopyable {
+public:
+    SkTDStack() : fCount(0), fTotalCount(0)
+    {
+        fInitialRec.fNext = NULL;
+        fRec = &fInitialRec;
+
+    //  fCount = kSlotCount;
+    }
+    ~SkTDStack()
+    {
+        Rec* rec = fRec;
+        while (rec != &fInitialRec)
+        {
+            Rec* next = rec->fNext;
+            sk_free(rec);
+            rec = next;
+        }
+    }
+
+    int count() const { return fTotalCount; }
+
+    T* push()
+    {
+        SkASSERT(fCount <= kSlotCount);
+        if (fCount == kSlotCount)
+        {
+            Rec* rec = (Rec*)sk_malloc_throw(sizeof(Rec));
+            rec->fNext = fRec;
+            fRec = rec;
+            fCount = 0;
+        }
+        ++fTotalCount;
+        return &fRec->fSlots[fCount++];
+    }
+    void push(const T& elem) { *this->push() = elem; }
+    const T& index(int idx) const
+    {
+        SkASSERT(fRec && fCount > idx);
+        return fRec->fSlots[fCount - idx - 1];
+    }   
+    T& index(int idx)
+    {
+        SkASSERT(fRec && fCount > idx);
+        return fRec->fSlots[fCount - idx - 1];
+    }   
+    const T& top() const
+    {
+        SkASSERT(fRec && fCount > 0);
+        return fRec->fSlots[fCount - 1];
+    }
+    T& top()
+    {
+        SkASSERT(fRec && fCount > 0);
+        return fRec->fSlots[fCount - 1];
+    }
+    void pop(T* elem)
+    {
+        if (elem)
+            *elem = fRec->fSlots[fCount - 1];
+        this->pop();
+    }
+    void pop()
+    {
+        SkASSERT(fCount > 0 && fRec);
+        --fTotalCount;
+        if (--fCount == 0)
+        {
+            if (fRec != &fInitialRec)
+            {
+                Rec* rec = fRec->fNext;
+                sk_free(fRec);
+                fCount = kSlotCount;
+                fRec = rec;
+            }
+            else
+                SkASSERT(fTotalCount == 0);
+        }
+    }
+
+private:
+    enum {
+        kSlotCount  = 8
+    };
+
+    struct Rec;
+    friend struct Rec;
+
+    struct Rec {
+        Rec* fNext;
+        T    fSlots[kSlotCount];
+    };
+    Rec     fInitialRec;
+    Rec*    fRec;
+    int     fCount, fTotalCount;
+};
+
+#endif
+
diff --git a/include/core/SkTDict.h b/include/core/SkTDict.h
new file mode 100644
index 0000000..0b92779
--- /dev/null
+++ b/include/core/SkTDict.h
@@ -0,0 +1,169 @@
+/*
+ * 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 SkTDict_DEFINED
+#define SkTDict_DEFINED
+
+#include "SkChunkAlloc.h"
+#include "SkTSearch.h"
+#include "SkTDArray.h"
+
+template <typename T> class SkTDict : SkNoncopyable {
+public:
+    SkTDict(size_t minStringAlloc) : fStrings(minStringAlloc) {}
+
+    void reset()
+    {
+        fArray.reset();
+        fStrings.reset();
+    }
+
+    int count() const { return fArray.count(); }
+
+    bool set(const char name[], const T& value)
+    {
+        return set(name, strlen(name), value);
+    }
+
+    bool set(const char name[], size_t len, const T& value)
+    {
+        SkASSERT(name);
+
+        int index = this->find_index(name, len);
+
+        if (index >= 0)
+        {
+            fArray[index].fValue = value;
+            return false;
+        }
+        else
+        {
+            Pair*   pair = fArray.insert(~index);
+            char*   copy = (char*)fStrings.alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType);
+            memcpy(copy, name, len);
+            copy[len] = '\0';
+            pair->fName = copy;
+            pair->fValue = value;
+            return true;
+        }
+    }
+
+    bool find(const char name[]) const
+    {
+        return this->find_index(name) >= 0;
+    }
+
+    bool find(const char name[], size_t len) const
+    {
+        return this->find_index(name, len) >= 0;
+    }
+
+    bool find(const char name[], T* value) const
+    {
+        return find(name, strlen(name), value);
+    }
+
+    bool find(const char name[], size_t len, T* value) const
+    {
+        int index = this->find_index(name, len);
+
+        if (index >= 0)
+        {
+            if (value)
+                *value = fArray[index].fValue;
+            return true;
+        }
+        return false;
+    }
+
+    bool findKey(T& value, const char** name) const
+    {
+        Pair* end = fArray.end();
+        for (Pair* pair = fArray.begin(); pair < end; pair++) {
+            if (pair->fValue != value)
+                continue;
+            *name = pair->fName;
+            return true;
+        }
+        return false;
+    }
+
+public:
+    struct Pair {
+        const char* fName;
+        T           fValue;
+
+        friend int operator<(const Pair& a, const Pair& b)
+        {
+            return strcmp(a.fName, b.fName);
+        }
+        friend int operator!=(const Pair& a, const Pair& b)
+        {
+            return strcmp(a.fName, b.fName);
+        }
+    };
+    friend class Iter;
+
+public:
+    class Iter {
+    public:
+        Iter(const SkTDict<T>& dict)
+        {
+            fIter = dict.fArray.begin();
+            fStop = dict.fArray.end();
+        }
+        const char* next(T* value)
+        {
+            const char* name = NULL;
+            if (fIter < fStop)
+            {
+                name = fIter->fName;
+                if (value)
+                    *value = fIter->fValue;
+                fIter += 1;
+            }
+            return name;
+        }
+    private:
+        Pair*   fIter;
+        Pair*   fStop;
+    };
+
+private:
+    SkTDArray<Pair> fArray;
+    SkChunkAlloc    fStrings;
+
+    int find_index(const char name[]) const
+    {
+        return find_index(name, strlen(name));
+    }
+
+    int find_index(const char name[], size_t len) const
+    {
+        SkASSERT(name);
+
+        int count = fArray.count();
+        int index = ~0;
+
+        if (count)
+            index = SkStrSearch(&fArray.begin()->fName, count, name, len, sizeof(Pair));
+        return index;
+    }
+    friend class Iter;
+};
+
+#endif
+
diff --git a/include/core/SkTSearch.h b/include/core/SkTSearch.h
new file mode 100644
index 0000000..f29e6f5
--- /dev/null
+++ b/include/core/SkTSearch.h
@@ -0,0 +1,167 @@
+/*
+ * 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 SkTSearch_DEFINED
+#define SkTSearch_DEFINED
+
+#include "SkTypes.h"
+
+template <typename T>
+int SkTSearch(const T* base, int count, const T& target, size_t elemSize)
+{
+    SkASSERT(count >= 0);
+    if (count <= 0)
+        return ~0;
+
+    SkASSERT(base != NULL); // base may be NULL if count is zero
+
+    int lo = 0;
+    int hi = count - 1;
+
+    while (lo < hi)
+    {
+        int mid = (hi + lo) >> 1;
+        const T* elem = (const T*)((const char*)base + mid * elemSize);
+
+        if (*elem < target)
+            lo = mid + 1;
+        else
+            hi = mid;
+    }
+
+    const T* elem = (const T*)((const char*)base + hi * elemSize);
+    if (*elem != target)
+    {
+        if (*elem < target)
+            hi += 1;
+        hi = ~hi;
+    }
+    return hi;
+}
+
+template <typename T>
+int SkTSearch(const T* base, int count, const T& target, size_t elemSize,
+              int (*compare)(const T&, const T&))
+{
+    SkASSERT(count >= 0);
+    if (count <= 0) {
+        return ~0;
+    }
+
+    SkASSERT(base != NULL); // base may be NULL if count is zero
+
+    int lo = 0;
+    int hi = count - 1;
+
+    while (lo < hi) {
+        int mid = (hi + lo) >> 1;
+        const T* elem = (const T*)((const char*)base + mid * elemSize);
+
+        if ((*compare)(*elem, target) < 0)
+            lo = mid + 1;
+        else
+            hi = mid;
+    }
+
+    const T* elem = (const T*)((const char*)base + hi * elemSize);
+    int pred = (*compare)(*elem, target);
+    if (pred != 0) {
+        if (pred < 0)
+            hi += 1;
+        hi = ~hi;
+    }
+    return hi;
+}
+
+template <typename T>
+int SkTSearch(const T** base, int count, const T* target, size_t elemSize,
+    int (*compare)(const T*, const T*))
+{
+    SkASSERT(count >= 0);
+    if (count <= 0)
+        return ~0;
+
+    SkASSERT(base != NULL); // base may be NULL if count is zero
+
+    int lo = 0;
+    int hi = count - 1;
+
+    while (lo < hi)
+    {
+        int mid = (hi + lo) >> 1;
+        const T* elem = *(const T**)((const char*)base + mid * elemSize);
+
+        if ((*compare)(elem, target) < 0)
+            lo = mid + 1;
+        else
+            hi = mid;
+    }
+
+    const T* elem = *(const T**)((const char*)base + hi * elemSize);
+    int pred = (*compare)(elem, target);
+    if (pred != 0)
+    {
+        if (pred < 0)
+            hi += 1;
+        hi = ~hi;
+    }
+    return hi;
+}
+
+int SkStrSearch(const char*const* base, int count, const char target[],
+                size_t target_len, size_t elemSize);
+int SkStrSearch(const char*const* base, int count, const char target[],
+                size_t elemSize);
+
+/** Like SkStrSearch, but treats target as if it were all lower-case. Assumes that
+    base points to a table of lower-case strings.
+*/
+int SkStrLCSearch(const char*const* base, int count, const char target[],
+                  size_t target_len, size_t elemSize);
+int SkStrLCSearch(const char*const* base, int count, const char target[],
+                  size_t elemSize);
+
+/** Helper class to convert a string to lower-case, but only modifying the ascii
+    characters. This makes the routine very fast and never changes the string
+    length, but it is not suitable for linguistic purposes. Normally this is
+    used for buiding and searching string tables.
+*/
+class SkAutoAsciiToLC {
+public:
+    SkAutoAsciiToLC(const char str[], size_t len = (size_t)-1);
+    ~SkAutoAsciiToLC();
+    
+    const char* lc() const { return fLC; }
+    size_t      length() const { return fLength; }
+
+private:
+    char*   fLC;    // points to either the heap or fStorage
+    size_t  fLength;
+    enum {
+        STORAGE = 64
+    };
+    char    fStorage[STORAGE+1];
+};
+
+extern "C" {
+    typedef int (*SkQSortCompareProc)(const void*, const void*);
+    void SkQSort(void* base, size_t count, size_t elemSize, SkQSortCompareProc);
+}
+
+SkDEBUGCODE(void SkQSort_UnitTest();)
+
+#endif
+
diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h
new file mode 100644
index 0000000..27ebd41
--- /dev/null
+++ b/include/core/SkTemplates.h
@@ -0,0 +1,217 @@
+/*
+ * 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 SkTemplates_DEFINED
+#define SkTemplates_DEFINED
+
+#include "SkTypes.h"
+
+/** \file SkTemplates.h
+
+    This file contains light-weight template classes for type-safe and exception-safe
+    resource management.
+*/
+
+/** \class SkAutoTCallVProc
+
+    Call a function when this goes out of scope. The template uses two
+    parameters, the object, and a function that is to be called in the destructor.
+    If detach() is called, the object reference is set to null. If the object
+    reference is null when the destructor is called, we do not call the
+    function.
+*/
+template <typename T, void (*P)(T*)> class SkAutoTCallVProc : SkNoncopyable {
+public:
+    SkAutoTCallVProc(T* obj): fObj(obj) {}
+    ~SkAutoTCallVProc() { if (fObj) P(fObj); }
+    T* detach() { T* obj = fObj; fObj = NULL; return obj; }
+private:
+    T* fObj;
+};
+
+/** \class SkAutoTCallIProc
+
+Call a function when this goes out of scope. The template uses two
+parameters, the object, and a function that is to be called in the destructor.
+If detach() is called, the object reference is set to null. If the object
+reference is null when the destructor is called, we do not call the
+function.
+*/
+template <typename T, int (*P)(T*)> class SkAutoTCallIProc : SkNoncopyable {
+public:
+    SkAutoTCallIProc(T* obj): fObj(obj) {}
+    ~SkAutoTCallIProc() { if (fObj) P(fObj); }
+    T* detach() { T* obj = fObj; fObj = NULL; return obj; }
+private:
+    T* fObj;
+};
+
+template <typename T> class SkAutoTDelete : SkNoncopyable {
+public:
+    SkAutoTDelete(T* obj) : fObj(obj) {}
+    ~SkAutoTDelete() { delete fObj; }
+
+    T*      get() const { return fObj; }
+    void    free() { delete fObj; fObj = NULL; }
+    T*      detach() { T* obj = fObj; fObj = NULL; return obj; }
+
+private:
+    T*  fObj;
+};
+
+template <typename T> class SkAutoTDeleteArray : SkNoncopyable {
+public:
+    SkAutoTDeleteArray(T array[]) : fArray(array) {}
+    ~SkAutoTDeleteArray() { delete[] fArray; }
+
+    T*      get() const { return fArray; }
+    void    free() { delete[] fArray; fArray = NULL; }
+    T*      detach() { T* array = fArray; fArray = NULL; return array; }
+
+private:
+    T*  fArray;
+};
+
+/** Allocate an array of T elements, and free the array in the destructor
+ */
+template <typename T> class SkAutoTArray : SkNoncopyable {
+public:
+    /** Allocate count number of T elements
+     */
+    SkAutoTArray(size_t count) {
+        fArray = NULL;
+        if (count) {
+            fArray = new T[count];
+        }
+        SkDEBUGCODE(fCount = count;)
+    }
+
+    ~SkAutoTArray() {
+        delete[] fArray;
+    }
+
+    /** Return the array of T elements. Will be NULL if count == 0
+     */
+    T* get() const { return fArray; }
+    
+    /** Return the nth element in the array
+     */
+    T&  operator[](int index) const {
+        SkASSERT((unsigned)index < fCount);
+        return fArray[index];
+    }
+
+private:
+    T*  fArray;
+    SkDEBUGCODE(size_t fCount;)
+};
+
+/** Wraps SkAutoTArray, with room for up to N elements preallocated
+ */
+template <size_t N, typename T> class SkAutoSTArray : SkNoncopyable {
+public:
+    /** Allocate count number of T elements
+     */
+    SkAutoSTArray(size_t count) {
+        if (count > N) {
+            fArray = new T[count];
+        } else if (count) {
+            fArray = new (fStorage) T[count];
+        } else {
+            fArray = NULL;
+        }
+        fCount = count;
+    }
+    
+    ~SkAutoSTArray() {
+        if (fCount > N) {
+            delete[] fArray;
+        } else {
+            T* start = fArray;
+            T* iter = start + fCount;
+            while (iter > start) {
+                (--iter)->~T();
+            }
+        }
+    }
+    
+    /** Return the number of T elements in the array
+     */
+    size_t count() const { return fCount; }
+    
+    /** Return the array of T elements. Will be NULL if count == 0
+     */
+    T* get() const { return fArray; }
+    
+    /** Return the nth element in the array
+     */
+    T&  operator[](int index) const {
+        SkASSERT((unsigned)index < fCount);
+        return fArray[index];
+    }
+    
+private:
+    size_t  fCount;
+    T*      fArray;
+    // since we come right after fArray, fStorage should be properly aligned
+    char    fStorage[N * sizeof(T)];
+};
+
+/** Allocate a temp array on the stack/heap.
+    Does NOT call any constructors/destructors on T (i.e. T must be POD)
+*/
+template <typename T> class SkAutoTMalloc : SkNoncopyable {
+public:
+    SkAutoTMalloc(size_t count)
+    {
+        fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
+    }
+    ~SkAutoTMalloc()
+    {
+        sk_free(fPtr);
+    }
+    T* get() const { return fPtr; }
+
+private:
+    T*  fPtr;
+};
+
+template <size_t N, typename T> class SkAutoSTMalloc : SkNoncopyable {
+public:
+    SkAutoSTMalloc(size_t count)
+    {
+        if (count <= N)
+            fPtr = fTStorage;
+        else
+            fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
+    }
+    ~SkAutoSTMalloc()
+    {
+        if (fPtr != fTStorage)
+            sk_free(fPtr);
+    }
+    T* get() const { return fPtr; }
+
+private:
+    T*          fPtr;
+    union {
+        uint32_t    fStorage32[(N*sizeof(T) + 3) >> 2];
+        T           fTStorage[1];   // do NOT want to invoke T::T()
+    };
+};
+
+#endif
+
diff --git a/include/core/SkThread.h b/include/core/SkThread.h
new file mode 100644
index 0000000..637492d
--- /dev/null
+++ b/include/core/SkThread.h
@@ -0,0 +1,68 @@
+/*
+ * 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 SkThread_DEFINED
+#define SkThread_DEFINED
+
+#include "SkTypes.h"
+#include "SkThread_platform.h"
+
+/****** SkThread_platform needs to define the following...
+
+int32_t sk_atomic_inc(int32_t*);
+int32_t sk_atomic_dec(int32_t*);
+
+class SkMutex {
+public:
+    SkMutex();
+    ~SkMutex();
+
+    void    acquire();
+    void    release();
+};
+
+****************/
+
+class SkAutoMutexAcquire : SkNoncopyable {
+public:
+    explicit SkAutoMutexAcquire(SkMutex& mutex) : fMutex(&mutex)
+    {
+        SkASSERT(fMutex != NULL);
+        mutex.acquire();
+    }
+    /** If the mutex has not been release, release it now.
+    */
+    ~SkAutoMutexAcquire()
+    {
+        if (fMutex)
+            fMutex->release();
+    }
+    /** If the mutex has not been release, release it now.
+    */
+    void release()
+    {
+        if (fMutex)
+        {
+            fMutex->release();
+            fMutex = NULL;
+        }
+    }
+        
+private:
+    SkMutex* fMutex;
+};
+
+#endif
diff --git a/include/core/SkThread_platform.h b/include/core/SkThread_platform.h
new file mode 100644
index 0000000..825b737
--- /dev/null
+++ b/include/core/SkThread_platform.h
@@ -0,0 +1,71 @@
+/*
+ * 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 SkThread_platform_DEFINED
+#define SkThread_platform_DEFINED
+
+#ifdef ANDROID
+
+#include <utils/threads.h>
+#include <utils/Atomic.h>
+
+#define sk_atomic_inc(addr)     android_atomic_inc(addr)
+#define sk_atomic_dec(addr)     android_atomic_dec(addr)
+
+class SkMutex : android::Mutex {
+public:
+    // if isGlobal is true, then ignore any errors in the platform-specific
+    // destructor
+    SkMutex(bool isGlobal = true) {}
+    ~SkMutex() {}
+
+    void    acquire() { this->lock(); }
+    void    release() { this->unlock(); }
+};
+
+#else
+
+/** Implemented by the porting layer, this function adds 1 to the int specified
+    by the address (in a thread-safe manner), and returns the previous value.
+*/
+int32_t sk_atomic_inc(int32_t* addr);
+/** Implemented by the porting layer, this function subtracts 1 to the int
+    specified by the address (in a thread-safe manner), and returns the previous
+    value.
+*/
+int32_t sk_atomic_dec(int32_t* addr);
+
+class SkMutex {
+public:
+    // if isGlobal is true, then ignore any errors in the platform-specific
+    // destructor
+    SkMutex(bool isGlobal = true);
+    ~SkMutex();
+
+    void    acquire();
+    void    release();
+
+private:
+    bool fIsGlobal;
+    enum {
+        kStorageIntCount = 12
+    };
+    uint32_t    fStorage[kStorageIntCount];
+};
+
+#endif
+
+#endif
diff --git a/include/core/SkTime.h b/include/core/SkTime.h
new file mode 100644
index 0000000..9ee7110
--- /dev/null
+++ b/include/core/SkTime.h
@@ -0,0 +1,72 @@
+/*
+ * 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 SkTime_DEFINED
+#define SkTime_DEFINED
+
+#include "SkTypes.h"
+
+/** \class SkTime
+    Platform-implemented utilities to return time of day, and millisecond counter.
+*/
+class SkTime {
+public:
+    struct DateTime {
+        uint16_t fYear;          //!< e.g. 2005
+        uint8_t  fMonth;         //!< 1..12
+        uint8_t  fDayOfWeek;     //!< 0..6, 0==Sunday
+        uint8_t  fDay;           //!< 1..31
+        uint8_t  fHour;          //!< 0..23
+        uint8_t  fMinute;        //!< 0..59
+        uint8_t  fSecond;        //!< 0..59
+    };
+    static void GetDateTime(DateTime*);
+
+    static SkMSec GetMSecs();
+};
+
+#if defined(SK_DEBUG) && defined(SK_BUILD_FOR_WIN32)
+    extern SkMSec gForceTickCount;
+#endif
+
+#define SK_TIME_FACTOR      1
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAutoTime {
+public:
+    // The label is not deep-copied, so its address must remain valid for the
+    // lifetime of this object
+    SkAutoTime(const char* label = NULL, SkMSec minToDump = 0) : fLabel(label)
+    {
+        fNow = SkTime::GetMSecs();
+        fMinToDump = minToDump;
+    }
+    ~SkAutoTime()
+    {
+        SkMSec dur = SkTime::GetMSecs() - fNow;
+        if (dur >= fMinToDump) {
+            SkDebugf("%s %d\n", fLabel ? fLabel : "", dur);
+        }
+    }
+private:
+    const char* fLabel;
+    SkMSec      fNow;
+    SkMSec      fMinToDump;
+};
+
+#endif
+
diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h
new file mode 100644
index 0000000..546edca
--- /dev/null
+++ b/include/core/SkTypeface.h
@@ -0,0 +1,131 @@
+/*
+ * 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 SkTypeface_DEFINED
+#define SkTypeface_DEFINED
+
+#include "SkRefCnt.h"
+
+class SkStream;
+class SkWStream;
+
+/** \class SkTypeface
+
+    The SkTypeface class specifies the typeface and intrinsic style of a font.
+    This is used in the paint, along with optionally algorithmic settings like
+    textSize, textSkewX, textScaleX, kFakeBoldText_Mask, to specify
+    how text appears when drawn (and measured).
+
+    Typeface objects are immutable, and so they can be shred between threads.
+    To enable this, Typeface inherits from the thread-safe version of SkRefCnt.
+*/
+class SkTypeface : public SkRefCnt {
+public:
+    /** Style specifies the intrinsic style attributes of a given typeface
+    */
+    enum Style {
+        kNormal = 0,
+        kBold   = 0x01,
+        kItalic = 0x02,
+
+        // helpers
+        kBoldItalic = 0x03
+    };
+
+    /** Returns the typeface's intrinsic style attributes
+    */
+    Style style() const { return fStyle; }
+    
+    /** DEPRECATED */
+    Style getStyle() const { return this->style(); }
+
+    /** Returns true if getStyle() has the kBold bit set.
+    */
+    bool isBold() const { return (fStyle & kBold) != 0; }
+
+    /** Returns true if getStyle() has the kItalic bit set.
+    */
+    bool isItalic() const { return (fStyle & kItalic) != 0; }
+    
+    uint32_t uniqueID() const { return fUniqueID; }
+
+    /** Return the uniqueID for the specified typeface. If the face is null,
+        resolve it to the default font and return its uniqueID.
+    */
+    static uint32_t UniqueID(const SkTypeface* face);
+
+    /** Return a new reference to the typeface that most closely matches the
+        requested familyName and style. Pass null as the familyName to return
+        the default font for the requested style. Will never return null
+        
+        @param familyName  May be NULL. The name of the font family.
+        @param style       The style (normal, bold, italic) of the typeface.
+        @return reference to the closest-matching typeface. Call must call
+                unref() when they are done.
+    */
+    static SkTypeface* Create(const char familyName[], Style style = kNormal);
+
+    /** Return a new reference to the typeface that most closely matches the
+        requested typeface and specified Style. Use this call if you want to
+        pick a new style from the same family of the existing typeface.
+        If family is NULL, this selects from the default font's family.
+        
+        @param family  May be NULL. The name of the existing type face.
+        @param s       The style (normal, bold, italic) of the type face.
+        @return reference to the closest-matching typeface. Call must call
+                unref() when they are done.
+    */
+    static SkTypeface* CreateFromTypeface(const SkTypeface* family, Style s);
+
+    /** Returns true if the two typefaces reference the same underlying font,
+        even if one is null (which maps to the default font).
+    */
+    static bool Equal(const SkTypeface* facea, const SkTypeface* faceb);
+    
+    /** Returns a 32bit hash value for the typeface. Takes care of mapping null
+        to the default typeface.
+    */
+    static uint32_t Hash(const SkTypeface* face);
+
+    /** Return a new typeface given a file. If the file does not exist, or is
+        not a valid font file, returns null.
+    */
+    static SkTypeface* CreateFromFile(const char path[]);
+    
+    /** Return a new typeface given a stream. If the stream is
+        not a valid font file, returns null. Ownership of the stream is
+        transferred, so the caller must not reference it again.
+    */
+    static SkTypeface* CreateFromStream(SkStream* stream);
+
+    // Serialization
+    void serialize(SkWStream*) const;
+    static SkTypeface* Deserialize(SkStream*);
+
+protected:
+    /** uniqueID must be unique (please!) and non-zero
+    */
+    SkTypeface(Style style, uint32_t uniqueID)
+        : fUniqueID(uniqueID), fStyle(style) {}
+
+private:
+    uint32_t    fUniqueID;
+    Style       fStyle;
+    
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/core/SkTypes.h b/include/core/SkTypes.h
new file mode 100644
index 0000000..0554c73
--- /dev/null
+++ b/include/core/SkTypes.h
@@ -0,0 +1,377 @@
+/*
+ * 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 SkTypes_DEFINED
+#define SkTypes_DEFINED
+
+#include "SkPreConfig.h"
+#include "SkUserConfig.h"
+#include "SkPostConfig.h"
+
+#ifndef SK_IGNORE_STDINT_DOT_H
+    #include <stdint.h>
+#endif
+
+#include <stdio.h>
+
+/** \file SkTypes.h
+*/
+
+/*
+    memory wrappers to be implemented by the porting layer (platform)
+*/
+
+/** Called internally if we run out of memory. The platform implementation must
+    not return, but should either throw an exception or otherwise exit.
+*/
+extern void  sk_out_of_memory(void);
+/** Called internally if we hit an unrecoverable error.
+    The platform implementation must not return, but should either throw
+    an exception or otherwise exit.
+*/
+extern void  sk_throw(void);
+
+enum {
+    SK_MALLOC_TEMP  = 0x01, //!< hint to sk_malloc that the requested memory will be freed in the scope of the stack frame
+    SK_MALLOC_THROW = 0x02  //!< instructs sk_malloc to call sk_throw if the memory cannot be allocated.
+};
+/** Return a block of memory (at least 4-byte aligned) of at least the
+    specified size. If the requested memory cannot be returned, either
+    return null (if SK_MALLOC_TEMP bit is clear) or call sk_throw()
+    (if SK_MALLOC_TEMP bit is set). To free the memory, call sk_free().
+*/
+extern void* sk_malloc_flags(size_t size, unsigned flags);
+/** Same as sk_malloc(), but hard coded to pass SK_MALLOC_THROW as the flag
+*/
+extern void* sk_malloc_throw(size_t size);
+/** Same as standard realloc(), but this one never returns null on failure. It will throw
+    an exception if it fails.
+*/
+extern void* sk_realloc_throw(void* buffer, size_t size);
+/** Free memory returned by sk_malloc(). It is safe to pass null.
+*/
+extern void  sk_free(void*);
+
+///////////////////////////////////////////////////////////////////////
+
+#define SK_INIT_TO_AVOID_WARNING    = 0
+
+#ifndef SkDebugf
+    void SkDebugf(const char format[], ...);
+#endif
+
+#ifdef SK_DEBUG
+    #define SkASSERT(cond)              SK_DEBUGBREAK(cond)
+    #define SkDEBUGCODE(code)           code
+    #define SkDECLAREPARAM(type, var)   , type var
+    #define SkPARAM(var)                , var
+//  #define SkDEBUGF(args       )       SkDebugf##args
+    #define SkDEBUGF(args       )       SkDebugf args
+    #define SkAssertResult(cond)        SkASSERT(cond)
+#else
+    #define SkASSERT(cond)
+    #define SkDEBUGCODE(code)
+    #define SkDEBUGF(args)
+    #define SkDECLAREPARAM(type, var)
+    #define SkPARAM(var)
+
+    // unlike SkASSERT, this guy executes its condition in the non-debug build
+    #define SkAssertResult(cond)        cond
+#endif
+
+///////////////////////////////////////////////////////////////////////
+
+/** Fast type for signed 8 bits. Use for parameter passing and local variables, not for storage
+*/
+typedef int         S8CPU;
+/** Fast type for unsigned 8 bits. Use for parameter passing and local variables, not for storage
+*/
+typedef int         S16CPU;
+/** Fast type for signed 16 bits. Use for parameter passing and local variables, not for storage
+*/
+typedef unsigned    U8CPU;
+/** Fast type for unsigned 16 bits. Use for parameter passing and local variables, not for storage
+*/
+typedef unsigned    U16CPU;
+
+/** Meant to be faster than bool (doesn't promise to be 0 or 1, just 0 or non-zero
+*/
+typedef int         SkBool;
+/** Meant to be a small version of bool, for storage purposes. Will be 0 or 1
+*/
+typedef uint8_t     SkBool8;
+
+#ifdef SK_DEBUG
+    int8_t      SkToS8(long);
+    uint8_t     SkToU8(size_t);
+    int16_t     SkToS16(long);
+    uint16_t    SkToU16(size_t);
+    int32_t     SkToS32(long);
+    uint32_t    SkToU32(size_t);
+#else
+    #define SkToS8(x)   ((int8_t)(x))
+    #define SkToU8(x)   ((uint8_t)(x))
+    #define SkToS16(x)  ((int16_t)(x))
+    #define SkToU16(x)  ((uint16_t)(x))
+    #define SkToS32(x)  ((int32_t)(x))
+    #define SkToU32(x)  ((uint32_t)(x))
+#endif
+
+/** Returns 0 or 1 based on the condition
+*/
+#define SkToBool(cond)  ((cond) != 0)
+
+#define SK_MaxS16   32767
+#define SK_MinS16   -32767
+#define SK_MaxU16   0xFFFF
+#define SK_MinU16   0
+#define SK_MaxS32   0x7FFFFFFF
+#define SK_MinS32   0x80000001
+#define SK_MaxU32   0xFFFFFFFF
+#define SK_MinU32   0
+#define SK_NaN32    0x80000000
+
+#ifndef SK_OFFSETOF
+    #define SK_OFFSETOF(type, field)    ((char*)&(((type*)1)->field) - (char*)1)
+#endif
+
+/** Returns the number of entries in an array (not a pointer)
+*/
+#define SK_ARRAY_COUNT(array)       (sizeof(array) / sizeof(array[0]))
+
+/** Returns x rounded up to a multiple of 2
+*/
+#define SkAlign2(x)     (((x) + 1) >> 1 << 1)
+/** Returns x rounded up to a multiple of 4
+*/
+#define SkAlign4(x)     (((x) + 3) >> 2 << 2)
+
+typedef uint32_t SkFourByteTag;
+#define SkSetFourByteTag(a, b, c, d)    (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+
+/** 32 bit integer to hold a unicode value
+*/
+typedef int32_t SkUnichar;
+/** 32 bit value to hold a millisecond count
+*/
+typedef uint32_t SkMSec;
+/** 1 second measured in milliseconds
+*/
+#define SK_MSec1 1000
+/** maximum representable milliseconds
+*/
+#define SK_MSecMax 0x7FFFFFFF
+/** Returns a < b for milliseconds, correctly handling wrap-around from 0xFFFFFFFF to 0
+*/
+#define SkMSec_LT(a, b)     ((int32_t)(a) - (int32_t)(b) < 0)
+/** Returns a <= b for milliseconds, correctly handling wrap-around from 0xFFFFFFFF to 0
+*/
+#define SkMSec_LE(a, b)     ((int32_t)(a) - (int32_t)(b) <= 0)
+
+
+/****************************************************************************
+    The rest of these only build with C++
+*/
+#ifdef __cplusplus
+
+/** Faster than SkToBool for integral conditions. Returns 0 or 1
+*/
+inline int Sk32ToBool(uint32_t n)
+{
+    return (n | (0-n)) >> 31;
+}
+
+template <typename T> inline void SkTSwap(T& a, T& b)
+{
+    T c(a);
+    a = b;
+    b = c;
+}
+
+inline int32_t SkAbs32(int32_t value)
+{
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (value < 0)
+        value = -value;
+    return value;
+#else
+    int32_t mask = value >> 31;
+    return (value ^ mask) - mask;
+#endif
+}
+
+inline int32_t SkMax32(int32_t a, int32_t b)
+{
+    if (a < b)
+        a = b;
+    return a;
+}
+
+inline int32_t SkMin32(int32_t a, int32_t b)
+{
+    if (a > b)
+        a = b;
+    return a;
+}
+
+inline int32_t SkSign32(int32_t a)
+{
+    return (a >> 31) | ((unsigned) -a >> 31);
+}
+
+inline int32_t SkFastMin32(int32_t value, int32_t max)
+{
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (value > max)
+        value = max;
+    return value;
+#else
+    int diff = max - value;
+    // clear diff if it is negative (clear if value > max)
+    diff &= (diff >> 31);
+    return value + diff;
+#endif
+}
+
+/** Returns signed 32 bit value pinned between min and max, inclusively
+*/
+inline int32_t SkPin32(int32_t value, int32_t min, int32_t max)
+{
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (value < min)
+        value = min;
+    if (value > max)
+        value = max;
+#else
+    if (value < min)
+        value = min;
+    else if (value > max)
+        value = max;
+#endif
+    return value;
+}
+
+inline uint32_t SkSetClearShift(uint32_t bits, bool cond, unsigned shift)
+{
+    SkASSERT((int)cond == 0 || (int)cond == 1);
+    return (bits & ~(1 << shift)) | ((int)cond << shift);
+}
+
+inline uint32_t SkSetClearMask(uint32_t bits, bool cond, uint32_t mask)
+{
+    return cond ? bits | mask : bits & ~mask;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+/** \class SkNoncopyable
+
+SkNoncopyable is the base class for objects that may do not want to
+be copied. It hides its copy-constructor and its assignment-operator.
+*/
+class SkNoncopyable {
+public:
+    SkNoncopyable() {}
+    
+private:
+    SkNoncopyable(const SkNoncopyable&);
+    SkNoncopyable& operator=(const SkNoncopyable&);
+};
+
+class SkAutoFree : SkNoncopyable {
+public:
+    SkAutoFree() : fPtr(NULL) {}
+    explicit SkAutoFree(void* ptr) : fPtr(ptr) {}
+    ~SkAutoFree() { sk_free(fPtr); }
+    
+    /** Return the currently allocate buffer, or null
+    */
+    void* get() const { return fPtr; }
+
+    /** Assign a new ptr allocated with sk_malloc (or null), and return the
+        previous ptr. Note it is the caller's responsibility to sk_free the
+        returned ptr.
+    */
+    void* set(void* ptr) {
+        void* prev = fPtr;
+        fPtr = ptr;
+        return prev;
+    }
+    
+    /** Transfer ownership of the current ptr to the caller, setting the
+        internal reference to null. Note the caller is reponsible for calling
+        sk_free on the returned address.
+    */
+    void* detach() { return this->set(NULL); }
+
+    /** Free the current buffer, and set the internal reference to NULL. Same
+        as calling sk_free(detach())
+    */
+    void free() {
+        sk_free(fPtr);
+        fPtr = NULL;
+    }
+
+private:
+    void* fPtr;
+    // illegal
+    SkAutoFree(const SkAutoFree&);
+    SkAutoFree& operator=(const SkAutoFree&);
+};
+
+class SkAutoMalloc : public SkAutoFree {
+public:
+    explicit SkAutoMalloc(size_t size)
+        : SkAutoFree(sk_malloc_flags(size, SK_MALLOC_THROW | SK_MALLOC_TEMP)) {}
+
+    SkAutoMalloc(size_t size, unsigned flags)
+        : SkAutoFree(sk_malloc_flags(size, flags)) {}
+    SkAutoMalloc() {}
+
+    void* alloc(size_t size,
+                unsigned flags = (SK_MALLOC_THROW | SK_MALLOC_TEMP)) {
+        sk_free(set(sk_malloc_flags(size, flags)));
+        return get();
+    }
+};
+
+template <size_t kSize> class SkAutoSMalloc : SkNoncopyable {
+public:
+    explicit SkAutoSMalloc(size_t size)
+    {
+        if (size <= kSize)
+            fPtr = fStorage;
+        else
+            fPtr = sk_malloc_flags(size, SK_MALLOC_THROW | SK_MALLOC_TEMP);
+    }
+    ~SkAutoSMalloc()
+    {
+        if (fPtr != (void*)fStorage)
+            sk_free(fPtr);
+    }
+    void* get() const { return fPtr; }
+private:
+    void*       fPtr;
+    uint32_t    fStorage[(kSize + 3) >> 2];
+    // illegal
+    SkAutoSMalloc(const SkAutoSMalloc&);
+    SkAutoSMalloc& operator=(const SkAutoSMalloc&);
+};
+
+#endif /* C++ */
+
+#endif
+
diff --git a/include/core/SkUnPreMultiply.h b/include/core/SkUnPreMultiply.h
new file mode 100644
index 0000000..4bdb980
--- /dev/null
+++ b/include/core/SkUnPreMultiply.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+
+#ifndef SkUnPreMultiply_DEFINED
+#define SkUnPreMultiply_DEFINED
+
+#include "SkColor.h"
+
+class SkUnPreMultiply {
+public:
+    typedef uint32_t Scale;
+    
+    // index this table with alpha [0..255]
+    static const Scale* GetScaleTable() {
+        return gTable;
+    }
+
+    static Scale GetScale(U8CPU alpha) {
+        SkASSERT(alpha <= 255);
+        return gTable[alpha];
+    }
+    
+    /** Usage:
+     
+        const Scale* table = SkUnPreMultiply::GetScaleTable();
+     
+        for (...) {
+            unsigned a = ...
+            SkUnPreMultiply::Scale scale = table[a];
+     
+            red = SkUnPreMultiply::ApplyScale(scale, red);
+            ...
+            // now red is unpremultiplied
+        }
+    */
+    static U8CPU ApplyScale(Scale scale, U8CPU component) {
+        SkASSERT(component <= 255);
+        return (scale * component + (1 << 23)) >> 24;
+    }
+    
+    static SkColor PMColorToColor(SkPMColor c);
+    
+private:
+    static const uint32_t gTable[256];
+};
+
+#endif
diff --git a/include/core/SkUnitMapper.h b/include/core/SkUnitMapper.h
new file mode 100644
index 0000000..5d1ea35
--- /dev/null
+++ b/include/core/SkUnitMapper.h
@@ -0,0 +1,38 @@
+/*
+ * 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 SkUnitMapper_DEFINED
+#define SkUnitMapper_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+
+#include "SkFlattenable.h"
+
+class SkUnitMapper : public SkFlattenable {
+public:
+    SkUnitMapper() {}
+
+    /** Given a value in [0..0xFFFF], return a value in the same range.
+    */
+    virtual uint16_t mapUnit16(uint16_t x) = 0;
+    
+protected:
+    SkUnitMapper(SkFlattenableReadBuffer& rb) : SkFlattenable(rb) {}
+};
+
+#endif
+
diff --git a/include/core/SkUserConfig.h b/include/core/SkUserConfig.h
new file mode 100644
index 0000000..85cda1c
--- /dev/null
+++ b/include/core/SkUserConfig.h
@@ -0,0 +1,104 @@
+/*
+ * 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 SkUserConfig_DEFINED
+#define SkUserConfig_DEFINED
+
+// for floats
+#ifdef SK_SCALAR_IS_FIXED
+#undef  SK_SCALAR_IS_FIXED
+#endif
+#define SK_SCALAR_IS_FLOAT
+
+// remove the x if you want to force us into SK_DEBUG mode
+#ifdef SK_RELEASE
+    #undef SK_RELEASE
+    #define SK_DEBUG
+#endif
+
+// remove the x if you want to force us into SK_RELEASE mode
+#ifdef SK_DEBUGx
+    #undef SK_DEBUG
+    #define SK_RELEASE
+#endif
+
+#ifdef ANDROID
+    #include <utils/misc.h>
+    #include <assert.h>
+
+    #define SK_CRASH() *(int *)(uintptr_t)0 = 0
+//    #define SK_CRASH() assert(0)
+
+#if 0
+    // force fixed
+    #define SK_SCALAR_IS_FIXED
+    #undef  SK_SCALAR_IS_FLOAT
+#else
+    // force floats
+    #ifdef SK_SCALAR_IS_FIXED
+        #undef  SK_SCALAR_IS_FIXED
+    #endif
+    #define SK_SCALAR_IS_FLOAT
+#endif
+
+    #define SK_CAN_USE_FLOAT
+    #define SK_SOFTWARE_FLOAT
+    #define SkLONGLONG int64_t
+
+    // replace some sw float routines (floor, ceil, etc.)
+    #define SK_USE_FLOATBITS
+
+    #if __BYTE_ORDER == __BIG_ENDIAN
+        #define SK_CPU_BENDIAN
+        #undef  SK_CPU_LENDIAN
+    #else
+        #define SK_CPU_LENDIAN
+        #undef  SK_CPU_BENDIAN
+    #endif
+
+    // define SkDebugf to record file/line
+    #define SkDebugf(...) Android_SkDebugf(__FILE__, __LINE__, \
+                                           __FUNCTION__, __VA_ARGS__)
+    void Android_SkDebugf(const char* file, int line, 
+                          const char* function, const char* format, ...);
+#endif
+
+/*  This file is included before all other headers, except for SkPreConfig.h.
+    That file uses various heuristics to make a "best guess" at settings for
+    the following build defines.
+
+    However, in this file you can override any of those decisions by either
+    defining new symbols, or #undef symbols that were already set.
+*/
+
+// experimental for now
+#define SK_SUPPORT_MIPMAP
+
+#ifdef SK_DEBUG
+    #define SK_SUPPORT_UNITTEST
+    /* Define SK_SIMULATE_FAILED_MALLOC to have
+     * sk_malloc throw an exception. Use this to
+     * detect unhandled memory leaks. */
+    //#define SK_SIMULATE_FAILED_MALLOC
+    //#define SK_FIND_MEMORY_LEAKS
+#endif
+
+#ifdef SK_BUILD_FOR_BREW
+    #include "SkBrewUserConfig.h"
+#endif
+
+#endif
+
diff --git a/include/core/SkUtils.h b/include/core/SkUtils.h
new file mode 100644
index 0000000..f3e3341
--- /dev/null
+++ b/include/core/SkUtils.h
@@ -0,0 +1,140 @@
+/*
+ * 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 SkUtils_DEFINED
+#define SkUtils_DEFINED
+
+#include "SkTypes.h"
+
+///////////////////////////////////////////////////////////////////////////
+
+/** Similar to memset(), but it assigns a 16bit value into the buffer.
+    @param buffer   The memory to have value copied into it
+    @param value    The 16bit value to be copied into buffer
+    @param count    The number of times value should be copied into the buffer.
+*/
+void sk_memset16_portable(uint16_t dst[], uint16_t value, int count);
+
+/** Similar to memset(), but it assigns a 32bit value into the buffer.
+    @param buffer   The memory to have value copied into it
+    @param value    The 32bit value to be copied into buffer
+    @param count    The number of times value should be copied into the buffer.
+*/
+void sk_memset32_portable(uint32_t dst[], uint32_t value, int count);
+
+#ifdef ANDROID
+    #include "cutils/memory.h"
+    
+    #define sk_memset16(dst, value, count)    android_memset16(dst, value, (count) << 1)
+    #define sk_memset32(dst, value, count)    android_memset32(dst, value, (count) << 2)
+#endif
+
+#ifndef sk_memset16
+    #define sk_memset16(dst, value, count)  sk_memset16_portable(dst, value, count)
+#endif
+
+#ifndef sk_memset32
+    #define sk_memset32(dst, value, count)  sk_memset32_portable(dst, value, count)
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////
+
+#define kMaxBytesInUTF8Sequence     4
+
+#ifdef SK_DEBUG
+    int SkUTF8_LeadByteToCount(unsigned c);
+#else
+    #define SkUTF8_LeadByteToCount(c)   ((((0xE5 << 24) >> ((unsigned)c >> 4 << 1)) & 3) + 1)
+#endif
+
+inline int SkUTF8_CountUTF8Bytes(const char utf8[])
+{
+    SkASSERT(utf8);
+    return SkUTF8_LeadByteToCount(*(const uint8_t*)utf8);
+}
+
+int         SkUTF8_CountUnichars(const char utf8[]);
+int         SkUTF8_CountUnichars(const char utf8[], size_t byteLength);
+SkUnichar   SkUTF8_ToUnichar(const char utf8[]);
+SkUnichar   SkUTF8_NextUnichar(const char**);
+SkUnichar   SkUTF8_PrevUnichar(const char**);
+
+/** Return the number of bytes need to convert a unichar
+    into a utf8 sequence. Will be 1..kMaxBytesInUTF8Sequence,
+    or 0 if uni is illegal.
+*/
+size_t      SkUTF8_FromUnichar(SkUnichar uni, char utf8[] = NULL);
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define SkUTF16_IsHighSurrogate(c)  (((c) & 0xFC00) == 0xD800)
+#define SkUTF16_IsLowSurrogate(c)   (((c) & 0xFC00) == 0xDC00)
+
+int SkUTF16_CountUnichars(const uint16_t utf16[]);
+int SkUTF16_CountUnichars(const uint16_t utf16[],
+                                  int numberOf16BitValues);
+// returns the current unichar and then moves past it (*p++)
+SkUnichar SkUTF16_NextUnichar(const uint16_t**);
+// this guy backs up to the previus unichar value, and returns it (*--p)
+SkUnichar SkUTF16_PrevUnichar(const uint16_t**);
+size_t SkUTF16_FromUnichar(SkUnichar uni, uint16_t utf16[] = NULL);
+
+size_t SkUTF16_ToUTF8(const uint16_t utf16[], int numberOf16BitValues,
+                           char utf8[] = NULL);
+
+class SkUtils {
+public:
+#ifdef SK_DEBUG
+    static void UnitTest();
+#endif
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAutoTrace {
+public:
+    /** NOTE: label contents are not copied, just the ptr is
+        retained, so DON'T DELETE IT.
+    */
+    SkAutoTrace(const char label[]) : fLabel(label) {
+        SkDebugf("--- trace: %s Enter\n", fLabel);
+    }
+    ~SkAutoTrace() {
+        SkDebugf("--- trace: %s Leave\n", fLabel);
+    }
+private:
+    const char* fLabel;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAutoMemoryUsageProbe {
+public:
+    /** Record memory usage in constructor, and dump the result
+        (delta and current total) in the destructor, with the optional
+        label. NOTE: label contents are not copied, just the ptr is
+        retained, so DON'T DELETE IT.
+    */
+    SkAutoMemoryUsageProbe(const char label[]);
+    ~SkAutoMemoryUsageProbe();
+private:
+    const char* fLabel;
+    size_t      fBytesAllocated;
+};
+
+#endif
+
diff --git a/include/core/SkWriter32.h b/include/core/SkWriter32.h
new file mode 100644
index 0000000..aeeb37d
--- /dev/null
+++ b/include/core/SkWriter32.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkWriter32_DEFINED
+#define SkWriter32_DEFINED
+
+#include "SkTypes.h"
+
+#include "SkScalar.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+
+class SkStream;
+class SkWStream;
+
+class SkWriter32 : SkNoncopyable {
+public:
+    SkWriter32(size_t minSize) {
+        fMinSize = minSize;
+        fSize = 0;
+        fHead = fTail = NULL;
+    }
+    ~SkWriter32();
+
+    bool writeBool(bool value) {
+        this->writeInt(value);
+        return value;
+    }
+    
+    void writeInt(int32_t value) {
+        *(int32_t*)this->reserve(sizeof(value)) = value;
+    }
+    
+    void write8(int32_t value) {
+        *(int32_t*)this->reserve(sizeof(value)) = value & 0xFF;
+    }
+    
+    void write16(int32_t value) {
+        *(int32_t*)this->reserve(sizeof(value)) = value & 0xFFFF;
+    }
+    
+    void write32(int32_t value) {
+        *(int32_t*)this->reserve(sizeof(value)) = value;
+    }
+    
+    void writeScalar(SkScalar value) {
+        *(SkScalar*)this->reserve(sizeof(value)) = value;
+    }
+    
+    void writePoint(const SkPoint& pt) {
+        *(SkPoint*)this->reserve(sizeof(pt)) = pt;
+    }
+    
+    void writeRect(const SkRect& rect) {
+        *(SkRect*)this->reserve(sizeof(rect)) = rect;
+    }
+    
+    // write count bytes (must be a multiple of 4)
+    void writeMul4(const void* values, size_t size) {
+        SkASSERT(SkAlign4(size) == size);
+        // if we could query how much is avail in the current block, we might
+        // copy that much, and then alloc the rest. That would reduce the waste
+        // in the current block
+        memcpy(this->reserve(size), values, size);
+    }
+    
+    void writePad(const void* src, size_t size);
+    
+    // return the current offset (will always be a multiple of 4)
+    uint32_t  size() const { return fSize; }
+    void      reset();
+    uint32_t* reserve(size_t size); // size MUST be multiple of 4
+    
+    // return the address of the 4byte int at the specified offset (which must
+    // be a multiple of 4. This does not allocate any new space, so the returned
+    // address is only valid for 1 int.
+    uint32_t* peek32(size_t offset);
+    
+    // copy into a single buffer (allocated by caller). Must be at least size()
+    void flatten(void* dst) const;
+    
+    // read from the stream, and write up to length bytes. Return the actual
+    // number of bytes written.
+    size_t readFromStream(SkStream*, size_t length);
+    
+    bool writeToStream(SkWStream*);
+
+private:
+    size_t      fMinSize;
+    uint32_t    fSize;
+    
+    struct Block;
+    Block*  fHead;
+    Block*  fTail;
+    
+    Block* newBlock(size_t bytes);
+};
+
+#endif
diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h
new file mode 100644
index 0000000..f7e6510
--- /dev/null
+++ b/include/core/SkXfermode.h
@@ -0,0 +1,115 @@
+/*
+ * 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 SkXfermode_DEFINED
+#define SkXfermode_DEFINED
+
+#include "SkFlattenable.h"
+#include "SkColor.h"
+
+/** \class SkXfermode
+
+    SkXfermode is the base class for objects that are called to implement custom
+    "transfer-modes" in the drawing pipeline. The static function Create(Modes)
+    can be called to return an instance of any of the predefined subclasses as
+    specified in the Modes enum. When an SkXfermode is assigned to an SkPaint,
+    then objects drawn with that paint have the xfermode applied.
+*/
+class SkXfermode : public SkFlattenable {
+public:
+    SkXfermode() {}
+
+    virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+    virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+    virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count,
+                          const SkAlpha aa[]);
+    virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+    
+    enum Coeff {
+        kZero_Coeff,
+        kOne_Coeff,
+        kSC_Coeff,
+        kISC_Coeff,
+        kDC_Coeff,
+        kIDC_Coeff,
+        kSA_Coeff,
+        kISA_Coeff,
+        kDA_Coeff,
+        kIDA_Coeff,
+        
+        kCoeffCount
+    };
+    virtual bool asCoeff(Coeff* src, Coeff* dst);
+
+protected:
+    SkXfermode(SkFlattenableReadBuffer& rb) : SkFlattenable(rb) {}
+    
+    /** The default implementation of xfer32/xfer16/xferA8 in turn call this
+        method, 1 color at a time (upscaled to a SkPMColor). The default
+        implmentation of this method just returns dst. If performance is
+        important, your subclass should override xfer32/xfer16/xferA8 directly.
+        
+        This method will not be called directly by the client, so it need not
+        be implemented if your subclass has overridden xfer32/xfer16/xferA8
+    */
+    virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst);
+
+private:
+    typedef SkFlattenable INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** \class SkProcXfermode
+
+    SkProcXfermode is a xfermode that applies the specified proc to its colors.
+    This class is not exported to java.
+*/
+class SkProcXfermode : public SkXfermode {
+public:
+    SkProcXfermode(SkXfermodeProc proc) : fProc(proc) {}
+
+    // overrides from SkXfermode
+    virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+    virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+    virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count,
+                          const SkAlpha aa[]);
+    virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+
+    // overrides from SkFlattenable
+    virtual Factory getFactory() { return CreateProc; }
+    virtual void    flatten(SkFlattenableWriteBuffer&);
+
+protected:
+    SkProcXfermode(SkFlattenableReadBuffer&);
+
+private:
+    SkXfermodeProc  fProc;
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkProcXfermode, (buffer)); }
+
+    typedef SkXfermode INHERITED;
+};
+
+#endif
+
diff --git a/include/effects/Sk1DPathEffect.h b/include/effects/Sk1DPathEffect.h
new file mode 100644
index 0000000..db01055
--- /dev/null
+++ b/include/effects/Sk1DPathEffect.h
@@ -0,0 +1,93 @@
+/*
+ * 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 Sk1DPathEffect_DEFINED
+#define Sk1DPathEffect_DEFINED
+
+#include "SkPathEffect.h"
+#include "SkPath.h"
+
+class SkPathMeasure;
+
+//  This class is not exported to java.
+class Sk1DPathEffect : public SkPathEffect {
+public:
+    //  override from SkPathEffect
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+protected:
+    /** Called at the start of each contour, returns the initial offset
+        into that contour.
+    */
+    virtual SkScalar begin(SkScalar contourLength) = 0;
+    /** Called with the current distance along the path, with the current matrix
+        for the point/tangent at the specified distance.
+        Return the distance to travel for the next call. If return <= 0, then that
+        contour is done.
+    */
+    virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure&) = 0;
+
+private:
+    typedef SkPathEffect INHERITED;
+};
+
+class SkPath1DPathEffect : public Sk1DPathEffect {
+public:
+    enum Style {
+        kTranslate_Style,   // translate the shape to each position
+        kRotate_Style,      // rotate the shape about its center
+        kMorph_Style,       // transform each point, and turn lines into curves
+        
+        kStyleCount
+    };
+    
+    /** Dash by replicating the specified path.
+        @param path The path to replicate (dash)
+        @param advance The space between instances of path
+        @param phase distance (mod advance) along path for its initial position
+        @param style how to transform path at each point (based on the current
+                     position and tangent)
+    */
+    SkPath1DPathEffect(const SkPath& path, SkScalar advance, SkScalar phase, Style);
+
+    // override from SkPathEffect
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+protected:
+    SkPath1DPathEffect(SkFlattenableReadBuffer& buffer);
+
+    // overrides from Sk1DPathEffect
+    virtual SkScalar begin(SkScalar contourLength);
+    virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure&);
+    // overrides from SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+    
+private:
+    SkPath      fPath;          // copied from constructor
+    SkScalar    fAdvance;       // copied from constructor
+    SkScalar    fInitialOffset; // computed from phase
+    Style       fStyle;         // copied from constructor
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkPath1DPathEffect, (buffer));
+    }
+
+    typedef Sk1DPathEffect INHERITED;
+};
+
+
+#endif
diff --git a/include/effects/Sk2DPathEffect.h b/include/effects/Sk2DPathEffect.h
new file mode 100644
index 0000000..6e54d0a
--- /dev/null
+++ b/include/effects/Sk2DPathEffect.h
@@ -0,0 +1,73 @@
+/*
+ * 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 Sk2DPathEffect_DEFINED
+#define Sk2DPathEffect_DEFINED
+
+#include "SkPathEffect.h"
+#include "SkMatrix.h"
+
+//  This class is not exported to java.
+class Sk2DPathEffect : public SkPathEffect {
+public:
+    Sk2DPathEffect(const SkMatrix& mat);
+
+    // overrides
+    //  This method is not exported to java.
+    virtual bool    filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+    // overrides from SkFlattenable
+    //  This method is not exported to java.
+    virtual void    flatten(SkFlattenableWriteBuffer&);
+    
+    //  This method is not exported to java.
+       virtual Factory getFactory();
+
+protected:
+    /** New virtual, to be overridden by subclasses.
+        This is called once from filterPath, and provides the
+        uv parameter bounds for the path. Subsequent calls to
+        next() will receive u and v values within these bounds,
+        and then a call to end() will signal the end of processing.
+    */
+    virtual void begin(const SkIRect& uvBounds, SkPath* dst);
+    virtual void next(const SkPoint& loc, int u, int v, SkPath* dst);
+    virtual void end(SkPath* dst);
+
+    /** Low-level virtual called per span of locations in the u-direction.
+        The default implementation calls next() repeatedly with each
+        location.
+    */
+    virtual void nextSpan(int u, int v, int ucount, SkPath* dst);
+
+    const SkMatrix& getMatrix() const { return fMatrix; }
+
+    // protected so that subclasses can call this during unflattening
+    Sk2DPathEffect(SkFlattenableReadBuffer&);
+
+private:
+    SkMatrix    fMatrix, fInverse;
+    // illegal
+    Sk2DPathEffect(const Sk2DPathEffect&);
+    Sk2DPathEffect& operator=(const Sk2DPathEffect&);
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+
+    friend class Sk2DPathEffectBlitter;
+    typedef SkPathEffect INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkAvoidXfermode.h b/include/effects/SkAvoidXfermode.h
new file mode 100644
index 0000000..32bc049
--- /dev/null
+++ b/include/effects/SkAvoidXfermode.h
@@ -0,0 +1,73 @@
+/*
+ * 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 SkAvoidXfermode_DEFINED
+#define SkAvoidXfermode_DEFINED
+
+#include "SkXfermode.h"
+
+/** \class SkAvoidXfermode
+
+    This xfermode will draw the src everywhere except on top of the specified
+    color.
+*/
+class SkAvoidXfermode : public SkXfermode {
+public:
+    enum Mode {
+        kAvoidColor_Mode,   //!< draw everywhere except on the opColor
+        kTargetColor_Mode   //!< draw only on top of the opColor
+    };
+
+    /** This xfermode will draw the src everywhere except on top of the opColor
+        or, depending on the Mode, draw only on top of the opColor.
+        @param opColor  the color to avoid (or to target depending on Mode).
+                        note: the alpha in opColor is ignored
+        @param tolerance    How closely we compare a pixel to the opColor.
+                            0 - only operate if exact match
+                            255 - maximum gradation (blending) based on how
+                            similar the pixel is to our opColor (max tolerance)
+        @param mode If we should avoid or target the opColor
+    */
+    SkAvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode);
+
+    // overrides from SkXfermode
+    virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+    virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+    virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count,
+                          const SkAlpha aa[]);
+    virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]);
+
+    // overrides from SkFlattenable
+    virtual Factory getFactory();
+    virtual void flatten(SkFlattenableWriteBuffer&);
+
+protected:
+    SkAvoidXfermode(SkFlattenableReadBuffer&);
+
+private:
+    SkColor     fOpColor;
+    uint32_t    fDistMul;   // x.14
+    Mode        fMode;
+    
+    static SkFlattenable* Create(SkFlattenableReadBuffer&);
+    
+    typedef SkXfermode INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkBlurDrawLooper.h b/include/effects/SkBlurDrawLooper.h
new file mode 100644
index 0000000..028b2eb
--- /dev/null
+++ b/include/effects/SkBlurDrawLooper.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkBlurDrawLooper_DEFINED
+#define SkBlurDrawLooper_DEFINED
+
+#include "SkDrawLooper.h"
+#include "SkColor.h"
+
+class SkMaskFilter;
+
+/** \class SkBlurDrawLooper
+    This class draws a shadow of the object (possibly offset), and then draws
+    the original object in its original position.
+    should there be an option to just draw the shadow/blur layer? webkit?
+*/
+class SkBlurDrawLooper : public SkDrawLooper {
+public:
+    SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, SkColor color);
+    virtual ~SkBlurDrawLooper();
+
+    // overrides from SkDrawLooper
+    virtual void init(SkCanvas*, SkPaint*);
+    virtual bool next();
+    virtual void restore();
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkBlurDrawLooper, (buffer));
+    }
+
+protected:
+    SkBlurDrawLooper(SkFlattenableReadBuffer&);
+    // overrides from SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+
+private:
+    SkCanvas*       fCanvas;
+    SkPaint*        fPaint;
+    SkMaskFilter*   fBlur;
+    SkScalar        fDx, fDy;
+    SkColor         fBlurColor;
+    SkColor         fSavedColor;    // remember the original
+    int             fSaveCount;
+
+    enum State {
+        kBeforeEdge,
+        kAfterEdge,
+        kDone
+    };
+    State   fState;
+    
+    typedef SkDrawLooper INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkBlurMaskFilter.h b/include/effects/SkBlurMaskFilter.h
new file mode 100644
index 0000000..b90ea5e
--- /dev/null
+++ b/include/effects/SkBlurMaskFilter.h
@@ -0,0 +1,58 @@
+/*
+ * 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 SkBlurMaskFilter_DEFINED
+#define SkBlurMaskFilter_DEFINED
+
+// we include this since our callers will need to at least be able to ref/unref
+#include "SkMaskFilter.h"
+#include "SkScalar.h"
+
+class SkBlurMaskFilter {
+public:
+    enum BlurStyle {
+        kNormal_BlurStyle,  //!< fuzzy inside and outside
+        kSolid_BlurStyle,   //!< solid inside, fuzzy outside
+        kOuter_BlurStyle,   //!< nothing inside, fuzzy outside
+        kInner_BlurStyle,   //!< fuzzy inside, nothing outside
+
+        kBlurStyleCount
+    };
+
+    /** Create a blur maskfilter.
+        @param radius   The radius to extend the blur from the original mask. Must be > 0.
+        @param style    The BlurStyle to use
+        @return The new blur maskfilter
+    */
+    static SkMaskFilter* Create(SkScalar radius, BlurStyle style);
+
+    /** Create an emboss maskfilter
+        @param direction    array of 3 scalars [x, y, z] specifying the direction of the light source
+        @param ambient      0...1 amount of ambient light
+        @param specular     coefficient for specular highlights (e.g. 8)
+        @param blurRadius   amount to blur before applying lighting (e.g. 3)
+        @return the emboss maskfilter
+    */
+    static SkMaskFilter* CreateEmboss(  const SkScalar direction[3],
+                                        SkScalar ambient, SkScalar specular,
+                                        SkScalar blurRadius);
+
+private:
+    SkBlurMaskFilter(); // can't be instantiated
+};
+
+#endif
+
diff --git a/include/effects/SkColorMatrix.h b/include/effects/SkColorMatrix.h
new file mode 100644
index 0000000..cee5d6e
--- /dev/null
+++ b/include/effects/SkColorMatrix.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2007 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 SkColorMatrix_DEFINED
+#define SkColorMatrix_DEFINED
+
+#include "SkScalar.h"
+
+class SkColorMatrix {
+public:
+    SkScalar    fMat[20];
+    
+    void setIdentity();
+    void setScale(SkScalar rScale, SkScalar gScale, SkScalar bScale,
+                  SkScalar aScale = SK_Scalar1);
+    void preScale(SkScalar rScale, SkScalar gScale, SkScalar bScale,
+                  SkScalar aScale = SK_Scalar1);
+    void postScale(SkScalar rScale, SkScalar gScale, SkScalar bScale,
+                   SkScalar aScale = SK_Scalar1);
+
+    enum Axis {
+        kR_Axis = 0,
+        kG_Axis = 1,
+        kB_Axis = 2
+    };
+    void setRotate(Axis, SkScalar degrees);
+    void setSinCos(Axis, SkScalar sine, SkScalar cosine);
+    void preRotate(Axis, SkScalar degrees);
+    void postRotate(Axis, SkScalar degrees);
+
+    void setConcat(const SkColorMatrix& a, const SkColorMatrix& b);
+    void preConcat(const SkColorMatrix& mat) { this->setConcat(*this, mat); }
+    void postConcat(const SkColorMatrix& mat) { this->setConcat(mat, *this); }
+
+    void setSaturation(SkScalar sat);
+    void setRGB2YUV();
+    void setYUV2RGB();
+};
+
+#endif
diff --git a/include/effects/SkColorMatrixFilter.h b/include/effects/SkColorMatrixFilter.h
new file mode 100644
index 0000000..f9194df
--- /dev/null
+++ b/include/effects/SkColorMatrixFilter.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007 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 SkColorMatrixFilter_DEFINED
+#define SkColorMatrixFilter_DEFINED
+
+#include "SkColorFilter.h"
+#include "SkColorMatrix.h"
+
+class SkColorMatrixFilter : public SkColorFilter {
+public:
+    SkColorMatrixFilter();
+    explicit SkColorMatrixFilter(const SkColorMatrix&);
+    SkColorMatrixFilter(const SkScalar array[20]);
+    
+    void setMatrix(const SkColorMatrix&);
+    void setArray(const SkScalar array[20]);
+    
+    // overrides from SkColorFilter
+    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor[]);
+    virtual void filterSpan16(const uint16_t src[], int count, uint16_t[]);
+    virtual uint32_t getFlags();
+    
+    // overrides for SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer& buffer);
+
+    struct State {
+        int32_t fArray[20];
+        int     fShift;
+        int32_t fResult[4];
+    };
+
+protected:
+    // overrides for SkFlattenable
+    virtual Factory getFactory();
+    
+    SkColorMatrixFilter(SkFlattenableReadBuffer& buffer);
+    
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer);
+
+    typedef void (*Proc)(State*, unsigned r, unsigned g, unsigned b,
+                         unsigned a);
+
+    Proc        fProc;
+    State       fState;
+    uint32_t    fFlags;
+    
+    void setup(const SkScalar array[20]);
+    
+    typedef SkColorFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkCornerPathEffect.h b/include/effects/SkCornerPathEffect.h
new file mode 100644
index 0000000..a459478
--- /dev/null
+++ b/include/effects/SkCornerPathEffect.h
@@ -0,0 +1,61 @@
+/*
+ * 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 SkCornerPathEffect_DEFINED
+#define SkCornerPathEffect_DEFINED
+
+#include "SkPathEffect.h"
+
+/** \class SkCornerPathEffect
+
+    SkCornerPathEffect is a subclass of SkPathEffect that can turn sharp corners
+    into various treatments (e.g. rounded corners)
+*/
+class SkCornerPathEffect : public SkPathEffect {
+public:
+    /** radius must be > 0 to have an effect. It specifies the distance from each corner
+        that should be "rounded".
+    */
+    SkCornerPathEffect(SkScalar radius);
+    virtual ~SkCornerPathEffect();
+
+    // overrides for SkPathEffect
+    //  This method is not exported to java.
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+    // overrides for SkFlattenable
+    //  This method is not exported to java.
+    virtual Factory getFactory();
+    //  This method is not exported to java.
+    virtual void flatten(SkFlattenableWriteBuffer&);
+
+protected:
+    SkCornerPathEffect(SkFlattenableReadBuffer&);
+
+private:
+    SkScalar    fRadius;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+    
+    // illegal
+    SkCornerPathEffect(const SkCornerPathEffect&);
+    SkCornerPathEffect& operator=(const SkCornerPathEffect&);
+    
+    typedef SkPathEffect INHERITED;
+};
+
+#endif
+
diff --git a/include/effects/SkDashPathEffect.h b/include/effects/SkDashPathEffect.h
new file mode 100644
index 0000000..cc414e3
--- /dev/null
+++ b/include/effects/SkDashPathEffect.h
@@ -0,0 +1,65 @@
+/*
+ * 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 SkDashPathEffect_DEFINED
+#define SkDashPathEffect_DEFINED
+
+#include "SkPathEffect.h"
+
+/** \class SkDashPathEffect
+
+    SkDashPathEffect is a subclass of SkPathEffect that implements dashing
+*/
+class SkDashPathEffect : public SkPathEffect {
+public:
+    /** The intervals array must contain an even number of entries (>=2), with the even
+        indices specifying the "on" intervals, and the odd indices specifying the "off"
+        intervals. phase is an offset into the intervals array (mod the sum of all of the
+        intervals).
+        Note: only affects framed paths
+    */
+    SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase, bool scaleToFit = false);
+    virtual ~SkDashPathEffect();
+
+    // overrides for SkPathEffect
+    //  This method is not exported to java.
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+    // overrides for SkFlattenable
+    //  This method is not exported to java.
+    virtual Factory getFactory();
+    //  This method is not exported to java.
+    virtual void flatten(SkFlattenableWriteBuffer&);
+
+protected:
+    SkDashPathEffect(SkFlattenableReadBuffer&);
+    
+private:
+    SkScalar*   fIntervals;
+    int32_t     fCount;
+    // computed from phase
+    SkScalar    fInitialDashLength;
+    int32_t     fInitialDashIndex;
+    SkScalar    fIntervalLength;
+    bool        fScaleToFit;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+
+    typedef SkPathEffect INHERITED;
+};
+
+#endif
+
diff --git a/include/effects/SkDiscretePathEffect.h b/include/effects/SkDiscretePathEffect.h
new file mode 100644
index 0000000..2950950
--- /dev/null
+++ b/include/effects/SkDiscretePathEffect.h
@@ -0,0 +1,56 @@
+/*
+ * 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 SkDiscretePathEffect_DEFINED
+#define SkDiscretePathEffect_DEFINED
+
+#include "SkPathEffect.h"
+
+/** \class SkDiscretePathEffect
+
+    This path effect chops a path into discrete segments, and randomly displaces them.
+*/
+class SkDiscretePathEffect : public SkPathEffect {
+public:
+    /** Break the path into segments of segLength length, and randomly move the endpoints
+        away from the original path by a maximum of deviation.
+        Note: works on filled or framed paths
+    */
+    SkDiscretePathEffect(SkScalar segLength, SkScalar deviation);
+
+    // overrides for SkPathEffect
+    //  This method is not exported to java.
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+
+    // overrides for SkFlattenable
+    //  This method is not exported to java.
+    virtual Factory getFactory();
+    //  This method is not exported to java.
+    virtual void flatten(SkFlattenableWriteBuffer&);
+
+protected:
+    SkDiscretePathEffect(SkFlattenableReadBuffer&);
+
+private:
+    SkScalar fSegLength, fPerterb;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+    
+    typedef SkPathEffect INHERITED;
+};
+
+#endif
+
diff --git a/include/effects/SkDrawExtraPathEffect.h b/include/effects/SkDrawExtraPathEffect.h
new file mode 100644
index 0000000..65e255a
--- /dev/null
+++ b/include/effects/SkDrawExtraPathEffect.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SK_DRAW_EXTRA_PATH_EFFECT_H
+#define SK_DRAW_EXTRA_PATH_EFFECT_H
+class SkAnimator;
+void InitializeSkExtraPathEffects(SkAnimator* animator);
+#endif
+
diff --git a/include/effects/SkEmbossMaskFilter.h b/include/effects/SkEmbossMaskFilter.h
new file mode 100644
index 0000000..042a2a6
--- /dev/null
+++ b/include/effects/SkEmbossMaskFilter.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 SkEmbossMaskFilter_DEFINED
+#define SkEmbossMaskFilter_DEFINED
+
+#include "SkMaskFilter.h"
+
+/** \class SkEmbossMaskFilter
+
+    This mask filter creates a 3D emboss look, by specifying a light and blur amount.
+*/
+class SkEmbossMaskFilter : public SkMaskFilter {
+public:
+    struct Light {
+        SkScalar    fDirection[3];  // x,y,z
+        uint16_t    fPad;
+        uint8_t     fAmbient;
+        uint8_t     fSpecular;      // exponent, 4.4 right now
+    };
+
+    SkEmbossMaskFilter(const Light& light, SkScalar blurRadius);
+
+    // overrides from SkMaskFilter
+    //  This method is not exported to java.
+    virtual SkMask::Format getFormat();
+    //  This method is not exported to java.
+    virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin);
+
+    // overrides from SkFlattenable
+
+    //  This method is not exported to java.
+    virtual Factory getFactory();
+    //  This method is not exported to java.
+    virtual void flatten(SkFlattenableWriteBuffer&);
+
+protected:
+    SkEmbossMaskFilter(SkFlattenableReadBuffer&);
+
+private:
+    Light       fLight;
+    SkScalar    fBlurRadius;
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+    
+    typedef SkMaskFilter INHERITED;
+};
+
+#endif
+
diff --git a/include/effects/SkGradientShader.h b/include/effects/SkGradientShader.h
new file mode 100644
index 0000000..9a8696c
--- /dev/null
+++ b/include/effects/SkGradientShader.h
@@ -0,0 +1,97 @@
+/*
+ * 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 SkGradientShader_DEFINED
+#define SkGradientShader_DEFINED
+
+#include "SkShader.h"
+
+class SkUnitMapper;
+
+/** \class SkGradientShader
+
+    SkGradientShader hosts factories for creating subclasses of SkShader that
+    render linear and radial gradients.
+*/
+class SkGradientShader {
+public:
+    /** Returns a shader that generates a linear gradient between the two
+        specified points.
+        <p />
+        CreateLinear returns a shader with a reference count of 1.
+        The caller should decrement the shader's reference count when done with the shader.
+        It is an error for count to be < 2.
+        @param  pts The start and end points for the gradient.
+        @param  colors  The array[count] of colors, to be distributed between the two points
+        @param  pos     May be NULL. array[count] of SkScalars, or NULL, of the relative position of
+                        each corresponding color in the colors array. If this is NULL,
+                        the the colors are distributed evenly between the start and end point.
+                        If this is not null, the values must begin with 0, end with 1.0, and
+                        intermediate values must be strictly increasing.
+        @param  count   Must be >=2. The number of colors (and pos if not NULL) entries. 
+        @param  mode    The tiling mode
+        @param  mapper  May be NULL. Callback to modify the spread of the colors.
+    */
+    static SkShader* CreateLinear(  const SkPoint pts[2],
+                                    const SkColor colors[], const SkScalar pos[], int count,
+                                    SkShader::TileMode mode,
+                                    SkUnitMapper* mapper = NULL);
+
+    /** Returns a shader that generates a radial gradient given the center and radius.
+        <p />
+        CreateRadial returns a shader with a reference count of 1.
+        The caller should decrement the shader's reference count when done with the shader.
+        It is an error for colorCount to be < 2, or for radius to be <= 0.
+        @param  center  The center of the circle for this gradient
+        @param  radius  Must be positive. The radius of the circle for this gradient
+        @param  colors  The array[count] of colors, to be distributed between the center and edge of the circle
+        @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
+                        each corresponding color in the colors array. If this is NULL,
+                        the the colors are distributed evenly between the center and edge of the circle.
+                        If this is not null, the values must begin with 0, end with 1.0, and
+                        intermediate values must be strictly increasing.
+        @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
+        @param  mode    The tiling mode
+        @param  mapper  May be NULL. Callback to modify the spread of the colors.
+    */
+    static SkShader* CreateRadial(  const SkPoint& center, SkScalar radius,
+                                    const SkColor colors[], const SkScalar pos[], int count,
+                                    SkShader::TileMode mode,
+                                    SkUnitMapper* mapper = NULL);
+
+    /** Returns a shader that generates a sweep gradient given a center.
+        <p />
+        CreateRadial returns a shader with a reference count of 1.
+        The caller should decrement the shader's reference count when done with the shader.
+        It is an error for colorCount to be < 2.
+        @param  cx      The X coordinate of the center of the sweep
+        @param  cx      The Y coordinate of the center of the sweep
+        @param  colors  The array[count] of colors, to be distributed around the center.
+        @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
+                        each corresponding color in the colors array. If this is NULL,
+                        the the colors are distributed evenly between the center and edge of the circle.
+                        If this is not null, the values must begin with 0, end with 1.0, and
+                        intermediate values must be strictly increasing.
+        @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
+        @param  mapper  May be NULL. Callback to modify the spread of the colors.
+    */
+    static SkShader* CreateSweep(SkScalar cx, SkScalar cy,
+                                 const SkColor colors[], const SkScalar pos[],
+                                 int count, SkUnitMapper* mapper = NULL);
+};
+
+#endif
+
diff --git a/include/effects/SkKernel33MaskFilter.h b/include/effects/SkKernel33MaskFilter.h
new file mode 100644
index 0000000..84136e2
--- /dev/null
+++ b/include/effects/SkKernel33MaskFilter.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkKernel33MaskFilter_DEFINED
+#define SkKernel33MaskFilter_DEFINED
+
+#include "SkMaskFilter.h"
+
+class SkKernel33ProcMaskFilter : public SkMaskFilter {
+public:
+    SkKernel33ProcMaskFilter(unsigned percent256 = 256)
+        : fPercent256(percent256) {}
+
+    virtual uint8_t computeValue(uint8_t* const* srcRows) = 0;
+    
+    // overrides from SkMaskFilter
+    virtual SkMask::Format getFormat();
+    virtual bool filterMask(SkMask*, const SkMask&, const SkMatrix&, SkIPoint*);
+
+    // overrides from SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer& wb);
+
+protected:
+    SkKernel33ProcMaskFilter(SkFlattenableReadBuffer& rb);
+
+private:
+    int fPercent256;
+    
+    typedef SkMaskFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkKernel33MaskFilter : public SkKernel33ProcMaskFilter {
+public:
+    SkKernel33MaskFilter(const int coeff[3][3], int shift, int percent256 = 256)
+        : SkKernel33ProcMaskFilter(percent256)
+    {
+        memcpy(fKernel, coeff, 9 * sizeof(int));
+        fShift = shift;
+    }
+    
+    // override from SkKernel33ProcMaskFilter
+    virtual uint8_t computeValue(uint8_t* const* srcRows);
+    
+    // overrides from SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer& wb);
+    virtual Factory getFactory();
+    
+private:
+    int fKernel[3][3];
+    int fShift;
+
+    SkKernel33MaskFilter(SkFlattenableReadBuffer& rb);
+    static SkFlattenable* Create(SkFlattenableReadBuffer& rb);
+    
+    typedef SkKernel33ProcMaskFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkLayerDrawLooper.h b/include/effects/SkLayerDrawLooper.h
new file mode 100644
index 0000000..670ac23
--- /dev/null
+++ b/include/effects/SkLayerDrawLooper.h
@@ -0,0 +1,70 @@
+#ifndef SkLayerDrawLooper_DEFINED
+#define SkLayerDrawLooper_DEFINED
+
+#include "SkDrawLooper.h"
+
+struct SkPoint;
+
+class SkLayerDrawLooper : public SkDrawLooper {
+public:
+            SkLayerDrawLooper();
+    virtual ~SkLayerDrawLooper();
+    
+    /** Call for each layer you want to add (from top to bottom).
+        This returns a paint you can modify, but that ptr is only valid until
+        the next call made to this object.
+     */
+    SkPaint* addLayer(SkScalar dx, SkScalar dy);
+    
+    /** Helper for addLayer() which passes (0, 0) for the offset parameters
+     */
+    SkPaint* addLayer() {
+        return this->addLayer(0, 0);
+    }
+    
+    // overrides from SkDrawLooper
+    virtual void init(SkCanvas*, SkPaint*);
+    virtual bool next();
+    virtual void restore();
+
+    // must be public for Registrar :(
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkLayerDrawLooper, (buffer));
+    }
+    
+protected:
+    SkLayerDrawLooper(SkFlattenableReadBuffer&);
+
+    // overrides from SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+    
+private:
+    struct Rec {
+        Rec*    fNext;
+        SkPaint fPaint;
+        SkPoint fOffset;
+        
+        static Rec* Reverse(Rec*);
+    };
+    Rec*    fRecs;
+    int     fCount;
+    
+    struct Iter {
+        SkPaint     fSavedPaint;
+        SkPaint*    fPaint;
+        SkCanvas*   fCanvas;
+        Rec*        fRec;
+    };
+    Iter    fIter;
+    
+    class MyRegistrar : public SkFlattenable::Registrar {
+    public:
+        MyRegistrar();
+    };
+    
+    typedef SkDrawLooper INHERITED;
+};
+
+
+#endif
diff --git a/include/effects/SkLayerRasterizer.h b/include/effects/SkLayerRasterizer.h
new file mode 100644
index 0000000..820f6fc
--- /dev/null
+++ b/include/effects/SkLayerRasterizer.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 SkLayerRasterizer_DEFINED
+#define SkLayerRasterizer_DEFINED
+
+#include "SkRasterizer.h"
+#include "SkDeque.h"
+#include "SkScalar.h"
+
+class SkPaint;
+
+class SkLayerRasterizer : public SkRasterizer {
+public:
+            SkLayerRasterizer();
+    virtual ~SkLayerRasterizer();
+    
+    void addLayer(const SkPaint& paint)
+    {
+        this->addLayer(paint, 0, 0);
+    }
+
+	/**	Add a new layer (above any previous layers) to the rasterizer.
+		The layer will extract those fields that affect the mask from
+		the specified paint, but will not retain a reference to the paint
+		object itself, so it may be reused without danger of side-effects.
+	*/
+    void addLayer(const SkPaint& paint, SkScalar dx, SkScalar dy);
+
+    // overrides from SkFlattenable
+    virtual Factory getFactory();
+    virtual void    flatten(SkFlattenableWriteBuffer&);
+
+protected:
+    SkLayerRasterizer(SkFlattenableReadBuffer&);
+
+    // override from SkRasterizer
+    virtual bool onRasterize(const SkPath& path, const SkMatrix& matrix,
+                             const SkIRect* clipBounds,
+                             SkMask* mask, SkMask::CreateMode mode);
+
+private:
+    SkDeque fLayers;
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+
+    typedef SkRasterizer INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkPaintFlagsDrawFilter.h b/include/effects/SkPaintFlagsDrawFilter.h
new file mode 100644
index 0000000..941be24
--- /dev/null
+++ b/include/effects/SkPaintFlagsDrawFilter.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPaintFlagsDrawFilter_DEFINED
+#define SkPaintFlagsDrawFilter_DEFINED
+
+#include "SkDrawFilter.h"
+
+class SkPaintFlagsDrawFilter : public SkDrawFilter {
+public:
+    SkPaintFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags);
+    
+    // overrides
+    virtual bool filter(SkCanvas*, SkPaint*, Type);
+    virtual void restore(SkCanvas*, SkPaint*, Type);
+    
+private:
+    uint32_t    fPrevFlags;     // local cache for filter/restore
+    uint16_t    fClearFlags;    // user specified
+    uint16_t    fSetFlags;      // user specified
+};
+
+#endif
+
diff --git a/include/effects/SkPixelXorXfermode.h b/include/effects/SkPixelXorXfermode.h
new file mode 100644
index 0000000..369e0e5
--- /dev/null
+++ b/include/effects/SkPixelXorXfermode.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 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 SkPixelXorXfermode_DEFINED
+#define SkPixelXorXfermode_DEFINED
+
+#include "SkXfermode.h"
+
+/** SkPixelXorXfermode implements a simple pixel xor (op ^ src ^ dst).
+    This transformation does not follow premultiplied conventions, therefore
+    this proc *always* returns an opaque color (alpha == 255). Thus it is
+    not really usefull for operating on blended colors.
+*/
+class SkPixelXorXfermode : public SkXfermode {
+public:
+    SkPixelXorXfermode(SkColor opColor) : fOpColor(opColor) {}
+
+    // override from SkFlattenable
+    virtual Factory getFactory();
+    virtual void flatten(SkFlattenableWriteBuffer&);
+
+protected:
+    // override from SkXfermode
+    virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst);
+
+private:
+    SkColor fOpColor;
+
+    SkPixelXorXfermode(SkFlattenableReadBuffer& rb);
+    // our private factory
+    static SkFlattenable* Create(SkFlattenableReadBuffer&);
+
+    typedef SkXfermode INHERITED;
+};
+
+#endif
+
diff --git a/include/effects/SkTransparentShader.h b/include/effects/SkTransparentShader.h
new file mode 100644
index 0000000..8bc60ff
--- /dev/null
+++ b/include/effects/SkTransparentShader.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 SkTransparentShader_DEFINED
+#define SkTransparentShader_DEFINED
+
+#include "SkShader.h"
+
+class SkTransparentShader : public SkShader {
+public:
+    SkTransparentShader() {}
+    virtual uint32_t getFlags();
+    virtual bool    setContext( const SkBitmap& device,
+                                const SkPaint& paint,
+                                const SkMatrix& matrix);
+    virtual void    shadeSpan(int x, int y, SkPMColor[], int count);
+    virtual void    shadeSpan16(int x, int y, uint16_t span[], int count);
+
+    // overrides for SkFlattenable
+    virtual Factory getFactory() { return Create; }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer)
+    {
+        this->INHERITED::flatten(buffer);
+    }
+        
+private:
+    // these are a cache from the call to setContext()
+    const SkBitmap* fDevice;
+    uint8_t         fAlpha;
+
+    SkTransparentShader(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+    
+    static SkFlattenable* Create(SkFlattenableReadBuffer& buffer)
+    {
+        return SkNEW_ARGS(SkTransparentShader, (buffer));
+    }
+
+    typedef SkShader INHERITED;
+};
+
+#endif
+
diff --git a/include/images/SkFlipPixelRef.h b/include/images/SkFlipPixelRef.h
new file mode 100644
index 0000000..3b4e97a
--- /dev/null
+++ b/include/images/SkFlipPixelRef.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkFlipPixelRef_DEFINED
+#define SkFlipPixelRef_DEFINED
+
+#include "SkBitmap.h"
+#include "SkPageFlipper.h"
+#include "SkPixelRef.h"
+#include "SkThread.h"
+
+class SkRegion;
+
+class SkFlipPixelRef : public SkPixelRef {
+public:
+            SkFlipPixelRef(SkBitmap::Config, int width, int height);
+    virtual ~SkFlipPixelRef();
+    
+    bool isDirty() const { return fFlipper.isDirty(); }
+    const SkRegion& dirtyRgn() const { return fFlipper.dirtyRgn(); }
+
+    void inval() { fFlipper.inval(); }
+    void inval(const SkIRect& rect) { fFlipper.inval(rect); }
+    void inval(const SkRegion& rgn) { fFlipper.inval(rgn); }
+    void inval(const SkRect& r, bool doAA) { fFlipper.inval(r, doAA); }
+
+    const SkRegion& beginUpdate(SkBitmap* device);
+    void endUpdate();
+    
+private:
+    void getFrontBack(const void** front, void** back) const {
+        if (front) {
+            *front = fPage0;
+        }
+        if (back) {
+            *back = fPage1;
+        }
+    }
+
+    void    swapPages();
+
+    // Helper to copy pixels from srcAddr to the dst bitmap, clipped to clip.
+    // srcAddr points to memory with the same config as dst.
+    static void CopyBitsFromAddr(const SkBitmap& dst, const SkRegion& clip,
+                                 const void* srcAddr);
+
+    // serialization
+
+public:
+    virtual Factory getFactory() const { return Create; }
+    virtual void flatten(SkFlattenableWriteBuffer&) const;
+    static SkPixelRef* Create(SkFlattenableReadBuffer& buffer);
+    
+protected:
+    virtual void* onLockPixels(SkColorTable**);
+    virtual void onUnlockPixels();
+
+    SkFlipPixelRef(SkFlattenableReadBuffer&);
+
+private:
+    SkMutex         fMutex;
+    SkPageFlipper   fFlipper;
+    
+    void*           fStorage;
+    void*           fPage0; // points into fStorage;
+    void*           fPage1; // points into fStorage;
+    size_t          fSize;  // size of 1 page. fStorage holds 2 pages
+    SkBitmap::Config fConfig;
+
+    typedef SkPixelRef INHERITED;
+};
+
+class SkAutoFlipUpdate : SkNoncopyable {
+public:
+    SkAutoFlipUpdate(SkFlipPixelRef* ref) : fRef(ref) {
+        fDirty = &ref->beginUpdate(&fBitmap);
+    }
+    ~SkAutoFlipUpdate() {
+        if (fRef) {
+            fRef->endUpdate();
+        }
+    }
+    
+    const SkBitmap& bitmap() const { return fBitmap; }
+    const SkRegion& dirty() const { return *fDirty; }
+    
+    // optional. This gets automatically called in the destructor (only once)
+    void endUpdate() {
+        if (fRef) {
+            fRef->endUpdate();
+            fRef = NULL;
+        }
+    }
+
+private:
+    SkFlipPixelRef* fRef;
+    SkBitmap        fBitmap;
+    const SkRegion* fDirty;
+};
+
+#endif
diff --git a/include/images/SkImageDecoder.h b/include/images/SkImageDecoder.h
new file mode 100644
index 0000000..4a18cab
--- /dev/null
+++ b/include/images/SkImageDecoder.h
@@ -0,0 +1,301 @@
+/*
+ * 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 SkImageDecoder_DEFINED
+#define SkImageDecoder_DEFINED
+
+#include "SkBitmap.h"
+#include "SkRefCnt.h"
+
+class SkStream;
+
+/** \class SkImageDecoder
+
+    Base class for decoding compressed images into a SkBitmap
+*/
+class SkImageDecoder {
+public:
+    virtual ~SkImageDecoder();
+    
+    enum Format {
+        kUnknown_Format,
+        kBMP_Format,
+        kGIF_Format,
+        kICO_Format,
+        kJPEG_Format,
+        kPNG_Format,
+        kWBMP_Format,
+        
+        kLastKnownFormat = kWBMP_Format
+    };
+        
+    /** Return the compressed data's format (see Format enum)
+    */
+    virtual Format getFormat() const;
+
+    /** Returns true if the decoder should try to dither the resulting image.
+        The default setting is true.
+    */
+    bool getDitherImage() const { return fDitherImage; }
+    
+    /** Set to true if the the decoder should try to dither the resulting image.
+        The default setting is true.
+    */
+    void setDitherImage(bool dither) { fDitherImage = dither; }
+
+    /** \class Peeker
+    
+        Base class for optional callbacks to retrieve meta/chunk data out of
+        an image as it is being decoded.
+    */
+    class Peeker : public SkRefCnt {
+    public:
+        /** Return true to continue decoding, or false to indicate an error, which
+            will cause the decoder to not return the image.
+        */
+        virtual bool peek(const char tag[], const void* data, size_t length) = 0;
+    };
+
+    Peeker* getPeeker() const { return fPeeker; }
+    Peeker* setPeeker(Peeker*);
+    
+    /** \class Peeker
+    
+        Base class for optional callbacks to retrieve meta/chunk data out of
+        an image as it is being decoded.
+    */
+    class Chooser : public SkRefCnt {
+    public:
+        virtual void begin(int count) {}
+        virtual void inspect(int index, SkBitmap::Config config, int width, int height) {}
+        /** Return the index of the subimage you want, or -1 to choose none of them.
+        */
+        virtual int choose() = 0;
+    };
+
+    Chooser* getChooser() const { return fChooser; }
+    Chooser* setChooser(Chooser*);
+
+    SkBitmap::Allocator* getAllocator() const { return fAllocator; }
+    SkBitmap::Allocator* setAllocator(SkBitmap::Allocator*);
+
+    // sample-size, if set to > 1, tells the decoder to return a smaller than
+    // original bitmap, sampling 1 pixel for every size pixels. e.g. if sample
+    // size is set to 3, then the returned bitmap will be 1/3 as wide and high,
+    // and will contain 1/9 as many pixels as the original.
+    // Note: this is a hint, and the codec may choose to ignore this, or only
+    // approximate the sample size.
+    int getSampleSize() const { return fSampleSize; }
+    void setSampleSize(int size);
+    
+    /** Reset the sampleSize to its default of 1
+     */
+    void resetSampleSize() { this->setSampleSize(1); }
+
+    /** Decoding is synchronous, but for long decodes, a different thread can
+        call this method safely. This sets a state that the decoders will
+        periodically check, and if they see it changed to cancel, they will
+        cancel. This will result in decode() returning false. However, there is
+        no guarantee that the decoder will see the state change in time, so
+        it is possible that cancelDecode() will be called, but will be ignored
+        and decode() will return true (assuming no other problems were
+        encountered).
+     
+        This state is automatically reset at the beginning of decode().
+     */
+    void cancelDecode() {
+        // now the subclass must query shouldCancelDecode() to be informed
+        // of the request
+        fShouldCancelDecode = true;
+    }
+
+    /** Passed to the decode method. If kDecodeBounds_Mode is passed, then
+        only the bitmap's width/height/config need be set. If kDecodePixels_Mode
+        is passed, then the bitmap must have pixels or a pixelRef.
+    */
+    enum Mode {
+        kDecodeBounds_Mode, //!< only return width/height/config in bitmap
+        kDecodePixels_Mode  //!< return entire bitmap (including pixels)
+    };
+    
+    /** Given a stream, decode it into the specified bitmap.
+        If the decoder can decompress the image, it calls bitmap.setConfig(),
+        and then if the Mode is kDecodePixels_Mode, call allocPixelRef(),
+        which will allocated a pixelRef. To access the pixel memory, the codec
+        needs to call lockPixels/unlockPixels on the
+        bitmap. It can then set the pixels with the decompressed image.
+        If the image cannot be decompressed, return false.
+        
+        note: document use of Allocator, Peeker and Chooser 
+    */
+    bool decode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref, Mode);
+
+    /** Given a stream, this will try to find an appropriate decoder object.
+        If none is found, the method returns NULL.
+    */
+    static SkImageDecoder* Factory(SkStream*);
+    
+    /** Decode the image stored in the specified file, and store the result
+        in bitmap. Return true for success or false on failure.
+
+        If pref is kNo_Config, then the decoder is free to choose the most natural
+        config given the image data. If pref something other than kNo_Config,
+        the decoder will attempt to decode the image into that format, unless
+        there is a conflict (e.g. the image has per-pixel alpha and the bitmap's
+        config does not support that), in which case the decoder will choose a
+        closest match configuration.
+    */
+    static bool DecodeFile(const char file[], SkBitmap* bitmap,
+                             SkBitmap::Config prefConfig, Mode);
+    static bool DecodeFile(const char file[], SkBitmap* bitmap)
+    {
+        return DecodeFile(file, bitmap, SkBitmap::kNo_Config, kDecodePixels_Mode);
+    }
+    /** Decode the image stored in the specified memory buffer, and store the
+        result in bitmap. Return true for success or false on failure.
+
+        If pref is kNo_Config, then the decoder is free to choose the most natural
+        config given the image data. If pref something other than kNo_Config,
+        the decoder will attempt to decode the image into that format, unless
+        there is a conflict (e.g. the image has per-pixel alpha and the bitmap's
+        config does not support that), in which case the decoder will choose a
+        closest match configuration.
+    */
+    static bool DecodeMemory(const void* buffer, size_t size, SkBitmap* bitmap,
+                             SkBitmap::Config prefConfig, Mode);
+    static bool DecodeMemory(const void* buffer, size_t size, SkBitmap* bitmap)
+    {
+        return DecodeMemory(buffer, size, bitmap, SkBitmap::kNo_Config,
+                            kDecodePixels_Mode);
+    }
+    /** Decode the image stored in the specified SkStream, and store the result
+        in bitmap. Return true for success or false on failure.
+
+        If pref is kNo_Config, then the decoder is free to choose the most
+        natural config given the image data. If pref something other than
+        kNo_Config, the decoder will attempt to decode the image into that
+        format, unless there is a conflict (e.g. the image has per-pixel alpha
+        and the bitmap's config does not support that), in which case the
+        decoder will choose a closest match configuration.
+    */
+    static bool DecodeStream(SkStream* stream, SkBitmap* bitmap,
+                             SkBitmap::Config prefConfig, Mode);
+    static bool DecodeStream(SkStream* stream, SkBitmap* bitmap)
+    {
+        return DecodeStream(stream, bitmap, SkBitmap::kNo_Config,
+                            kDecodePixels_Mode);
+    }
+    
+    /*  Given a format, return true if there is a currently installed decoder
+        for that format. Since a given build may not include all codecs (to save
+        code-size), this may return false.
+    */
+    static bool SupportsFormat(Format);
+
+    /** Return the default config for the running device.
+        Currently this used as a suggestion to image decoders that need to guess
+        what config they should decode into.
+        Default is kNo_Config, but this can be changed with SetDeviceConfig()
+    */
+    static SkBitmap::Config GetDeviceConfig();
+    /** Set the default config for the running device.
+        Currently this used as a suggestion to image decoders that need to guess
+        what config they should decode into.
+        Default is kNo_Config.
+        This can be queried with GetDeviceConfig()
+    */
+    static void SetDeviceConfig(SkBitmap::Config);
+
+  /** @cond UNIT_TEST */
+    SkDEBUGCODE(static void UnitTest();)
+  /** @endcond */
+
+protected:
+    // must be overridden in subclasses. This guy is called by decode(...)
+    virtual bool onDecode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref,
+                          Mode) = 0;
+
+    /** Can be queried from within onDecode, to see if the user (possibly in
+        a different thread) has requested the decode to cancel. If this returns
+        true, your onDecode() should stop and return false.
+        Each subclass needs to decide how often it can query this, to balance
+        responsiveness with performance.
+     
+        Calling this outside of onDecode() may return undefined values.
+     */
+
+public:
+    bool shouldCancelDecode() const { return fShouldCancelDecode; }
+
+protected:    
+    SkImageDecoder();
+
+    // helper function for decoders to handle the (common) case where there is only
+    // once choice available in the image file.
+    bool chooseFromOneChoice(SkBitmap::Config config, int width, int height) const;
+
+    /*  Helper for subclasses. Call this to allocate the pixel memory given the bitmap's
+        width/height/rowbytes/config. Returns true on success. This method handles checking
+        for an optional Allocator.
+    */
+    bool allocPixelRef(SkBitmap*, SkColorTable*) const;
+
+private:
+    Peeker*                 fPeeker;
+    Chooser*                fChooser;
+    SkBitmap::Allocator*    fAllocator;
+    int                     fSampleSize;
+    bool                    fDitherImage;
+    mutable bool            fShouldCancelDecode;
+
+    // illegal
+    SkImageDecoder(const SkImageDecoder&);
+    SkImageDecoder& operator=(const SkImageDecoder&);
+};
+
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+
+class SkWStream;
+
+class SkImageEncoder {
+public:
+    enum Type {
+        kJPEG_Type,
+        kPNG_Type
+    };
+    static SkImageEncoder* Create(Type);
+
+    virtual ~SkImageEncoder();
+    
+    /*  Quality ranges from 0..100 */
+
+    bool encodeFile(const char file[], const SkBitmap&, int quality = 80);
+    bool encodeStream(SkWStream*, const SkBitmap&, int quality = 80);
+
+    static bool EncodeFile(const char file[], const SkBitmap&, Type,
+                           int quality = 80);
+    static bool EncodeStream(SkWStream*, const SkBitmap&, Type,
+                           int quality = 80);
+
+protected:
+    virtual bool onEncode(SkWStream*, const SkBitmap&, int quality) = 0;
+};
+
+#endif /* SK_SUPPORT_IMAGE_ENCODE */
+
+///////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/include/images/SkImageRef.h b/include/images/SkImageRef.h
new file mode 100644
index 0000000..e94b7d4
--- /dev/null
+++ b/include/images/SkImageRef.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkImageRef_DEFINED
+#define SkImageRef_DEFINED
+
+#include "SkPixelRef.h"
+#include "SkBitmap.h"
+#include "SkImageDecoder.h"
+#include "SkString.h"
+
+class SkImageRefPool;
+class SkStream;
+
+// define this to enable dumping whenever we add/remove/purge an imageref
+//#define DUMP_IMAGEREF_LIFECYCLE
+
+class SkImageRef : public SkPixelRef {
+public:
+    /** Create a new imageref from a stream. NOTE: the stream is not copied, but
+        since it may be accessed from another thread, the caller must ensure
+        that this imageref is the only owner of the stream. i.e. - sole
+        ownership of the stream object is transferred to this imageref object.
+    
+        @param stream The stream containing the encoded image data. Ownership
+                      of this stream is transferred to the imageref, and
+                      therefore the stream's ownercount must be 1.
+        @param config The preferred config of the decoded bitmap.
+        @param sampleSize Requested sampleSize for decoding. Defaults to 1.
+    */
+    SkImageRef(SkStream*, SkBitmap::Config config, int sampleSize = 1);
+    virtual ~SkImageRef();
+    
+    /** Return true if the image can be decoded. If so, and bitmap is non-null,
+        call its setConfig() with the corresponding values, but explicitly will
+        not set its pixels or colortable. Use SkPixelRef::lockPixels() for that.
+     
+        If there has been an error decoding the bitmap, this will return false
+        and ignore the bitmap parameter.
+    */
+    bool getInfo(SkBitmap* bm);
+
+    // overrides
+    virtual void flatten(SkFlattenableWriteBuffer&) const;
+
+protected:
+    /** Override if you want to install a custom allocator.
+        When this is called we will have already acquired the mutex!
+    */
+    virtual bool onDecode(SkImageDecoder* codec, SkStream*, SkBitmap*,
+                          SkBitmap::Config, SkImageDecoder::Mode);
+
+    /*  Overrides from SkPixelRef
+        When these are called, we will have already acquired the mutex!
+     */
+
+    virtual void* onLockPixels(SkColorTable**);
+    // override this in your subclass to clean up when we're unlocking pixels
+    virtual void onUnlockPixels();
+    
+    SkImageRef(SkFlattenableReadBuffer&);
+
+    SkBitmap fBitmap;
+
+private:    
+    SkStream* setStream(SkStream*);
+    // called with mutex already held. returns true if the bitmap is in the
+    // requested state (or further, i.e. has pixels)
+    bool prepareBitmap(SkImageDecoder::Mode);
+
+    SkStream*           fStream;
+    SkBitmap::Config    fConfig;
+    int                 fSampleSize;
+    bool                fErrorInDecoding;
+    
+    friend class SkImageRefPool;
+    
+    SkImageRef*  fPrev, *fNext;    
+    size_t ramUsed() const;
+    
+    typedef SkPixelRef INHERITED;
+};
+
+#endif
diff --git a/include/images/SkImageRef_GlobalPool.h b/include/images/SkImageRef_GlobalPool.h
new file mode 100644
index 0000000..a9d4dce
--- /dev/null
+++ b/include/images/SkImageRef_GlobalPool.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkImageRef_GlobalPool_DEFINED
+#define SkImageRef_GlobalPool_DEFINED
+
+#include "SkImageRef.h"
+
+class SkImageRef_GlobalPool : public SkImageRef {
+public:
+    // if pool is null, use the global pool
+    SkImageRef_GlobalPool(SkStream*, SkBitmap::Config, int sampleSize = 1);
+    virtual ~SkImageRef_GlobalPool();
+    
+    // overrides
+    virtual Factory getFactory() const {
+        return Create;
+    }
+    static SkPixelRef* Create(SkFlattenableReadBuffer&);
+
+    // API to control the global pool
+
+    /** Return the amount specified as the budget for the cache (in bytes).
+     */
+    static size_t GetRAMBudget();
+    
+    /** Set a new budget value for the cache.
+     */
+    static void SetRAMBudget(size_t);
+    
+    /** Return how much ram is currently in use by the global cache.
+     */
+    static size_t GetRAMUsed();
+    
+    /** Free up (approximately) enough such that the amount used by the cache
+     is <= the specified amount. Since some images may be "in use", the
+     amount actually freed may not always result in a ram usage value <=
+     to the requested amount. In addition, because of the
+     chunky nature of the cache, the resulting usage may be < the requested
+     amount.
+     */
+    static void SetRAMUsed(size_t usageInBytes);
+    
+    static void DumpPool();
+
+protected:
+    virtual bool onDecode(SkImageDecoder* codec, SkStream* stream,
+                          SkBitmap* bitmap, SkBitmap::Config config,
+                          SkImageDecoder::Mode mode);
+    
+    virtual void onUnlockPixels();
+    
+    SkImageRef_GlobalPool(SkFlattenableReadBuffer&);
+
+private:
+    typedef SkImageRef INHERITED;
+};
+
+#endif
diff --git a/include/images/SkMovie.h b/include/images/SkMovie.h
new file mode 100644
index 0000000..45962a5
--- /dev/null
+++ b/include/images/SkMovie.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkMovie_DEFINED
+#define SkMovie_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkCanvas.h"
+
+class SkStream;
+
+class SkMovie : public SkRefCnt {
+public:
+    /** Try to create a movie from the stream. If the stream format is not
+        supported, return NULL.
+    */
+    static SkMovie* DecodeStream(SkStream*);
+    /** Try to create a movie from the specified file path. If the file is not
+        found, or the format is not supported, return NULL. If a movie is
+        returned, the stream may be retained by the movie (via ref()) until
+        the movie is finished with it (by calling unref()).
+    */
+    static SkMovie* DecodeFile(const char path[]);
+    /** Try to create a movie from the specified memory.
+        If the format is not supported, return NULL. If a movie is returned,
+        the data will have been read or copied, and so the caller may free
+        it.
+    */
+    static SkMovie* DecodeMemory(const void* data, size_t length);
+
+    SkMSec  duration();
+    int     width();
+    int     height();
+    int     isOpaque();
+    
+    /** Specify the time code (between 0...duration) to sample a bitmap
+        from the movie. Returns true if this time code generated a different
+        bitmap/frame from the previous state (i.e. true means you need to
+        redraw).
+    */
+    bool setTime(SkMSec);
+
+    // return the right bitmap for the current time code
+    const SkBitmap& bitmap();
+    
+protected:
+    struct Info {
+        SkMSec  fDuration;
+        int     fWidth;
+        int     fHeight;
+        bool    fIsOpaque;
+    };
+
+    virtual bool onGetInfo(Info*) = 0;
+    virtual bool onSetTime(SkMSec) = 0;
+    virtual bool onGetBitmap(SkBitmap*) = 0;
+
+    // visible for subclasses
+    SkMovie();
+
+private:
+    Info        fInfo;
+    SkMSec      fCurrTime;
+    SkBitmap    fBitmap;
+    bool        fNeedBitmap;
+    
+    void ensureInfo();
+};
+
+#endif
diff --git a/include/images/SkPageFlipper.h b/include/images/SkPageFlipper.h
new file mode 100644
index 0000000..0d791ee
--- /dev/null
+++ b/include/images/SkPageFlipper.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPageFlipper_DEFINED
+#define SkPageFlipper_DEFINED
+
+#include "SkRegion.h"
+
+/** SkPageFlipper manages alternating inval/dirty regions for a rectangular area
+    (like a bitmap). You call inval() to accumulate inval areas, and then when
+    you're ready to "flip" pages (i.e. draw into the one you've been
+    invalidating) you call update, which swaps the inval regions, and returns
+    two things to you: 1) the final inval region to be drawn into, and 2) the
+    region of pixels that should be copied from the "front" page onto the one
+    you're about to draw into. This copyBits region will be disjoint from the
+    inval region, so both need to be handled.
+ */
+class SkPageFlipper {
+public:
+    SkPageFlipper();
+    SkPageFlipper(int width, int height);
+    
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+
+    void resize(int width, int height);
+
+    bool isDirty() const { return !fDirty1->isEmpty(); }
+    const SkRegion& dirtyRgn() const { return *fDirty1; }
+
+    void inval();
+    void inval(const SkIRect&);
+    void inval(const SkRegion&);
+    void inval(const SkRect&, bool antialias);
+
+    /** When you're ready to write to the back page, call update. The returned
+        region is the invalidate are that needs to be drawn to. The copyBits
+        region (provided by the caller) is the area that should be copied from
+        the front page to the back page (will not intersect with the returned
+        inval region.
+     
+        Once this is called, the two internal regions are swapped, so the *new*
+        back inval region is ready to receive new inval calls.
+     */
+    const SkRegion& update(SkRegion* copyBits);
+
+private:
+    SkRegion*   fDirty0;
+    SkRegion*   fDirty1;
+    SkRegion    fDirty0Storage;
+    SkRegion    fDirty1Storage;
+    int         fWidth;
+    int         fHeight;
+};
+
+#endif
+
diff --git a/include/ports/SkOSFile.h b/include/ports/SkOSFile.h
new file mode 100644
index 0000000..de8090c
--- /dev/null
+++ b/include/ports/SkOSFile.h
@@ -0,0 +1,85 @@
+/*
+ * 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 SkOSFile_DEFINED
+#define SkOSFile_DEFINED
+
+#include "SkString.h"
+
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)
+    #include <dirent.h>
+#endif
+
+struct SkFILE;
+
+enum SkFILE_Flags {
+    kRead_SkFILE_Flag   = 0x01,
+    kWrite_SkFILE_Flag  = 0x02
+};
+
+SkFILE* sk_fopen(const char path[], SkFILE_Flags);
+void    sk_fclose(SkFILE*);
+
+size_t  sk_fgetsize(SkFILE*);
+/** Return true if the file could seek back to the beginning
+*/
+bool    sk_frewind(SkFILE*);
+
+size_t  sk_fread(void* buffer, size_t byteCount, SkFILE*);
+size_t  sk_fwrite(const void* buffer, size_t byteCount, SkFILE*);
+void    sk_fflush(SkFILE*);
+
+int     sk_fseek( SkFILE*, size_t, int );
+size_t  sk_ftell( SkFILE* );
+
+class SkOSFile {
+public:
+    class Iter {
+    public:
+        Iter();
+        Iter(const char path[], const char suffix[] = NULL);
+        ~Iter();
+
+        void reset(const char path[], const char suffix[] = NULL);
+        bool next(SkString* name, bool getDir = false);
+
+    private:
+#ifdef SK_BUILD_FOR_WIN
+        HANDLE      fHandle;
+        uint16_t*   fPath16;
+#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)
+        DIR*        fDIR;
+        SkString    fPath, fSuffix;
+#endif
+    };
+};
+
+class SkUTF16_Str {
+public:
+    SkUTF16_Str(const char src[]);
+    ~SkUTF16_Str()
+    {
+        sk_free(fStr);
+    }
+    const uint16_t* get() const { return fStr; }
+
+private:
+    uint16_t*   fStr;
+};
+
+#endif
+
diff --git a/include/ports/SkStream_Win.h b/include/ports/SkStream_Win.h
new file mode 100644
index 0000000..1b17450
--- /dev/null
+++ b/include/ports/SkStream_Win.h
@@ -0,0 +1,53 @@
+/*
+ * 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 SkStream_Win_DEFINED
+#define SkStream_Win_DEFINED
+
+#ifndef SK_BUILD_FOR_WIN
+#error "only valid for windows and wince builds"
+#endif
+
+#ifndef SkStream_DEFINED
+#include "SkStream.h"
+#endif
+#include "SkString.h"
+#include "Wininet.h"
+
+/** \cond ZERO */
+class SkURLStream : public SkStream {
+public:
+    SkURLStream(const char url[] = NULL);
+    virtual ~SkURLStream();
+
+    /** Close the current URL, and open a new URL.
+        If URL is null, just close the current URL.
+    */
+    void setURL(const char url[]);
+
+    // overrides
+    virtual bool rewind();
+    virtual size_t read(void* buffer, size_t size);
+    
+private:
+    SkString fURL;
+    HINTERNET fConnection;
+    HINTERNET fURLStream;
+};
+
+/** \endcond */
+#endif // SkStream_Win_DEFINED
+
diff --git a/include/svg/SkSVGAttribute.h b/include/svg/SkSVGAttribute.h
new file mode 100644
index 0000000..810d11c
--- /dev/null
+++ b/include/svg/SkSVGAttribute.h
@@ -0,0 +1,49 @@
+/*
+ * 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 SkSVGAttribute_DEFINED
+#define SkSVGAttribute_DEFINED
+
+#include "SkTypes.h"
+
+struct SkSVGAttribute {
+    const char* fName;
+#ifdef SK_DEBUG
+    size_t fOffset;
+#endif
+};
+
+#ifndef SK_OFFSETOF
+#define SK_OFFSETOF(a, b) (((size_t) (&(((a*) 1)->b)))-1)
+#endif
+
+#ifdef SK_DEBUG
+#define SVG_ATTRIBUTE(attr) { #attr, SK_OFFSETOF(BASE_CLASS, f_##attr) }
+#define SVG_LITERAL_ATTRIBUTE(svgAttr, cAttr) { #svgAttr, SK_OFFSETOF(BASE_CLASS, cAttr) }
+#else
+#define SVG_ATTRIBUTE(attr) { #attr }
+#define SVG_LITERAL_ATTRIBUTE(svgAttr, cAttr) { #svgAttr }
+#endif
+
+#define SVG_ADD_ATTRIBUTE(attr) \
+    if (f_##attr.size() > 0) \
+        parser._addAttributeLen(#attr, f_##attr.c_str(), f_##attr.size())
+
+#define SVG_ADD_ATTRIBUTE_ALIAS(attr, alias) \
+    if (f_##alias.size() > 0) \
+        parser._addAttributeLen(#attr, f_##alias.c_str(), f_##alias.size())
+
+#endif // SkSVGAttribute_DEFINED
diff --git a/include/svg/SkSVGBase.h b/include/svg/SkSVGBase.h
new file mode 100644
index 0000000..4d24aed
--- /dev/null
+++ b/include/svg/SkSVGBase.h
@@ -0,0 +1,33 @@
+/*
+ * 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 SkSVGBase_DEFINED
+#define SkSVGBase_DEFINED
+
+#include "SkSVGAttribute.h"
+
+class SkSVGParser;
+
+class SkSVGBase {
+public:
+    virtual ~SkSVGBase();
+    virtual void addAttribute(SkSVGParser& parser, int attrIndex, 
+        const char* attrValue, size_t attrLength);
+    virtual int getAttributes(const SkSVGAttribute** attrPtr) = 0;
+};
+
+#endif // SkSVGBase_DEFINEDes(const SkSVGAttribute** attrPtr) = 0;
+
diff --git a/include/svg/SkSVGPaintState.h b/include/svg/SkSVGPaintState.h
new file mode 100644
index 0000000..f3f74e1
--- /dev/null
+++ b/include/svg/SkSVGPaintState.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 SkSVGPaintState_DEFINED
+#define SkSVGPaintState_DEFINED
+
+#include "SkSVGBase.h"
+#include "SkString.h"
+
+class SkSVGPaint : public SkSVGBase {
+public:
+    enum Field {
+        kInitial = -1,
+        kClipPath,
+        kClipRule,
+        kEnableBackground,
+        kFill,
+        kFillRule,
+        kFilter,
+        kFontFamily,
+        kFontSize,
+        kLetterSpacing,
+        kMask,
+        kOpacity,
+        kStopColor,
+        kStopOpacity,
+        kStroke,
+        kStroke_Dasharray,
+        kStroke_Linecap,
+        kStroke_Linejoin,
+        kStroke_Miterlimit,
+        kStroke_Width,
+        kStyle,
+        kTransform,
+        kTerminal
+    };
+
+    SkSVGPaint();
+    virtual void addAttribute(SkSVGParser& parser, int attrIndex, 
+        const char* attrValue, size_t attrLength);
+    bool flush(SkSVGParser& , bool isFlushable, bool isDef);
+    virtual int getAttributes(const SkSVGAttribute** attrPtr); 
+    static void Push(SkSVGPaint** head, SkSVGPaint* add);
+    static void Pop(SkSVGPaint** head);
+    SkString* operator[](int index);
+    SkString fInitial;
+    SkString f_clipPath;
+    SkString f_clipRule;
+    SkString f_enableBackground;
+    SkString f_fill;
+    SkString f_fillRule;
+    SkString f_filter;
+    SkString f_fontFamily;
+    SkString f_fontSize;
+    SkString f_letterSpacing;
+    SkString f_mask;
+    SkString f_opacity;
+    SkString f_stopColor;
+    SkString f_stopOpacity;
+    SkString f_stroke;
+    SkString f_strokeDasharray;
+    SkString f_strokeLinecap;
+    SkString f_strokeLinejoin;
+    SkString f_strokeMiterlimit;
+    SkString f_strokeWidth;
+    SkString f_style; // unused, but allows array access to the rest
+    SkString f_transform;
+#ifdef SK_DEBUG
+    SkString fTerminal;
+#endif
+    SkString fTransformID;
+    static SkSVGAttribute gAttributes[];
+    static const int kAttributesSize;
+private:
+    void setSave(SkSVGParser& );
+    bool writeChangedAttributes(SkSVGParser& , SkSVGPaint& , bool* changed);
+    bool writeChangedElements(SkSVGParser& , SkSVGPaint& , bool* changed);
+    SkSVGPaint* fNext;
+    friend class SkSVGParser;
+    typedef SkSVGPaint BASE_CLASS;
+};
+
+#endif // SkSVGPaintState_DEFINED
diff --git a/include/svg/SkSVGParser.h b/include/svg/SkSVGParser.h
new file mode 100644
index 0000000..86ae4c2
--- /dev/null
+++ b/include/svg/SkSVGParser.h
@@ -0,0 +1,81 @@
+/*
+ * 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 SkSVGParser_DEFINED
+#define SkSVGParser_DEFINED
+
+#include "SkMatrix.h"
+#include "SkTDict.h"
+#include "SkTDStack.h"
+#include "SkSVGPaintState.h"
+#include "SkSVGTypes.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkXMLParser.h"
+#include "SkXMLWriter.h"
+
+class SkSVGBase;
+class SkSVGElement;
+
+class SkSVGParser : public SkXMLParser {
+public:
+    SkSVGParser();
+    virtual ~SkSVGParser();
+    void _addAttribute(const char* attrName, const char* attrValue) {
+        fXMLWriter.addAttribute(attrName, attrValue); }
+    void _addAttribute(const char* attrName, SkString& attrValue) {
+        fXMLWriter.addAttribute(attrName, attrValue.c_str()); }
+    void _addAttributeLen(const char* attrName, const char* attrValue, size_t len) {
+        fXMLWriter.addAttributeLen(attrName, attrValue, len); }
+    void _endElement() { fXMLWriter.endElement(); }
+    int findAttribute(SkSVGBase* , const char* attrValue, size_t len, bool isPaint);
+    const char* getFinal();
+    SkTDict<SkSVGElement*>& getIDs() { return fIDs; }
+    SkString& getPaintLast(SkSVGPaint::Field field);
+    void _startElement(const char name[]) { fXMLWriter.startElement(name); }
+    void translate(SkSVGElement*, bool isDef);
+    void translateMatrix(SkString& , SkString* id);
+    static void ConvertToArray(SkString& vals);
+protected:
+    virtual bool onAddAttribute(const char name[], const char value[]);
+    bool onAddAttributeLen(const char name[], const char value[], size_t len);
+    virtual bool onEndElement(const char elem[]);
+    virtual bool onStartElement(const char elem[]);
+    bool onStartElementLen(const char elem[], size_t len);
+    virtual bool onText(const char text[], int len);
+private:
+    bool isStrokeAndFill(SkSVGPaint** stroke, SkSVGPaint** fill);
+    static SkSVGElement* CreateElement(SkSVGTypes type, SkSVGElement* parent);
+    static void Delete(SkTDArray<SkSVGElement*>& fChildren);
+    static SkSVGTypes GetType(const char name[], size_t len);
+    SkSVGPaint* fHead;
+    SkSVGPaint fEmptyPaint; 
+    SkSVGPaint fLastFlush;
+    SkString fLastColor;
+    SkMatrix fLastTransform;
+    SkTDArray<SkSVGElement*> fChildren;
+    SkTDict<SkSVGElement*> fIDs;
+    SkTDArray<SkSVGElement*> fParents;
+    SkDynamicMemoryWStream fStream;
+    SkXMLStreamWriter fXMLWriter;
+    SkSVGElement*   fCurrElement;
+    SkBool8 fInSVG;
+    SkBool8 fSuppressPaint;
+    friend class SkSVGPaint;
+    friend class SkSVGGradient;
+};
+
+#endif // SkSVGParser_DEFINED
diff --git a/include/svg/SkSVGTypes.h b/include/svg/SkSVGTypes.h
new file mode 100644
index 0000000..38d63ce
--- /dev/null
+++ b/include/svg/SkSVGTypes.h
@@ -0,0 +1,46 @@
+/*
+ * 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 SkSVGTypes_DEFINED
+#define SkSVGTypes_DEFINED
+
+enum SkSVGTypes {
+    SkSVGType_Circle,
+    SkSVGType_ClipPath,
+    SkSVGType_Defs,
+    SkSVGType_Ellipse,
+    SkSVGType_FeColorMatrix,
+    SkSVGType_Filter,
+    SkSVGType_G,
+    SkSVGType_Image,
+    SkSVGType_Line,
+    SkSVGType_LinearGradient,
+    SkSVGType_Mask,
+    SkSVGType_Metadata,
+    SkSVGType_Path,
+    SkSVGType_Polygon,
+    SkSVGType_Polyline,
+    SkSVGType_RadialGradient,
+    SkSVGType_Rect,
+    SkSVGType_SVG,
+    SkSVGType_Stop,
+    SkSVGType_Symbol,
+    SkSVGType_Text,
+    SkSVGType_Tspan,
+    SkSVGType_Use
+};
+
+#endif // SkSVGTypes_DEFINED
diff --git a/include/utils/SkCamera.h b/include/utils/SkCamera.h
new file mode 100644
index 0000000..8bbcabf
--- /dev/null
+++ b/include/utils/SkCamera.h
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+
+
+//  Inspired by Rob Johnson's most excellent QuickDraw GX sample code
+
+#ifndef SkCamera_DEFINED
+#define SkCamera_DEFINED
+
+#include "Sk64.h"
+#include "SkMatrix.h"
+
+class SkCanvas;
+
+#ifdef SK_SCALAR_IS_FIXED
+    typedef SkFract SkUnitScalar;
+    #define SK_UnitScalar1          SK_Fract1
+    #define SkUnitScalarMul(a, b)   SkFractMul(a, b)
+    #define SkUnitScalarDiv(a, b)   SkFractDiv(a, b)
+#else
+    typedef float   SkUnitScalar;
+    #define SK_UnitScalar1          SK_Scalar1
+    #define SkUnitScalarMul(a, b)   SkScalarMul(a, b)
+    #define SkUnitScalarDiv(a, b)   SkScalarDiv(a, b)
+#endif
+
+struct SkUnit3D {
+    SkUnitScalar    fX, fY, fZ;
+
+    void set(SkUnitScalar x, SkUnitScalar y, SkUnitScalar z)
+    {
+        fX = x; fY = y; fZ = z;
+    }
+    static SkUnitScalar Dot(const SkUnit3D&, const SkUnit3D&);
+    static void Cross(const SkUnit3D&, const SkUnit3D&, SkUnit3D* cross);
+};
+
+struct SkPoint3D {
+    SkScalar    fX, fY, fZ;
+
+    void set(SkScalar x, SkScalar y, SkScalar z)
+    {
+        fX = x; fY = y; fZ = z;
+    }
+    SkScalar    normalize(SkUnit3D*) const;
+};
+typedef SkPoint3D SkVector3D;
+
+struct SkMatrix3D {
+    SkScalar    fMat[3][4];
+    
+    void reset();
+
+    void setRow(int row, SkScalar a, SkScalar b, SkScalar c, SkScalar d = 0)
+    {
+        SkASSERT((unsigned)row < 3);
+        fMat[row][0] = a;
+        fMat[row][1] = b;
+        fMat[row][2] = c;
+        fMat[row][3] = d;
+    }
+
+    void setRotateX(SkScalar deg);
+    void setRotateY(SkScalar deg);
+    void setRotateZ(SkScalar deg);
+    void setTranslate(SkScalar x, SkScalar y, SkScalar z);
+    
+    void preRotateX(SkScalar deg);
+    void preRotateY(SkScalar deg);
+    void preRotateZ(SkScalar deg);
+    void preTranslate(SkScalar x, SkScalar y, SkScalar z);
+
+    void setConcat(const SkMatrix3D& a, const SkMatrix3D& b);
+    void mapPoint(const SkPoint3D& src, SkPoint3D* dst) const;
+    void mapVector(const SkVector3D& src, SkVector3D* dst) const;
+
+    void mapPoint(SkPoint3D* v) const
+    {
+        this->mapPoint(*v, v);
+    }
+    void mapVector(SkVector3D* v) const
+    {
+        this->mapVector(*v, v);
+    }
+};
+
+class SkPatch3D {
+public:
+    SkPatch3D();
+
+    void    reset();
+    void    transform(const SkMatrix3D&, SkPatch3D* dst = NULL) const;
+
+    // dot a unit vector with the patch's normal
+    SkScalar dotWith(SkScalar dx, SkScalar dy, SkScalar dz) const;
+    SkScalar dotWith(const SkVector3D& v) const
+    {
+        return this->dotWith(v.fX, v.fY, v.fZ);
+    }
+
+    // depreicated, but still here for animator (for now)
+    void rotate(SkScalar x, SkScalar y, SkScalar z) {}
+    void rotateDegrees(SkScalar x, SkScalar y, SkScalar z) {}
+
+private:
+public: // make public for SkDraw3D for now
+    SkVector3D  fU, fV;
+    SkPoint3D   fOrigin;
+    
+    friend class SkCamera3D;
+};
+
+class SkCamera3D {
+public:
+    SkCamera3D();
+
+    void reset();
+    void update();
+    void patchToMatrix(const SkPatch3D&, SkMatrix* matrix) const;
+
+    SkPoint3D   fLocation;
+    SkPoint3D   fAxis;
+    SkPoint3D   fZenith;
+    SkPoint3D   fObserver;
+
+private:
+    mutable SkMatrix    fOrientation;
+    mutable bool        fNeedToUpdate;
+
+    void doUpdate() const;
+};
+
+class Sk3DView : SkNoncopyable {
+public:
+    Sk3DView();
+    ~Sk3DView();
+
+    void save();
+    void restore();
+
+    void translate(SkScalar x, SkScalar y, SkScalar z);
+    void rotateX(SkScalar deg);
+    void rotateY(SkScalar deg);
+    void rotateZ(SkScalar deg);
+
+    void getMatrix(SkMatrix*) const;
+    void applyToCanvas(SkCanvas*) const;
+
+    SkScalar dotWithNormal(SkScalar dx, SkScalar dy, SkScalar dz) const;
+    
+private:
+    struct Rec {
+        Rec*        fNext;
+        SkMatrix3D  fMatrix;
+    };
+    Rec*        fRec;
+    Rec         fInitialRec;
+    SkCamera3D  fCamera;
+};
+
+#endif
+
diff --git a/include/utils/SkCullPoints.h b/include/utils/SkCullPoints.h
new file mode 100644
index 0000000..cee64e2
--- /dev/null
+++ b/include/utils/SkCullPoints.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 SkCullPoints_DEFINED
+#define SkCullPoints_DEFINED
+
+#include "SkRect.h"
+
+class SkCullPoints {
+public:
+    SkCullPoints();
+    SkCullPoints(const SkIRect& r);
+    
+    void    reset(const SkIRect& r);
+    
+    /** Start a contour at (x,y). Follow this with call(s) to lineTo(...)
+    */
+    void    moveTo(int x, int y);
+    
+    enum LineToResult {
+        kNo_Result,             //!< line segment was completely clipped out
+        kLineTo_Result,         //!< path.lineTo(pts[1]);
+        kMoveToLineTo_Result    //!< path.moveTo(pts[0]); path.lineTo(pts[1]);
+    };
+    /** Connect a line to the previous call to lineTo (or moveTo).
+    */
+    LineToResult lineTo(int x, int y, SkIPoint pts[2]);
+
+private:
+    SkIRect      fR;             // the caller's rectangle
+    SkIPoint     fAsQuad[4];     // cache of fR as 4 points
+    SkIPoint     fPrevPt;        // private state
+    LineToResult fPrevResult;   // private state
+    
+    bool sect_test(int x0, int y0, int x1, int y1) const;
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+
+class SkPath;
+
+/** \class SkCullPointsPath
+
+    Similar to SkCullPoints, but this class handles the return values
+    from lineTo, and automatically builds a SkPath with the result(s).
+*/
+class SkCullPointsPath {
+public:
+    SkCullPointsPath();
+    SkCullPointsPath(const SkIRect& r, SkPath* dst);
+
+    void reset(const SkIRect& r, SkPath* dst);
+    
+    void    moveTo(int x, int y);
+    void    lineTo(int x, int y);
+
+private:
+    SkCullPoints    fCP;
+    SkPath*         fPath;
+};
+
+#endif
diff --git a/include/utils/SkDumpCanvas.h b/include/utils/SkDumpCanvas.h
new file mode 100644
index 0000000..b919627
--- /dev/null
+++ b/include/utils/SkDumpCanvas.h
@@ -0,0 +1,139 @@
+#ifndef SkDumpCanvas_DEFINED
+#define SkDumpCanvas_DEFINED
+
+#include "SkCanvas.h"
+
+/** This class overrides all the draw methods on SkCanvas, and formats them
+    as text, and then sends that to a Dumper helper object.
+ 
+    Typical use might be to dump a display list to a log file to see what is
+    being drawn.
+ */
+class SkDumpCanvas : public SkCanvas {
+public:
+    class Dumper;
+
+    explicit SkDumpCanvas(Dumper* = 0);
+    virtual ~SkDumpCanvas();
+    
+    enum Verb {
+        kNULL_Verb,
+
+        kSave_Verb,
+        kRestore_Verb,
+        
+        kMatrix_Verb,
+        
+        kClip_Verb,
+        
+        kDrawPaint_Verb,
+        kDrawPoints_Verb,
+        kDrawRect_Verb,
+        kDrawPath_Verb,
+        kDrawBitmap_Verb,
+        kDrawText_Verb,
+        kDrawPicture_Verb,
+        kDrawVertices_Verb
+    };
+    
+    /** Subclasses of this are installed on the DumpCanvas, and then called for
+        each drawing command.
+     */
+    class Dumper : public SkRefCnt {
+    public:
+        virtual void dump(SkDumpCanvas*, SkDumpCanvas::Verb, const char str[],
+                          const SkPaint*) = 0;
+    };
+        
+    Dumper* getDumper() const { return fDumper; }
+    void    setDumper(Dumper*);
+    
+    // overrides from SkCanvas
+
+    virtual int save(SaveFlags flags = kMatrixClip_SaveFlag);
+    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags flags = kARGB_ClipLayer_SaveFlag);
+    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 = SkRegion::kIntersect_Op);
+    virtual bool clipPath(const SkPath& path,
+                          SkRegion::Op op = SkRegion::kIntersect_Op);
+    virtual bool clipRegion(const SkRegion& deviceRgn,
+                            SkRegion::Op op = SkRegion::kIntersect_Op);
+
+    virtual void drawPaint(const SkPaint& paint);
+    virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                            const SkPaint& paint);
+    virtual void drawRect(const SkRect& rect, const SkPaint& paint);
+    virtual void drawPath(const SkPath& path, const SkPaint& paint);
+    virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+                            const SkPaint* paint = NULL);
+    virtual void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                                const SkRect& dst, const SkPaint* paint = NULL);
+    virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                  const SkPaint* paint = NULL);
+    virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+                            const SkPaint* paint = NULL);
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+                          SkScalar y, const SkPaint& paint);
+    virtual void drawPosText(const void* text, size_t byteLength,
+                             const SkPoint pos[], const SkPaint& paint);
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                              const SkScalar xpos[], SkScalar constY,
+                              const SkPaint& paint);
+    virtual void drawTextOnPath(const void* text, size_t byteLength,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint);
+    virtual void drawPicture(SkPicture& picture);
+    virtual void drawVertices(VertexMode vmode, int vertexCount,
+                              const SkPoint vertices[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint);
+
+private:
+    Dumper* fDumper;
+    
+    void dump(Verb, const SkPaint*, const char format[], ...);
+
+    typedef SkCanvas INHERITED;
+};
+
+/** Formats the draw commands, and send them to a function-pointer provided
+    by the caller.
+ */
+class SkFormatDumper : public SkDumpCanvas::Dumper {
+public:
+    SkFormatDumper(void (*)(const char text[], void* refcon), void* refcon);
+    
+    // override from baseclass that does the formatting, and in turn calls
+    // the function pointer that was passed to the constructor
+    virtual void dump(SkDumpCanvas*, SkDumpCanvas::Verb, const char str[],
+                      const SkPaint*);
+    
+private:
+    void (*fProc)(const char*, void*);
+    void* fRefcon;
+    
+    typedef SkDumpCanvas::Dumper INHERITED;
+};
+
+/** Subclass of Dumper that dumps the drawing command to SkDebugf
+ */
+class SkDebugfDumper : public SkFormatDumper {
+public:
+    SkDebugfDumper();
+
+private:
+    typedef SkFormatDumper INHERITED;
+};
+
+#endif
diff --git a/include/utils/SkGLCanvas.h b/include/utils/SkGLCanvas.h
new file mode 100644
index 0000000..40fc52d
--- /dev/null
+++ b/include/utils/SkGLCanvas.h
@@ -0,0 +1,80 @@
+/*
+ * 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 SkGLCanvas_DEFINED
+#define SkGLCanvas_DEFINED
+
+#include "SkCanvas.h"
+
+#ifdef SK_BUILD_FOR_MAC
+    #include <OpenGL/gl.h>
+#elif defined(ANDROID)
+    #include <GLES/gl.h>
+#endif
+
+class SkGLDevice;
+class SkGLClipIter;
+
+class SkGLCanvas : public SkCanvas {
+public:
+    // notice, we do NOT allow the SkCanvas(bitmap) constructor, since that
+    // does not create a SkGLDevice, which we require
+    SkGLCanvas();
+    virtual ~SkGLCanvas();
+
+    // overrides from SkCanvas
+
+    virtual bool getViewport(SkIPoint*) const;
+    virtual bool setViewport(int width, int height);
+
+    virtual SkDevice* createDevice(SkBitmap::Config, int width, int height,
+                                   bool isOpaque, bool isForLayer);
+
+    // settings for the global texture cache
+
+    static size_t GetTextureCacheMaxCount();
+    static void SetTextureCacheMaxCount(size_t count);
+
+    static size_t GetTextureCacheMaxSize();
+    static void SetTextureCacheMaxSize(size_t size);
+
+    /** Call glDeleteTextures for all textures (including those for text)
+        This should be called while the gl-context is still valid. Its purpose
+        is to free up gl resources. Note that if a bitmap or text is drawn after
+        this call, new caches will be created.
+    */
+    static void DeleteAllTextures();
+
+    /** Forget all textures without calling delete (including those for text).
+        This should be called if the gl-context has changed, and the texture
+        IDs that have been cached are no longer valid.
+    */
+    static void AbandonAllTextures();
+
+private:
+    SkIPoint fViewportSize;
+
+    // need to disallow this guy
+    virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap) {
+        sk_throw();
+        return NULL;
+    }
+
+    typedef SkCanvas INHERITED;
+};
+
+#endif
+
diff --git a/include/utils/SkInterpolator.h b/include/utils/SkInterpolator.h
new file mode 100644
index 0000000..c03eb23
--- /dev/null
+++ b/include/utils/SkInterpolator.h
@@ -0,0 +1,139 @@
+/*
+ * 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 SkInterpolator_DEFINED
+#define SkInterpolator_DEFINED
+
+#include "SkScalar.h"
+
+class SkInterpolatorBase : SkNoncopyable {
+public:
+    enum Result {
+        kNormal_Result,
+        kFreezeStart_Result,
+        kFreezeEnd_Result
+    };
+protected:
+    SkInterpolatorBase();
+    ~SkInterpolatorBase();
+public:
+    void    reset(int elemCount, int frameCount);
+
+    /** Return the start and end time for this interpolator.
+        If there are no key frames, return false.
+        @param startTime If not null, returns the time (in milliseconds) of the
+                         first keyframe. If there are no keyframes, this param
+                         is ignored (left unchanged).
+        @param endTime If not null, returns the time (in milliseconds) of the
+                       last keyframe. If there are no keyframes, this parameter
+                       is ignored (left unchanged).
+        @return True if there are key frames, or false if there are none.
+    */
+    bool    getDuration(SkMSec* startTime, SkMSec* endTime) const;
+
+
+    /** Set the whether the repeat is mirrored.
+        @param mirror If true, the odd repeats interpolate from the last key
+                      frame and the first.
+    */
+    void setMirror(bool mirror) {
+        fFlags = SkToU8((fFlags & ~kMirror) | (int)mirror);
+    }
+
+    /** Set the repeat count. The repeat count may be fractional.
+        @param repeatCount Multiplies the total time by this scalar.
+    */
+    void    setRepeatCount(SkScalar repeatCount) { fRepeat = repeatCount; }
+
+    /** Set the whether the repeat is mirrored.
+        @param reset If true, the odd repeats interpolate from the last key
+                     frame and the first.
+    */
+    void setReset(bool reset) {
+        fFlags = SkToU8((fFlags & ~kReset) | (int)reset);
+    }
+
+    Result  timeToT(SkMSec time, SkScalar* T, int* index, SkBool* exact) const;
+
+protected:
+    enum Flags {
+        kMirror = 1,
+        kReset = 2,
+        kHasBlend = 4
+    };
+    static SkScalar ComputeRelativeT(SkMSec time, SkMSec prevTime,
+                             SkMSec nextTime, const SkScalar blend[4] = NULL);
+    int16_t fFrameCount;
+    uint8_t fElemCount;
+    uint8_t fFlags;
+    SkScalar fRepeat;
+    struct SkTimeCode {
+        SkMSec  fTime;
+        SkScalar fBlend[4];
+    };
+    SkTimeCode* fTimes;     // pointer into fStorage
+    void* fStorage;
+#ifdef SK_DEBUG
+    SkTimeCode(* fTimesArray)[10];
+#endif
+};
+
+class SkInterpolator : public SkInterpolatorBase {
+public:
+    SkInterpolator();
+    SkInterpolator(int elemCount, int frameCount);
+    void    reset(int elemCount, int frameCount);
+
+    /** Add or replace a key frame, copying the values[] data into the
+        interpolator.
+        @param index    The index of this frame (frames must be ordered by time)
+        @param time The millisecond time for this frame
+        @param values   The array of values [elemCount] for this frame. The data
+                        is copied into the interpolator.
+        @param blend    A positive scalar specifying how to blend between this
+                        and the next key frame. [0...1) is a cubic lag/log/lag
+                        blend (slow to change at the beginning and end)
+                        1 is a linear blend (default)
+    */
+    bool setKeyFrame(int index, SkMSec time, const SkScalar values[],
+                     const SkScalar blend[4] = NULL);
+
+    /** Return the computed values given the specified time. Return whether
+        those values are the result of pinning to either the first
+        (kFreezeStart) or last (kFreezeEnd), or from interpolated the two
+        nearest key values (kNormal).
+        @param time The time to sample (in milliseconds)
+        @param (may be null) where to write the computed values.
+    */
+    Result timeToValues(SkMSec time, SkScalar values[] = NULL) const;
+
+    SkDEBUGCODE(static void UnitTest();)
+private:
+    SkScalar* fValues;  // pointer into fStorage
+#ifdef SK_DEBUG
+    SkScalar(* fScalarsArray)[10];
+#endif
+    typedef SkInterpolatorBase INHERITED;
+};
+
+/** Given all the parameters are [0...1], apply the cubic specified by (0,0)
+    (bx,by) (cx,cy) (1,1) to value, returning the answer, also [0...1].
+*/
+SkScalar SkUnitCubicInterp(SkScalar value, SkScalar bx, SkScalar by,
+                           SkScalar cx, SkScalar cy);
+
+#endif
+
diff --git a/include/utils/SkNinePatch.h b/include/utils/SkNinePatch.h
new file mode 100644
index 0000000..a655621
--- /dev/null
+++ b/include/utils/SkNinePatch.h
@@ -0,0 +1,40 @@
+/*
+ * 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 SkNinePatch_DEFINED
+#define SkNinePatch_DEFINED
+
+#include "SkRect.h"
+#include "SkRegion.h"
+
+class SkBitmap;
+class SkCanvas;
+class SkPaint;
+
+class SkNinePatch {
+public:
+    static void DrawNine(SkCanvas* canvas, const SkRect& dst,
+                     const SkBitmap& bitmap, const SkIRect& margins,
+                     const SkPaint* paint = NULL);
+    
+    static void DrawMesh(SkCanvas* canvas, const SkRect& dst,
+                         const SkBitmap& bitmap,
+                         const int32_t xDivs[], int numXDivs,
+                         const int32_t yDivs[], int numYDivs,
+                         const SkPaint* paint = NULL);
+};
+
+#endif
diff --git a/include/utils/SkParse.h b/include/utils/SkParse.h
new file mode 100644
index 0000000..57d040c
--- /dev/null
+++ b/include/utils/SkParse.h
@@ -0,0 +1,45 @@
+/*
+ * 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 SkParse_DEFINED
+#define SkParse_DEFINED
+
+#include "SkColor.h"
+#include "SkMath.h"
+
+class SkParse {
+public:
+    static int Count(const char str[]); // number of scalars or int values
+    static int Count(const char str[], char separator);
+    static const char* FindColor(const char str[], SkColor* value);
+    static const char* FindHex(const char str[], uint32_t* value);
+    static const char* FindMSec(const char str[], SkMSec* value);
+    static const char* FindNamedColor(const char str[], size_t len, SkColor* color);
+    static const char* FindS32(const char str[], int32_t* value);
+    static const char* FindScalar(const char str[], SkScalar* value);
+    static const char* FindScalars(const char str[], SkScalar value[], int count);
+
+    static bool FindBool(const char str[], bool* value);
+    // return the index of str in list[], or -1 if not found
+    static int  FindList(const char str[], const char list[]);
+#ifdef SK_SUPPORT_UNITTEST
+    static void TestColor();
+    static void UnitTest();
+#endif
+};
+
+#endif
+
diff --git a/include/utils/SkParsePaint.h b/include/utils/SkParsePaint.h
new file mode 100644
index 0000000..d23cd92
--- /dev/null
+++ b/include/utils/SkParsePaint.h
@@ -0,0 +1,34 @@
+/*
+ * 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 SkParsePaint_DEFINED
+#define SkParsePaint_DEFINED
+
+#include "SkPaint.h"
+#include "SkDOM.h"
+
+/** "color"             color
+    "opacity"           scalar  [0..1]
+    "stroke-width"      scalar  (0...inf)
+    "text-size"         scalar  (0..inf)
+    "is-stroke"         bool
+    "is-antialias"      bool
+    "is-lineartext"     bool
+*/
+void SkPaint_Inflate(SkPaint*, const SkDOM&, const SkDOM::Node*);
+
+#endif
+
diff --git a/include/utils/SkProxyCanvas.h b/include/utils/SkProxyCanvas.h
new file mode 100644
index 0000000..1bc411a
--- /dev/null
+++ b/include/utils/SkProxyCanvas.h
@@ -0,0 +1,90 @@
+#ifndef SkProxyCanvas_DEFINED
+#define SkProxyCanvas_DEFINED
+
+#include "SkCanvas.h"
+
+/** This class overrides all virtual methods on SkCanvas, and redirects them
+    to a "proxy", another SkCanvas instance. It can be the basis for
+    intercepting (and possibly modifying) calls to a canvas.
+ 
+    There must be a proxy installed before the proxycanvas can be used (i.e.
+    before its virtual methods can be called).
+ */
+class SkProxyCanvas : public SkCanvas {
+public:
+    SkProxyCanvas() : fProxy(NULL) {}
+    SkProxyCanvas(SkCanvas* proxy);
+    virtual ~SkProxyCanvas();
+    
+    SkCanvas*   getProxy() const { return fProxy; }
+    void        setProxy(SkCanvas* proxy);
+    
+    // overrides from SkCanvas
+
+    virtual bool getViewport(SkIPoint* size) const;
+    virtual bool setViewport(int x, int y);
+    
+    virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap);
+
+    virtual int save(SaveFlags flags = kMatrixClip_SaveFlag);
+    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags flags = kARGB_ClipLayer_SaveFlag);
+    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 = SkRegion::kIntersect_Op);
+    virtual bool clipPath(const SkPath& path,
+                          SkRegion::Op op = SkRegion::kIntersect_Op);
+    virtual bool clipRegion(const SkRegion& deviceRgn,
+                            SkRegion::Op op = SkRegion::kIntersect_Op);
+
+    virtual void drawPaint(const SkPaint& paint);
+    virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                            const SkPaint& paint);
+    virtual void drawRect(const SkRect& rect, const SkPaint& paint);
+    virtual void drawPath(const SkPath& path, const SkPaint& paint);
+    virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+                            const SkPaint* paint = NULL);
+    virtual void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                                const SkRect& dst, const SkPaint* paint = NULL);
+    virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                  const SkPaint* paint = NULL);
+    virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+                            const SkPaint* paint = NULL);
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+                          SkScalar y, const SkPaint& paint);
+    virtual void drawPosText(const void* text, size_t byteLength,
+                             const SkPoint pos[], const SkPaint& paint);
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                              const SkScalar xpos[], SkScalar constY,
+                              const SkPaint& paint);
+    virtual void drawTextOnPath(const void* text, size_t byteLength,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint);
+    virtual void drawPicture(SkPicture& picture);
+    virtual void drawVertices(VertexMode vmode, int vertexCount,
+                              const SkPoint vertices[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint);
+    
+    virtual SkBounder* setBounder(SkBounder* bounder);
+    virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter);
+
+    virtual SkDevice* createDevice(SkBitmap::Config, int width, int height,
+                                   bool isOpaque, bool isForLayer);
+
+private:
+    SkCanvas*   fProxy;
+    
+    typedef SkCanvas INHERITED;
+};
+
+#endif
diff --git a/include/utils/SkTextBox.h b/include/utils/SkTextBox.h
new file mode 100644
index 0000000..2c34448
--- /dev/null
+++ b/include/utils/SkTextBox.h
@@ -0,0 +1,77 @@
+/*
+ * 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 SkTextBox_DEFINED
+#define SkTextBox_DEFINED
+
+#include "SkCanvas.h"
+
+/** \class SkTextBox
+
+    SkTextBox is a helper class for drawing 1 or more lines of text
+    within a rectangle. The textbox is positioned and clipped by its Frame.
+    The Margin rectangle controls where the text is drawn relative to
+    the Frame. Line-breaks occur inside the Margin rectangle.
+
+    Spacing is a linear equation used to compute the distance between lines
+    of text. Spacing consists of two scalars: mul and add, and the spacing
+    between lines is computed as: spacing = paint.getTextSize() * mul + add
+*/
+class SkTextBox {
+public:
+    SkTextBox();
+
+    enum Mode {
+        kOneLine_Mode,
+        kLineBreak_Mode,
+
+        kModeCount
+    };
+    Mode    getMode() const { return (Mode)fMode; }
+    void    setMode(Mode);
+
+    enum SpacingAlign {
+        kStart_SpacingAlign,
+        kCenter_SpacingAlign,
+        kEnd_SpacingAlign,
+
+        kSpacingAlignCount
+    };
+    SpacingAlign    getSpacingAlign() const { return (SpacingAlign)fSpacingAlign; }
+    void            setSpacingAlign(SpacingAlign);
+
+    void    getBox(SkRect*) const;
+    void    setBox(const SkRect&);
+    void    setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
+
+    void    getSpacing(SkScalar* mul, SkScalar* add) const;
+    void    setSpacing(SkScalar mul, SkScalar add);
+
+    void    draw(SkCanvas*, const char text[], size_t len, const SkPaint&);
+
+private:
+    SkRect      fBox;
+    SkScalar    fSpacingMul, fSpacingAdd;
+    uint8_t     fMode, fSpacingAlign;
+};
+
+class SkTextLineBreaker {
+public:
+    static int CountLines(const char text[], size_t len, const SkPaint&, SkScalar width);
+};
+
+#endif
+
diff --git a/include/utils/SkUnitMappers.h b/include/utils/SkUnitMappers.h
new file mode 100644
index 0000000..51708b6
--- /dev/null
+++ b/include/utils/SkUnitMappers.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkUnitMappers_DEFINED
+#define SkUnitMappers_DEFINED
+
+#include "SkUnitMapper.h"
+
+/** This discretizes the range [0...1) into N discret values.
+*/
+class SkDiscreteMapper : public SkUnitMapper {
+public:
+    SkDiscreteMapper(int segments);
+    // override from SkUnitMapper
+    virtual uint16_t mapUnit16(uint16_t x);
+
+protected:
+    SkDiscreteMapper(SkFlattenableReadBuffer& );
+    // overrides from SkFlattenable
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory();
+private:
+    int     fSegments;
+    SkFract fScale;    // computed from fSegments
+
+    static SkFlattenable* Create(SkFlattenableReadBuffer& buffer);
+    
+    typedef SkUnitMapper INHERITED;
+};
+
+/** This returns cos(x), to simulate lighting a sphere, where 0 maps to the
+    center of the sphere, and 1 maps to the edge.
+*/
+class SkCosineMapper : public SkUnitMapper {
+public:
+    SkCosineMapper() {}
+    // override from SkUnitMapper
+    virtual uint16_t mapUnit16(uint16_t x);
+
+protected:
+    SkCosineMapper(SkFlattenableReadBuffer&);
+    // overrides from SkFlattenable
+    virtual Factory getFactory();
+
+private:
+    static SkFlattenable* Create(SkFlattenableReadBuffer&);
+
+    typedef SkUnitMapper INHERITED;
+};
+
+#endif
+
diff --git a/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..2b43f34
--- /dev/null
+++ b/include/views/SkEvent.h
@@ -0,0 +1,244 @@
+/*
+ * 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"
+
+//class SkOSWindow;
+
+/** 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); }
+
+    /** 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); }
+
+    /** 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); }
+
+    /** 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();
+
+    ////////////////////////////////////////////////////
+    /** 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..157abb1
--- /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;
+struct 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/SkMetaData.h b/include/views/SkMetaData.h
new file mode 100644
index 0000000..21739fe
--- /dev/null
+++ b/include/views/SkMetaData.h
@@ -0,0 +1,154 @@
+/*
+ * 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 SkMetaData_DEFINED
+#define SkMetaData_DEFINED
+
+#include "SkScalar.h"
+
+class SkMetaData {
+public:
+    SkMetaData();
+    SkMetaData(const SkMetaData& src);
+    ~SkMetaData();
+
+    SkMetaData& operator=(const SkMetaData& src);
+
+    void    reset();
+
+    bool    findS32(const char name[], int32_t* value = NULL) const;
+    bool    findScalar(const char name[], SkScalar* value = NULL) const;
+    const SkScalar* findScalars(const char name[], int* count, SkScalar values[] = NULL) const;
+    const char* findString(const char name[]) const;
+    bool    findPtr(const char name[], void** value = NULL) const;
+    bool    findBool(const char name[], bool* value = NULL) const;
+
+    bool    hasS32(const char name[], int32_t value) const
+    {
+        int32_t v;
+        return this->findS32(name, &v) && v == value;
+    }
+    bool    hasScalar(const char name[], SkScalar value) const
+    {
+        SkScalar    v;
+        return this->findScalar(name, &v) && v == value;
+    }
+    bool    hasString(const char name[], const char value[]) const
+    {
+        const char* v = this->findString(name);
+        return  v == NULL && value == NULL ||
+                v != NULL && value != NULL && !strcmp(v, value);
+    }
+    bool    hasPtr(const char name[], void* value) const
+    {
+        void*   v;
+        return this->findPtr(name, &v) && v == value;
+    }
+    bool    hasBool(const char name[], bool value) const
+    {
+        bool    v;
+        return this->findBool(name, &v) && v == value;
+    }
+
+    void    setS32(const char name[], int32_t value);
+    void    setScalar(const char name[], SkScalar value);
+    SkScalar* setScalars(const char name[], int count, const SkScalar values[] = NULL);
+    void    setString(const char name[], const char value[]);
+    void    setPtr(const char name[], void* value);
+    void    setBool(const char name[], bool value);
+
+    bool    removeS32(const char name[]);
+    bool    removeScalar(const char name[]);
+    bool    removeString(const char name[]);
+    bool    removePtr(const char name[]);
+    bool    removeBool(const char name[]);
+
+    SkDEBUGCODE(static void UnitTest();)
+
+    enum Type {
+        kS32_Type,
+        kScalar_Type,
+        kString_Type,
+        kPtr_Type,
+        kBool_Type,
+
+        kTypeCount
+    };
+
+    struct Rec;
+    class Iter;
+    friend class Iter;
+
+    class Iter {
+    public:
+        Iter() : fRec(NULL) {}
+        Iter(const SkMetaData&);
+
+        /** Reset the iterator, so that calling next() will return the first
+            data element. This is done implicitly in the constructor.
+        */
+        void    reset(const SkMetaData&);
+
+        /** Each time next is called, it returns the name of the next data element,
+            or null when there are no more elements. If non-null is returned, then the
+            element's type is returned (if not null), and the number of data values
+            is returned in count (if not null).
+        */
+        const char* next(Type*, int* count);
+
+    private:
+        Rec* fRec;
+    };
+
+public:
+    struct Rec {
+        Rec*        fNext;
+        uint16_t    fDataCount; // number of elements
+        uint8_t     fDataLen;   // sizeof a single element
+#ifdef SK_DEBUG
+        Type        fType;
+#else
+        uint8_t     fType;
+#endif
+
+#ifdef SK_DEBUG
+        const char* fName;
+        union {
+            int32_t     fS32;
+            SkScalar    fScalar;
+            const char* fString;
+            void*       fPtr;
+            bool        fBool;
+        } fData;
+#endif
+
+        const void* data() const { return (this + 1); }
+        void*       data() { return (this + 1); }
+        const char* name() const { return (const char*)this->data() + fDataLen * fDataCount; }
+        char*       name() { return (char*)this->data() + fDataLen * fDataCount; }
+
+        static Rec* Alloc(size_t);
+        static void Free(Rec*);
+    };
+    Rec*    fRec;
+
+    const Rec* find(const char name[], Type) const;
+    void* set(const char name[], const void* data, size_t len, Type, int count);
+    bool remove(const char name[], Type);
+};
+
+#endif
+
diff --git a/include/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/SkOSSound.h b/include/views/SkOSSound.h
new file mode 100644
index 0000000..5d77955
--- /dev/null
+++ b/include/views/SkOSSound.h
@@ -0,0 +1,36 @@
+/*
+ * 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 SkOSSound_DEFINED
+#define SkOSSound_DEFINED
+
+#include "SkTypes.h"
+
+class SkOSSound {
+public:
+    static void Play(const char path[]);
+    static void Pause();
+    static void Resume();
+    static bool TogglePause();  // returns true if we are now playing, or false if we're now paused
+    static void Stop();
+
+    //  volume runs from 0 (silent) to 0xFF (max-volume)
+    static uint8_t GetVolume();
+    static void SetVolume(U8CPU volume);
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_Mac.h b/include/views/SkOSWindow_Mac.h
new file mode 100644
index 0000000..1f6a1fa
--- /dev/null
+++ b/include/views/SkOSWindow_Mac.h
@@ -0,0 +1,49 @@
+/*
+ * 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 "SkWindow.h"
+
+class SkOSWindow : public SkWindow {
+public:
+    SkOSWindow(void* hwnd);
+
+    void*   getHWND() const { return fHWND; }
+    void    updateSize();
+
+    static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+
+    static pascal OSStatus SkOSWindow::EventHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData );
+
+protected:
+    // 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    doPaint(void* ctx);
+
+    typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_Unix.h b/include/views/SkOSWindow_Unix.h
new file mode 100644
index 0000000..26f51be
--- /dev/null
+++ b/include/views/SkOSWindow_Unix.h
@@ -0,0 +1,60 @@
+/*
+ * 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>
+
+struct SkUnixWindow {
+  Display* fDisplay;
+  Window fWin;
+  size_t fOSWin;
+};
+
+class SkOSWindow : public SkWindow {
+public:
+    SkOSWindow(Display* display, Window win);
+
+    void*   getHWND() const { return (void*)fUnixWindow.fWin; }
+  void* getDisplay() const { return (void*)fUnixWindow.fDisplay; }
+  void* getUnixWindow() const { return (void*)&fUnixWindow; }
+  void  setSize(int width, int height);
+    void    updateSize();
+
+    static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+
+    static bool WndProc(SkUnixWindow* w,  XEvent &e);
+
+protected:
+    // overrides from SkWindow
+    virtual void onHandleInval(const SkIRect&);
+    // overrides from SkView
+    virtual void onAddMenu(const SkOSMenu*);
+
+private:
+    SkUnixWindow  fUnixWindow;
+
+    void    doPaint();
+
+    void*   fMBar;
+
+    typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_Win.h b/include/views/SkOSWindow_Win.h
new file mode 100644
index 0000000..6d821db
--- /dev/null
+++ b/include/views/SkOSWindow_Win.h
@@ -0,0 +1,59 @@
+/*
+ * 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);
+
+    void*   getHWND() const { return fHWND; }
+    void    setSize(int width, int height);
+    void    updateSize();
+
+    static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+
+    static bool WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+    static bool SkOSWindow::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*);
+
+private:
+    void*   fHWND;
+
+    void    doPaint(void* ctx);
+
+    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/SkView.h b/include/views/SkView.h
new file mode 100644
index 0000000..d5d89df
--- /dev/null
+++ b/include/views/SkView.h
@@ -0,0 +1,345 @@
+/*
+ * 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,
+
+        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
+
+        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; }
+    /** Helper to set/clear the view's kVisible_Mask flag */
+    void        setVisibleP(bool);
+    void        setEnabledP(bool);
+    void        setFocusableP(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. */
+    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&);
+    /** 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&);
+    virtual SkCanvas* beforeChildren(SkCanvas* c) { return c; }
+    virtual void afterChildren(SkCanvas* orig) {}
+    /** Override this if you might handle the click
+    */
+    virtual Click*  onFindClickHandler(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..4dd8866
--- /dev/null
+++ b/include/views/SkWidgetViews.h
@@ -0,0 +1,310 @@
+/*
+ * 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&);
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+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..96262ee
--- /dev/null
+++ b/include/views/SkWindow.h
@@ -0,0 +1,102 @@
+/*
+ * 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 "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 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);
+    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*);
+    void    setTitle(const char title[]);
+
+protected:
+    virtual bool onEvent(const SkEvent&);
+
+    // 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;
+
+    typedef SkView INHERITED;
+};
+
+///////////////////////////////////////////////////////////
+
+#ifndef SK_USE_WXWIDGETS
+#ifdef SK_BUILD_FOR_MAC
+    #include "SkOSWindow_Mac.h"
+#elif defined(SK_BUILD_FOR_WIN)
+    #include "SkOSWindow_Win.h"
+#elif defined(SK_BUILD_FOR_UNIXx)
+  #include "SkOSWindow_Unix.h"
+#endif
+#else
+  #include "SkOSWindow_wxwidgets.h"
+#endif
+
+#endif
+
diff --git a/include/xml/SkBML_WXMLParser.h b/include/xml/SkBML_WXMLParser.h
new file mode 100644
index 0000000..faa127d
--- /dev/null
+++ b/include/xml/SkBML_WXMLParser.h
@@ -0,0 +1,54 @@
+/*
+ * 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 SkBML_WXMLParser_DEFINED
+#define SkBML_WXMLParser_DEFINED
+
+#include "SkString.h"
+#include "SkXMLParser.h"
+
+class SkStream;
+class SkWStream;
+
+class BML_WXMLParser : public SkXMLParser {
+public:
+    BML_WXMLParser(SkWStream& writer);
+    virtual ~BML_WXMLParser();
+    static void Write(SkStream& s, const char filename[]);
+  
+  /** @cond UNIT_TEST */
+  SkDEBUGCODE(static void UnitTest();)
+  /** @endcond */  
+private:
+    virtual bool onAddAttribute(const char name[], const char value[]);
+    virtual bool onEndElement(const char name[]);
+    virtual bool onStartElement(const char name[]);
+    BML_WXMLParser& operator=(const BML_WXMLParser& src);
+#ifdef SK_DEBUG
+    int fElemsCount, fElemsReused;
+    int fAttrsCount, fNamesReused, fValuesReused;
+#endif
+    SkWStream&  fWriter;
+    char*       fElems[256];
+    char*       fAttrNames[256];
+    char*       fAttrValues[256];
+
+    // important that these are U8, so we get automatic wrap-around
+    U8  fNextElem, fNextAttrName, fNextAttrValue;
+};
+
+#endif // SkBML_WXMLParser_DEFINED
+
diff --git a/include/xml/SkBML_XMLParser.h b/include/xml/SkBML_XMLParser.h
new file mode 100644
index 0000000..f056bca
--- /dev/null
+++ b/include/xml/SkBML_XMLParser.h
@@ -0,0 +1,39 @@
+/*
+ * 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 SkBML_XMLParser_DEFINED
+#define SkBML_XMLParser_DEFINED
+
+class SkStream;
+class SkWStream;
+class SkXMLParser;
+class SkXMLWriter;
+
+class BML_XMLParser {
+public:
+    /** Read the byte XML stream and write the decompressed XML.
+    */
+    static void Read(SkStream& s, SkXMLWriter& writer);
+    /** Read the byte XML stream and write the decompressed XML into a writable stream.
+    */
+    static void Read(SkStream& s, SkWStream& output);
+    /** Read the byte XML stream and write the decompressed XML into an XML parser.
+    */
+    static void Read(SkStream& s, SkXMLParser& output);
+};
+
+#endif // SkBML_XMLParser_DEFINED
+
diff --git a/include/xml/SkDOM.h b/include/xml/SkDOM.h
new file mode 100644
index 0000000..74e2492
--- /dev/null
+++ b/include/xml/SkDOM.h
@@ -0,0 +1,100 @@
+/*
+ * 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 SkDOM_DEFINED
+#define SkDOM_DEFINED
+
+#include "SkChunkAlloc.h"
+#include "SkMath.h"
+#include "SkScalar.h"
+#include "SkTemplates.h"
+
+struct SkDOMNode;
+struct SkDOMAttr;
+
+class SkDOM {
+public:
+    SkDOM();
+    ~SkDOM();
+
+    typedef SkDOMNode Node;
+    typedef SkDOMAttr Attr;
+
+    /** Returns null on failure
+    */
+    const Node* build(const char doc[], size_t len);
+    const Node* copy(const SkDOM& dom, const Node* node);
+
+    const Node* getRootNode() const;
+
+    enum Type {
+        kElement_Type,
+        kText_Type
+    };
+    Type    getType(const Node*) const;
+
+    const char* getName(const Node*) const;
+    const Node* getFirstChild(const Node*, const char elem[] = NULL) const;
+    const Node* getNextSibling(const Node*, const char elem[] = NULL) const;
+
+    const char* findAttr(const Node*, const char attrName[]) const;
+    const Attr* getFirstAttr(const Node*) const;
+    const Attr* getNextAttr(const Node*, const Attr*) const;
+    const char* getAttrName(const Node*, const Attr*) const;
+    const char* getAttrValue(const Node*, const Attr*) const;
+    
+    // helpers for walking children
+    int countChildren(const Node* node, const char elem[] = NULL) const;
+
+    // helpers for calling SkParse
+    bool findS32(const Node*, const char name[], int32_t* value) const;
+    bool findScalars(const Node*, const char name[], SkScalar value[], int count) const;
+    bool findHex(const Node*, const char name[], uint32_t* value) const;
+    bool findBool(const Node*, const char name[], bool*) const;
+    int  findList(const Node*, const char name[], const char list[]) const;
+
+    bool findScalar(const Node* node, const char name[], SkScalar value[]) const
+    {
+        return this->findScalars(node, name, value, 1);
+    }
+
+    bool hasAttr(const Node*, const char name[], const char value[]) const;
+    bool hasS32(const Node*, const char name[], int32_t value) const;
+    bool hasScalar(const Node*, const char name[], SkScalar value) const;
+    bool hasHex(const Node*, const char name[], uint32_t value) const;
+    bool hasBool(const Node*, const char name[], bool value) const;
+
+    class AttrIter {
+    public:
+        AttrIter(const class SkDOM&, const Node*);
+        const char* next(const char** value);
+    private:
+        const Attr* fAttr;
+        const Attr* fStop;
+    };
+
+    SkDEBUGCODE(void dump(const Node* node = NULL, int tabLevel = 0) const;)
+    SkDEBUGCODE(static void UnitTest();)
+
+private:
+    SkChunkAlloc    fAlloc;
+    Node*           fRoot;
+    friend class AttrIter;
+    friend class SkDOMParser;
+};
+
+#endif
+
diff --git a/include/xml/SkJS.h b/include/xml/SkJS.h
new file mode 100644
index 0000000..fe76352
--- /dev/null
+++ b/include/xml/SkJS.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#include "SkTypes.h"
+#include "SkWindow.h"
+
+extern "C" {
+    typedef long JSWord;
+    typedef JSWord jsword;
+    typedef jsword  jsval;
+    typedef struct JSRuntime JSRuntime;
+    typedef struct JSContext JSContext;
+    typedef struct JSObject JSObject;
+}
+
+class SkString;
+
+class SkJS : public SkOSWindow {
+public:
+    SkJS(void* hwnd);
+    ~SkJS();
+    SkBool EvaluateScript(const char* script, jsval* rVal);
+    SkBool ValueToString(jsval value, SkString* string);
+#ifdef SK_DEBUG
+    static void Test(void* hwnd);
+#endif
+protected:
+    void InitializeDisplayables(const SkBitmap& , JSContext *, JSObject *, JSObject *);
+    void DisposeDisplayables();
+    JSRuntime *fRuntime;
+    JSContext *fContext;
+    JSObject *fGlobal;
+};
+
diff --git a/include/xml/SkXMLParser.h b/include/xml/SkXMLParser.h
new file mode 100644
index 0000000..ddad273
--- /dev/null
+++ b/include/xml/SkXMLParser.h
@@ -0,0 +1,161 @@
+/*
+ * 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 SkXMLParser_DEFINED
+#define SkXMLParser_DEFINED
+
+#include "SkMath.h"
+#include "SkString.h"
+
+class SkStream;
+
+class SkDOM;
+struct SkDOMNode;
+
+class SkXMLParserError {
+public:
+    enum ErrorCode {
+        kNoError,
+        kEmptyFile,
+        kUnknownElement,
+        kUnknownAttributeName,
+        kErrorInAttributeValue,
+        kDuplicateIDs,
+        kUnknownError
+    };
+
+    SkXMLParserError();
+    virtual ~SkXMLParserError();
+    ErrorCode getErrorCode() const { return fCode; }
+    virtual void getErrorString(SkString* str) const;
+    int getLineNumber() const { return fLineNumber; }
+    int getNativeCode() const { return fNativeCode; }
+    bool hasError() const { return fCode != kNoError || fNativeCode != -1; }
+    bool hasNoun() const { return fNoun.size() > 0; }
+    void reset();
+    void setCode(ErrorCode code) { fCode = code; }
+    void setNoun(const SkString& str) { fNoun.set(str); }
+    void setNoun(const char* ch)  { fNoun.set(ch); }
+    void setNoun(const char* ch, size_t len) { fNoun.set(ch, len); }
+protected:
+    ErrorCode fCode;
+private:
+    int fLineNumber;
+    int fNativeCode;
+    SkString fNoun;
+    friend class SkXMLParser;
+};
+
+class SkXMLParser {
+public:
+            SkXMLParser(SkXMLParserError* parserError = NULL);
+    virtual ~SkXMLParser();
+
+    /** Returns true for success
+    */
+    bool parse(const char doc[], size_t len);
+    bool parse(SkStream& docStream);
+    bool parse(const SkDOM&, const SkDOMNode*);
+
+    static void GetNativeErrorString(int nativeErrorCode, SkString* str);
+
+protected:
+    // override in subclasses; return true to stop parsing
+    virtual bool onStartElement(const char elem[]);
+    virtual bool onAddAttribute(const char name[], const char value[]);
+    virtual bool onEndElement(const char elem[]);
+    virtual bool onText(const char text[], int len);
+
+public:
+    // public for ported implementation, not meant for clients to call
+    virtual bool startElement(const char elem[]);
+    virtual bool addAttribute(const char name[], const char value[]);
+    virtual bool endElement(const char elem[]);
+    virtual bool text(const char text[], int len); 
+    void* fParser;
+protected:
+    SkXMLParserError* fError;
+private:
+    void reportError(void* parser);
+};
+
+class SkXMLPullParser {
+public:
+            SkXMLPullParser();
+    explicit SkXMLPullParser(SkStream*);
+    virtual ~SkXMLPullParser();
+
+    SkStream*   getStream() const { return fStream; }
+    SkStream*   setStream(SkStream* stream);
+
+    enum EventType {
+        ERROR = -1,
+        START_DOCUMENT,
+        END_DOCUMENT,
+        START_TAG,
+        END_TAG,
+        TEXT,
+        CDSECT,
+        ENTITY_REF,
+        IGNORABLE_WHITESPACE,
+        PROCESSING_INSTRUCTION,
+        COMMENT,
+        DOCDECL
+    };
+
+    EventType   nextToken();
+    EventType   getEventType() const { return fCurr.fEventType; }
+
+    struct AttrInfo {
+        const char* fName;
+        const char* fValue;
+    };
+    
+    int         getDepth() const { return fDepth; }
+    const char* getName();
+    int         getAttributeCount();
+    void        getAttributeInfo(int, AttrInfo*);
+    const char* getText();
+    bool        isWhitespace();
+    
+protected:
+    virtual bool onEntityReplacement(const char name[],
+                                     SkString* replacement);
+
+public:
+    struct Curr {
+        EventType   fEventType;
+        const char* fName;
+        AttrInfo*   fAttrInfos;
+        int         fAttrInfoCount;
+        bool        fIsWhitespace;
+    };
+
+private:
+    // implemented in the porting layer
+    bool        onInit();   // return false on failure
+    EventType   onNextToken();
+    void        onExit();
+    
+    SkStream*   fStream;
+    Curr        fCurr;
+    int         fDepth;
+    
+    struct Impl;
+    Impl*   fImpl;
+};
+
+#endif
diff --git a/include/xml/SkXMLWriter.h b/include/xml/SkXMLWriter.h
new file mode 100644
index 0000000..742e7f1
--- /dev/null
+++ b/include/xml/SkXMLWriter.h
@@ -0,0 +1,93 @@
+/*
+ * 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 SkXMLWriter_DEFINED
+#define SkXMLWriter_DEFINED
+
+#include "SkTDArray.h"
+#include "SkString.h"
+#include "SkDOM.h"
+
+class SkWStream;
+class SkXMLParser;
+
+class SkXMLWriter {
+public:
+            SkXMLWriter(bool doEscapeMarkup = true);
+    virtual ~SkXMLWriter();
+
+    void    addS32Attribute(const char name[], int32_t value);
+    void    addAttribute(const char name[], const char value[]);
+    void    addAttributeLen(const char name[], const char value[], size_t length);
+    void    addHexAttribute(const char name[], uint32_t value, int minDigits = 0);
+    void    addScalarAttribute(const char name[], SkScalar value);
+    void    endElement() { this->onEndElement(); }
+    void    startElement(const char elem[]);
+    void    startElementLen(const char elem[], size_t length);
+    void    writeDOM(const SkDOM&, const SkDOM::Node*, bool skipRoot);
+    void    flush();
+    virtual void writeHeader();
+
+protected:
+    virtual void onStartElementLen(const char elem[], size_t length) = 0;
+    virtual void onAddAttributeLen(const char name[], const char value[], size_t length) = 0;
+    virtual void onEndElement() = 0;
+
+    struct Elem {
+        SkString    fName;
+        bool        fHasChildren;
+    };
+    void doEnd(Elem* elem);
+    bool doStart(const char name[], size_t length);
+    Elem* getEnd();
+    const char* getHeader();
+    SkTDArray<Elem*> fElems;
+
+private:
+    bool fDoEscapeMarkup;
+    // illegal
+    SkXMLWriter& operator=(const SkXMLWriter&);
+};
+
+class SkXMLStreamWriter : public SkXMLWriter {
+public:
+    SkXMLStreamWriter(SkWStream*);
+    virtual ~SkXMLStreamWriter();
+    virtual void    writeHeader();
+    SkDEBUGCODE(static void UnitTest();)
+protected:
+    virtual void onStartElementLen(const char elem[], size_t length);
+    virtual void onEndElement();
+    virtual void onAddAttributeLen(const char name[], const char value[], size_t length);
+private:
+    SkWStream&      fStream;
+};
+
+class SkXMLParserWriter : public SkXMLWriter {
+public:
+    SkXMLParserWriter(SkXMLParser*);
+    virtual ~SkXMLParserWriter();
+protected:
+    virtual void onStartElementLen(const char elem[], size_t length);
+    virtual void onEndElement();
+    virtual void onAddAttributeLen(const char name[], const char value[], size_t length);
+private:
+    SkXMLParser&        fParser;
+};
+
+
+#endif
+
diff --git a/samplecode/SampleAll.cpp b/samplecode/SampleAll.cpp
new file mode 100644
index 0000000..968263b
--- /dev/null
+++ b/samplecode/SampleAll.cpp
@@ -0,0 +1,791 @@
+#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 "SkShaderExtras.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>
+
+extern void Dump();
+    
+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.setPorterDuffXfermode(SkPorterDuff::kSrc_Mode);
+    rast->addLayer(p);
+}
+
+static void r1(SkLayerRasterizer* rast, SkPaint& p)
+{
+    rast->addLayer(p);
+
+    p.setAlpha(0x40);
+    p.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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::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.setPorterDuffXfermode(SkPorterDuff::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
+}
+
+static void test_math()
+{
+    float x;
+    const float PI = 3.141593;
+    
+    for (x = 0; x < 1; x += 0.05f)
+        printf("atan(%g) = %g\n", x, atanf(x) * 180/PI);
+    for (x = 1; x < 10000000; x *= 2)
+        printf("atan(%g) = %g\n", x, atanf(x) * 180/PI);
+}
+
+class DemoView : public SkView {
+public:
+    DemoView()
+    {
+        test_math();
+    }
+	
+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 onDraw(SkCanvas* canvas)
+    {
+        canvas->drawColor(SK_ColorWHITE);
+        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);
+        }
+        Dump();
+    }
+    
+    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;
+		int 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 = SkPorterDuff::CreateXfermode(SkPorterDuff::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 = SkPorterDuff::CreateXfermode(SkPorterDuff::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 (int index = 0; index < SK_ARRAY_COUNT(gRastProcs); index++)
+            drawOneRaster(canvas);
+    }
+    
+    void drawOneRaster(SkCanvas* canvas)
+    {        
+        canvas->save();
+//        canvas->scale(SK_Scalar1*2, SK_Scalar1*2, 0, 0);
+
+        SkScalar    x = SkIntToScalar(20);
+        SkScalar    y = SkIntToScalar(40);
+        SkPaint     paint;
+        
+        paint.setAntiAlias(true);
+        paint.setTextSize(SkIntToScalar(48));
+        paint.setTypeface(SkTypeface::Create("sans-serif", SkTypeface::kBold));
+
+        SkString str("GOOGLE");
+
+        for (int 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);
+            if (0)
+            {
+                SkPath path;
+                paint.getTextPath(str.c_str(), str.size(), x + SkIntToScalar(260), y, &path);
+                canvas->drawPath(path, paint);
+            }
+
+            y += paint.getFontSpacing();
+        }
+
+        canvas->restore();
+        
+        if (0)
+        {
+            SkPoint pts[] = { 0, 0, 0, SkIntToScalar(150) };
+            SkColor colors[] = { 0xFFE6E6E6, 0xFFFFFFFF };
+            SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
+
+            paint.reset();
+            paint.setShader(s)->unref();
+            canvas->drawRectCoords(0, 0, SkIntToScalar(120), SkIntToScalar(150), paint);
+        }
+        
+        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 SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DemoView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp
new file mode 100644
index 0000000..5ff8362
--- /dev/null
+++ b/samplecode/SampleApp.cpp
@@ -0,0 +1,563 @@
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkGLCanvas.h"
+#include "SkGraphics.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "SkWindow.h"
+
+#include "SampleCode.h"
+
+#include <AGL/agl.h>
+#include <OpenGL/gl.h>
+
+#define ANIMATING_EVENTTYPE "nextSample"
+#define ANIMATING_DELAY     750
+
+#define USE_OFFSCREEN
+
+SkViewRegister* SkViewRegister::gHead;
+SkViewRegister::SkViewRegister(SkViewFactory fact) : fFact(fact) {
+    static bool gOnce;
+    if (!gOnce) {
+        gHead = NULL;
+        gOnce = true;
+    }
+    
+    fChain = gHead;
+    gHead = this;
+}
+
+static AGLContext   gAGLContext;
+
+static void init_gl(WindowRef wref) {
+    GLint major, minor;
+    
+    aglGetVersion(&major, &minor);
+    SkDebugf("---- agl version %d %d\n", major, minor);
+    
+    const GLint pixelAttrs[] = {
+        AGL_RGBA,
+        AGL_DEPTH_SIZE, 32,
+        AGL_OFFSCREEN,
+        AGL_NONE
+    };
+    
+    AGLPixelFormat format = aglCreatePixelFormat(pixelAttrs);
+    SkDebugf("----- agl format %p\n", format);
+    gAGLContext = aglCreateContext(format, NULL);
+    SkDebugf("----- agl context %p\n", gAGLContext);
+    aglDestroyPixelFormat(format);
+
+    aglEnable(gAGLContext, GL_BLEND);
+    aglEnable(gAGLContext, GL_LINE_SMOOTH);
+    aglEnable(gAGLContext, GL_POINT_SMOOTH);
+    aglEnable(gAGLContext, GL_POLYGON_SMOOTH);
+    
+    aglSetCurrentContext(gAGLContext);
+}
+
+static void setup_offscreen_gl(const SkBitmap& offscreen, WindowRef wref) {
+    GLboolean success = true;
+
+#ifdef USE_OFFSCREEN
+    success = aglSetOffScreen(gAGLContext,
+                                        offscreen.width(),
+                                        offscreen.height(),
+                                        offscreen.rowBytes(),
+                                        offscreen.getPixels());
+#else
+    success = aglSetWindowRef(gAGLContext, wref);
+#endif
+
+    GLenum err = aglGetError();
+    if (err) {
+        SkDebugf("---- setoffscreen %d %d %s [%d %d]\n", success, err,
+                 aglErrorString(err), offscreen.width(), offscreen.height());
+    }
+    
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
+    glEnable(GL_TEXTURE_2D);
+
+    glClearColor(0, 0, 0, 0);
+    glClear(GL_COLOR_BUFFER_BIT);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static const char gTitleEvtName[] = "SampleCode_Title_Event";
+static const char gPrefSizeEvtName[] = "SampleCode_PrefSize_Event";
+
+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);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class SampleWindow : public SkOSWindow {
+public:
+	SampleWindow(void* hwnd);
+	virtual ~SampleWindow();
+
+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 bool onEvent(const SkEvent& evt);
+
+#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 onClick(Click* click);
+	virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+    virtual bool onHandleKeyUp(SkKey key);
+#endif
+private:
+    const SkViewRegister* fCurr;
+    
+    SkPicture* fPicture;
+    SkGLCanvas* fGLCanvas;
+    SkPath fClipPath;
+    
+    enum CanvasType {
+        kRaster_CanvasType,
+        kPicture_CanvasType,
+        kOpenGL_CanvasType
+    };
+    CanvasType fCanvasType;
+
+    bool fUseClip;
+    bool fRepeatDrawing;
+    bool fAnimating;
+    
+    int fScrollTestX, fScrollTestY;
+    
+    void loadView(SkView*);
+    void updateTitle();
+    bool nextSample();
+
+    void postAnimatingEvent() {
+        if (fAnimating) {
+            SkEvent* evt = new SkEvent(ANIMATING_EVENTTYPE);
+            evt->post(this->getSinkID(), ANIMATING_DELAY);
+        }
+    }
+    
+    
+    static CanvasType cycle_canvastype(CanvasType);
+
+    typedef SkOSWindow INHERITED;
+};
+
+SampleWindow::CanvasType SampleWindow::cycle_canvastype(CanvasType ct) {
+    static const CanvasType gCT[] = {
+        kPicture_CanvasType,
+        kOpenGL_CanvasType,
+        kRaster_CanvasType
+    };
+    return gCT[ct];
+}
+
+SampleWindow::SampleWindow(void* hwnd) : INHERITED(hwnd) {
+    init_gl((WindowRef)hwnd);
+    
+    fPicture = NULL;
+    fGLCanvas = NULL;
+
+    fCanvasType = kRaster_CanvasType;
+    fUseClip = false;
+    fRepeatDrawing = false;
+    fAnimating = false;
+
+    fScrollTestX = fScrollTestY = 0;
+
+//	this->setConfig(SkBitmap::kRGB_565_Config);
+	this->setConfig(SkBitmap::kARGB_8888_Config);
+	this->setVisibleP(true);
+
+    fCurr = SkViewRegister::Head();
+    this->loadView(fCurr->factory()());
+}
+
+SampleWindow::~SampleWindow() {
+    delete fPicture;
+    delete fGLCanvas;
+}
+
+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) {
+#ifndef USE_OFFSCREEN
+    aglSetWindowRef(gAGLContext, NULL);
+#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 kOpenGL_CanvasType: {
+            //SkGLCanvas::DeleteAllTextures();  // just for testing
+            SkDevice* device = canvas->getDevice();
+            const SkBitmap& bitmap = device->accessBitmap(true);
+            // first clear the raster bitmap, so we don't see any leftover bits
+            bitmap.eraseColor(0);
+            // now setup our glcanvas
+            setup_offscreen_gl(bitmap, (WindowRef)this->getHWND());
+            fGLCanvas = new SkGLCanvas;
+            fGLCanvas->setViewport(bitmap.width(), bitmap.height());
+            canvas = fGLCanvas;
+            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) {
+    switch (fCanvasType) {
+        case kRaster_CanvasType:
+            break;
+        case kPicture_CanvasType:
+            if (false) {
+                SkPicture* pict = new SkPicture(*fPicture);
+                fPicture->unref();
+                orig->drawPicture(*pict);
+                pict->unref();
+            } 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;
+        case kOpenGL_CanvasType:
+            glFlush();
+            delete fGLCanvas;
+            fGLCanvas = NULL;
+#ifdef USE_OFFSCREEN
+            reverseRedAndBlue(orig->getDevice()->accessBitmap(true));
+#endif
+            break;
+    }
+    
+//    if ((fScrollTestX | fScrollTestY) != 0)
+    {
+        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);
+    }
+}
+
+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];
+}
+
+bool SampleWindow::nextSample() {
+    if (fCurr) {
+        fCurr = fCurr->next();
+        if (NULL == fCurr) {
+            fCurr = SkViewRegister::Head();
+        }
+        this->loadView(fCurr->factory()());
+        return true;
+    }
+    return false;
+}
+
+bool SampleWindow::onEvent(const SkEvent& evt) {
+    if (evt.isType(ANIMATING_EVENTTYPE)) {
+        if (fAnimating) {
+            this->nextSample();
+            this->postAnimatingEvent();
+        }
+        return true;
+    }
+    return this->INHERITED::onEvent(evt);
+}
+
+
+bool SampleWindow::onHandleChar(SkUnichar uni) {
+    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;
+    }
+    
+    if ('a' == uni) {
+        fAnimating = !fAnimating;
+        this->postAnimatingEvent();
+        this->updateTitle();
+    }
+    
+    return this->INHERITED::onHandleChar(uni);
+}
+
+#include "SkDumpCanvas.h"
+
+bool SampleWindow::onHandleKey(SkKey key) {
+    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:
+            fUseClip = !fUseClip;
+            this->updateTitle();
+            this->inval(NULL);
+            return true;
+        case kDown_SkKey:
+            this->setConfig(cycle_configs(this->getBitmap().config()));
+            this->updateTitle();
+            return true;
+        case kOK_SkKey:
+            if (true) {
+                SkDebugfDumper dumper;
+                SkDumpCanvas dc(&dumper);
+                this->draw(&dc);
+            } else {
+                fRepeatDrawing = !fRepeatDrawing;
+                if (fRepeatDrawing) {
+                    this->inval(NULL);
+                }
+            }
+            return true;
+        default:
+            break;
+    }
+    return this->INHERITED::onHandleKey(key);
+}
+
+void SampleWindow::loadView(SkView* view) {
+    SkView::F2BIter iter(this);
+    SkView* prev = iter.next();
+    if (prev) {
+        prev->detachFromParent();
+    }
+    view->setVisibleP(true);
+    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: "
+};
+
+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> ");
+    }
+
+    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
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkOSWindow* create_sk_window(void* hwnd) {
+	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);
+    setenv("ANDROID_ROOT", "/android/device/data", 0);
+	SkGraphics::Init(true);
+	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..7bc862b
--- /dev/null
+++ b/samplecode/SampleArc.cpp
@@ -0,0 +1,187 @@
+#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 "SkShaderExtras.h"
+#include "Sk1DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkPorterDuff.h"
+#include "SkLayerRasterizer.h"
+
+class ArcsView : public SkView {
+public:
+	ArcsView()
+    {
+        fSweep = SkIntToScalar(100);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)
+    {
+        if (SampleCode::TitleQ(*evt))
+        {
+            SampleCode::TitleR(evt, "Arcs");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas)
+    {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+    
+    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 (int 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 onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(canvas);
+
+        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);
+        
+        fSweep += SK_Scalar1/4;
+        if (fSweep > SkIntToScalar(360))
+            fSweep = 0;
+        
+        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 SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ArcsView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleBitmapRect.cpp b/samplecode/SampleBitmapRect.cpp
new file mode 100644
index 0000000..5919084
--- /dev/null
+++ b/samplecode/SampleBitmapRect.cpp
@@ -0,0 +1,111 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkPorterDuff.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkImageRef.h"
+#include "SkOSFile.h"
+#include "SkStream.h"
+
+#define SPECIFIC_IMAGE  "/skimages/main.gif"
+
+class BitmapRectView : public SkView {
+public:
+    SkBitmap fBitmap;
+    int      fCurrX, fCurrY;
+
+	BitmapRectView() {
+        SkImageDecoder::DecodeFile(SPECIFIC_IMAGE, &fBitmap);
+        fCurrX = fCurrY = 0;
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)
+    {
+        if (SampleCode::TitleQ(*evt))
+        {
+            SkString str("BitmapRect: ");
+            str.append(SPECIFIC_IMAGE);
+            SampleCode::TitleR(evt, str.c_str());
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas)
+    {
+        canvas->drawColor(SK_ColorGRAY);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(canvas);
+        
+        canvas->drawBitmap(fBitmap, 0, 0, NULL);
+        
+        SkIRect subset;
+        const int SRC_WIDTH = 16;
+        const int SRC_HEIGHT = 16;
+        
+        subset.set(0, 0, SRC_WIDTH, SRC_HEIGHT);
+        subset.offset(fCurrX, fCurrY);
+        
+        SkDebugf("---- src x=%d y=%d\n", subset.fLeft, subset.fTop);
+        
+        SkRect  dst0, dst1;
+        SkScalar y = SkIntToScalar(fBitmap.height() + 16);
+        
+        dst0.set(SkIntToScalar(50), y,
+                 SkIntToScalar(50+SRC_WIDTH),
+                 y + SkIntToScalar(SRC_HEIGHT));
+        dst1 = dst0;
+        dst1.offset(SkIntToScalar(200), 0);
+        dst1.fRight = dst1.fLeft + 8 * dst0.width();
+        dst1.fBottom = dst1.fTop + 8 * dst0.height();
+        
+        canvas->drawBitmapRect(fBitmap, &subset, dst0, NULL);
+        canvas->drawBitmapRect(fBitmap, &subset, dst1, NULL);
+        
+        SkPaint paint;
+        paint.setColor(0x88FF0000);
+        canvas->drawRect(dst0, paint);
+        paint.setColor(0x880000FF);
+        canvas->drawRect(dst1, paint);
+    }
+    
+    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:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new BitmapRectView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleCamera.cpp b/samplecode/SampleCamera.cpp
new file mode 100644
index 0000000..9a8d1ef
--- /dev/null
+++ b/samplecode/SampleCamera.cpp
@@ -0,0 +1,106 @@
+#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"
+
+class CameraView : public SkView {
+public:
+	CameraView()
+    {
+        fRX = fRY = fRZ = 0;
+    }
+    
+    virtual ~CameraView()
+    {
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)
+    {
+        if (SampleCode::TitleQ(*evt))
+        {
+            SampleCode::TitleR(evt, "Camera");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas)
+    {
+        canvas->drawColor(0xFFDDDDDD);
+//        canvas->drawColor(0, SkPorterDuff::kClear_Mode);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(canvas);
+
+        canvas->translate(this->width()/2, this->height()/2);
+
+        Sk3DView    view;
+        view.rotateX(SkIntToScalar(fRX));
+        view.rotateY(SkIntToScalar(fRY));
+        view.applyToCanvas(canvas);
+        
+        SkPaint paint;
+        SkScalar rad = SkIntToScalar(50);
+        SkScalar dim = rad*2;
+
+        if (view.dotWithNormal(0, 0, SK_Scalar1) < 0) {
+            paint.setColor(SK_ColorRED);
+        }
+        
+        paint.setAntiAlias(true);
+
+#if 0
+        SkEmbossMaskFilter::Light light;
+        light.fDirection[0] = SK_Scalar1;
+        light.fDirection[1] = SK_Scalar1;
+        light.fDirection[2] = SK_Scalar1;
+        light.fAmbient = 180;
+        light.fSpecular = 16 * 2;
+        paint.setMaskFilter(new SkEmbossMaskFilter(light, SkIntToScalar(4)));
+#endif
+
+        canvas->drawCircle(0, 0, rad, paint);
+        canvas->drawCircle(-dim, -dim, rad, paint);
+        canvas->drawCircle(-dim,  dim, rad, paint);
+        canvas->drawCircle( dim, -dim, rad, paint);
+        canvas->drawCircle( dim,  dim, rad, paint);
+        
+        fRY += 1;
+        if (fRY >= 360)
+            fRY = 0;
+        this->inval(NULL);
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) 
+    {
+        SkScalar angle = SkScalarDiv(this->height()/2 - y, this->height());
+        fRX = SkScalarRound(angle * 180);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+    virtual bool onClick(Click* click) 
+    {
+        return this->INHERITED::onClick(click);
+    }
+    
+private:
+    int fRX, fRY, fRZ;
+    typedef SkView 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..bfb92d4
--- /dev/null
+++ b/samplecode/SampleCircle.cpp
@@ -0,0 +1,137 @@
+#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.39999998, 1, 21.3999996, 21 };
+    SkPath p;
+    p.addOval(r);
+    SkRect r2;
+    p.computeBounds(&r2, SkPath::kFast_BoundsType);
+    SkASSERT(r == r2);
+#endif
+}
+
+class CircleView : public SkView {
+public:
+    static const SkScalar ANIM_DX = SK_Scalar1 / 67;
+    static const SkScalar ANIM_DY = SK_Scalar1 / 29;
+    static const SkScalar ANIM_RAD = SK_Scalar1 / 19;
+    SkScalar fDX, fDY, fRAD;
+
+    CircleView() {
+        fDX = fDY = fRAD = 0;
+    }
+    
+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 drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    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);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        test_circlebounds(canvas);
+        
+        if (false) {
+          //  canvas->translate(SK_ScalarHalf, SK_ScalarHalf);
+
+            SkPaint paint;
+            paint.setAntiAlias(true);
+            paint.setStyle(SkPaint::kStroke_Style);
+            canvas->drawCircle(SkIntToScalar(10), SkIntToScalar(10), SkIntToScalar(2), paint);
+            
+            SkIRect r;
+            r.set(7, 7, 13, 13);
+            SkRect dst;
+            dst.set(SkIntToScalar(100), SkIntToScalar(10), SkIntToScalar(200), SkIntToScalar(110));
+            blowup(canvas, r, dst);
+            return;
+        }
+        
+        // test that degenerate rects do nothing
+        if (true) {
+            SkScalar x = SkIntToScalar(30);
+            SkRect  r;
+            r.set(x, x, x, x);
+            SkPaint p;
+            canvas->drawRect(r, p);
+            p.setAntiAlias(true);
+            canvas->drawRect(r, p);
+        }
+
+        SkScalar dx = SkIntToScalar(32);
+        SkScalar dy = SkIntToScalar(32);
+        
+        canvas->translate(dx + fDX, dy + fDY);
+        drawSix(canvas, dx, dy);
+
+        canvas->translate(dx, 0);
+        canvas->translate(SK_ScalarHalf, SK_ScalarHalf);
+        drawSix(canvas, dx, dy);
+        
+        fDX += ANIM_DX;
+        fDY += ANIM_DY;
+        fRAD += ANIM_RAD;
+        this->inval(NULL);
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+const SkScalar CircleView::ANIM_DX;
+const SkScalar CircleView::ANIM_DY;
+const SkScalar CircleView::ANIM_RAD;
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new CircleView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleCode.h b/samplecode/SampleCode.h
new file mode 100644
index 0000000..ff660f2
--- /dev/null
+++ b/samplecode/SampleCode.h
@@ -0,0 +1,38 @@
+#ifndef SampleCode_DEFINED
+#define SampleCode_DEFINED
+
+#include "SkEvent.h"
+
+class SampleCode {
+public:
+    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);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class SkView;
+
+typedef SkView* (*SkViewFactory)();
+
+class SkViewRegister : SkNoncopyable {
+public:
+    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;
+};
+
+#endif
+
diff --git a/samplecode/SampleCull.cpp b/samplecode/SampleCull.cpp
new file mode 100644
index 0000000..ea1bb77
--- /dev/null
+++ b/samplecode/SampleCull.cpp
@@ -0,0 +1,230 @@
+#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 SkView {
+public:
+	CullView()
+    {
+        fClip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160));
+        
+        SkRandom    rand;
+        
+        for (int i = 0; i < 50; i++) {
+            int x = nextScalarRange(rand, -fClip.width()*1, fClip.width()*2);
+            int 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);
+    }
+    
+    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);
+    }
+    
+    void drawBG(SkCanvas* canvas)
+    {
+        canvas->drawColor(0xFFDDDDDD);
+        
+    #if 0
+        SkPaint paint;
+        
+        paint.setAntiAliasOn(true);
+        paint.setTextSize(SkIntToScalar(20));
+        paint.setTypeface(SkTypeface::Create("serif", SkTypeface::kBoldItalic))->unref();
+
+        uint16_t    text[20];
+        
+        text[0] = 'H';
+        text[1] = 'i';
+        text[2] = ' ';
+        for (int i = 3; i < 20; i++)
+            text[i] = 0x3040 + i;
+        canvas->drawText16(text, 20, SkIntToScalar(20), SkIntToScalar(20), paint);
+    #endif
+    }
+    
+    virtual void onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(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);
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) 
+    {
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+    virtual bool onClick(Click* click) 
+    {
+        return this->INHERITED::onClick(click);
+    }
+    
+private:
+    SkRect      fClip;
+    SkIPoint*   fPoints;
+    SkPath      fPath;
+    int         fPtCount;
+
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new CullView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleDither.cpp b/samplecode/SampleDither.cpp
new file mode 100644
index 0000000..7f7877a
--- /dev/null
+++ b/samplecode/SampleDither.cpp
@@ -0,0 +1,198 @@
+#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 "SkShaderExtras.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 (unsigned y = 0; y < bm.height(); y++) {
+        DITHER_4444_SCAN(y);
+        
+        SkPMColor* p = bm.getAddr32(0, y);
+        for (unsigned 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 SkView {
+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;
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Dither");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+//        canvas->drawColor(0xFFDDDDDD);
+        canvas->drawColor(0xFF181818);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(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);
+    }
+
+    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:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DitherView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleDrawLooper.cpp b/samplecode/SampleDrawLooper.cpp
new file mode 100644
index 0000000..1a7a870
--- /dev/null
+++ b/samplecode/SampleDrawLooper.cpp
@@ -0,0 +1,103 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkRandom.h"
+#include "SkLayerDrawLooper.h"
+#include "SkBlurMaskFilter.h"
+
+#include <pthread.h>
+
+#define WIDTH   200
+#define HEIGHT  200
+
+class LooperView : public SkView {
+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;
+        
+        for (int i = 0; i < SK_ARRAY_COUNT(gParams); i++) {
+            SkPaint* paint = fLooper->addLayer(gParams[i].fOffset,
+                                               gParams[i].fOffset);
+            paint->setAntiAlias(true);
+            paint->setColor(gParams[i].fColor);
+            paint->setStyle(gParams[i].fStyle);
+            paint->setStrokeWidth(gParams[i].fWidth);
+            paint->setTextSize(SkIntToScalar(72));
+            if (gParams[i].fBlur > 0) {
+                SkMaskFilter* mf = SkBlurMaskFilter::Create(SkIntToScalar(gParams[i].fBlur),
+                                                            SkBlurMaskFilter::kNormal_BlurStyle);
+                paint->setMaskFilter(mf)->unref();
+            }
+        }
+    }
+    
+    virtual ~LooperView() {
+        fLooper->safeUnref();
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "DrawLooper");
+            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);
+        
+        SkPaint  paint;
+        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);
+    }
+    
+    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 LooperView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleEmboss.cpp b/samplecode/SampleEmboss.cpp
new file mode 100644
index 0000000..cdc8b47
--- /dev/null
+++ b/samplecode/SampleEmboss.cpp
@@ -0,0 +1,78 @@
+#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 "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+class EmbossView : public SkView {
+    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);
+    }
+
+    void drawBG(SkCanvas* canvas)
+    {
+        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(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 SkView 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..221b6f8
--- /dev/null
+++ b/samplecode/SampleEncode.cpp
@@ -0,0 +1,254 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkPorterDuff.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkImageRef.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 SkView {
+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 (!codec->encodeFile(path.c_str(), fBitmaps[i])) {
+                    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
+    }
+    
+    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);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+//        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        if (fBitmapCount == 0) {
+            return;
+        }
+        
+        SkPaint paint;
+        if (false) {
+//            SkColor colors[] = { 0xFE000000, SK_ColorWHITE };
+            SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
+            SkShader* shader = SkGradientShader::CreateSweep(SkIntToScalar(50), SkIntToScalar(50),
+                                                             colors, NULL, 2);
+            paint.setShader(shader)->unref();
+
+            SkRect r;
+            r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
+            canvas->drawRect(r, paint);
+            
+            canvas->translate(SkIntToScalar(200), SkIntToScalar(200));
+            paint.setAntiAlias(true);
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(SkIntToScalar(10));
+            canvas->drawOval(r, paint);
+            return;
+        }
+        
+        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);
+    }
+    
+    virtual bool onClick(Click* click) {
+        return this->INHERITED::onClick(click);
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new EncodeView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleFillType.cpp b/samplecode/SampleFillType.cpp
new file mode 100644
index 0000000..bb268cd
--- /dev/null
+++ b/samplecode/SampleFillType.cpp
@@ -0,0 +1,101 @@
+#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 SkView {
+    SkPath fPath;
+public:
+	FillTypeView() {
+        const SkScalar radius = SkIntToScalar(45);
+        fPath.addCircle(SkIntToScalar(50), SkIntToScalar(50), radius);
+        fPath.addCircle(SkIntToScalar(100), SkIntToScalar(100), radius);
+    }
+    
+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 drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+    
+    void showPath(SkCanvas* canvas, int x, int y, SkPath::FillType ft,
+                  SkScalar scale, const SkPaint& paint) {
+
+        const SkRect r = { 0, 0, SkIntToScalar(150), SkIntToScalar(150) };
+
+        canvas->save();
+        canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+        canvas->clipRect(r);
+        canvas->drawColor(SK_ColorWHITE);
+        fPath.setFillType(ft);
+        canvas->translate(r.centerX(), r.centerY());
+        canvas->scale(scale, scale);
+        canvas->translate(-r.centerX(), -r.centerY());
+        canvas->drawPath(fPath, paint);
+        canvas->restore();
+    }
+    
+    void showFour(SkCanvas* canvas, SkScalar scale, const SkPaint& paint) {
+        showPath(canvas,   0,   0, SkPath::kWinding_FillType,
+                 scale, paint);
+        showPath(canvas, 200,   0, SkPath::kEvenOdd_FillType,
+                 scale, paint);
+        showPath(canvas,  00, 200, SkPath::kInverseWinding_FillType,
+                 scale, paint);
+        showPath(canvas, 200, 200, SkPath::kInverseEvenOdd_FillType,
+                 scale, paint);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        drawBG(canvas);
+        
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+        
+        SkPaint paint;
+        const SkScalar scale = SkIntToScalar(5)/4;
+
+        paint.setAntiAlias(false);
+
+        showFour(canvas, SK_Scalar1, paint);
+        canvas->translate(SkIntToScalar(450), 0);
+        showFour(canvas, scale, paint);
+
+        paint.setAntiAlias(true);
+
+        canvas->translate(SkIntToScalar(-450), SkIntToScalar(450));
+        showFour(canvas, SK_Scalar1, paint);
+        canvas->translate(SkIntToScalar(450), 0);
+        showFour(canvas, scale, paint);
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        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 FillTypeView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleFilter.cpp b/samplecode/SampleFilter.cpp
new file mode 100644
index 0000000..8d26470
--- /dev/null
+++ b/samplecode/SampleFilter.cpp
@@ -0,0 +1,164 @@
+#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 "SkShaderExtras.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
+    SkRect r;
+    
+    r.set(x, y,
+          x + SkIntToScalar(bm.width() * 2),
+          y + SkIntToScalar(bm.height() * 2));
+    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+                                               SkShader::kRepeat_TileMode);
+    paint->setShader(s)->unref();
+    canvas->drawRect(r, *paint);
+    paint->setShader(NULL);
+    return r.width() * 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 SkView {
+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);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)
+    {
+        if (SampleCode::TitleQ(*evt))
+        {
+            SampleCode::TitleR(evt, "Filter");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas)
+    {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(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);
+    }
+    
+    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:
+    typedef SkView 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..30ac2bd
--- /dev/null
+++ b/samplecode/SampleFilter2.cpp
@@ -0,0 +1,124 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkPorterDuff.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+
+static const char* gNames[] = {
+    "/skimages/background_01.png"
+};
+
+class Filter2View : public SkView {
+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);
+        }
+        for (int i = fBitmapCount/2; i < fBitmapCount; i++) {
+            SkImageDecoder::DecodeFile(gNames[i-fBitmapCount/2], &fBitmaps[i],
+                                       SkBitmap::kRGB_565_Config,
+                                       SkImageDecoder::kDecodePixels_Mode);
+        }
+        fCurrIndex = 0;
+    }
+    
+    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);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+//        canvas->drawColor(0xFFDDDDDD);
+        canvas->drawColor(SK_ColorGRAY);
+//        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(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 SkView 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..fb63f71
--- /dev/null
+++ b/samplecode/SampleFontCache.cpp
@@ -0,0 +1,171 @@
+#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 SkView {
+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;
+            pthread_attr_t  attr;
+            
+            status = pthread_attr_init(&attr);
+            SkASSERT(0 == status);
+            status = pthread_create(&fMThreads[i], &attr,  measure_proc, NULL);
+            SkASSERT(0 == status);
+
+            fBitmaps[i].setConfig(SkBitmap::kRGB_565_Config, 320, 240);
+            fBitmaps[i].allocPixels();
+            status = pthread_create(&fDThreads[i], &attr,  draw_proc, &fBitmaps[i]);
+            SkASSERT(0 == status);
+        }
+    }
+    
+    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);
+    }
+    
+    void drawBG(SkCanvas* canvas)
+    {
+        canvas->drawColor(0xFFDDDDDD);
+//        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(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);
+    }
+    
+    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 FontCacheView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleGL.cpp b/samplecode/SampleGL.cpp
new file mode 100644
index 0000000..7180e3e
--- /dev/null
+++ b/samplecode/SampleGL.cpp
@@ -0,0 +1,207 @@
+#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 "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+// effects
+#include "SkGradientShader.h"
+#include "SkShaderExtras.h"
+#include "SkUnitMappers.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+
+#include "SkGLCanvas.h"
+
+#include <AGL/agl.h>
+#include <OpenGL/gl.h>
+
+extern void* gSampleWind;
+
+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.setAntiAlias(true);
+    paint.setShader(SkGradientShader::CreateLinear(pts, colors, pos,
+            SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode, um))->unref();
+    
+    SkRect r;
+    r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
+    canvas.drawOval(r, paint);
+}
+
+static void premulBitmap(const SkBitmap& bm) {
+    for (int y = 0; y < bm.height(); 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 scale = SkAlpha255To256(a);
+            r = SkAlphaMul(r, scale);
+            g = SkAlphaMul(g, scale);
+            b = SkAlphaMul(b, scale);
+            *p++ = SkPackARGB32(a, r, g, b);
+        }
+    }
+}
+
+class GLView : public SkView {
+public:
+    AGLContext fCtx;
+    SkBitmap    fOffscreen;
+    SkBitmap    fTexture[3];
+
+	GLView() {
+        makebm(&fTexture[0], SkBitmap::kARGB_8888_Config, 64, 100);
+        makebm(&fTexture[1], SkBitmap::kRGB_565_Config, 64, 100);
+        makebm(&fTexture[2], SkBitmap::kARGB_4444_Config, 64, 100);
+
+        GLint major, minor;
+        
+        aglGetVersion(&major, &minor);
+        SkDebugf("---- version %d %d\n", major, minor);
+        
+        GLint attr[] = {
+            AGL_RGBA,
+            AGL_DEPTH_SIZE, 32,
+            AGL_OFFSCREEN,
+            AGL_NONE
+        };
+
+        SkDebugf("------ attr %p %d\n", attr, sizeof(attr));
+        AGLPixelFormat format = aglCreatePixelFormat(attr);
+        SkDebugf("----- format %p\n", format);
+        fCtx = aglCreateContext(format, 0);
+        SkDebugf("----- context %p\n", fCtx);
+        GLboolean success;  //= aglSetWindowRef(fCtx, (WindowRef)gSampleWind);
+//        SkDebugf("----- aglSetWindowRef %d\n", success);
+
+        aglEnable(fCtx, GL_BLEND);
+        aglEnable(fCtx, GL_LINE_SMOOTH);
+        aglEnable(fCtx, GL_POINT_SMOOTH);
+        aglEnable(fCtx, GL_POLYGON_SMOOTH);
+
+        fOffscreen.setConfig(SkBitmap::kARGB_8888_Config, 300, 300);
+        fOffscreen.allocPixels();
+        
+        success = aglSetOffScreen(fCtx,
+                                  fOffscreen.width(),
+                                  fOffscreen.height(),
+                                  fOffscreen.rowBytes(),
+                                  fOffscreen.getPixels());
+        GLenum err = aglGetError();
+        SkDebugf("---- setoffscreen %d %d %s\n", success, err, aglErrorString(err));
+        
+        aglSetCurrentContext(fCtx);
+        glOrtho(0, fOffscreen.width(),
+                fOffscreen.height(), 0,
+                -1, 1);
+
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
+
+        glEnable(GL_TEXTURE_2D);
+}
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "GL");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        SkGLCanvas c(fOffscreen.width(), fOffscreen.height());
+
+        glClearColor(0, 0, 0, 0);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        SkPaint p;
+        
+        p.setAntiAlias(true);
+
+        c.drawColor(SK_ColorWHITE);
+
+        p.setColor(SK_ColorRED);
+        c.drawCircle(SkIntToScalar(40), SkIntToScalar(40), SkIntToScalar(20), p);
+        
+        p.setColor(SK_ColorGREEN);
+        p.setStrokeWidth(SkIntToScalar(6));
+        p.setStrokeCap(SkPaint::kRound_Cap);
+        c.drawLine(SkIntToScalar(10), SkIntToScalar(10), SkIntToScalar(40), SkIntToScalar(50), p);
+        
+      //  c.scale(SkIntToScalar(3)/2, SkIntToScalar(3)/2);
+        p.setColor(0x880000FF);
+        c.drawCircle(SkIntToScalar(40), SkIntToScalar(40), SkIntToScalar(20), p);
+
+        for (int i = 0; i < SK_ARRAY_COUNT(fTexture); i++) {
+            c.drawBitmap(fTexture[i], SkIntToScalar(10), SkIntToScalar(100), NULL);
+            c.translate(SkIntToScalar(fTexture[i].width()), 0);
+        }
+        p.setColor(SK_ColorBLUE);
+        c.drawRectCoords(SkIntToScalar(10), SkIntToScalar(100),
+                         SkIntToScalar(10+fTexture[0].width()),
+                         SkIntToScalar(100+fTexture[0].height()),
+                         p);
+
+        ////////
+        glFlush();
+        premulBitmap(fOffscreen);
+        canvas->drawBitmap(fOffscreen, SkIntToScalar(10), SkIntToScalar(10), NULL);
+    }
+    
+private:
+
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new GLView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleImage.cpp b/samplecode/SampleImage.cpp
new file mode 100644
index 0000000..b0f8656
--- /dev/null
+++ b/samplecode/SampleImage.cpp
@@ -0,0 +1,159 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkPorterDuff.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkShaderExtras.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"
+};
+
+// ownership of the stream is transferred
+static bool SetImageRef(SkBitmap* bitmap, SkStream* stream,
+                        SkBitmap::Config pref, const char name[] = NULL)
+{
+    if (SkImageDecoder::DecodeStream(stream, bitmap, pref,
+                                     SkImageDecoder::kDecodeBounds_Mode)) {
+        SkASSERT(bitmap->config() != SkBitmap::kNo_Config);
+    
+        SkImageRef* ref = new SkImageRef_GlobalPool(stream, bitmap->config());
+        ref->setURI(name);
+        bitmap->setPixelRef(ref)->unref();
+        return true;
+    } else {
+        delete stream;
+        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();
+        }
+        
+        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..2c55ab0
--- /dev/null
+++ b/samplecode/SampleImageDir.cpp
@@ -0,0 +1,319 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkPorterDuff.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkShaderExtras.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);
+}
+
+// ownership of the stream is transferred
+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);
+#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());
+            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;
+        }
+        
+        SkScalar scale = SK_Scalar1 * 999/1000;
+//        scale = SK_Scalar1/2;
+        
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+   //     canvas->scale(scale, scale);
+        
+        SkScalar x = SkIntToScalar(32), y = SkIntToScalar(32);
+        SkPaint paint;
+        
+    //    x += fDX;
+    //    y += fDY;
+    
+//        paint.setLooper(new SkBlurDrawLooper(SkIntToScalar(12), 0, 0, 0xDD000000))->unref();
+        
+#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/SampleLayers.cpp b/samplecode/SampleLayers.cpp
new file mode 100644
index 0000000..fcf7107
--- /dev/null
+++ b/samplecode/SampleLayers.cpp
@@ -0,0 +1,259 @@
+#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 "SkShaderExtras.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkUtils.h"
+#include "SkKey.h"
+#include "SkPorterDuff.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->setXfermode(SkPorterDuff::CreateXfermode(SkPorterDuff::kDstIn_Mode))->unref();
+}
+
+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_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        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.setPorterDuffXfermode(SkPorterDuff::kDstIn_Mode);
+        canvas->drawRect(bounds, p);
+
+        canvas->restore();
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        this->inval(NULL);
+        
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+    virtual bool onClick(Click* click) {
+        return this->INHERITED::onClick(click);
+    }
+
+	virtual bool handleKey(SkKey key) {
+        this->inval(NULL);
+        return true;
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LayersView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleLines.cpp b/samplecode/SampleLines.cpp
new file mode 100644
index 0000000..eed0e5a
--- /dev/null
+++ b/samplecode/SampleLines.cpp
@@ -0,0 +1,147 @@
+#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 "SkShaderExtras.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 SkView {
+public:
+	LinesView()
+    {
+        unsigned r = 0x1F;
+        unsigned g = 0x3F;
+        for (unsigned a = 0; a <= 0xF; a++) {
+            unsigned scale = 16 - SkAlpha15To16(a);
+            unsigned sr = (a << 1) | (a >> 3);
+            unsigned dr = r * scale >> 4;
+            unsigned sg = (a << 2) | (a >> 2);
+            unsigned dg = g * scale >> 4;
+            
+            unsigned ssg = sg & ~(~(a >> 3) & 1);
+            
+            printf("4444 sa=%d sr=%d sg=%d da=%d dr=%d dg=%d total-r=%d total-g=%d %d\n",
+                   a, sr, sg, scale, dr, dg, sr+dr, sg+dg, ssg+dg);
+        }
+        
+        for (unsigned aa = 0; aa <= 0xFF; aa++) {
+            unsigned invScale = SkAlpha255To256(255 - aa);
+            unsigned dst = SkAlphaMul(0xFF, invScale);
+            printf("8888 sa=%02x dst=%02x sum=%d %s\n", aa, dst, aa+dst,
+                   (aa+dst) > 0xFF ? "OVERFLOW" : "");
+        }
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)
+    {
+        if (SampleCode::TitleQ(*evt))
+        {
+            SampleCode::TitleR(evt, "Lines");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void drawBG(SkCanvas* canvas)
+    {
+//        canvas->drawColor(0xFFDDDDDD);
+        canvas->drawColor(SK_ColorWHITE);
+   //     canvas->drawColor(SK_ColorBLACK);
+    }
+    
+    /*
+     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 onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(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 SkView 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..3cc0c81
--- /dev/null
+++ b/samplecode/SampleMeasure.cpp
@@ -0,0 +1,139 @@
+#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 "SkShaderExtras.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 (int 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 SkView {
+public:
+    SkPaint fPaint;
+
+	MeasureView()
+    {
+        fPaint.setAntiAlias(true);
+        fPaint.setTextSize(SkIntToScalar(64));
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)
+    {
+        if (SampleCode::TitleQ(*evt))
+        {
+            SampleCode::TitleR(evt, "Measure");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas)
+    {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(canvas);
+        
+        canvas->translate(fPaint.getTextSize(), fPaint.getTextSize());
+        doMeasure(canvas, fPaint, "Hamburgefons");
+    }
+    
+    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:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new MeasureView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleMovie.cpp b/samplecode/SampleMovie.cpp
new file mode 100644
index 0000000..ed1a844
--- /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() {
+        fMovie->safeUnref();
+    }
+
+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..5c075b5
--- /dev/null
+++ b/samplecode/SampleNinePatch.cpp
@@ -0,0 +1,63 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkNinePatch.h"
+#include "SkPaint.h"
+#include "SkUnPreMultiply.h"
+
+class NinePatchView : public SkView {
+public:
+    SkBitmap fBM;
+
+	NinePatchView() {
+        SkImageDecoder::DecodeFile("/skimages/folder_background.9.png", &fBM);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "NinePatch");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+        
+        canvas->drawBitmap(fBM, 0, 0);
+        
+        SkIRect margins;
+        SkRect  dst;
+        int d = 25;
+        
+        margins.set(d, d, d, d);
+        dst.set(0, 0, SkIntToScalar(200), SkIntToScalar(200));
+        dst.offset(SkIntToScalar(fBM.width()), 0);
+        dst.offset(SkIntToScalar(2), SkIntToScalar(2));
+        
+        SkNinePatch::DrawNine(canvas, dst, fBM, margins);
+        
+        int cx = fBM.width()/2;
+        int cy = fBM.height()/2;
+        SkPMColor pm = *fBM.getAddr32(cx, cy);
+        SkColor c = SkUnPreMultiply::PMColorToColor(pm);
+        SkColor pm2 = SkPreMultiplyColor(c);
+        //SkDebugf("--- pm %x c %x pm2 %x\n", pm, c, pm2);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new NinePatchView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleOverflow.cpp b/samplecode/SampleOverflow.cpp
new file mode 100644
index 0000000..229683f
--- /dev/null
+++ b/samplecode/SampleOverflow.cpp
@@ -0,0 +1,106 @@
+#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 SkView {
+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);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+
+        DrawRoundRect();
+        TestOverflowHitTest();
+    }
+    
+private:
+    typedef SkView 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..6b1adfd
--- /dev/null
+++ b/samplecode/SamplePageFlip.cpp
@@ -0,0 +1,173 @@
+#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(reinterpret_cast<SkColor>(ref) | (0xFF << 24));
+    
+    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, SkPorterDuff::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 SkView {
+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);
+        }
+    }
+    
+    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);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+//        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(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 SkView 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..2e55db2
--- /dev/null
+++ b/samplecode/SamplePatch.cpp
@@ -0,0 +1,418 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkPorterDuff.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkImageRef.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/beach.jpg", &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() { 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(300), 0);
+    patch->draw(canvas, paint, 10, 10, true, false);
+    canvas->translate(SkIntToScalar(300), 0);
+    patch->draw(canvas, paint, 10, 10, false, true);
+    canvas->translate(SkIntToScalar(300), 0);
+    patch->draw(canvas, paint, 10, 10, true, true);
+}
+
+class PatchView : public SkView {
+    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(90);
+        const SkScalar T = SkIntToScalar(64);
+        fPts[0].set(S*1, T);
+        fPts[1].set(S*2, T);
+        fPts[2].set(S*3, T);
+        fPts[3].set(S*4, T);
+        fPts[4].set(S*4, T*2);
+        fPts[5].set(S*4, T*3);
+        fPts[6].set(S*4, T*4);
+        fPts[7].set(S*3, T*4);
+        fPts[8].set(S*2, T*4);
+        fPts[9].set(S*1, T*4);
+        fPts[10].set(S*1, T*3);
+        fPts[11].set(S*1, T*2);
+    }
+    
+    virtual ~PatchView() {
+        fShader0->safeUnref();
+        fShader1->safeUnref();
+    }
+    
+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);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorGRAY);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        SkPaint paint;
+        paint.setDither(true);
+        paint.setFilterBitmap(true);
+
+        if (false) {
+            SkPath p;
+            p.moveTo(0, 0);
+            p.lineTo(SkIntToScalar(30000), SkIntToScalar(30000));
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(SkIntToScalar(4));
+            paint.setAntiAlias(true);
+            canvas->scale(SkIntToScalar(3), SkIntToScalar(3));
+            canvas->drawPath(p, paint);
+            return;
+        }
+        
+        if (false) {
+            for (int dy = -1; dy <= 2; dy++) {
+                canvas->save();
+                if (dy == 2) {
+                    canvas->translate(0, SK_Scalar1/2);
+                } else {
+                    canvas->translate(0, SkIntToScalar(dy)/100);
+                }
+            
+                SkBitmap bm;
+                bm.setConfig(SkBitmap::kARGB_8888_Config, 20, 20);
+                bm.allocPixels();
+                SkCanvas c(bm);
+                SkRect r = { 0, 0, 20*SK_Scalar1, SK_Scalar1 };
+                for (int y = 0; y < 20; y++) {
+                    SkPaint p;
+                    p.setARGB(0xFF, y*5&0xFF, y*13&0xFF, y*29&0xFF);
+                    c.drawRect(r, p);
+                    r.offset(0, SK_Scalar1);
+                }
+                SkIRect src;
+                SkRect  dst;
+                
+                static const int srcPts[] = {
+                 //   2, 0, 15, 2,
+                    2, 2, 15, 16,
+                    17, 2, 2, 16,
+                    19, 2, 1, 16,
+                //    2, 18, 15, 2
+                };
+                static const double dstPts[] = {
+                //    7, 262 15, 24.5,
+                    7, 286.5, 15, 16,
+                    22, 286.5, 5, 16,
+                    27, 286.5, 1, 16,
+                 //   7, 302.5, 15, 24.5
+                };
+                
+                SkPaint p;
+//                p.setFilterBitmap(true);
+                const int* s = srcPts;
+                const double* d = dstPts;
+                for (int i = 0; i < 3; i++) {
+                    src.set(s[0], s[1], s[0]+s[2], s[1]+s[3]);
+                    dst.set(SkDoubleToScalar(d[0]),
+                            SkDoubleToScalar(d[1]),
+                            SkDoubleToScalar(d[0]+d[2]),
+                            SkDoubleToScalar(d[1]+d[3]));
+                    canvas->drawBitmapRect(bm, &src, dst, &p);
+                    canvas->translate(SkDoubleToScalar(1), 0);
+                    s += 4;
+                    d += 4;
+                }
+                canvas->restore();
+                canvas->translate(SkIntToScalar(32), 0);
+            }
+            return;
+        }
+        
+        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 (int 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 SkView 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..04d006a
--- /dev/null
+++ b/samplecode/SamplePath.cpp
@@ -0,0 +1,164 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkPorterDuff.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+class PathView : public SkView {
+public:
+    int fDStroke, fStroke, fMinStroke, fMaxStroke;
+    SkPath fPath[6];
+    bool fShowHairline;
+    
+	PathView()
+    {
+        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));
+    }
+    
+    virtual ~PathView()
+    {
+    }
+    
+    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 drawBG(SkCanvas* canvas)
+    {
+        canvas->drawColor(0xFFDDDDDD);
+//        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    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 onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(canvas);
+        
+        canvas->translate(SkIntToScalar(50), SkIntToScalar(50));
+
+        static const SkPaint::Join gJoins[] = {
+            SkPaint::kBevel_Join,
+            SkPaint::kMiter_Join,
+            SkPaint::kRound_Join
+        };
+
+        for (int i = 0; i < SK_ARRAY_COUNT(gJoins); i++)
+        {
+            canvas->save();
+            for (int 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);
+    }
+    
+    virtual bool onClick(Click* click) 
+    {
+        return this->INHERITED::onClick(click);
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PathView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePathEffects.cpp b/samplecode/SamplePathEffects.cpp
new file mode 100644
index 0000000..4082288
--- /dev/null
+++ b/samplecode/SamplePathEffects.cpp
@@ -0,0 +1,283 @@
+#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 "SkShaderExtras.h"
+#include "Sk1DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkColorPriv.h"
+#include "SkPixelXorXfermode.h"
+
+static void test_grow(SkPath* path)
+{
+    for (int i = 0; i < 100000; i++)
+    {
+        path->lineTo(i, i);
+        path->lineTo(i, i*2);
+    }
+}
+
+#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 "SkPorterDuff.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.setPorterDuffXfermode(SkPorterDuff::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 SkView {
+    SkPath  fPath;
+    SkPoint fClickPt;
+public:
+	PathEffectView()
+    {
+        SkRandom    rand;
+        int         steps = 20;
+        SkScalar    dist = SkIntToScalar(500);
+        SkScalar    x = SkIntToScalar(20);
+        SkScalar    y = SkIntToScalar(50);
+        
+        fPath.moveTo(x, y);
+        for (int i = 0; i < steps; i++)
+        {
+            x += dist/steps;
+            fPath.lineTo(x, y + SkIntToScalar(rand.nextS() % 25));
+        }
+
+        fClickPt.set(SkIntToScalar(200), SkIntToScalar(200));
+    }
+	
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)
+    {
+            if (SampleCode::TitleQ(*evt))
+            {
+                SampleCode::TitleR(evt, "PathEffects");
+                return true;
+            }
+            return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas)
+    {
+        canvas->drawColor(0xFFDDDDDD);
+
+#if 0
+        SkPath path;
+        test_grow(&path);
+        SkPaint p;
+        
+        p.setAntiAlias(true);
+        p.setStyle(SkPaint::kStroke_Style);
+        p.setStrokeWidth(SK_Scalar1);
+        canvas->drawPath(path, p);
+        path.close();
+#endif
+    }
+    
+    virtual void onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(canvas);
+        
+        if (true)
+        {
+            canvas->drawColor(SK_ColorWHITE);
+            
+            SkPixelXorXfermode  mode(SK_ColorWHITE);
+            SkPaint             paint;
+            
+            paint.setColor(SK_ColorRED);
+            paint.setXfermode(&mode);
+            paint.setStrokeWidth(SkIntToScalar(8));
+            
+            canvas->drawLine(SkIntToScalar(100), SkIntToScalar(100),
+                             SkIntToScalar(200), SkIntToScalar(200), paint);
+            canvas->drawLine(SkIntToScalar(100), SkIntToScalar(200),
+                             SkIntToScalar(200), SkIntToScalar(100), paint);
+         //   return;
+        }
+        
+        if (false)
+        {
+            SkPath  path;
+            SkPoint pts[] = { SkIntToScalar(100), SkIntToScalar(100),
+                              SkIntToScalar(200), SkIntToScalar(100),
+                              SkIntToScalar(100), SkIntToScalar(200)
+                            };
+            SkPaint paint;
+            
+            pts[2] = fClickPt;
+
+            paint.setAntiAlias(true);
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(SkIntToScalar(5));
+            
+            path.moveTo(pts[0]);
+            path.arcTo(pts[1], pts[2], SkIntToScalar(50));
+            canvas->drawPath(path, paint);
+            
+            paint.setStrokeWidth(0);
+            paint.setColor(SK_ColorRED);
+            canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
+            canvas->drawLine(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, paint);
+            return;
+        }
+        
+        gPhase -= SK_Scalar1;
+        this->inval(nil);
+        
+        SkPaint paint;
+        
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(SkIntToScalar(5));
+        canvas->drawPath(fPath, paint);
+        paint.setStrokeWidth(0);
+        
+        paint.setColor(SK_ColorRED);
+        paint.setPathEffect(make_pe(1))->unref();
+        canvas->drawPath(fPath, paint);
+        
+        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);
+        
+        {
+            SkRect  oval;
+            
+            oval.set(SkIntToScalar(50), SkIntToScalar(100),
+                     SkIntToScalar(150), SkIntToScalar(150));
+            canvas->drawRoundRect(oval, SkIntToScalar(8), SkIntToScalar(8), paint);
+        }
+        
+        {
+            SkRect  bounds;
+            SkPaint paint;
+            
+            paint.setAntiAlias(true);
+            paint.setAlpha(0x80);
+            paint.setColorFilter(
+                SkColorFilter::CreatePorterDuffFilter(
+                    SkColorSetARGB(0x44, 0, 0xFF, 0), SkPorterDuff::kSrcATop_Mode))->unref();
+            
+            bounds.set(SkIntToScalar(10), SkIntToScalar(10), SkIntToScalar(150), SkIntToScalar(70));
+            canvas->saveLayer(&bounds, &paint,
+                              (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
+            
+            paint.setColorFilter(NULL);
+            paint.setColor(SK_ColorRED);
+            canvas->drawOval(bounds, paint);
+
+            paint.setColor(SK_ColorBLUE);
+            paint.setAlpha(0x80);
+            bounds.inset(SkIntToScalar(10), SkIntToScalar(10));
+            canvas->drawOval(bounds, paint);
+            
+            canvas->restore();
+        }
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PathEffectView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePicture.cpp b/samplecode/SamplePicture.cpp
new file mode 100644
index 0000000..48de7df
--- /dev/null
+++ b/samplecode/SamplePicture.cpp
@@ -0,0 +1,158 @@
+#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 "SkPicture.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+
+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 SkView {
+public:
+	PictureView() {
+        fPicture = new SkPicture;
+        SkCanvas* canvas = fPicture->beginRecording(100, 100);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        
+        drawCircle(canvas, 50, SK_ColorBLACK);
+        fSubPicture = new SkPicture;
+        canvas->drawPicture(*fSubPicture);
+        canvas->translate(SkIntToScalar(50), 0);
+        canvas->drawPicture(*fSubPicture);
+        canvas->translate(0, SkIntToScalar(50));
+        canvas->drawPicture(*fSubPicture);
+        canvas->translate(SkIntToScalar(-50), 0);
+        canvas->drawPicture(*fSubPicture);
+        // fPicture now has (4) references to us. We can release ours, and just
+        // unref fPicture in our destructor, and it will in turn take care of
+        // the other references to fSubPicture
+        fSubPicture->unref();
+    }
+    
+    virtual ~PictureView() {
+        fPicture->unref();
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Picture");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void drawBG(SkCanvas* canvas) {
+//        canvas->drawColor(0xFFDDDDDD);
+        canvas->drawColor(SK_ColorWHITE);
+   //     canvas->drawColor(SK_ColorBLACK);
+    }
+    
+    void drawSomething(SkCanvas* canvas) {
+        SkPaint paint;
+        
+        paint.setAntiAlias(true);
+    
+        paint.setColor(SK_ColorRED);
+        canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50),
+                           SkIntToScalar(40), paint);
+        paint.setColor(SK_ColorBLACK);
+        paint.setTextSize(SkIntToScalar(40));
+        canvas->drawText("Picture", 7, SkIntToScalar(50), SkIntToScalar(62),
+                         paint);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(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();
+        
+        // test that we can re-record a subpicture, and see the results
+        
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(250));
+        drawCircle(fSubPicture->beginRecording(50, 50), 25,
+                   fRand.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;
+    SkRandom    fRand;
+
+    typedef SkView 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..2c19658
--- /dev/null
+++ b/samplecode/SamplePoints.cpp
@@ -0,0 +1,122 @@
+#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 "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+
+static SkRandom gRand;
+
+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 }
+};
+
+#ifdef SK_DEBUG
+    #define REPEAT_COUNT    1
+#else
+    #define REPEAT_COUNT    5000
+#endif
+
+class PointsView : public SkView {
+    bool fAA;
+public:
+	PointsView() : fAA(false) {}
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)
+    {
+        if (SampleCode::TitleQ(*evt))
+        {
+            SampleCode::TitleR(evt, "Points");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void drawBG(SkCanvas* canvas)
+    {
+//        canvas->drawColor(0xFFDDDDDD);
+        canvas->drawColor(SK_ColorWHITE);
+   //     canvas->drawColor(SK_ColorBLACK);
+    }
+    
+    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 onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(canvas);
+        
+        canvas->translate(SK_Scalar1, SK_Scalar1);
+        
+        SkRandom rand;
+        SkPaint  p0, p1, p2, p3;
+        const size_t n = 99;
+        const int TIMES = 1;
+        
+        p0.setColor(SK_ColorRED);
+        p1.setColor(SK_ColorGREEN);
+        p2.setColor(SK_ColorBLUE);
+        p3.setColor(SK_ColorWHITE);
+        
+     //   fAA = !fAA;
+        
+        p0.setAntiAlias(fAA);
+        p1.setAntiAlias(fAA);
+        p2.setAntiAlias(fAA);
+        p3.setAntiAlias(fAA);
+        
+        p0.setStrokeWidth(SkIntToScalar(4));
+        p2.setStrokeWidth(SkIntToScalar(6));
+
+        SkPoint* pts = new SkPoint[n];
+        fill_pts(pts, n, &rand);
+
+//        SkMSec now = SkTime::GetMSecs();
+        for (int times = 0; times < TIMES; times++)
+        {
+            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);
+        }
+  //      printf("----- msecs %d\n", SkTime::GetMSecs() - now);
+        delete[] pts;
+    }
+    
+private:
+
+    typedef SkView 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..98e4484
--- /dev/null
+++ b/samplecode/SamplePolyToPoly.cpp
@@ -0,0 +1,165 @@
+#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 SkView {
+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);
+            SkDebugf("--- setPolyToPoly1 %d\n", success);
+
+            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);
+            SkDebugf("--- setPolyToPoly2 %d\n", success);
+            
+            {
+                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);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+    }
+
+    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 onDraw(SkCanvas* canvas) {
+        this->drawBG(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 SkView 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..8958c82
--- /dev/null
+++ b/samplecode/SampleRegion.cpp
@@ -0,0 +1,329 @@
+#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_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 paint_rgn(SkCanvas* canvas, const SkRegion& rgn, const SkPaint& paint)
+{
+    SkRegion::Iterator  iter(rgn);
+    
+    for (; !iter.done(); iter.next())
+    {
+        SkRect    r;
+        r.set(iter.rect());
+        canvas->drawRect(r, paint);
+    }
+}
+
+class RegionView : public SkView {
+public:
+	RegionView() 
+	{
+        fBase.set(100, 100, 150, 150);
+        fRect = fBase;
+        fRect.inset(5, 5);
+        fRect.offset(25, 25);
+    }
+
+    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);
+    }
+    
+    void drawBG(SkCanvas* canvas)
+    {
+        canvas->drawColor(0xFFDDDDDD);
+        return;
+
+#if 0
+        SkColorTable    ct;
+        SkPMColor       colors[] = { SK_ColorRED, SK_ColorBLUE };
+        ct.setColors(colors, 2);
+        ct.setFlags(ct.getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+
+        SkBitmap        bm;
+        bm.setConfig(SkBitmap::kIndex8_Config, 20, 20, 21);
+        bm.setColorTable(&ct);
+        bm.allocPixels();
+        sk_memset16((uint16_t*)bm.getAddr8(0, 0), 0x0001, bm.rowBytes() * bm.height() / 2);
+#endif
+#if 0
+        SkBitmap        bm;
+        bm.setConfig(SkBitmap::kRGB_565_Config, 20, 20, 42);
+        bm.allocPixels();
+        sk_memset32((uint32_t*)bm.getAddr16(0, 0), 0x0000FFFF, bm.rowBytes() * bm.height() / 4);
+#endif
+#if 1
+        SkBitmap        bm;
+        bm.setConfig(SkBitmap::kARGB_8888_Config, 20, 20);
+        bm.allocPixels();
+        sk_memset32((uint32_t*)bm.getAddr32(0, 0), 0xFFDDDDDD, bm.rowBytes() * bm.height() / 4);
+#endif
+
+        SkPaint paint;
+
+//        SkShader* shader = SkShader::CreateBitmapShader(bm, false, SkPaint::kBilinear_FilterType, SkShader::kRepeat_TileMode);
+        SkPoint pts[] = { 0, 0, SkIntToScalar(100), SkIntToScalar(0) };
+        SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
+        SkShader* shader = SkGradientShader::CreateLinear(pts, colors, nil, 2, SkShader::kMirror_TileMode);
+        paint.setShader(shader)->unref();
+
+        canvas->drawPaint(paint);
+    }
+
+    virtual void onDraw(SkCanvas* canvas)
+    {
+        if (true) {
+            SkRect r = { 0, 0, 1 << 30, 1 << 30 };
+            bool open = canvas->clipRect(r);
+            SkDebugf("---- giant clip is %d\n", open);
+        }
+        this->drawBG(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 (int 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);
+
+            if (true)
+            {
+                canvas->save();
+                canvas->translate(0, SkIntToScalar(200));
+                this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
+                canvas->restore();
+            }
+            
+            canvas->translate(SkIntToScalar(200), 0);
+        }
+
+        if (false)
+        {
+            SkBitmap    bitmap;
+            
+            bitmap.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+            bitmap.allocPixels();
+            bitmap.eraseColor(0);
+
+            SkCanvas    canvas(bitmap);
+            SkPaint     paint;
+            SkRect      r;
+            
+            paint.setAntiAlias(true);
+            paint.setARGB(0xFF, 0xFF, 0, 0xFF);
+            r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
+            canvas.drawOval(r, paint);
+
+            SkImageEncoder* en = SkImageEncoder::Create(SkImageEncoder::kPNG_Type);
+            en->encodeFile("testfile.png", bitmap);
+            delete en;
+        }
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) 
+    {
+        return fRect.contains(SkScalarRound(x), SkScalarRound(y)) ? new Click(this) : nil;
+    }
+    
+    virtual bool onClick(Click* click) 
+    {
+        fRect.offset(click->fICurr.fX - click->fIPrev.fX,
+                     click->fICurr.fY - click->fIPrev.fY);
+        this->inval(nil);
+        return true;
+    }
+    
+private:
+    SkIRect    fBase, fRect;
+    
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new RegionView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleShaders.cpp b/samplecode/SampleShaders.cpp
new file mode 100644
index 0000000..4d82182
--- /dev/null
+++ b/samplecode/SampleShaders.cpp
@@ -0,0 +1,157 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkPorterDuff.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkShaderExtras.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 = SkPorterDuff::CreateXfermode(SkPorterDuff::kDstIn_Mode);
+
+    SkShader* shader = new SkComposeShader(shaderB, shaderA, mode);
+    shaderA->unref();
+    shaderB->unref();
+    mode->unref();
+    
+    return shader;
+}
+
+class ShaderView : public SkView {
+public:
+    SkShader*   fShader;
+    SkBitmap    fBitmap;
+
+	ShaderView()
+    {
+        SkImageDecoder::DecodeFile("/cover.png", &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 = SkPorterDuff::CreateXfermode(SkPorterDuff::kDstIn_Mode);
+
+        fShader = new SkComposeShader(shaderA, shaderB, mode);
+        shaderA->unref();
+        shaderB->unref();
+        mode->unref();
+    }
+    virtual ~ShaderView()
+    {
+        fShader->safeUnref();
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)
+    {
+            if (SampleCode::TitleQ(*evt))
+            {
+                SampleCode::TitleR(evt, "Shaders");
+                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->drawBitmap(fBitmap, 0, 0);
+        
+        {
+            SkIRect src;
+            SkRect  dst;
+            
+            src.set(20, 50, 120, 70);
+            dst.set(src);
+            dst.offset(SkIntToScalar(300), 0);
+
+            canvas->drawBitmapRect(fBitmap, &src, dst);
+        }
+
+        canvas->translate(SkIntToScalar(80), SkIntToScalar(80));
+        
+        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);
+
+        r.set(0, 0, SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height()));
+
+        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 SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ShaderView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleStrokeText.cpp b/samplecode/SampleStrokeText.cpp
new file mode 100644
index 0000000..8b78585
--- /dev/null
+++ b/samplecode/SampleStrokeText.cpp
@@ -0,0 +1,148 @@
+#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 "SkShaderExtras.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 (int 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.computeBounds(&bounds, SkPath::kExact_BoundsType);
+    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.setPorterDuffXfermode(SkPorterDuff::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 (int 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.computeBounds(&bounds, SkPath::kExact_BoundsType);
+    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.setPorterDuffXfermode(SkPorterDuff::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 SkView {
+    bool fAA;
+public:
+	StrokeTextView() : fAA(false) {}
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "StrokeText");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFF333333);
+        canvas->drawColor(0xFFCC8844);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(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 SkView 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..d021672
--- /dev/null
+++ b/samplecode/SampleTests.cpp
@@ -0,0 +1,99 @@
+#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 "SkShaderExtras.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkUtils.h"
+#include "SkKey.h"
+#include "SkPorterDuff.h"
+#include "SkXfermode.h"
+#include "SkDrawFilter.h"
+
+#include "test.h"
+
+class TestsView : public SkView {
+public:
+    skia::Test::Iter fIter;
+
+	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);
+        
+        skia::Test* test = fIter.next();
+        if (NULL == test) {
+            fIter.reset();
+            test = fIter.next();
+        }
+        
+        SkIPoint    size;
+        test->getSize(&size);
+        
+        SkBitmap    bitmap;
+        bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.fX, size.fY);
+        bitmap.allocPixels();
+        bitmap.eraseColor(0);
+        
+        SkCanvas c(bitmap);
+        test->draw(&c);
+        
+        canvas->drawBitmap(bitmap, SkIntToScalar(10), SkIntToScalar(10), NULL);
+        
+        SkString str;
+        test->getString(skia::Test::kTitle, &str);
+        SkDebugf("--- %s\n", str.c_str());
+        delete 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..9fc3708
--- /dev/null
+++ b/samplecode/SampleText.cpp
@@ -0,0 +1,790 @@
+#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 "SkShaderExtras.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); }
+
+//#ifdef TEST_CLICKX
+
+static void test_typefaceCache()
+{
+    SkTypeface* t0 = SkTypeface::Create("sans-serif", SkTypeface::kNormal);
+    SkTypeface* t1 = SkTypeface::Create(NULL, SkTypeface::kNormal);
+    SkTypeface* t2 = SkTypeface::Create("arial", SkTypeface::kNormal);
+    SkTypeface* t3 = SkTypeface::Create("helvetica", SkTypeface::kItalic);
+    
+    SkASSERT(t0 == t1);
+    SkASSERT(t0 == t2);
+    SkASSERT(t0 == t3);
+}
+
+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 = n;
+        mm = m;
+    }
+
+    nn = paint.breakText(text, length, width, &mm);
+    SkASSERT(nn == 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 }
+};
+
+#ifdef SK_DEBUG
+    #define REPEAT_COUNT    1
+#else
+    #define REPEAT_COUNT    5000
+#endif
+
+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("    ");
+}
+
+#if 0
+#include "badrects.cpp"
+
+static void make_badrgn(SkRegion* rgn, int insetAmount)
+{
+    SkRect16    r, bounds;
+    int         i;
+    
+    rgn->setEmpty();
+    bounds.setEmpty();
+
+    for (i = 0; i < SK_ARRAY_COUNT(badrects); i++)
+    {
+        SkASSERT(badrects[i].width > 0 && badrects[i].height > 0);
+
+        r.set(badrects[i].x, badrects[i].y, badrects[i].x + badrects[i].width, badrects[i].y + badrects[i].height);
+        r.inset(insetAmount, insetAmount);
+        rgn->op(r, SkRegion::kUnion_Op);
+        bounds.join(r);
+    }
+    SkASSERT(bounds == rgn->getBounds());
+
+    for (i = 0; i < SK_ARRAY_COUNT(badrects); i++)
+    {
+        r.set(badrects[i].x, badrects[i].y, badrects[i].x + badrects[i].width, badrects[i].y + badrects[i].height);        
+        SkASSERT(rgn->contains(r));
+    }
+}
+#endif
+
+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 test_poly()
+{
+    static const SkPoint dst[] = {
+        SkIntToScalar(2), SkIntToScalar(1),
+        SkIntToScalar(5), SkIntToScalar(1),
+        SkIntToScalar(5), SkIntToScalar(3),
+        SkIntToScalar(2), SkIntToScalar(3)
+    };
+    
+    static const SkPoint src[] = {
+        SkIntToScalar(0), SkIntToScalar(0),
+        SkIntToScalar(1), SkIntToScalar(0),
+        SkIntToScalar(1), SkIntToScalar(1),
+        SkIntToScalar(0), SkIntToScalar(1)
+    };
+    
+    SkMatrix matrix;
+    
+    if (matrix.setPolyToPoly(src, dst, 4))
+    {
+        SkPoint pt = { SK_Scalar1/2, SK_Scalar1/2 };
+        matrix.mapPoints(&pt, 1);        
+        printf("---- x = %g y = %g\n", SkScalarToFloat(pt.fX), SkScalarToFloat(pt.fY));
+    }
+    else
+        printf("---- setPolyToPoly failed\n");
+}
+
+#include "SkColorShader.h"
+
+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 TEST_CLICKX
+    test_break(canvas, text, length, x, y, p, clickX);
+#endif
+
+#ifdef SK_DEBUG
+    if (false)
+    {
+        SkColorShader   shader;
+        p.setShader(&shader);
+        x += SkIntToScalar(180);
+        canvas->drawText(text, length, x, y, p);
+        p.setShader(NULL);
+    }
+
+    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 SkView {
+public:
+	TextSpeedView()
+    {
+        fMF = makemf();
+
+        fHints = 0;
+
+        if (false)
+        {
+            static const char extra[] = { '.', ',', ':', ';', '!' };
+            SkPaint   paint, paint2;
+
+            paint2.setTypeface(SkTypeface::Create(NULL, SkTypeface::kItalic))->unref();
+
+            for (int i = 0; i < 26; i++)
+                ::dump('a' + i, count_char_points(paint, 'a' + i), count_char_points(paint2, 'a' + i));
+            for (int j = 0; j < SK_ARRAY_COUNT(extra); j++)
+                ::dump(extra[j], count_char_points(paint, extra[j]), count_char_points(paint2, extra[j]));
+
+            printf("--- ave reduction = %g%%\n", 100. * (gOld - gNew) / gOld);
+        }
+        
+        if (true)
+        {
+            SkPoint pts[] = { SkIntToScalar(20), 0, SkIntToScalar(256+20), 0 };
+            SkColor colors[] = { SkColorSetARGB(0, 255, 255, 255), SkColorSetARGB(255, 255, 255, 255) };
+            fGradient = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
+        }
+        
+        fClickX = 0;
+
+        test_breakText();        
+        test_typefaceCache();
+        test_poly();
+    }
+    
+    virtual ~TextSpeedView()
+    {
+        fGradient->unref();
+        fMF->safeUnref();
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt)
+    {
+        if (SampleCode::TitleQ(*evt))
+        {
+            SampleCode::TitleR(evt, "Text");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas)
+    {
+//        canvas->drawColor(0xFFDDDDDD);
+        canvas->drawColor(SK_ColorWHITE);
+   //     canvas->drawColor(SK_ColorBLACK);
+    }
+    
+    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 onDraw(SkCanvas* canvas)
+    {
+        if (false)
+        {
+            canvas->translate(SkIntToScalar(480), 0);
+            canvas->rotate(SkIntToScalar(90));
+        }
+        
+        this->drawBG(canvas);
+        
+        if (false)
+        {
+            SkPaint p;
+            
+            p.setAntiAlias(true);
+            p.setSubpixelText(true);
+         //   p.setLinearText(true);
+            
+            SkScalar size = SkIntToScalar(6);
+            SkMSec   dur = 0;
+            const int LOOP = 16;
+            const int TIMES = 10;
+            
+            for (int times = 0; times < TIMES; times++)
+            {
+                SkMSec now = SkTime::GetMSecs();
+                for (int loop = 0; loop < LOOP; loop++)
+                {
+                    p.setTextSize(size);
+                    size += SK_Scalar1/5;
+                    canvas->drawText("Hamburgefons", 12, SkIntToScalar(10), SkIntToScalar(50), p);
+                }
+                dur += SkTime::GetMSecs() - now;
+                SkGraphics::SetFontCacheUsed(0);
+            }
+            
+            printf("----- duration = %g\n", dur * 1.0 / TIMES);
+            this->inval(NULL);
+            return;
+        }
+        
+        if (false)
+        {
+            SkPaint p;
+            p.setAntiAlias(true);
+            for (int i = 6; i <= 36; i++)
+            {
+                SkRect r;
+                SkPaint::FontMetrics m;
+                p.setTextSize(SkIntToScalar(i));
+                p.getFontMetrics(&m);
+                int ascent = SkScalarRound(m.fAscent);
+                int descent = SkScalarRound(m.fDescent);
+                for (uint8_t c = ' '; c <= 127; c++)
+                {
+                    p.getTextWidths(&c, 1, NULL, &r);
+                    if (SkScalarRound(r.fTop) < ascent)
+                        printf("PS %d --- %c [%d] top=%g, ascent=%g ymax=%g\n", i, c, c,
+                                SkScalarToFloat(r.fTop), SkScalarToFloat(m.fAscent), SkScalarToFloat(m.fTop));
+                    if (SkScalarRound(r.fBottom) > descent)
+                        printf("PS %d --- %c [%d] bottom=%g, descent=%g ymin=%g\n", i, c, c,
+                                SkScalarToFloat(r.fBottom), SkScalarToFloat(m.fDescent), SkScalarToFloat(m.fBottom));
+                }
+            }
+        }
+        
+        if (false)
+        {
+            SkPaint p;
+            p.setShader(fGradient);
+
+#ifdef SK_RELEASE
+            SkMSec now = SkTime::GetMSecs();
+            for (int i = 0; i < 100; i++)
+#endif
+            canvas->drawPaint(p);
+#ifdef SK_RELEASE
+            printf("----- %d ms\n", SkTime::GetMSecs() - now);
+            this->inval(NULL);
+#endif
+            return;
+        }
+        
+        if (false)
+        {
+            SkBitmap    bm;
+            
+            make_textstrip(&bm);
+            canvas->translate(0, SkIntToScalar(50));
+            for (int i = 0; i < 10; i++)
+            {
+                float gamma = 1 + i * 0.2f;
+                SkPowerMode mode(SkFloatToScalar(1 / gamma));
+                SkPaint     p;
+                p.setXfermode(&mode);
+                
+                canvas->drawBitmap(bm, 0, SkIntToScalar(i) * bm.height(), &p);
+            }
+            return;
+        }
+        
+        if (false)
+        {
+            SkPaint paint;
+            
+            paint.setAntiAlias(true);
+            paint.setDevKernText(true);
+            SkMSec now = SkTime::GetMSecs();
+            for (int i = 0; i < 1000000; i++)
+            {
+                paint.measureText("Hamburgefons", 15, NULL, NULL);
+            }
+            printf("--------- measure %d\n", SkTime::GetMSecs() - now);
+            this->inval(NULL);
+            return;
+        }
+
+        if (false)
+        {
+            SkRegion    rgn;
+            SkPath      path;
+            SkPaint     paint;
+            
+        //    make_badrgn(&rgn, -2);
+            
+            if (false)
+            {
+                paint.setColor(SK_ColorBLUE);
+                canvas->drawIRect(rgn.getBounds(), paint);
+            }
+            paint.setColor(SK_ColorRED);
+            draw_rgn(rgn, canvas, paint);
+            
+            rgn.getBoundaryPath(&path);
+            paint.setARGB(0x80, 0, 0, 0xFF);
+            canvas->drawPath(path, paint);
+            return;
+        }
+
+        if (false)
+        {
+            SkRect r = { SkIntToScalar(50), SkIntToScalar(50), SkIntToScalar(300), SkIntToScalar(300) };
+            SkPaint p;
+            
+            p.setStyle(SkPaint::kStroke_Style);
+            p.setAlpha(0x80);
+            p.setStrokeWidth(SkIntToScalar(20));
+            canvas->drawRect(r, p);
+        }
+        
+        if (false)
+        {
+            SkPaint p;
+            SkRect r = { SkIntToScalar(100), SkIntToScalar(100), SkIntToScalar(104), SkIntToScalar(104) };
+         //   r.offset(SK_ScalarHalf, SK_ScalarHalf);
+            p.setStyle(SkPaint::kStroke_Style);
+            p.setStrokeWidth(SK_Scalar1*2);
+        //    p.setAntiAliasOn(true);
+            canvas->drawRect(r, p);
+            return;
+        }
+        
+        if (false)
+        {
+            Sk64    aa, bb;
+            int64_t a = (int64_t)6062080 * -30596;
+            int64_t b = (int64_t)4816896 * 57957;
+            aa.setMul(6062080, -30596);
+            bb.setMul(4816896, 57957);
+
+            a += b;
+            b = a >> 16;
+
+//            SkFixed c = aa.addGetFixed(bb);
+            
+            printf("%d %d\n", (int)a, a >> 32);
+            
+            SkBitmap    bm;
+            SkPaint     paint;
+            SkScalar    scale = SkFloatToScalar(0.5625f);
+            SkScalar    x = SkIntToScalar(100);
+            SkScalar    y = SkIntToScalar(100);
+            
+            //paint.setFilterType(SkPaint::kBilinear_FilterType);
+            
+            SkImageDecoder::DecodeFile("/app_web_browser.png", &bm);
+            
+           // canvas->drawBitmap(bm, x, y, paint);
+            x += SkIntToScalar(100);
+            canvas->save();
+                canvas->translate(x, y);
+                canvas->scale(SkIntToScalar(2)/1, SkIntToScalar(2)/1);
+                canvas->translate(-x, -y);
+                canvas->drawBitmap(bm, x, y, &paint);
+            canvas->restore();
+            x += SkIntToScalar(100);
+            canvas->save();
+                canvas->translate(x, y);
+                canvas->scale(scale, scale);
+                canvas->translate(-x, -y);
+            //    canvas->drawBitmap(bm, x, y, paint);
+            canvas->restore();
+            return;
+        }
+        
+        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);
+
+//        paint.setTypeface(SkTypeface::Create(NULL, SkTypeface::kItalic))->unref();
+        paint.setAntiAlias(true);
+        paint.setFlags(paint.getFlags() | gHints[index].fFlags);
+        
+        SkMSec now = 0;
+        if (REPEAT_COUNT > 1)
+            now = SkTime::GetMSecs();
+
+        SkRect clip;
+        clip.set(SkIntToScalar(25), SkIntToScalar(34), SkIntToScalar(88), SkIntToScalar(155));
+        
+        if (0) {
+            canvas->clipRect(clip);
+        }
+
+        if (0) {
+            SkPath clipPath;        
+            clipPath.addOval(clip);
+            canvas->clipPath(clipPath);
+        }
+
+        const char* text = "Hamburgefons";
+        size_t length = strlen(text);
+
+#ifdef TEST_CLICKX
+        {
+            SkPaint p;
+            
+            p.setColor(SK_ColorGREEN);
+            p.setAntiAlias(true);
+            canvas->drawLine(fClickX, 0, fClickX, SkIntToScalar(1000), p);
+        }
+#endif
+
+        for (int j = 0; j < REPEAT_COUNT; j++)
+        {
+            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);
+            }
+        }
+        
+        if (REPEAT_COUNT > 1)
+        {
+            printf("--------- FPS = %g\n", REPEAT_COUNT * 1000. / (SkTime::GetMSecs() - now));
+            this->inval(NULL);
+        }
+    }
+    
+    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;
+    SkShader* fGradient;
+
+    typedef SkView 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..f2a6530
--- /dev/null
+++ b/samplecode/SampleTextAlpha.cpp
@@ -0,0 +1,123 @@
+#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 "SkPorterDuff.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkImageRef.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 SkView {
+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);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(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 SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TextAlphaView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTextEffects.cpp b/samplecode/SampleTextEffects.cpp
new file mode 100644
index 0000000..e335343
--- /dev/null
+++ b/samplecode/SampleTextEffects.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 "SkShaderExtras.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.setPorterDuffXfermode(SkPorterDuff::kSrc_Mode);
+    rast->addLayer(p);
+}
+
+static void r1(SkLayerRasterizer* rast, SkPaint& p)
+{
+    rast->addLayer(p);
+
+    p.setAlpha(0x40);
+    p.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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.setPorterDuffXfermode(SkPorterDuff::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::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.setPorterDuffXfermode(SkPorterDuff::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 SkView {
+    SkTypeface* fFace;
+public:
+	TextEffectView()
+    {
+        fFace = SkTypeface::CreateFromFile("/Users/reed/Downloads/p052024l.pfb");
+    }
+    
+    virtual ~TextEffectView()
+    {
+        fFace->safeUnref();
+    }
+
+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);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas)
+    {
+        this->drawBG(canvas);
+        
+        canvas->save();
+//        canvas->scale(SK_Scalar1*2, SK_Scalar1*2, 0, 0);
+
+        SkScalar    x = SkIntToScalar(20);
+        SkScalar    y = SkIntToScalar(40);
+        SkPaint     paint;
+        
+        paint.setAntiAlias(true);
+        paint.setTextSize(SkIntToScalar(48));
+        paint.setTypeface(SkTypeface::Create("sans-serif", SkTypeface::kBold));
+
+        SkString str("GOOGLE ");
+        str.appendUnichar(0x5700);
+
+        paint.setTypeface(fFace);
+        
+        for (int i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++)
+        {
+            apply_shader(&paint, i);
+            
+          //  paint.setMaskFilter(NULL);
+          //  paint.setColor(SK_ColorBLACK);
+
+#if 0
+            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);
+
+            if (0)
+            {
+                SkPath path;
+                paint.getTextPath(str.c_str(), str.size(), x + SkIntToScalar(260), y, &path);
+                canvas->drawPath(path, paint);
+            }
+
+            y += paint.getFontSpacing();
+        }
+
+        canvas->restore();
+        
+        if (0)
+        {
+            SkPoint pts[] = { 0, 0, 0, SkIntToScalar(150) };
+            SkColor colors[] = { 0xFFE6E6E6, 0xFFFFFFFF };
+            SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
+
+            paint.reset();
+            paint.setShader(s);
+            canvas->drawRectCoords(0, 0, SkIntToScalar(120), SkIntToScalar(150), paint);
+        }
+        
+        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);
+        }
+    }
+    
+    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 SkView 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..382b4d9
--- /dev/null
+++ b/samplecode/SampleTextOnPath.cpp
@@ -0,0 +1,443 @@
+#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 "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTypeface.h"
+#include "SkAvoidXfermode.h"
+
+#define REPEAT_COUNT    1
+
+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);
+        SkScalar tw0 = sum_widths(widths, count);
+        paint.getTextWidths(text, count, widths, rects);
+        SkScalar tw1 = sum_widths(widths, count);
+        SkASSERT(tw0 == tw1);
+
+        SkScalar w0 = paint.measureText(text, count, NULL);
+        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 = "Android";
+    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 SkView {
+public:
+    SkPath      fPath;
+    SkScalar    fHOffset;
+
+	TextOnPathView() {
+        SkRect r;
+        r.set(SkIntToScalar(100), SkIntToScalar(100),
+              SkIntToScalar(300), SkIntToScalar(300));
+        fPath.addOval(r);
+        
+        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);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+#if 0        
+        SkRect r;
+        SkPaint p;
+        SkRandom rand;
+        p.setAntiAlias(true);
+        
+        for (int i = 0; i < 100; i++) {
+            SkScalar x = rand.nextUScalar1() * 300 + SkIntToScalar(50);
+            SkScalar y = rand.nextUScalar1() * 200 + SkIntToScalar(50);
+            SkScalar w = rand.nextUScalar1() * 10;
+            SkScalar h = rand.nextUScalar1() * 10;
+            r.set(x, y, x + w, y + h);
+            canvas->drawRect(r, p);
+        }
+        
+        test_textBounds(canvas);
+//        return;
+
+        SkBitmap    bm;
+        if (SkImageDecoder::DecodeFile("/loading_tile.png",
+                                       &bm, SkBitmap::kRGB_565_Config, true))
+            canvas->drawBitmap(bm, 0, 0);
+#endif
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(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(SkIntToScalar(200), 0);
+        test_textpathmatrix(canvas);
+
+        test_bitmap_blur(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 SkView INHERITED;
+};
+
+static const uint16_t gTest0[] = { 0, 0, 1, 1 };
+static const uint16_t gTest1[] = { 1, 2, 3, 4, 5, 6 };
+static const uint16_t gTest2[] = { 0, 0, 0, 1, 2, 3, 3, 3 };
+static const uint16_t gTest3[] = { 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 0, 0, 1 };
+
+#include "SkRandom.h"
+static SkRandom gRand;
+static void rand_fill(uint16_t buffer[], int count) {
+    for (int i = 0; i < count; i++)
+        buffer[i] = (uint16_t)gRand.nextU();
+}
+
+static void test_pack16() {
+    static const struct {
+        const uint16_t* fSrc;
+        int             fCount;
+    } gTests[] = {
+        { gTest0, SK_ARRAY_COUNT(gTest0) },
+        { gTest1, SK_ARRAY_COUNT(gTest1) },
+        { gTest2, SK_ARRAY_COUNT(gTest2) },
+        { gTest3, SK_ARRAY_COUNT(gTest3) }
+    };
+    
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); i++) {
+        uint8_t dst[100];
+        size_t dstSize = SkPackBits::Pack16(gTests[i].fSrc,
+                                            gTests[i].fCount, dst);
+        printf("Test[%d] orig size = %d, dst size = %d",
+               i, gTests[i].fCount, (int)dstSize);
+        uint16_t src[100];
+        int srcCount = SkPackBits::Unpack16(dst, dstSize, src);
+        printf(", src size = %d", srcCount);
+        bool match = gTests[i].fCount == srcCount && memcmp(gTests[i].fSrc, src,
+                                    gTests[i].fCount * sizeof(uint16_t)) == 0;
+        printf(", match = %d\n", match);
+    }
+    
+    for (int n = 1000; n; n--) {
+        size_t size = 50;
+        uint16_t src[100], src2[100];
+        uint8_t dst[200];
+        rand_fill(src, size);
+
+        size_t dstSize = SkPackBits::Pack16(src, size, dst);
+        size_t maxSize = SkPackBits::ComputeMaxSize16(size);
+        SkASSERT(maxSize >= dstSize);
+
+        int srcCount = SkPackBits::Unpack16(dst, dstSize, src2);
+        SkASSERT(size == srcCount);
+        bool match = memcmp(src, src2, size * sizeof(uint16_t)) == 0;
+        SkASSERT(match);
+    }
+}
+
+static const uint8_t gTest80[] = { 0, 0, 1, 1 };
+static const uint8_t gTest81[] = { 1, 2, 3, 4, 5, 6 };
+static const uint8_t gTest82[] = { 0, 0, 0, 1, 2, 3, 3, 3 };
+static const uint8_t gTest83[] = { 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 0, 0, 1 };
+static const uint8_t gTest84[] = { 1, 0, 3, 0, 0, 0, 2, 1, 1, 2 };
+
+static void rand_fill(uint8_t buffer[], int count) {
+    for (int i = 0; i < count; i++)
+        buffer[i] = (uint8_t)((gRand.nextU() >> 8) & 0x3);
+}
+
+static void test_pack8() {
+    static const struct {
+        const uint8_t* fSrc;
+        int             fCount;
+    } gTests[] = {
+        { gTest80, SK_ARRAY_COUNT(gTest80) },
+        { gTest81, SK_ARRAY_COUNT(gTest81) },
+        { gTest82, SK_ARRAY_COUNT(gTest82) },
+        { gTest83, SK_ARRAY_COUNT(gTest83) },
+        { gTest84, SK_ARRAY_COUNT(gTest84) }
+    };
+    
+    for (size_t i = 4; i < SK_ARRAY_COUNT(gTests); i++) {
+        uint8_t dst[100];
+        size_t maxSize = SkPackBits::ComputeMaxSize8(gTests[i].fCount);
+        size_t dstSize = SkPackBits::Pack8(gTests[i].fSrc,
+                                           gTests[i].fCount, dst);
+        SkASSERT(dstSize <= maxSize);
+        printf("Test[%d] orig size = %d, dst size = %d", i,
+               gTests[i].fCount, (int)dstSize);
+        uint8_t src[100];
+        int srcCount = SkPackBits::Unpack8(dst, dstSize, src);
+        printf(", src size = %d", srcCount);
+        bool match = gTests[i].fCount == srcCount &&
+                    memcmp(gTests[i].fSrc, src,
+                           gTests[i].fCount * sizeof(uint8_t)) == 0;
+        printf(", match = %d\n", match);
+    }
+
+    for (size_t size = 1; size <= 512; size += 1) {
+        for (int n = 200; n; n--) {
+            uint8_t src[600], src2[600];
+            uint8_t dst[600];
+            rand_fill(src, size);
+
+            size_t dstSize = SkPackBits::Pack8(src, size, dst);
+            size_t maxSize = SkPackBits::ComputeMaxSize8(size);
+            SkASSERT(maxSize >= dstSize);
+
+            int srcCount = SkPackBits::Unpack8(dst, dstSize, src2);
+            SkASSERT(size == srcCount);
+            bool match = memcmp(src, src2, size * sizeof(uint8_t)) == 0;
+            SkASSERT(match);
+            
+            for (int j = 0; j < 200; j++) {
+                size_t skip = gRand.nextU() % size;
+                size_t write = gRand.nextU() % size;
+                if (skip + write > size) {
+                    write = size - skip;
+                }
+                SkPackBits::Unpack8(src, skip, write, dst);
+                bool match = memcmp(src, src2 + skip, write) == 0;
+                SkASSERT(match);
+            }
+        }
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() {
+    static bool gOnce;
+    if (!gOnce) {
+//        test_pack8();
+        gOnce = true;
+    }
+    return new TextOnPathView;
+}
+
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTiling.cpp b/samplecode/SampleTiling.cpp
new file mode 100644
index 0000000..559d9d4
--- /dev/null
+++ b/samplecode/SampleTiling.cpp
@@ -0,0 +1,171 @@
+#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 "SkTypeface.h"
+
+// effects
+#include "SkGradientShader.h"
+#include "SkShaderExtras.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 SkView {
+    SkBlurDrawLooper    fLooper;
+public:
+	TilingView()
+            : fLooper(SkIntToScalar(1), SkIntToScalar(2), SkIntToScalar(2),
+                      0x88000000) {
+        for (int 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);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        SkRect r = { 0, 0, SkIntToScalar(gWidth*2), SkIntToScalar(gHeight*2) };
+
+        static const char* gConfigNames[] = { "8888", "565", "4444" };
+    
+        static const bool           gFilters[] = { false, true };
+        static const char*          gFilterNames[] = {     "point",                     "bilinear" };
+    
+        static const SkShader::TileMode gModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode };
+        static const char*          gModeNames[] = {    "C",                    "R",                   "M" };
+
+        SkScalar y = SkIntToScalar(24);
+        SkScalar x = SkIntToScalar(10);
+
+        for (int kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
+            for (int ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
+                SkPaint p;
+                SkString str;
+                p.setAntiAlias(true);
+                p.setDither(true);
+                p.setLooper(&fLooper);
+                str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]);
+
+                p.setTextAlign(SkPaint::kCenter_Align);
+                canvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);
+                
+                x += r.width() * 4 / 3;
+            }
+        }
+        
+        y += SkIntToScalar(16);
+
+        for (int i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
+            for (int j = 0; j < SK_ARRAY_COUNT(gFilters); j++) {
+                x = SkIntToScalar(10);
+                for (int kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
+                    for (int ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
+                        SkPaint paint;
+                        setup(&paint, fTexture[i], gFilters[j], gModes[kx], gModes[ky]);
+                        paint.setDither(true);
+                        
+                        canvas->save();
+                        canvas->translate(x, y);
+                        canvas->drawRect(r, paint);
+                        canvas->restore();
+                        
+                        x += r.width() * 4 / 3;
+                    }
+                }
+                {
+                    SkPaint p;
+                    SkString str;
+                    p.setAntiAlias(true);
+                    p.setLooper(&fLooper);
+                    str.printf("%s, %s", gConfigNames[i], gFilterNames[j]);
+                    canvas->drawText(str.c_str(), str.size(), x, y + r.height() * 2 / 3, p);
+                }
+
+                y += r.height() * 4 / 3;
+            }
+        }
+        
+    #ifdef SK_RELEASE
+        this->inval(NULL);
+    #endif
+    }
+    
+    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 TilingView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTypeface.cpp b/samplecode/SampleTypeface.cpp
new file mode 100644
index 0000000..e78c525
--- /dev/null
+++ b/samplecode/SampleTypeface.cpp
@@ -0,0 +1,92 @@
+#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 "SkShaderExtras.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 TypefaceView : public SkView {
+    SkTypeface* fFaces[gFaceCount];
+
+public:
+	TypefaceView() {
+        for (int i = 0; i < gFaceCount; i++) {
+            fFaces[i] = SkTypeface::Create(gFaces[i].fName, gFaces[i].fStyle);
+        }
+    }
+    
+    virtual ~TypefaceView() {
+        for (int i = 0; i < gFaceCount; i++) {
+            fFaces[i]->safeUnref();
+        }
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Typefaces");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(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;
+        
+        for (int i = 0; i < gFaceCount; i++) {
+            paint.setTypeface(fFaces[i]);
+            canvas->drawText(text, textLen, x, y, paint);
+            y += dy;
+        }
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TypefaceView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleVertices.cpp b/samplecode/SampleVertices.cpp
new file mode 100644
index 0000000..54dada2
--- /dev/null
+++ b/samplecode/SampleVertices.cpp
@@ -0,0 +1,279 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkPorterDuff.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkShaderExtras.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkImageRef.h"
+#include "SkOSFile.h"
+#include "SkStream.h"
+#include "SkNinePatch.h"
+
+void setup_vertexbug(SkPoint verts[], SkPoint texs[], uint16_t index[]);
+
+static void drawbug(SkCanvas* canvas, SkScalar scale) {
+    SkBitmap    bm, bm2;
+    
+    SkImageDecoder::DecodeFile("/skimages/btn_default_normal.9.png", &bm);
+    SkPaint paint;
+    
+    SkIRect subset;
+    subset.set(1, 1, bm.width() - 1, bm.height() - 1);
+    bm.extractSubset(&bm2, subset);
+    
+#if 0
+    SkPoint verts[16], texs[16];
+    uint16_t    index[54];
+    
+    SkShader* s = SkShader::CreateBitmapShader(bm2, SkShader::kClamp_TileMode,
+                                        SkShader::kClamp_TileMode);
+    paint.setShader(s)->unref();
+    
+    setup_vertexbug(verts, texs, index);
+    int indexCount = 6;    // 54
+    canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 16, verts, texs,
+                         NULL, NULL, &index[6], indexCount, paint);
+
+#if 0
+    paint.setShader(NULL);
+    canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 16, verts, NULL,
+                         NULL, NULL, index, indexCount, paint);
+#endif
+#else
+    SkRect  dst;
+    SkIRect margin;
+    
+    dst.set(SkIntToScalar(10), SkIntToScalar(10),
+            SkIntToScalar(100) + scale,
+            SkIntToScalar(40) + scale);
+    margin.set(9, 9, 9, 9);
+    SkNinePatch::DrawNine(canvas, dst, bm2, margin, NULL);
+#endif
+}
+
+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 VerticesView : public SkView {
+    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;
+    }
+    
+    virtual ~VerticesView() {
+        fShader0->safeUnref();
+        fShader1->safeUnref();
+    }
+    
+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);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorGRAY);
+    }
+    
+    SkScalar fScale;
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+#if 1
+        canvas->drawColor(SK_ColorWHITE);
+        canvas->translate(SK_Scalar1/2, SkIntToScalar(15) + SK_Scalar1/2);
+        canvas->scale(SkIntToScalar(3)/2, SkIntToScalar(3)/2);
+        drawbug(canvas, fScale);
+        fScale += SK_Scalar1/93;
+        this->inval(NULL);
+        return;
+#endif
+        
+        SkPaint paint;
+        paint.setDither(true);
+        paint.setFilterBitmap(true);
+        
+        for (int 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 SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new VerticesView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleXfermodes.cpp b/samplecode/SampleXfermodes.cpp
new file mode 100644
index 0000000..5a05fc8
--- /dev/null
+++ b/samplecode/SampleXfermodes.cpp
@@ -0,0 +1,266 @@
+#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 "SkShaderExtras.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 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);
+}
+
+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 SkView {
+    SkBitmap    fBitmap;
+    SkBitmap    fBG;
+    SkBitmap    fSrcB, fDstB;
+
+    void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha) {
+        SkPaint p;
+        
+        canvas->drawBitmap(fSrcB, 0, 0, &p);        
+        p.setAlpha(alpha);
+        p.setXfermode(mode);
+        canvas->drawBitmap(fDstB, 0, 0, &p);
+    }
+    
+public:
+	XfermodesView() {
+        const int W = 64;
+        const int H = 64;
+        
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, W, H);
+        fBitmap.allocPixels();
+        
+        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);
+    }
+
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+        return;
+        SkShader* s = SkShader::CreateBitmapShader(fBG,
+                                                   SkShader::kRepeat_TileMode,
+                                                   SkShader::kRepeat_TileMode);
+        SkPaint p;
+        SkMatrix m;
+        
+        p.setShader(s)->unref();
+        m.setScale(SkIntToScalar(8), SkIntToScalar(8));
+        s->setLocalMatrix(m);
+        canvas->drawPaint(p);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        if (false) {
+            test_srcover565(canvas);
+        }
+        
+        if (false) {
+            SkPaint paint;
+            paint.setFlags(0x43);
+            paint.setTextSize(SkIntToScalar(128));
+            SkMatrix matrix;
+            matrix.reset();
+            matrix.set(0, 0x0019d049);
+            matrix.set(2, 0x712cf400);
+            matrix.set(4, 0x0019c96f);
+            matrix.set(5, 0xF8d76598);
+            canvas->concat(matrix);
+            canvas->drawText("HamburgefonsHamburgefonsHamburgefonsHamburgefons",
+                             48, 0, 0, paint);
+            return;
+        }
+
+        const struct {
+            SkPorterDuff::Mode  fMode;
+            const char*         fLabel;
+        } gModes[] = {
+            { SkPorterDuff::kClear_Mode,    "Clear"     },
+            { SkPorterDuff::kSrc_Mode,      "Src"       },
+            { SkPorterDuff::kDst_Mode,      "Dst"       },
+            { SkPorterDuff::kSrcOver_Mode,  "SrcOver"   },
+            { SkPorterDuff::kDstOver_Mode,  "DstOver"   },
+            { SkPorterDuff::kSrcIn_Mode,    "SrcIn"     },
+            { SkPorterDuff::kDstIn_Mode,    "DstIn"     },
+            { SkPorterDuff::kSrcOut_Mode,   "SrcOut"    },
+            { SkPorterDuff::kDstOut_Mode,   "DstOut"    },
+            { SkPorterDuff::kSrcATop_Mode,  "SrcATop"   },
+            { SkPorterDuff::kDstATop_Mode,  "DstATop"   },
+            { SkPorterDuff::kXor_Mode,      "Xor"       },
+            { SkPorterDuff::kDarken_Mode,   "Darken"    },
+            { SkPorterDuff::kLighten_Mode,  "Lighten"   },
+            { SkPorterDuff::kMultiply_Mode, "Multiply"  },
+            { SkPorterDuff::kScreen_Mode,   "Screen"    }
+        };
+        
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
+        
+        SkCanvas c(fBitmap);
+        const SkScalar w = SkIntToScalar(fBitmap.width());
+        const SkScalar h = SkIntToScalar(fBitmap.height());
+        SkShader* s = SkShader::CreateBitmapShader(fBG,
+                                                   SkShader::kRepeat_TileMode,
+                                                   SkShader::kRepeat_TileMode);
+        SkMatrix m;
+        m.setScale(SkIntToScalar(6), SkIntToScalar(6));
+        s->setLocalMatrix(m);
+        
+        SkPaint labelP;
+        labelP.setAntiAlias(true);
+        labelP.setTextAlign(SkPaint::kCenter_Align);
+
+        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 = SkPorterDuff::CreateXfermode(gModes[i].fMode);
+
+                fBitmap.eraseColor(0);
+                draw_mode(&c, mode, twice ? 0x88 : 0xFF);
+                mode->safeUnref();
+                
+                SkPaint p;
+                SkRect r;
+                r.set(x, y, x+w, y+h);
+                r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+                p.setStyle(SkPaint::kStroke_Style);
+                canvas->drawRect(r, p);
+                p.setStyle(SkPaint::kFill_Style);
+                p.setShader(s);
+                r.inset(SK_ScalarHalf, SK_ScalarHalf);
+                canvas->drawRect(r, p);
+
+                canvas->drawBitmap(fBitmap, x, y, NULL);
+
+                canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel),
+                                 x + w/2, y - labelP.getTextSize()/2, labelP);
+
+                x += w + SkIntToScalar(10);
+                if ((i & 3) == 3) {
+                    x = x0;
+                    y += h + SkIntToScalar(30);
+                }
+            }
+            x0 += SkIntToScalar(330);
+        }
+        s->unref();
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new XfermodesView; }
+static SkViewRegister reg(MyFactory);
+
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/SkAnimate.h b/src/animator/SkAnimate.h
new file mode 100644
index 0000000..a4281d3
--- /dev/null
+++ b/src/animator/SkAnimate.h
@@ -0,0 +1,43 @@
+/* libs/graphics/animator/SkAnimate.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 SkAnimate_DEFINED
+#define SkAnimate_DEFINED
+
+#include "SkAnimateBase.h"
+#include "SkDisplayType.h"
+#include "SkIntArray.h"
+#include "SkUtils.h"
+
+class SkAnimate : public SkAnimateBase {
+    DECLARE_MEMBER_INFO(Animate);
+    SkAnimate();
+    virtual ~SkAnimate();
+    virtual int components();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void onEndElement(SkAnimateMaker& maker);
+protected:
+    bool resolveCommon(SkAnimateMaker& );
+    int fComponents;
+private:
+    typedef SkAnimateBase INHERITED;
+};
+
+#endif // SkAnimateField_DEFINED
+
diff --git a/src/animator/SkAnimate3DSchema.xsd b/src/animator/SkAnimate3DSchema.xsd
new file mode 100644
index 0000000..5063b75
--- /dev/null
+++ b/src/animator/SkAnimate3DSchema.xsd
@@ -0,0 +1,39 @@
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

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

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

+

+	<xs:simpleType name="Patch" >

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

+		</xs:restriction>

+	</xs:simpleType>

+

+	<xs:simpleType name="Point" >

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

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

+		</xs:restriction>

+	</xs:simpleType>

+

+	<xs:element name="camera">

+		<xs:complexType >

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

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

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

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

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

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

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

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

+		</xs:complexType>

+	</xs:element>

+

+	<xs:element name="patch">

+		<xs:complexType >

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

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

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

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

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

+		</xs:complexType>

+	</xs:element>

+

+</xs:schema>

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

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

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

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

+<XSDDesignerLayout />
diff --git a/src/animator/SkAnimateSet.cpp b/src/animator/SkAnimateSet.cpp
new file mode 100644
index 0000000..dd636da
--- /dev/null
+++ b/src/animator/SkAnimateSet.cpp
@@ -0,0 +1,98 @@
+/* libs/graphics/animator/SkAnimateSet.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 "SkAnimateSet.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateProperties.h"
+#include "SkParse.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkSet::fInfo[] = {
+    SK_MEMBER(begin, MSec),
+    SK_MEMBER(dur, MSec),
+    SK_MEMBER_PROPERTY(dynamic, Boolean),
+    SK_MEMBER(field, String),
+//  SK_MEMBER(formula, DynamicString),
+    SK_MEMBER(lval, DynamicString),
+//  SK_MEMBER_PROPERTY(reset, Boolean),
+    SK_MEMBER_PROPERTY(step, Int),
+    SK_MEMBER(target, DynamicString),
+    SK_MEMBER(to, DynamicString)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkSet);
+
+SkSet::SkSet() {
+    dur = 1; 
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkSet::dump(SkAnimateMaker* maker) {
+    INHERITED::dump(maker);
+    if (dur != 1) {
+#ifdef SK_CAN_USE_FLOAT
+        SkDebugf("dur=\"%g\" ", SkScalarToFloat(SkScalarDiv(dur,1000)));
+#else
+        SkDebugf("dur=\"%x\" ", SkScalarDiv(dur,1000));
+#endif
+    }
+    //don't want double />\n's
+    SkDebugf("/>\n");
+
+}
+#endif
+
+void SkSet::refresh(SkAnimateMaker& maker) {
+    fFieldInfo->setValue(maker, &fValues, 0, fFieldInfo->fCount, NULL, 
+        fFieldInfo->getType(), to);
+}
+
+void SkSet::onEndElement(SkAnimateMaker& maker) {
+    if (resolveCommon(maker) == false)
+        return;
+    if (fFieldInfo == NULL) {
+        maker.setErrorCode(SkDisplayXMLParserError::kFieldNotInTarget);
+        return;
+    }
+    fReset = dur != 1;
+    SkDisplayTypes outType = fFieldInfo->getType();
+    int comps = outType == SkType_String || outType == SkType_DynamicString ? 1 :
+        fFieldInfo->getSize((const SkDisplayable*) fTarget) / sizeof(int);
+    if (fValues.getType() == SkType_Unknown) {
+        fValues.setType(outType);
+        fValues.setCount(comps);
+        if (outType == SkType_String || outType == SkType_DynamicString)
+            fValues[0].fString = SkNEW(SkString);
+        else
+            memset(fValues.begin(), 0, fValues.count() * sizeof(fValues.begin()[0]));
+    } else {
+        SkASSERT(fValues.getType() == outType);
+        if (fFieldInfo->fType == SkType_Array)
+            comps = fValues.count();
+        else
+            SkASSERT(fValues.count() == comps);
+    }
+    if (formula.size() > 0) {
+        comps = 1;
+        outType = SkType_MSec;
+    }
+    fFieldInfo->setValue(maker, &fValues, fFieldOffset, comps, this, outType, formula.size() > 0 ? formula : to);
+    fComponents = fValues.count();
+}
diff --git a/src/animator/SkAnimateSet.h b/src/animator/SkAnimateSet.h
new file mode 100644
index 0000000..9f9ecfb
--- /dev/null
+++ b/src/animator/SkAnimateSet.h
@@ -0,0 +1,36 @@
+/* libs/graphics/animator/SkAnimateSet.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 SkAnimateSet_DEFINED
+#define SkAnimateSet_DEFINED
+
+#include "SkAnimate.h"
+
+class SkSet : public SkAnimate {
+    DECLARE_MEMBER_INFO(Set);
+    SkSet();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual void refresh(SkAnimateMaker& );
+private:
+    typedef SkAnimate INHERITED;
+};
+
+#endif // SkAnimateSet_DEFINED
+
diff --git a/src/animator/SkAnimator.cpp b/src/animator/SkAnimator.cpp
new file mode 100644
index 0000000..242398b
--- /dev/null
+++ b/src/animator/SkAnimator.cpp
@@ -0,0 +1,724 @@
+/* libs/graphics/animator/SkAnimator.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 "SkAnimator.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkDisplayApply.h"
+#include "SkDisplayMovie.h"
+#include "SkDisplayTypes.h"
+#include "SkDisplayXMLParser.h"
+#include "SkStream.h"
+#include "SkScript.h"
+#include "SkScript2.h" //   compiled script experiment
+#include "SkSystemEventTypes.h"
+#include "SkTypedArray.h"
+#ifdef ANDROID
+#include "SkDrawExtraPathEffect.h"
+#endif
+#ifdef SK_DEBUG
+#include "SkTime.h"
+#endif
+
+#if defined SK_BUILD_FOR_WIN32 && defined SK_DEBUG
+    #define _static
+    extern const char gMathPrimerText[];
+    extern const char gMathPrimerBinary[];
+#else
+    #define _static static
+#endif
+
+#if !defined SK_BUILD_FOR_BREW || defined SK_DEBUG
+    _static const char gMathPrimerText[] = 
+    "<screenplay>"
+        "<Math id=\"Math\"/>"
+        "<Number id=\"Number\"/>"
+    "</screenplay>";
+#endif
+
+#if defined SK_BUILD_FOR_BREW || defined SK_DEBUG
+    _static const char gMathPrimerBinary[] = 
+    "\x0Ascreenplay\x04Mathbid\x04Math@@";  // !!! now out of date -- does not include Number
+#endif
+
+#if defined SK_BUILD_FOR_BREW
+    #define gMathPrimer gMathPrimerBinary
+#else
+    #define gMathPrimer gMathPrimerText
+#endif
+
+SkAnimator::SkAnimator() : fMaker(NULL) {
+    initialize();
+}
+
+SkAnimator::~SkAnimator() {
+    SkDELETE(fMaker);
+}
+
+void SkAnimator::addExtras(SkExtras* extras) {
+    *fMaker->fExtras.append() = extras;
+}
+
+bool SkAnimator::appendStream(SkStream* stream) {
+    return decodeStream(stream);
+}
+
+bool SkAnimator::decodeMemory(const void* buffer, size_t size)
+{
+    fMaker->fFileName.reset();
+    SkDisplayXMLParser parser(*fMaker);
+    return parser.parse((const char*)buffer, size);
+}
+
+bool SkAnimator::decodeStream(SkStream* stream)
+{
+    SkDisplayXMLParser parser(*fMaker);
+    bool result = parser.parse(*stream);
+    fMaker->setErrorString();
+    return result;
+}
+
+bool SkAnimator::decodeDOM(const SkDOM& dom, const SkDOMNode* node)
+{
+    fMaker->fFileName.reset();
+    SkDisplayXMLParser parser(*fMaker);
+    return parser.parse(dom, node);
+}
+
+bool SkAnimator::decodeURI(const char uri[]) {
+//  SkDebugf("animator decode %s\n", uri);
+
+//    SkStream* stream = SkStream::GetURIStream(fMaker->fPrefix.c_str(), uri);
+    SkStream* stream = new SkFILEStream(uri);
+
+    SkAutoTDelete<SkStream> autoDel(stream);
+    setURIBase(uri);
+    return decodeStream(stream);
+}
+
+bool SkAnimator::doCharEvent(SkUnichar code) {
+    if (code == 0)
+        return false;
+    struct SkEventState state;
+    state.fCode = code;
+    fMaker->fEnableTime = fMaker->getAppTime();
+    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyChar, &state);
+    fMaker->notifyInval();
+    return result;
+}
+
+bool SkAnimator::doClickEvent(int clickState, SkScalar x, SkScalar y) {
+    SkASSERT(clickState >= 0 && clickState <= 2);
+    struct SkEventState state;
+    state.fX = x;
+    state.fY = y;
+    fMaker->fEnableTime = fMaker->getAppTime();
+    bool result = fMaker->fEvents.doEvent(*fMaker, 
+        clickState == 0 ? SkDisplayEvent::kMouseDown : 
+        clickState == 1 ? SkDisplayEvent::kMouseDrag : 
+        SkDisplayEvent::kMouseUp, &state);
+    fMaker->notifyInval();
+    return result;
+}
+
+bool SkAnimator::doKeyEvent(SkKey code) {
+    if (code == 0)
+        return false;
+    struct SkEventState state;
+    state.fCode = code;
+    fMaker->fEnableTime = fMaker->getAppTime();
+    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyPress, &state);
+    fMaker->notifyInval();
+    return result;
+}
+
+bool SkAnimator::doKeyUpEvent(SkKey code) {
+    if (code == 0)
+        return false;
+    struct SkEventState state;
+    state.fCode = code;
+    fMaker->fEnableTime = fMaker->getAppTime();
+    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kKeyPressUp, &state);
+    fMaker->notifyInval();
+    return result;
+}
+
+bool SkAnimator::doUserEvent(const SkEvent& evt) {
+    fMaker->fEnableTime = fMaker->getAppTime();
+    return onEvent(evt);
+}
+
+SkAnimator::DifferenceType SkAnimator::draw(SkCanvas* canvas, SkPaint* paint, SkMSec time) {
+    if (paint == NULL)
+        return draw(canvas, time);
+    fMaker->fScreenplay.time = time;
+    fMaker->fCanvas = canvas;
+    fMaker->fPaint = paint;
+    fMaker->fDisplayList.fHasUnion = false;
+    int result = fMaker->fDisplayList.draw(*fMaker, time);
+    if (result)
+        result += fMaker->fDisplayList.fHasUnion;
+    return (DifferenceType) result;
+}
+
+SkAnimator::DifferenceType SkAnimator::draw(SkCanvas* canvas, SkMSec time) {
+    SkPaint paint;
+    return draw(canvas, &paint, time);
+}
+    
+#ifdef SK_DEBUG
+void SkAnimator::eventDone(const SkEvent& ) {
+}
+#endif
+
+bool SkAnimator::findClickEvent(SkScalar x, SkScalar y) {
+    struct SkEventState state;
+    state.fDisable = true;
+    state.fX = x;
+    state.fY = y;
+    fMaker->fEnableTime = fMaker->getAppTime();
+    bool result = fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kMouseDown, &state);
+    fMaker->notifyInval();
+    return result;
+}
+
+const SkAnimator* SkAnimator::getAnimator(const SkDisplayable* displayable) const {
+    if (displayable->getType() != SkType_Movie)
+        return NULL;
+    const SkDisplayMovie* movie = (const SkDisplayMovie*) displayable;
+    return movie->getAnimator();
+}
+
+const SkDisplayable* SkAnimator::getElement(const char* id) {
+    SkDisplayable* element;
+    if (fMaker->find(id, &element) == false)
+        return NULL;
+    return (const SkDisplayable*) element;
+}
+
+SkElementType SkAnimator::getElementType(const SkDisplayable* ae) {
+    SkDisplayable* element = (SkDisplayable*) ae;
+    const SkMemberInfo* info = SkDisplayType::GetMembers(fMaker, element->getType(), NULL);
+    return (SkElementType) SkDisplayType::Find(fMaker, info);
+}
+
+SkElementType SkAnimator::getElementType(const char* id) {
+    const SkDisplayable* element = getElement(id);
+    return getElementType(element);
+}
+
+const SkMemberInfo* SkAnimator::getField(const SkDisplayable* ae, const char* field) {
+    SkDisplayable* element = (SkDisplayable*) ae;
+    const SkMemberInfo* info = element->getMember(field);
+    return (const SkMemberInfo*) info;
+}
+
+const SkMemberInfo* SkAnimator::getField(const char* elementID, const char* field) {
+    const SkDisplayable* element = getElement(elementID);
+    return getField(element, field);
+}
+
+SkFieldType SkAnimator::getFieldType(const SkMemberInfo* ai) {
+    const SkMemberInfo* info = (const SkMemberInfo*) ai;
+    return (SkFieldType) info->getType();
+}
+
+SkFieldType SkAnimator::getFieldType(const char* id, const char* fieldID) {
+    const SkMemberInfo* field = getField(id, fieldID);
+    return getFieldType(field);
+}
+
+ static bool getArrayCommon(const SkDisplayable* ae, const SkMemberInfo* ai,
+     int index, SkOperand* operand, SkDisplayTypes type) {
+    const SkDisplayable* element = (const SkDisplayable*) ae;
+    const SkMemberInfo* info = (const SkMemberInfo*) ai;
+    SkASSERT(info->fType == SkType_Array);
+    return info->getArrayValue(element, index, operand);
+}
+
+int32_t SkAnimator::getArrayInt(const SkDisplayable* ae, 
+        const SkMemberInfo* ai, int index) {
+    SkOperand operand;
+    bool result = getArrayCommon(ae, ai, index, &operand, SkType_Int);
+    return result ? operand.fS32 : SK_NaN32;
+}
+
+int32_t SkAnimator::getArrayInt(const char* id, const char* fieldID, int index) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return SK_NaN32;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return SK_NaN32;
+    return getArrayInt(element, field, index);
+}
+
+SkScalar SkAnimator::getArrayScalar(const SkDisplayable* ae, 
+        const SkMemberInfo* ai, int index) {
+    SkOperand operand;
+    bool result = getArrayCommon(ae, ai, index, &operand, SkType_Float);
+    return result ? operand.fScalar : SK_ScalarNaN;
+}
+
+SkScalar SkAnimator::getArrayScalar(const char* id, const char* fieldID, int index) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return SK_ScalarNaN;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return SK_ScalarNaN;
+    return getArrayScalar(element, field, index);
+}
+
+const char* SkAnimator::getArrayString(const SkDisplayable* ae, 
+        const SkMemberInfo* ai, int index) {
+    SkOperand operand;
+    bool result = getArrayCommon(ae, ai, index, &operand, SkType_String);
+    return result ? operand.fString->c_str() : NULL;
+}
+
+const char* SkAnimator::getArrayString(const char* id, const char* fieldID, int index) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return NULL;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return NULL;
+    return getArrayString(element, field, index);
+}
+
+SkMSec SkAnimator::getInterval() {
+    return fMaker->fMinimumInterval == (SkMSec) -1 ? 0 : fMaker->fMinimumInterval;
+}
+
+void SkAnimator::getInvalBounds(SkRect* inval) {
+    if (fMaker->fDisplayList.fHasUnion) {
+        inval->fLeft = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fLeft);
+        inval->fTop = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fTop);
+        inval->fRight = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fRight);
+        inval->fBottom = SkIntToScalar(fMaker->fDisplayList.fInvalBounds.fBottom);
+    } else {
+        inval->fLeft = inval->fTop = -SK_ScalarMax;
+        inval->fRight = inval->fBottom = SK_ScalarMax;
+    }
+}
+
+const SkXMLParserError* SkAnimator::getParserError() {
+    return &fMaker->fError;
+}
+
+const char* SkAnimator::getParserErrorString() {
+    if (fMaker->fErrorString.size() == 0 && fMaker->fError.hasError())
+        fMaker->setErrorString();
+    return fMaker->fErrorString.c_str();
+}
+
+int32_t SkAnimator::getInt(const SkDisplayable* element, const SkMemberInfo* info) {
+    if (info->fType != SkType_MemberProperty) {
+        SkOperand operand;
+        if (info->getType() == SkType_Int) {
+            info->getValue(element, &operand, 1);
+            return operand.fS32;
+        }
+        return SK_NaN32;
+    }
+    SkScriptValue scriptValue;
+    bool success = element->getProperty(info->propertyIndex(), &scriptValue);
+    if (success && scriptValue.fType == SkType_Int)
+        return scriptValue.fOperand.fS32;
+    return SK_NaN32;
+}
+
+int32_t SkAnimator::getInt(const char* id, const char* fieldID) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return SK_NaN32;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return SK_NaN32;
+    return getInt(element, field);
+}
+
+SkScalar SkAnimator::getScalar(const SkDisplayable* element, const SkMemberInfo* info) {
+    if (info->fType != SkType_MemberProperty) {
+        SkOperand operand;
+        if (info->getType() == SkType_Float) {
+            info->getValue(element, &operand, 1);
+            return operand.fScalar;
+        }
+        return SK_ScalarNaN;
+    }
+    SkScriptValue scriptValue;
+    bool success = element->getProperty(info->propertyIndex(), &scriptValue);
+    if (success && scriptValue.fType == SkType_Float)
+        return scriptValue.fOperand.fScalar;
+    return SK_ScalarNaN;
+}
+
+SkScalar SkAnimator::getScalar(const char* id, const char* fieldID) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return SK_ScalarNaN;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return SK_ScalarNaN;
+    return getScalar(element, field);
+}
+
+const char* SkAnimator::getString(const SkDisplayable* ae, 
+        const SkMemberInfo* ai) {
+    const SkDisplayable* element = (const SkDisplayable*) ae;
+    const SkMemberInfo* info = (const SkMemberInfo*) ai;
+    SkString* temp;
+    info->getString(element, &temp);
+    return temp->c_str();
+}
+
+const char* SkAnimator::getString(const char* id, const char* fieldID) {
+    const SkDisplayable* element = getElement(id);
+    if (element == NULL)
+        return NULL;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return NULL;
+    return getString(element, field);
+}
+
+const char* SkAnimator::getURIBase() {
+    return fMaker->fPrefix.c_str();
+}
+
+void SkAnimator::initialize() {
+    SkDELETE(fMaker);
+    fMaker = SkNEW_ARGS(SkAnimateMaker, (this, NULL, NULL));
+    decodeMemory(gMathPrimer, sizeof(gMathPrimer)-1);
+#ifdef ANDROID
+    InitializeSkExtraPathEffects(this);
+#endif
+}
+
+
+#ifdef SK_DEBUG
+bool SkAnimator::isTrackingEvents() {
+    return false;
+}
+#endif
+
+bool SkAnimator::onEvent(const SkEvent& evt) {
+#ifdef SK_DEBUG
+    SkAnimator* root = fMaker->getRoot();
+    if (root == NULL)
+        root = this;
+    if (root->isTrackingEvents())
+        root->eventDone(evt);
+#endif
+    if (evt.isType(SK_EventType_OnEnd)) {
+        SkEventState eventState;
+        bool success = evt.findPtr("anim", (void**) &eventState.fDisplayable);
+        SkASSERT(success);
+        success = evt.findS32("time", (int32_t*) &fMaker->fEnableTime);
+        SkASSERT(success);
+        fMaker->fAdjustedStart = fMaker->getAppTime() - fMaker->fEnableTime;
+        fMaker->fEvents.doEvent(*fMaker, SkDisplayEvent::kOnEnd, &eventState);
+        fMaker->fAdjustedStart = 0;
+        goto inval;
+    }
+    if (evt.isType(SK_EventType_Delay)) {
+        fMaker->doDelayedEvent();
+        goto inval;
+    }
+    {
+        const char* id = evt.findString("id");
+        if (id == NULL)
+            return false;
+        SkDisplayable** firstMovie = fMaker->fMovies.begin();
+        SkDisplayable** endMovie = fMaker->fMovies.end();
+        for (SkDisplayable** ptr = firstMovie; ptr < endMovie; ptr++) {
+            SkDisplayMovie* movie = (SkDisplayMovie*) *ptr;
+            movie->doEvent(evt);
+        }
+        {
+            SkDisplayable* event;
+            if (fMaker->find(id, &event) == false)
+                return false;
+    #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+            SkString debugOut;
+            SkMSec realTime = fMaker->getAppTime();
+            debugOut.appendS32(realTime - fMaker->fDebugTimeBase);
+            debugOut.append(" onEvent id=");
+            debugOut.append(id);
+    #endif
+            SkMSec time = evt.getFast32();
+            if (time != 0) {
+                SkMSec app  = fMaker->getAppTime();
+                fMaker->setEnableTime(app, time);
+    #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+                debugOut.append(" time=");
+                debugOut.appendS32(time - fMaker->fDebugTimeBase);
+                debugOut.append(" adjust=");
+                debugOut.appendS32(fMaker->fAdjustedStart);
+    #endif
+            }
+    #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+            SkDebugf("%s\n", debugOut.c_str());
+    #endif
+            SkASSERT(event->isEvent());
+            SkDisplayEvent* displayEvent = (SkDisplayEvent*) event;
+            displayEvent->populateInput(*fMaker, evt);
+            displayEvent->enableEvent(*fMaker);
+        }
+    }
+inval:
+    fMaker->notifyInval();
+    return true;
+}
+
+void SkAnimator::onEventPost(SkEvent* evt, SkEventSinkID sinkID)
+{
+#ifdef SK_DEBUG
+    SkAnimator* root = fMaker->getRoot();
+    if (root) {
+        root->onEventPost(evt, sinkID);
+        return;
+    }
+#else
+    SkASSERT(sinkID == this->getSinkID() || this->getHostEventSinkID() == sinkID);
+#endif
+    SkEvent::Post(evt, sinkID);
+}
+
+void SkAnimator::onEventPostTime(SkEvent* evt, SkEventSinkID sinkID, SkMSec time)
+{
+#ifdef SK_DEBUG
+    SkAnimator* root = fMaker->getRoot();
+    if (root) {
+        root->onEventPostTime(evt, sinkID, time);
+        return;
+    }
+#else
+    SkASSERT(sinkID == this->getSinkID() || this->getHostEventSinkID() == sinkID);
+#endif
+    SkEvent::PostTime(evt, sinkID, time);
+}
+
+void SkAnimator::reset() {
+    fMaker->fDisplayList.reset();
+}
+
+SkEventSinkID SkAnimator::getHostEventSinkID() const {
+    return fMaker->fHostEventSinkID; 
+}
+
+void SkAnimator::setHostEventSinkID(SkEventSinkID target) {
+    fMaker->fHostEventSinkID = target; 
+}
+
+void SkAnimator::onSetHostHandler(Handler ) {
+}
+
+void SkAnimator::setJavaOwner(Handler ) {
+}
+
+bool SkAnimator::setArrayString(const char* id, const char* fieldID, const char** array, int num)
+{
+    SkTypedArray tArray(SkType_String);
+    tArray.setCount(num);
+    for (int i = 0; i < num; i++) {
+        SkOperand op;
+        op.fString = new SkString(array[i]);
+        tArray[i] = op;
+    }
+    return setArray(id, fieldID, tArray);
+}
+bool SkAnimator::setArrayInt(const char* id, const char* fieldID, const int* array, int num)
+{
+    SkTypedArray tArray(SkType_Int);
+    tArray.setCount(num);
+    for (int i = 0; i < num; i++) {
+        SkOperand op;   
+        op.fS32 = array[i];
+        tArray[i] = op;
+    }
+    return setArray(id, fieldID, tArray);
+}
+
+bool SkAnimator::setArray(SkDisplayable* element, const SkMemberInfo* info, SkTypedArray array) {
+    if (info->fType != SkType_Array)
+        return false;   //the field is not an array
+    //i think we can handle the case where the displayable itself is an array differently from the
+    //case where it has an array - for one thing, if it is an array, i think we can change its type
+    //if it's not, we cannot
+    SkDisplayTypes type = element->getType();
+    if (type == SkType_Array) {
+        SkDisplayArray* dispArray = (SkDisplayArray*) element;
+        dispArray->values = array;  
+        return true;
+    }
+    else
+        return false;   //currently i don't care about this case
+}
+
+bool SkAnimator::setArray(const char* id, const char* fieldID, SkTypedArray array) {
+    SkDisplayable* element = (SkDisplayable*) getElement(id);
+    //should I go ahead and change all 'NULL's to 'NULL'?
+    if (element == NULL)
+        return false;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return false;
+    return setArray(element, field, array);
+}
+
+bool SkAnimator::setInt(SkDisplayable* element, const SkMemberInfo* info, int32_t s32) {
+    if (info->fType != SkType_MemberProperty) {
+        SkOperand operand;
+        operand.fS32 = s32;
+        SkASSERT(info->getType() == SkType_Int);
+        info->setValue(element, &operand, 1);
+    } else {
+        SkScriptValue scriptValue;
+        scriptValue.fType = SkType_Int;
+        scriptValue.fOperand.fS32 = s32;
+        element->setProperty(info->propertyIndex(), scriptValue);
+    }
+    return true;
+}
+
+bool SkAnimator::setInt(const char* id, const char* fieldID, int32_t s32) {
+    SkDisplayable* element = (SkDisplayable*) getElement(id);
+    if (element == NULL)
+        return false;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return false;
+    return setInt(element, field, s32);
+}
+
+bool SkAnimator::setScalar(SkDisplayable* element, const SkMemberInfo* info, SkScalar scalar) {
+    if (info->fType != SkType_MemberProperty) {
+        SkOperand operand;
+        operand.fScalar = scalar;
+        SkASSERT(info->getType() == SkType_Float);
+        info->setValue(element, &operand, 1);
+    } else {
+        SkScriptValue scriptValue;
+        scriptValue.fType = SkType_Float;
+        scriptValue.fOperand.fScalar = scalar;
+        element->setProperty(info->propertyIndex(), scriptValue);
+    }
+    return true;
+}
+
+bool SkAnimator::setScalar(const char* id, const char* fieldID, SkScalar scalar) {
+    SkDisplayable* element = (SkDisplayable*) getElement(id);
+    if (element == NULL)
+        return false;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return false;
+    return setScalar(element, field, scalar);
+}
+
+bool SkAnimator::setString(SkDisplayable* element, 
+        const SkMemberInfo* info, const char* str) {
+    // !!! until this is fixed, can't call script with global references from here 
+    info->setValue(*fMaker, NULL, 0, info->fCount, element, info->getType(), str, strlen(str));
+    return true;
+}
+
+bool SkAnimator::setString(const char* id, const char* fieldID, const char* str) {
+    SkDisplayable* element = (SkDisplayable*) getElement(id);
+    if (element == NULL)
+        return false;
+    const SkMemberInfo* field = getField(element, fieldID);
+    if (field == NULL)
+        return false;
+    return setString(element, field, str);
+}
+
+void SkAnimator::setTimeline(const Timeline& timeline) {
+    fMaker->fTimeline = &timeline;
+}
+
+void SkAnimator::setURIBase(const char* uri) {
+    if (uri)
+    {
+        const char* tail = strrchr(uri, '/');
+        if (tail) {
+            SkString prefix(uri, tail - uri + 1);
+            if (uri[0] != '.' /*SkStream::IsAbsoluteURI(uri)*/)
+                fMaker->fPrefix.reset();
+            fMaker->fPrefix.append(prefix);
+            fMaker->fFileName.set(tail + 1);
+        } else
+            fMaker->fFileName.set(uri);
+    }
+}
+
+#ifdef SK_DEBUG
+bool SkAnimator::NoLeaks() {
+#ifdef SK_BUILD_FOR_MAC
+    if (SkDisplayable::fAllocations.count() == 0)
+        return true;
+//  return SkDisplayable::fAllocationCount == 0;
+    SkDebugf("!!! leaked %d displayables:\n", SkDisplayable::fAllocations.count());
+    for (SkDisplayable** leak = SkDisplayable::fAllocations.begin(); leak < SkDisplayable::fAllocations.end(); leak++)
+        SkDebugf("%08x %s\n", *leak, (*leak)->id);
+#endif
+    return false;
+}
+#endif
+
+#ifdef SK_SUPPORT_UNITTEST
+#include "SkAnimatorScript.h"
+#include "SkBase64.h"
+#include "SkParse.h"
+#include "SkMemberInfo.h"
+
+#define unittestline(type)  { #type , type::UnitTest }
+#endif
+
+
+void SkAnimator::Init(bool runUnitTests) {
+#ifdef SK_SUPPORT_UNITTEST
+    if (runUnitTests == false)
+        return;
+    static const struct {
+        const char* fTypeName;
+        void (*fUnitTest)( );
+    } gUnitTests[] = {
+        unittestline(SkBase64),
+        unittestline(SkDisplayType),
+        unittestline(SkParse),
+        unittestline(SkScriptEngine),
+//      unittestline(SkScriptEngine2),  // compiled script experiment
+        unittestline(SkAnimatorScript)
+    };
+    for (int i = 0; i < (int)SK_ARRAY_COUNT(gUnitTests); i++)
+    {
+        SkDebugf("SkAnimator: Running UnitTest for %s\n", gUnitTests[i].fTypeName);
+        gUnitTests[i].fUnitTest();
+        SkDebugf("SkAnimator: End UnitTest for %s\n", gUnitTests[i].fTypeName);
+    }
+#endif
+}
+
+void SkAnimator::Term() {
+}
+
+
+
diff --git a/src/animator/SkAnimatorScript.cpp b/src/animator/SkAnimatorScript.cpp
new file mode 100644
index 0000000..eafe1db
--- /dev/null
+++ b/src/animator/SkAnimatorScript.cpp
@@ -0,0 +1,607 @@
+/* libs/graphics/animator/SkAnimatorScript.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 "SkAnimatorScript.h"
+#include "SkAnimateBase.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayTypes.h"
+#include "SkExtras.h"
+#include "SkMemberInfo.h"
+#include "SkParse.h"
+
+static const SkDisplayEnumMap gEnumMaps[] = {
+    { SkType_AddMode, "indirect|immediate" },
+    { SkType_Align, "left|center|right" },
+    { SkType_ApplyMode, "create|immediate|once" },
+    { SkType_ApplyTransition, "normal|reverse" },
+    { SkType_BitmapEncoding, "jpeg|png" },
+    { SkType_BitmapFormat, "none|A1|A8|Index8|RGB16|RGB32" },
+    { SkType_Boolean, "false|true" },
+    { SkType_Cap, "butt|round|square" },
+    { SkType_EventCode, "none|leftSoftKey|rightSoftKey|home|back|send|end|key0|key1|key2|key3|key4|key5|key6|key7|key8|key9|star|hash|up|down|left|right|OK|volUp|volDown|camera" },
+    { SkType_EventKind, "none|keyChar|keyPress|keyPressUp|mouseDown|mouseDrag|mouseMove|mouseUp|onEnd|onLoad|user" },
+    { SkType_EventMode, "deferred|immediate" },
+    { SkType_FillType, "winding|evenOdd" },
+    { SkType_FilterType, "none|bilinear" },
+    { SkType_FontStyle, "normal|bold|italic|boldItalic" },
+    { SkType_FromPathMode, "normal|angle|position" },
+    { SkType_Join, "miter|round|blunt" },
+    { SkType_MaskFilterBlurStyle, "normal|solid|outer|inner" },
+    { SkType_PathDirection, "cw|ccw" },
+    { SkType_Style, "fill|stroke|strokeAndFill" },
+    { SkType_TextBoxAlign, "start|center|end" },
+    { SkType_TextBoxMode, "oneLine|lineBreak" },
+    { SkType_TileMode, "clamp|repeat|mirror" },
+    { SkType_Xfermode, "clear|src|dst|srcOver|dstOver|srcIn|dstIn|srcOut|dstOut|"
+        "srcATop|dstATop|xor|darken|lighten" },
+};
+
+static int gEnumMapCount = SK_ARRAY_COUNT(gEnumMaps);
+
+SkAnimatorScript::SkAnimatorScript(SkAnimateMaker& maker, SkDisplayable* working, SkDisplayTypes type)
+    : SkScriptEngine(SkScriptEngine::ToOpType(type)), fMaker(maker), fParent(NULL), fWorking(working)
+{
+    memberCallBack(EvalMember, (void*) this);
+    memberFunctionCallBack(EvalMemberFunction, (void*) this);
+    boxCallBack(Box, (void*) this);
+    unboxCallBack(Unbox, (void*) &maker);
+    propertyCallBack(EvalID, (void*) this); // must be first (entries are prepended, will be last), since it never fails
+    propertyCallBack(Infinity, (void*) this);
+    propertyCallBack(NaN, (void*) this);
+    functionCallBack(Eval, (void*) this);
+    functionCallBack(IsFinite, (void*) this);
+    functionCallBack(IsNaN, (void*) this);
+    if (type == SkType_ARGB) {
+        functionCallBack(EvalRGB, (void*) this);
+        propertyCallBack(EvalNamedColor, (void*) &maker.fIDs);
+    }
+    if (SkDisplayType::IsEnum(&maker, type)) {
+        // !!! for SpiderMonkey, iterate through the enum values, and map them to globals
+        const SkDisplayEnumMap& map = GetEnumValues(type);
+        propertyCallBack(EvalEnum, (void*) map.fValues); 
+    } 
+    for (SkExtras** extraPtr = maker.fExtras.begin(); extraPtr < maker.fExtras.end(); extraPtr++) {
+        SkExtras* extra = *extraPtr;
+        if (extra->fExtraCallBack)
+            propertyCallBack(extra->fExtraCallBack, extra->fExtraStorage);
+    }
+}
+
+SkAnimatorScript::~SkAnimatorScript() {
+    for (SkDisplayable** dispPtr = fTrackDisplayable.begin(); dispPtr < fTrackDisplayable.end(); dispPtr++)
+        delete *dispPtr;
+}
+
+bool SkAnimatorScript::evaluate(const char* original, SkScriptValue* result, SkDisplayTypes type) {
+        const char* script = original;
+        bool success = evaluateScript(&script, result);
+        if (success == false || result->fType != type) {
+            fMaker.setScriptError(*this);
+            return false;
+        }
+        return true;
+}
+
+bool SkAnimatorScript::Box(void* user, SkScriptValue* scriptValue) {
+    SkAnimatorScript* engine = (SkAnimatorScript*) user;
+    SkDisplayTypes type = scriptValue->fType;
+    SkDisplayable* displayable;
+    switch (type) {
+        case SkType_Array: {
+            SkDisplayArray* boxedValue = new SkDisplayArray(*scriptValue->fOperand.fArray);
+            displayable = boxedValue;
+            } break;
+        case SkType_Boolean: {
+            SkDisplayBoolean* boxedValue = new SkDisplayBoolean;
+            displayable = boxedValue;
+            boxedValue->value = !! scriptValue->fOperand.fS32;
+            } break;
+        case SkType_Int: {
+            SkDisplayInt* boxedValue = new SkDisplayInt;
+            displayable = boxedValue;
+            boxedValue->value = scriptValue->fOperand.fS32;
+            } break;
+        case SkType_Float: {
+            SkDisplayFloat* boxedValue = new SkDisplayFloat;
+            displayable = boxedValue;
+            boxedValue->value = scriptValue->fOperand.fScalar;
+            } break;
+        case SkType_String: {
+            SkDisplayString* boxedValue = new SkDisplayString(*scriptValue->fOperand.fString);
+            displayable = boxedValue;
+            } break;
+        case SkType_Displayable: 
+            scriptValue->fOperand.fObject = scriptValue->fOperand.fDisplayable;
+            scriptValue->fType = SkType_Displayable;
+            return true;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    engine->track(displayable);
+    scriptValue->fOperand.fObject = displayable;
+    scriptValue->fType = SkType_Displayable;
+    return true;
+}
+
+bool SkAnimatorScript::Eval(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+        void* eng, SkScriptValue* value) {
+    if (SK_LITERAL_STR_EQUAL("eval", function, len) == false)
+        return false;
+    if (params.count() != 1)
+        return false;
+    SkAnimatorScript* host = (SkAnimatorScript*) eng;
+    SkAnimatorScript engine(host->fMaker, host->fWorking, SkScriptEngine::ToDisplayType(host->fReturnType));
+    SkScriptValue* scriptValue = params.begin();
+    bool success = true;
+    if (scriptValue->fType == SkType_String) {
+        const char* script = scriptValue->fOperand.fString->c_str();
+        success = engine.evaluateScript(&script, value);
+    } else
+        *value = *scriptValue;
+    return success;
+}
+
+bool SkAnimatorScript::EvalEnum(const char* token, size_t len, void* callBack, SkScriptValue* value) {
+    const char* tokens = (const char*) callBack;
+    value->fType = SkType_Int;
+    if (MapEnums(tokens, token, len, (int*)&value->fOperand.fS32))
+        return true; 
+    return false;
+}
+
+bool SkAnimatorScript::EvalID(const char* token, size_t len, void* user, SkScriptValue* value) {
+    SkAnimatorScript* engine = (SkAnimatorScript*) user;
+    SkTDict<SkDisplayable*>* ids = &engine->fMaker.fIDs;
+    SkDisplayable* displayable;
+    bool success = ids->find(token, len, &displayable); 
+    if (success == false) {
+        displayable = engine->fWorking;
+        if (SK_LITERAL_STR_EQUAL("parent", token, len)) {
+            SkDisplayable* parent = displayable->getParent();
+            if (parent == false)
+                parent = engine->fParent;
+            if (parent) {
+                value->fOperand.fDisplayable = parent;
+                value->fType = SkType_Displayable;
+                return true;
+            }
+        }
+        if (displayable && EvalMember(token, len, displayable, engine, value))
+            return true;
+        value->fOperand.fString = NULL;
+        value->fType = SkType_String;   
+    } else {
+        SkDisplayable* working = engine->fWorking;
+        value->fOperand.fDisplayable = displayable;
+        value->fType = SkType_Displayable;
+        if (displayable->canContainDependents() && working && working->isAnimate()) {
+            SkAnimateBase* animator = (SkAnimateBase*) working;
+            if (animator->isDynamic()) {
+                SkDisplayDepend* depend = (SkDisplayDepend* ) displayable;
+                depend->addDependent(working);
+            }
+        }
+    }
+    return true;
+}
+
+bool SkAnimatorScript::EvalNamedColor(const char* token, size_t len, void* callback, SkScriptValue* value) {
+        value->fType = SkType_Int;
+    if (SkParse::FindNamedColor(token, len, (SkColor*) &value->fOperand.fS32) != NULL)
+        return true;
+    return false;
+}
+
+bool SkAnimatorScript::EvalRGB(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+        void* eng, SkScriptValue* value) {
+    if (SK_LITERAL_STR_EQUAL("rgb", function, len) == false)
+        return false;
+    if (params.count() != 3)
+        return false;
+    SkScriptEngine* engine = (SkScriptEngine*) eng;
+    unsigned result = 0xFF000000;
+    int shift = 16;
+    for (SkScriptValue* valuePtr = params.begin(); valuePtr < params.end(); valuePtr++) {
+        engine->convertTo(SkType_Int, valuePtr);
+        result |= SkClampMax(valuePtr->fOperand.fS32, 255) << shift;
+        shift -= 8;
+    }
+    value->fOperand.fS32 = result;
+    value->fType = SkType_Int;
+    return true;
+}
+
+bool SkAnimatorScript::EvalMemberCommon(SkScriptEngine* engine, const SkMemberInfo* info, 
+        SkDisplayable* displayable, SkScriptValue* value) {
+    SkDisplayTypes original;
+    SkDisplayTypes type = original = (SkDisplayTypes) info->getType();
+    if (info->fType == SkType_Array)
+        type = SkType_Array;
+    switch (type) {
+        case SkType_ARGB:
+            type = SkType_Int;
+        case SkType_Boolean:
+        case SkType_Int:
+        case SkType_MSec:
+        case SkType_Float:
+            SkASSERT(info->getCount() == 1);
+            if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) 
+                value->fOperand.fS32 = *(int32_t*) info->memberData(displayable);   // OK for SkScalar too
+            if (type == SkType_MSec) {
+                value->fOperand.fScalar = SkScalarDiv((SkScalar) value->fOperand.fS32, 1000); // dividing two ints is the same as dividing two scalars 
+                type = SkType_Float;
+            }
+            break;
+        case SkType_String: {
+            SkString* displayableString;
+            if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) {
+                info->getString(displayable, &displayableString);
+                value->fOperand.fString = new SkString(*displayableString);
+            }
+            } break;
+        case SkType_Array: {
+            SkASSERT(info->fType != SkType_MemberProperty); // !!! incomplete
+            SkTDOperandArray* displayableArray = (SkTDOperandArray*) info->memberData(displayable);
+            if (displayable->getType() == SkType_Array) {
+                SkDisplayArray* typedArray = (SkDisplayArray*) displayable;
+                original = typedArray->values.getType();
+            }
+            SkASSERT(original != SkType_Unknown);
+            SkTypedArray* array = value->fOperand.fArray = new SkTypedArray(original);
+            engine->track(array);
+            int count = displayableArray->count();
+            if (count > 0) {
+                array->setCount(count);
+                memcpy(array->begin(), displayableArray->begin(), count * sizeof(SkOperand));
+            }
+            } break;
+        default:
+            SkASSERT(0); // unimplemented
+    }
+    value->fType = type;
+    return true;
+}
+
+bool SkAnimatorScript::EvalMember(const char* member, size_t len, void* object, void* eng, 
+        SkScriptValue* value) {
+    SkScriptEngine* engine = (SkScriptEngine*) eng;
+    SkDisplayable* displayable = (SkDisplayable*) object;
+    SkString name(member, len);
+    SkDisplayable* named = displayable->contains(name);
+    if (named) {
+        value->fOperand.fDisplayable = named;
+        value->fType = SkType_Displayable;
+        return true;
+    }
+    const SkMemberInfo* info = displayable->getMember(name.c_str());
+    if (info == NULL)
+        return false;
+    if (info->fType == SkType_MemberProperty) {
+        if (displayable->getProperty(info->propertyIndex(), value) == false) {
+            SkASSERT(0);
+            return false;
+        }
+    }
+    return EvalMemberCommon(engine, info, displayable, value);
+}
+
+bool SkAnimatorScript::EvalMemberFunction(const char* member, size_t len, void* object, 
+        SkTDArray<SkScriptValue>& params, void* eng, SkScriptValue* value) {
+    SkScriptEngine* engine = (SkScriptEngine*) eng;
+    SkDisplayable* displayable = (SkDisplayable*) object;
+    SkString name(member, len);
+    const SkMemberInfo* info = displayable->getMember(name.c_str());
+    SkASSERT(info != NULL); /* !!! error handling unimplemented */
+    if (info->fType != SkType_MemberFunction) {
+        SkASSERT(0);
+        return false;
+    }
+    displayable->executeFunction(displayable, info->functionIndex(), params, info->getType(), 
+        value);
+    return EvalMemberCommon(engine, info, displayable, value);
+}
+
+bool SkAnimatorScript::EvaluateDisplayable(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, SkDisplayable** result) {
+    SkAnimatorScript engine(maker, displayable, SkType_Displayable);
+    SkScriptValue value;
+    bool success = engine.evaluate(script, &value, SkType_Displayable);
+    if (success)
+        *result = value.fOperand.fDisplayable;
+    return success;
+}
+
+bool SkAnimatorScript::EvaluateInt(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, int32_t* result) {
+    SkAnimatorScript engine(maker, displayable, SkType_Int);
+    SkScriptValue value;
+    bool success = engine.evaluate(script, &value, SkType_Int);
+    if (success)
+        *result = value.fOperand.fS32;
+    return success;
+}
+
+bool SkAnimatorScript::EvaluateFloat(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, SkScalar* result) {
+    SkAnimatorScript engine(maker, displayable, SkType_Float);
+    SkScriptValue value;
+    bool success = engine.evaluate(script, &value, SkType_Float);
+    if (success)
+        *result = value.fOperand.fScalar;
+    return success;
+}
+
+bool SkAnimatorScript::EvaluateString(SkAnimateMaker& maker, SkDisplayable* displayable, const char* script, SkString* result) {
+    SkAnimatorScript engine(maker, displayable, SkType_String);
+    SkScriptValue value;
+    bool success = engine.evaluate(script, &value, SkType_String);
+    if (success)
+        result->set(*(value.fOperand.fString));
+  return success;
+}
+
+bool SkAnimatorScript::EvaluateString(SkAnimateMaker& maker, SkDisplayable* displayable, SkDisplayable* parent, const char* script, SkString* result) {
+    SkAnimatorScript engine(maker, displayable, SkType_String);
+    engine.fParent = parent;
+    SkScriptValue value;
+    bool success = engine.evaluate(script, &value, SkType_String);
+    if (success)
+        result->set(*(value.fOperand.fString));
+  return success;
+}
+
+const SkDisplayEnumMap& SkAnimatorScript::GetEnumValues(SkDisplayTypes type) {
+    int index = SkTSearch<SkDisplayTypes>(&gEnumMaps[0].fType, gEnumMapCount, type, 
+        sizeof(SkDisplayEnumMap));
+    SkASSERT(index >= 0);
+    return gEnumMaps[index];
+}
+
+bool SkAnimatorScript::Infinity(const char* token, size_t len, void* user, SkScriptValue* value) {
+    if (SK_LITERAL_STR_EQUAL("Infinity", token, len) == false)
+        return false;
+    value->fType = SkType_Float;
+    value->fOperand.fScalar = SK_ScalarInfinity;
+    return true;
+}
+
+bool SkAnimatorScript::IsFinite(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+        void* eng, SkScriptValue* value) {
+    if (SK_LITERAL_STR_EQUAL(function, "isFinite", len) == false)
+        return false;
+    if (params.count() != 1)
+        return false;
+    SkScriptValue* scriptValue = params.begin();
+    SkDisplayTypes type = scriptValue->fType;
+    SkScalar scalar = scriptValue->fOperand.fScalar;
+    value->fType = SkType_Int;
+    value->fOperand.fS32 = type == SkType_Float ? SkScalarIsNaN(scalar) == false && 
+        SkScalarAbs(scalar) != SK_ScalarInfinity    : type == SkType_Int;
+    return true;
+}
+
+bool SkAnimatorScript::IsNaN(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+        void* eng, SkScriptValue* value) {
+    if (SK_LITERAL_STR_EQUAL("isNaN", function, len) == false)
+        return false;
+    if (params.count() != 1)
+        return false;
+    SkScriptValue* scriptValue = params.begin();
+    value->fType = SkType_Int;
+    value->fOperand.fS32 = scriptValue->fType == SkType_Float ? SkScalarIsNaN(scriptValue->fOperand.fScalar) : 0;
+    return true;
+}
+
+bool SkAnimatorScript::MapEnums(const char* ptr, const char* match, size_t len, int* value) {
+    int index = 0;
+    bool more = true;
+    do {
+        const char* last = strchr(ptr, '|');
+        if (last == NULL) {
+            last = &ptr[strlen(ptr)];
+            more = false;
+        }
+        size_t length = last - ptr;
+        if (len == length && strncmp(ptr, match, length) == 0) {
+            *value = index;
+            return true;
+        }
+        index++;
+        ptr = last + 1;
+    } while (more);
+    return false;
+}
+
+bool SkAnimatorScript::NaN(const char* token, size_t len, void* user, SkScriptValue* value) {
+    if (SK_LITERAL_STR_EQUAL("NaN", token, len) == false)
+        return false;
+    value->fType = SkType_Float;
+    value->fOperand.fScalar = SK_ScalarNaN;
+    return true;
+}
+
+#if 0
+bool SkAnimatorScript::ObjectToString(void* object, void* user, SkScriptValue* value) {
+    SkTDict<SkDisplayable*>* ids = (SkTDict<SkDisplayable*>*) user;
+    SkDisplayable* displayable = (SkDisplayable*) object;
+    const char* key;
+    bool success = ids->findKey(displayable, &key); 
+    if (success == false)
+        return false;
+    value->fOperand.fString =   new SkString(key);
+    value->fType = SkType_String;
+    return true;
+}
+#endif
+
+bool SkAnimatorScript::Unbox(void* m, SkScriptValue* scriptValue) {
+    SkAnimateMaker* maker = (SkAnimateMaker*) m;
+    SkASSERT((unsigned) scriptValue->fType == (unsigned) SkType_Displayable);
+    SkDisplayable* displayable = (SkDisplayable*) scriptValue->fOperand.fObject;
+    SkDisplayTypes type = displayable->getType();
+    switch (displayable->getType()) {
+        case SkType_Array: {
+            SkDisplayArray* boxedValue = (SkDisplayArray*) displayable;
+            scriptValue->fOperand.fArray = &boxedValue->values;
+            } break;
+        case SkType_Boolean: {
+            SkDisplayBoolean* boxedValue = (SkDisplayBoolean*) displayable;
+            scriptValue->fOperand.fS32 = boxedValue->value;
+            } break;
+        case SkType_Int: {
+            SkDisplayInt* boxedValue = (SkDisplayInt*) displayable;
+            scriptValue->fOperand.fS32 = boxedValue->value;
+            } break;
+        case SkType_Float: {
+            SkDisplayFloat* boxedValue = (SkDisplayFloat*) displayable;
+            scriptValue->fOperand.fScalar = boxedValue->value;
+            } break;
+        case SkType_String: {
+            SkDisplayString* boxedValue = (SkDisplayString*) displayable;
+            scriptValue->fOperand.fString = SkNEW_ARGS(SkString, (boxedValue->value));
+            } break;
+        default: {
+            const char* id;
+            bool success = maker->findKey(displayable, &id);
+            SkASSERT(success);
+            scriptValue->fOperand.fString = SkNEW_ARGS(SkString, (id));
+            type = SkType_String;
+        }
+    }
+    scriptValue->fType = type;
+    return true;
+}
+
+#if defined SK_SUPPORT_UNITTEST
+
+#include "SkAnimator.h"
+
+static const char scriptTestSetup[]  = 
+"<screenplay>\n"
+    "<text id='label' text='defg'/>\n"
+    "<add id='addLabel' use='label'/>\n"
+    "<text id='text1' text='test'/>\n"
+    "<apply scope='addLabel'>\n"
+        "<set target='label' field='text' to='#script:text1.text'/>\n"
+    "</apply>\n"
+    "<apply>\n"
+        "<paint id='labelPaint'>\n"
+            "<emboss id='emboss' direction='[1,1,1]'  />\n"
+        "</paint>\n"
+        "<animate id='animation' field='direction' target='emboss' from='[1,1,1]' to='[-1,1,1]' dur='1'/>\n"
+        "<set lval='direction[0]' target='emboss' to='-1' />\n"
+    "</apply>\n"
+    "<color id='testColor' color='0 ? rgb(0,0,0) : rgb(255,255,255)' />\n"
+    "<color id='xColor' color='rgb(12,34,56)' />\n"
+    "<array id='emptyArray' />\n"
+    "<array id='intArray' values='[1, 4, 6]' />\n"
+    "<int id='idx' value='2' />\n"
+    "<int id='idy' value='2' />\n"
+    "<string id='alpha' value='abc' />\n"
+    "<rect id='testRect' left='Math.cos(0)' top='2' right='12' bottom='5' />\n"
+    "<event id='evt'>\n"
+        "<input name='x' />\n"
+        "<apply scope='idy'>\n"
+            "<set field='value' to='evt.x.int' />\n"
+        "</apply>\n"
+    "</event>\n"
+"</screenplay>";
+
+#if !defined(SK_BUILD_FOR_BREW)
+
+#define DEFAULT_ANSWER   , 0
+
+static const SkScriptNAnswer scriptTests[]  = {
+    { "label.text.length == 4", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+//  { "labelPaint.measureText(label.text) > 0 ? labelPaint.measureText(label.text)+10 : 40", SkType_Float, 0, SkIntToScalar(0x23)  },
+    {   "Number.POSITIVE_INFINITY >= Number.MAX_VALUE ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "Infinity >= Number.MAX_VALUE ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "Number.NEGATIVE_INFINITY <= -Number.MAX_VALUE ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "Number.MIN_VALUE > 0 ? 1 : 0", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "isNaN(Number.NaN)", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "isNaN(NaN)", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "Math.sin(0)", SkType_Float, 0, SkIntToScalar(0) DEFAULT_ANSWER },
+    {   "alpha+alpha", SkType_String, 0, 0, "abcabc" },
+    {   "intArray[4]", SkType_Unknown DEFAULT_ANSWER DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "emptyArray[4]", SkType_Unknown DEFAULT_ANSWER DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "idx", SkType_Int, 2 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "intArray.length", SkType_Int, 3 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "intArray.values[0]", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "intArray[0]", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "idx.value", SkType_Int, 2 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "alpha.value", SkType_String, 0, 0, "abc" },
+    {   "alpha", SkType_String, 0, 0, "abc" },
+    {   "alpha.value+alpha.value", SkType_String, 0, 0, "abcabc" },
+    {   "alpha+idx", SkType_String, 0, 0, "abc2" },
+    {   "idx+alpha", SkType_String, 0, 0, "2abc" },
+    {   "intArray[idx]", SkType_Int, 6 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "alpha.slice(1,2)", SkType_String, 0, 0, "b" },
+    {   "alpha.value.slice(1,2)", SkType_String, 0, 0, "b" },
+    {   "testRect.left+2", SkType_Float, 0, SkIntToScalar(3) DEFAULT_ANSWER },
+    {   "0 ? Math.sin(0) : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? intArray[0] : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? intArray.values[0] : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? idx : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? idx.value : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? alpha.slice(1,2) : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    {   "0 ? alpha.value.slice(1,2) : 1", SkType_Int, 1 DEFAULT_ANSWER DEFAULT_ANSWER },
+    { "idy", SkType_Int, 3 DEFAULT_ANSWER DEFAULT_ANSWER }
+};
+#endif
+
+#define SkScriptNAnswer_testCount   SK_ARRAY_COUNT(scriptTests)
+
+void SkAnimatorScript::UnitTest() {
+#if !defined(SK_BUILD_FOR_BREW) && defined(SK_SUPPORT_UNITTEST)
+    SkAnimator animator;
+    SkASSERT(animator.decodeMemory(scriptTestSetup, sizeof(scriptTestSetup)-1));
+    SkEvent evt;
+    evt.setString("id", "evt");
+    evt.setS32("x", 3);
+    animator.doUserEvent(evt);
+    // set up animator with memory script above, then run value tests
+    for (unsigned index = 0; index < SkScriptNAnswer_testCount; index++) {
+        SkAnimatorScript engine(*animator.fMaker, NULL, scriptTests[index].fType);
+        SkScriptValue value;
+        const char* script = scriptTests[index].fScript;
+        bool success = engine.evaluateScript(&script, &value);
+        if (success == false) {
+            SkDebugf("script failed: %s\n", scriptTests[index].fScript);
+            SkASSERT(scriptTests[index].fType == SkType_Unknown);
+            continue;
+        }
+        SkASSERT(value.fType == scriptTests[index].fType);
+        SkScalar error;
+        switch (value.fType) {
+            case SkType_Int:
+                SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
+                break;
+            case SkType_Float:
+                error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
+                SkASSERT(error < SK_Scalar1 / 10000);
+                break;
+            case SkType_String:
+                SkASSERT(strcmp(value.fOperand.fString->c_str(), scriptTests[index].fStringAnswer) == 0);
+                break;
+            default:
+                SkASSERT(0);
+        }
+    }
+#endif
+}
+
+#endif
+
+
diff --git a/src/animator/SkAnimatorScript.h b/src/animator/SkAnimatorScript.h
new file mode 100644
index 0000000..2fdb029
--- /dev/null
+++ b/src/animator/SkAnimatorScript.h
@@ -0,0 +1,84 @@
+/* libs/graphics/animator/SkAnimatorScript.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 SkAnimatorScript_DEFINED
+#define SkAnimatorScript_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkScript.h"
+#include "SkTypedArray.h"
+
+class SkAnimateMaker;
+struct SkMemberInfo;
+
+struct SkDisplayEnumMap {
+    SkDisplayTypes fType;
+    const char* fValues;
+};
+
+class SkAnimatorScript : public SkScriptEngine {
+public:
+    SkAnimatorScript(SkAnimateMaker& , SkDisplayable* , SkDisplayTypes type);
+    ~SkAnimatorScript();
+    bool evaluate(const char* script, SkScriptValue* , SkDisplayTypes type);
+    void track(SkDisplayable* displayable) { 
+        SkASSERT(fTrackDisplayable.find(displayable) < 0);  
+        *fTrackDisplayable.append() = displayable; }
+    static bool EvaluateDisplayable(SkAnimateMaker& , SkDisplayable* , const char* script, SkDisplayable** );
+    static bool EvaluateFloat(SkAnimateMaker& , SkDisplayable* , const char* script, SkScalar* );
+    static bool EvaluateInt(SkAnimateMaker& , SkDisplayable* , const char* script, int32_t* );
+    static bool EvaluateString(SkAnimateMaker& , SkDisplayable* , const char* script, SkString* );
+    static bool EvaluateString(SkAnimateMaker& , SkDisplayable* , SkDisplayable* parent, const char* script, SkString* );
+    static bool MapEnums(const char* ptr, const char* match, size_t len, int* value);
+protected:
+    static bool Box(void* user, SkScriptValue* );
+    static bool Eval(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+        void* callBack, SkScriptValue* );
+    static bool EvalEnum(const char* token, size_t len, void* callBack, SkScriptValue* );
+    static bool EvalID(const char* token, size_t len, void* callBack, SkScriptValue* );
+    static bool EvalMember(const char* member, size_t len, void* object, void* eng, 
+        SkScriptValue* value);
+    static bool EvalMemberCommon(SkScriptEngine* , const SkMemberInfo* info, 
+        SkDisplayable* displayable, SkScriptValue* value);
+    static bool EvalMemberFunction(const char* member, size_t len, void* object, 
+        SkTDArray<SkScriptValue>& params, void* user, SkScriptValue* value);
+    static bool EvalNamedColor(const char* token, size_t len, void* callBack, SkScriptValue* );
+    static bool EvalRGB(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+        void* callBack, SkScriptValue* );
+    static const SkDisplayEnumMap& GetEnumValues(SkDisplayTypes type); 
+    static bool Infinity(const char* token, size_t len, void* callBack, SkScriptValue* );
+    static bool IsFinite(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+        void* callBack, SkScriptValue* );
+    static bool IsNaN(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+        void* callBack, SkScriptValue* );
+    static bool NaN(const char* token, size_t len, void* callBack, SkScriptValue* );
+    static bool Unbox(void* , SkScriptValue* scriptValue);
+    SkTDDisplayableArray fTrackDisplayable;
+    SkAnimateMaker& fMaker;
+    SkDisplayable* fParent;
+    SkDisplayable* fWorking;
+private:
+    friend class SkDump;
+    friend struct SkScriptNAnswer;
+#ifdef SK_SUPPORT_UNITTEST
+public:
+    static void UnitTest();
+#endif
+};
+
+#endif // SkAnimatorScript_DEFINED
+
diff --git a/src/animator/SkAnimatorScript2.cpp b/src/animator/SkAnimatorScript2.cpp
new file mode 100644
index 0000000..08dbf16
--- /dev/null
+++ b/src/animator/SkAnimatorScript2.cpp
@@ -0,0 +1,618 @@
+#include "SkAnimatorScript2.h"
+#include "SkAnimateBase.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayTypes.h"
+#include "SkExtras.h"
+#include "SkMemberInfo.h"
+#include "SkOpArray.h"
+#include "SkParse.h"
+#include "SkScript2.h"
+#include "SkScriptCallBack.h"
+
+static const SkDisplayEnumMap gEnumMaps[] = {
+	{ SkType_AddMode, "indirect|immediate" },
+	{ SkType_Align, "left|center|right" },
+	{ SkType_ApplyMode, "immediate|once" },
+	{ SkType_ApplyTransition, "reverse" },
+	{ SkType_BitmapEncoding, "jpeg|png" },
+	{ SkType_BitmapFormat, "none|A1|A8|Index8|RGB16|RGB32" },
+	{ SkType_Boolean, "false|true" },
+	{ SkType_Cap, "butt|round|square" },
+	{ SkType_EventCode, "none|up|down|left|right|back|end|OK|send|leftSoftKey|rightSoftKey|key0|key1|key2|key3|key4|key5|key6|key7|key8|key9|star|hash" },
+	{ SkType_EventKind, "none|keyChar|keyPress|mouseDown|mouseDrag|mouseMove|mouseUp|onEnd|onLoad|user" },
+	{ SkType_EventMode, "deferred|immediate" },
+	{ SkType_FillType, "winding|evenOdd" },
+	{ SkType_FilterType, "none|bilinear" },
+	{ SkType_FromPathMode, "normal|angle|position" },
+	{ SkType_Join, "miter|round|blunt" },
+	{ SkType_MaskFilterBlurStyle, "normal|solid|outer|inner" },
+	{ SkType_PathDirection, "cw|ccw" },
+	{ SkType_Style, "fill|stroke|strokeAndFill" },
+	{ SkType_TextBoxAlign, "start|center|end" },
+	{ SkType_TextBoxMode, "oneLine|lineBreak" },
+	{ SkType_TileMode, "clamp|repeat|mirror" },
+	{ SkType_Xfermode, "clear|src|dst|srcOver|dstOver|srcIn|dstIn|srcOut|dstOut|"
+		"srcATop|dstATop|xor|darken|lighten" },
+};
+
+static int gEnumMapCount = SK_ARRAY_COUNT(gEnumMaps);
+
+
+class SkAnimatorScript_Box : public SkScriptCallBackConvert {
+public:
+	SkAnimatorScript_Box() {}
+
+	~SkAnimatorScript_Box() {
+		for (SkDisplayable** dispPtr = fTrackDisplayable.begin(); dispPtr < fTrackDisplayable.end(); dispPtr++)
+			delete *dispPtr;
+	}
+
+	virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) {
+		SkDisplayable* displayable;
+		switch (type) {
+			case SkOperand2::kArray: {
+				SkDisplayArray* boxedValue = new SkDisplayArray(*operand->fArray);
+				displayable = boxedValue;
+				} break;
+			case SkOperand2::kS32: {
+				SkDisplayInt* boxedValue = new SkDisplayInt;
+				displayable = boxedValue;
+				boxedValue->value = operand->fS32;
+				} break;
+			case SkOperand2::kScalar: {
+				SkDisplayFloat* boxedValue = new SkDisplayFloat;
+				displayable = boxedValue;
+				boxedValue->value = operand->fScalar;
+				} break;
+			case SkOperand2::kString: {
+				SkDisplayString* boxedValue = new SkDisplayString(*operand->fString);
+				displayable = boxedValue;
+				} break;
+			case SkOperand2::kObject: 
+				return true;
+			default:
+				SkASSERT(0);
+				return false;
+		}
+		track(displayable);
+		operand->fObject = (void*) displayable;
+		return true;
+	}
+
+	virtual SkOperand2::OpType getReturnType(int index) { 
+		return SkOperand2::kObject; 
+	}
+
+	virtual Type getType() const { 
+		return kBox; 
+	}
+
+	void track(SkDisplayable* displayable) { 
+		SkASSERT(fTrackDisplayable.find(displayable) < 0);  
+		*fTrackDisplayable.append() = displayable; 
+	}
+
+	SkTDDisplayableArray fTrackDisplayable;
+};
+
+
+class SkAnimatorScript_Enum : public SkScriptCallBackProperty {
+public:
+	SkAnimatorScript_Enum(const char* tokens) : fTokens(tokens) {}
+
+	virtual bool getConstValue(const char* name, int len, SkOperand2* value) { 
+		return SkAnimatorScript2::MapEnums(fTokens, name, len, &value->fS32);
+	}
+
+private:
+	const char* fTokens;
+};
+
+	// !!! if type is string, call invoke
+	// if any other type, return original value
+		// distinction is undone: could do this by returning index == 0 only if param is string
+		// still, caller of getParamTypes will attempt to convert param to string (I guess)
+class SkAnimatorScript_Eval : public SkScriptCallBackFunction {
+public:
+	SkAnimatorScript_Eval(SkAnimatorScript2* engine) : fEngine(engine) {}
+
+	virtual bool getIndex(const char* name, int len, size_t* result) {
+		if (SK_LITERAL_STR_EQUAL("eval", name, len) != 0)
+			return false;
+		*result = 0;
+		return true;
+	}
+	
+	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
+		types->setCount(1);
+		SkOperand2::OpType* type = types->begin();
+		type[0] = SkOperand2::kString;
+	}
+
+	virtual bool invoke(size_t index, SkOpArray* params, SkOperand2* answer) {
+		SkAnimatorScript2 engine(fEngine->getMaker(), fEngine->getWorking(), 
+			SkAnimatorScript2::ToDisplayType(fEngine->getReturnType()));
+		SkOperand2* op = params->begin();
+		const char* script = op->fString->c_str();
+		SkScriptValue2 value;
+		return engine.evaluateScript(&script, &value);
+		SkASSERT(value.fType == fEngine->getReturnType());
+		*answer = value.fOperand;
+		// !!! incomplete ?
+		return true;
+	}
+
+private:
+	SkAnimatorScript2* fEngine;
+};
+
+class SkAnimatorScript_ID : public SkScriptCallBackProperty {
+public:
+	SkAnimatorScript_ID(SkAnimatorScript2* engine) : fEngine(engine) {}
+
+	virtual bool getIndex(const char* token, int len, size_t* result) {  
+		SkDisplayable* displayable;
+		bool success = fEngine->getMaker().find(token, len, &displayable);
+		if (success == false) {
+			*result = 0;
+		} else {
+			*result = (size_t) displayable;
+			SkDisplayable* working = fEngine->getWorking();
+			if (displayable->canContainDependents() && working && working->isAnimate()) {
+				SkAnimateBase* animator = (SkAnimateBase*) working;
+				if (animator->isDynamic()) {
+					SkDisplayDepend* depend = (SkDisplayDepend* ) displayable;
+					depend->addDependent(working);
+				}
+			}
+		}
+		return true; 
+	}
+
+	virtual bool getResult(size_t ref, SkOperand2* answer) {
+		answer->fObject = (void*) ref;
+		return true;
+	}
+
+	virtual SkOperand2::OpType getReturnType(size_t index) {
+		return index == 0 ? SkOperand2::kString : SkOperand2::kObject;
+	}
+
+private:
+	SkAnimatorScript2* fEngine;
+};
+
+
+class SkAnimatorScript_Member : public SkScriptCallBackMember {
+public:
+
+	SkAnimatorScript_Member(SkAnimatorScript2* engine) : fEngine(engine) {}
+
+	bool getMemberReference(const char* member, size_t len, void* object, SkScriptValue2* ref) {
+		SkDisplayable* displayable = (SkDisplayable*) object;
+		SkString name(member, len);
+		SkDisplayable* named = displayable->contains(name);
+		if (named) {
+			ref->fType = SkOperand2::kObject;
+			ref->fOperand.fObject = named;
+			return true;
+		}
+		const SkMemberInfo* info = displayable->getMember(name.c_str());
+		if (info == NULL)
+			return false;	// !!! add additional error info?
+		ref->fType = SkAnimatorScript2::ToOpType(info->getType());
+		ref->fOperand.fObject = (void*) info;
+		return true;
+	}
+
+	bool invoke(size_t ref, void* object, SkOperand2* value) {
+		const SkMemberInfo* info = (const SkMemberInfo* ) ref;
+		SkDisplayable* displayable = (SkDisplayable*) object;
+		if (info->fType == SkType_MemberProperty) {
+			if (displayable->getProperty2(info->propertyIndex(), value) == false) {
+				return false;
+			}
+		}
+		return fEngine->evalMemberCommon(info, displayable, value);
+	}
+
+	SkAnimatorScript2* fEngine;
+};
+
+
+class SkAnimatorScript_MemberFunction : public SkScriptCallBackMemberFunction {
+public:
+	SkAnimatorScript_MemberFunction(SkAnimatorScript2* engine) : fEngine(engine) {}
+
+	bool getMemberReference(const char* member, size_t len, void* object, SkScriptValue2* ref) {
+		SkDisplayable* displayable = (SkDisplayable*) object;
+		SkString name(member, len);
+		const SkMemberInfo* info = displayable->getMember(name.c_str());
+		if (info == NULL || info->fType != SkType_MemberFunction)
+			return false;	// !!! add additional error info?
+		ref->fType = SkAnimatorScript2::ToOpType(info->getType());
+		ref->fOperand.fObject = (void*) info;
+		return true;
+	}
+
+	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
+		types->setCount(3);
+		SkOperand2::OpType* type = types->begin();
+		type[0] = type[1] = type[2] = SkOperand2::kS32;
+	}
+
+	bool invoke(size_t ref, void* object, SkOpArray* params, SkOperand2* value)
+	{
+		const SkMemberInfo* info = (const SkMemberInfo* ) ref;
+		SkDisplayable* displayable = (SkDisplayable*) object;
+		displayable->executeFunction2(displayable, info->functionIndex(), params, info->getType(), 
+			value);
+		return fEngine->evalMemberCommon(info, displayable, value);
+	}
+
+	SkAnimatorScript2* fEngine;
+};
+
+
+class SkAnimatorScript_NamedColor : public SkScriptCallBackProperty {
+public:
+	virtual bool getConstValue(const char* name, int len, SkOperand2* value) {
+		return SkParse::FindNamedColor(name, len, (SkColor*) &value->fS32) != NULL;
+	}
+};
+
+
+class SkAnimatorScript_RGB : public SkScriptCallBackFunction {
+public:
+	virtual bool getIndex(const char* name, int len, size_t* result) {
+		if (SK_LITERAL_STR_EQUAL("rgb", name, len) != 0)
+			return false;
+		*result = 0;
+		return true;
+	}
+
+	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
+		types->setCount(3);
+		SkOperand2::OpType* type = types->begin();
+		type[0] = type[1] = type[2] = SkOperand2::kS32;
+	}
+
+	virtual bool invoke(size_t index, SkOpArray* params, SkOperand2* answer) {
+		SkASSERT(index == 0);
+		unsigned result = 0xFF000000;
+		int shift = 16;
+		for (int index = 0; index < 3; index++) {
+			result |= SkClampMax(params->begin()[index].fS32, 255) << shift;
+			shift -= 8;
+		}
+		answer->fS32 = result;
+		return true;
+	}
+
+};
+
+
+class SkAnimatorScript_Unbox : public SkScriptCallBackConvert {
+public:
+	SkAnimatorScript_Unbox(SkAnimatorScript2* engine) : fEngine(engine) {}
+
+	virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) {
+		SkASSERT(type == SkOperand2::kObject);
+		SkDisplayable* displayable = (SkDisplayable*) operand->fObject;
+		switch (displayable->getType()) {
+			case SkType_Array: {
+				SkDisplayArray* boxedValue = (SkDisplayArray*) displayable;
+				operand->fArray = new SkOpArray(SkAnimatorScript2::ToOpType(boxedValue->values.getType()));
+				int count = boxedValue->values.count();
+				operand->fArray->setCount(count);
+				memcpy(operand->fArray->begin(), boxedValue->values.begin(), count * sizeof(SkOperand2));
+				fEngine->track(operand->fArray);
+				} break;
+			case SkType_Boolean: {
+				SkDisplayBoolean* boxedValue = (SkDisplayBoolean*) displayable;
+				operand->fS32 = boxedValue->value;
+				} break;
+			case SkType_Int: {
+				SkDisplayInt* boxedValue = (SkDisplayInt*) displayable;
+				operand->fS32 = boxedValue->value;
+				} break;
+			case SkType_Float: {
+				SkDisplayFloat* boxedValue = (SkDisplayFloat*) displayable;
+				operand->fScalar = boxedValue->value;
+				} break;
+			case SkType_String: {
+				SkDisplayString* boxedValue = (SkDisplayString*) displayable;
+				operand->fString = SkNEW_ARGS(SkString, (boxedValue->value));
+				} break;
+			default: {
+				const char* id;
+				bool success = fEngine->getMaker().findKey(displayable, &id);
+				SkASSERT(success);
+				operand->fString = SkNEW_ARGS(SkString, (id));
+			}
+		}
+		return true;
+	}
+
+	virtual SkOperand2::OpType getReturnType(int /*index*/, SkOperand2* operand) { 
+		SkDisplayable* displayable = (SkDisplayable*) operand->fObject;
+		switch (displayable->getType()) {
+			case SkType_Array:
+				return SkOperand2::kArray;
+			case SkType_Int:
+				return SkOperand2::kS32;
+			case SkType_Float:
+				return SkOperand2::kScalar;
+			case SkType_String:
+			default:
+				return SkOperand2::kString;
+		}
+	}
+
+	virtual Type getType() const { 
+		return kUnbox; 
+	}
+
+	SkAnimatorScript2* fEngine;
+};
+
+SkAnimatorScript2::SkAnimatorScript2(SkAnimateMaker& maker, SkDisplayable* working, SkDisplayTypes type) : 
+		SkScriptEngine2(ToOpType(type)), fMaker(maker), fWorking(working) {
+	*fCallBackArray.append() = new SkAnimatorScript_Member(this);
+	*fCallBackArray.append() = new SkAnimatorScript_MemberFunction(this);
+	*fCallBackArray.append() = new SkAnimatorScript_Box();
+	*fCallBackArray.append() = new SkAnimatorScript_Unbox(this);
+	*fCallBackArray.append() = new SkAnimatorScript_ID(this);
+	if (type == SkType_ARGB) {
+		*fCallBackArray.append() = new SkAnimatorScript_RGB();
+		*fCallBackArray.append() = new SkAnimatorScript_NamedColor();
+	}
+	if (SkDisplayType::IsEnum(&maker, type)) {
+		// !!! for SpiderMonkey, iterate through the enum values, and map them to globals
+		const SkDisplayEnumMap& map = GetEnumValues(type);
+		*fCallBackArray.append() = new SkAnimatorScript_Enum(map.fValues); 
+	}
+	*fCallBackArray.append() = new SkAnimatorScript_Eval(this);
+#if 0		// !!! no extra support for now
+	for (SkExtras** extraPtr = maker.fExtras.begin(); extraPtr < maker.fExtras.end(); extraPtr++) {
+		SkExtras* extra = *extraPtr;
+		if (extra->fExtraCallBack)
+			*fCallBackArray.append() = new propertyCallBack(extra->fExtraCallBack, extra->fExtraStorage);
+	}
+#endif
+}
+
+SkAnimatorScript2::~SkAnimatorScript2() {
+	SkScriptCallBack** end = fCallBackArray.end();
+	for (SkScriptCallBack** ptr = fCallBackArray.begin(); ptr < end; ptr++)
+		delete *ptr;
+}
+
+bool SkAnimatorScript2::evalMemberCommon(const SkMemberInfo* info, 
+		SkDisplayable* displayable, SkOperand2* value) {
+	SkDisplayTypes original;
+	SkDisplayTypes type = original = (SkDisplayTypes) info->getType();
+	if (info->fType == SkType_Array)
+		type = SkType_Array;
+	switch (type) {
+		case SkType_ARGB:
+			type = SkType_Int;
+		case SkType_Boolean:
+		case SkType_Int:
+		case SkType_MSec:
+		case SkType_Float:
+			SkASSERT(info->getCount() == 1);
+			if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) 
+				value->fS32 = *(int32_t*) info->memberData(displayable);	// OK for SkScalar too
+			if (type == SkType_MSec) {
+				value->fScalar = SkScalarDiv((SkScalar) value->fS32, 1000); // dividing two ints is the same as dividing two scalars 
+				type = SkType_Float;
+			}
+			break;
+		case SkType_String: {
+			SkString* displayableString;
+			if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) {
+				info->getString(displayable, &displayableString);
+				value->fString = new SkString(*displayableString);
+			}
+			} break;
+		case SkType_Array: {
+			SkASSERT(info->fType != SkType_MemberProperty); // !!! incomplete
+			SkTDOperandArray* displayableArray = (SkTDOperandArray*) info->memberData(displayable);
+			if (displayable->getType() == SkType_Array) {
+				SkDisplayArray* typedArray = (SkDisplayArray*) displayable;
+				original = typedArray->values.getType();
+			}
+			SkASSERT(original != SkType_Unknown);
+			SkOpArray* array = value->fArray = new SkOpArray(ToOpType(original));
+			track(array);
+			int count = displayableArray->count();
+			if (count > 0) {
+				array->setCount(count);
+				memcpy(array->begin(), displayableArray->begin(), count * sizeof(SkOperand2));
+			}
+			} break;
+		default:
+			SkASSERT(0); // unimplemented
+	}
+	return true;
+}
+
+const SkDisplayEnumMap& SkAnimatorScript2::GetEnumValues(SkDisplayTypes type) {
+	int index = SkTSearch<SkDisplayTypes>(&gEnumMaps[0].fType, gEnumMapCount, type, 
+		sizeof(SkDisplayEnumMap));
+	SkASSERT(index >= 0);
+	return gEnumMaps[index];
+}
+
+SkDisplayTypes SkAnimatorScript2::ToDisplayType(SkOperand2::OpType type) {
+	int val = type;
+	switch (val) {
+		case SkOperand2::kNoType:
+			return SkType_Unknown;
+		case SkOperand2::kS32:
+			return SkType_Int;
+		case SkOperand2::kScalar:
+			return SkType_Float;
+		case SkOperand2::kString:
+			return SkType_String;
+		case SkOperand2::kArray:
+			return SkType_Array;
+		case SkOperand2::kObject:
+			return SkType_Displayable;
+		default:
+			SkASSERT(0);
+			return SkType_Unknown;
+	}
+}
+
+SkOperand2::OpType SkAnimatorScript2::ToOpType(SkDisplayTypes type) {
+	if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type))
+		return SkOperand2::kObject;
+	if (SkDisplayType::IsEnum(NULL /* fMaker */, type))
+		return SkOperand2::kS32;
+	switch (type) {
+		case SkType_ARGB:
+		case SkType_MSec:
+		case SkType_Int:
+			return SkOperand2::kS32;
+		case SkType_Float:
+		case SkType_Point:
+		case SkType_3D_Point:
+			return SkOperand2::kScalar;
+		case SkType_Base64:
+		case SkType_DynamicString:
+		case SkType_String:
+			return SkOperand2::kString;
+		case SkType_Array:
+			return SkOperand2::kArray;
+		case SkType_Unknown:
+			return SkOperand2::kNoType;
+		default:
+			SkASSERT(0);
+			return SkOperand2::kNoType;
+	}
+}
+
+bool SkAnimatorScript2::MapEnums(const char* ptr, const char* match, size_t len, int* value) {
+	int index = 0;
+	bool more = true;
+	do {
+		const char* last = strchr(ptr, '|');
+		if (last == NULL) {
+			last = &ptr[strlen(ptr)];
+			more = false;
+		}
+		size_t length = last - ptr;
+		if (len == length && strncmp(ptr, match, length) == 0) {
+			*value = index;
+			return true;
+		}
+		index++;
+		ptr = last + 1;
+	} while (more);
+	return false;
+}
+
+#if defined SK_DEBUG
+
+#include "SkAnimator.h"
+
+static const char scriptTestSetup[]  = 
+"<screenplay>"
+	"<apply>"
+		"<paint>"
+			"<emboss id='emboss' direction='[1,1,1]'  />"
+		"</paint>"
+		"<animateField id='animation' field='direction' target='emboss' from='[1,1,1]' to='[-1,1,1]' dur='1'/>"
+		"<set lval='direction[0]' target='emboss' to='-1' />"
+	"</apply>"
+	"<color id='testColor' color='0 ? rgb(0,0,0) : rgb(255,255,255)' />"
+	"<color id='xColor' color='rgb(12,34,56)' />"
+	"<typedArray id='emptyArray' />"
+	"<typedArray id='intArray' values='[1, 4, 6]' />"
+	"<s32 id='idx' value='2' />"
+	"<s32 id='idy' value='2' />"
+	"<string id='alpha' value='abc' />"
+	"<rectangle id='testRect' left='Math.cos(0)' top='2' right='12' bottom='5' />"
+	"<event id='evt'>"
+		"<input name='x' />"
+		"<apply scope='idy'>"
+			"<set field='value' to='evt.x.s32' />"
+		"</apply>"
+	"</event>"
+"</screenplay>";
+
+#if !defined(SK_BUILD_FOR_BREW)
+static const SkScriptNAnswer scriptTests[]  = {
+	{	"alpha+alpha", SkType_String, 0, 0, "abcabc" },
+	{	"0 ? Math.sin(0) : 1", SkType_Int, 1 },
+	{	"intArray[4]", SkType_Unknown },
+	{	"emptyArray[4]", SkType_Unknown },
+	{	"idx", SkType_Int, 2 },
+	{	"intArray.length", SkType_Int, 3 },
+	{	"intArray.values[0]", SkType_Int, 1 },
+	{	"intArray[0]", SkType_Int, 1 },
+	{	"idx.value", SkType_Int, 2 },
+	{	"alpha.value", SkType_String, 0, 0, "abc" },
+	{	"alpha", SkType_String, 0, 0, "abc" },
+	{	"alpha.value+alpha.value", SkType_String, 0, 0, "abcabc" },
+	{	"alpha+idx", SkType_String, 0, 0, "abc2" },
+	{	"idx+alpha", SkType_String, 0, 0, "2abc" },
+	{	"intArray[idx]", SkType_Int, 6 },
+	{	"alpha.slice(1,2)", SkType_String, 0, 0, "b" },
+	{	"alpha.value.slice(1,2)", SkType_String, 0, 0, "b" },
+	{	"Math.sin(0)", SkType_Float, 0, SkIntToScalar(0) },
+	{	"testRect.left+2", SkType_Float, 0, SkIntToScalar(3) },
+	{	"0 ? intArray[0] : 1", SkType_Int, 1 },
+	{	"0 ? intArray.values[0] : 1", SkType_Int, 1 },
+	{	"0 ? idx : 1", SkType_Int, 1 },
+	{	"0 ? idx.value : 1", SkType_Int, 1 },
+	{	"0 ? alpha.slice(1,2) : 1", SkType_Int, 1 },
+	{	"0 ? alpha.value.slice(1,2) : 1", SkType_Int, 1 },
+	{ "idy", SkType_Int, 3 }
+};
+#endif
+
+#define SkScriptNAnswer_testCount	SK_ARRAY_COUNT(scriptTests)
+
+void SkAnimatorScript2::UnitTest() {
+#if !defined(SK_BUILD_FOR_BREW) && defined(SK_SUPPORT_UNITTEST)
+	SkAnimator animator;
+	SkASSERT(animator.decodeMemory(scriptTestSetup, sizeof(scriptTestSetup)-1));
+	SkEvent evt;
+	evt.setString("id", "evt");
+	evt.setS32("x", 3);
+	animator.doUserEvent(evt);
+	// set up animator with memory script above, then run value tests
+	for (int index = 0; index < SkScriptNAnswer_testCount; index++) {
+		SkAnimatorScript2 engine(*animator.fMaker, NULL, scriptTests[index].fType);
+		SkScriptValue2 value;
+		const char* script = scriptTests[index].fScript;
+		bool success = engine.evaluateScript(&script, &value);
+		if (success == false) {
+			SkASSERT(scriptTests[index].fType == SkType_Unknown);
+			continue;
+		}
+		SkASSERT(value.fType == ToOpType(scriptTests[index].fType));
+		SkScalar error;
+		switch (value.fType) {
+			case SkOperand2::kS32:
+				SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
+				break;
+			case SkOperand2::kScalar:
+				error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
+				SkASSERT(error < SK_Scalar1 / 10000);
+				break;
+			case SkOperand2::kString:
+				SkASSERT(value.fOperand.fString->equals(scriptTests[index].fStringAnswer));
+				break;
+			default:
+				SkASSERT(0);
+		}
+	}
+#endif
+}
+
+#endif
+
diff --git a/src/animator/SkAnimatorScript2.h b/src/animator/SkAnimatorScript2.h
new file mode 100644
index 0000000..61261e9
--- /dev/null
+++ b/src/animator/SkAnimatorScript2.h
@@ -0,0 +1,43 @@
+#ifndef SkAnimatorScript2_DEFINED
+#define SkAnimatorScript2_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkScript2.h"
+#include "SkTypedArray.h"
+
+class SkAnimateMaker;
+struct SkMemberInfo;
+
+#ifndef SkAnimatorScript_DEFINED
+struct SkDisplayEnumMap {
+	SkDisplayTypes fType;
+	const char* fValues;
+};
+#endif
+
+class SkAnimatorScript2 : public SkScriptEngine2 {
+public:
+	SkAnimatorScript2(SkAnimateMaker& , SkDisplayable* working, SkDisplayTypes type);
+	~SkAnimatorScript2();
+	bool evalMemberCommon(const SkMemberInfo* info, 
+		SkDisplayable* displayable, SkOperand2* value);
+	SkAnimateMaker& getMaker() { return fMaker; }
+	SkDisplayable* getWorking() { return fWorking; }
+	static bool MapEnums(const char* ptr, const char* match, size_t len, int* value);
+	static const SkDisplayEnumMap& GetEnumValues(SkDisplayTypes type); 
+	static SkDisplayTypes ToDisplayType(SkOperand2::OpType type);
+	static SkOperand2::OpType ToOpType(SkDisplayTypes type);
+private:
+	SkAnimateMaker& fMaker;
+	SkDisplayable* fWorking;
+	friend class SkDump;
+	friend struct SkScriptNAnswer;
+	// illegal
+	SkAnimatorScript2& operator=(const SkAnimatorScript2&);
+#ifdef SK_DEBUG
+public:
+	static void UnitTest();
+#endif
+};
+
+#endif // SkAnimatorScript2_DEFINED
diff --git a/src/animator/SkBase64.cpp b/src/animator/SkBase64.cpp
new file mode 100644
index 0000000..e192a52
--- /dev/null
+++ b/src/animator/SkBase64.cpp
@@ -0,0 +1,188 @@
+/* libs/graphics/animator/SkBase64.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 "SkBase64.h"
+
+#define DecodePad -2
+#define EncodePad 64
+
+static const char encode[] = 
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "abcdefghijklmnopqrstuvwxyz"
+    "0123456789+/=";
+
+static const signed char decodeData[] = {
+    62, -1, -1, -1, 63,
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, DecodePad, -1, -1,
+    -1,  0,  1,  2,  3,  4,  5,  6, 7,  8,  9, 10, 11, 12, 13, 14,
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+};
+
+SkBase64::SkBase64() : fLength((size_t) -1), fData(NULL) {
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable 'two', etc. may be used without having been initialized
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+SkBase64::Error SkBase64::decode(const void* srcPtr, size_t size, bool writeDestination) {
+    unsigned char* dst = (unsigned char*) fData;
+    const unsigned char* dstStart = (const unsigned char*) fData;
+    const unsigned char* src = (const unsigned char*) srcPtr;
+    bool padTwo = false;
+    bool padThree = false;
+    const unsigned char* end = src + size;
+    while (src < end) {
+        unsigned char bytes[4];
+        int byte = 0;
+        do {
+            unsigned char srcByte = *src++;
+            if (srcByte == 0)
+                goto goHome;
+            if (srcByte <= ' ')
+                continue; // treat as white space
+            if (srcByte < '+' || srcByte > 'z')
+                return kBadCharError;
+            signed char decoded = decodeData[srcByte - '+'];
+            bytes[byte] = decoded;
+            if (decoded < 0) {
+                if (decoded == DecodePad) 
+                    goto handlePad;
+                return kBadCharError;
+            } else
+                byte++;
+            if (*src)
+                continue;
+            if (byte == 0)
+                goto goHome;
+            if (byte == 4)
+                break;
+handlePad:
+            if (byte < 2)
+                return kPadError;
+            padThree = true;
+            if (byte == 2)
+                padTwo = true;
+            break;
+        } while (byte < 4);
+        int two, three;
+        if (writeDestination) {
+            int one = (uint8_t) (bytes[0] << 2);
+            two = bytes[1];
+            one |= two >> 4;
+            two = (uint8_t) (two << 4);
+            three = bytes[2];
+            two |= three >> 2;
+            three = (uint8_t) (three << 6);
+            three |= bytes[3];
+            SkASSERT(one < 256 && two < 256 && three < 256);
+            *dst = (unsigned char) one;
+        }
+        dst++;
+        if (padTwo) 
+            break;
+        if (writeDestination)
+            *dst = (unsigned char) two;
+        dst++;
+        if (padThree)
+            break;
+        if (writeDestination)
+            *dst = (unsigned char) three;
+        dst++;
+    }
+goHome:
+    fLength = dst - dstStart;
+    return kNoError;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  
+#pragma warning ( pop )
+#endif
+
+size_t SkBase64::Encode(const void* srcPtr, size_t length, void* dstPtr) {
+    const unsigned char* src = (const unsigned char*) srcPtr;
+    unsigned char* dst = (unsigned char*) dstPtr;
+    if (dst) {
+        size_t remainder = length % 3;
+        const unsigned char* end = &src[length - remainder];
+        while (src < end) {
+            unsigned a = *src++;
+            unsigned b = *src++;
+            unsigned c = *src++;
+            int      d = c & 0x3F;
+            c = (c >> 6 | b << 2) & 0x3F; 
+            b = (b >> 4 | a << 4) & 0x3F;
+            a = a >> 2;
+            *dst++ = encode[a];
+            *dst++ = encode[b];
+            *dst++ = encode[c];
+            *dst++ = encode[d];
+        }
+        if (remainder > 0) {
+            int k1 = 0;
+            int k2 = EncodePad;
+            int a = (uint8_t) *src++;
+            if (remainder == 2)
+            {
+                int b = *src++;
+                k1 = b >> 4;
+                k2 = (b << 2) & 0x3F;
+            }
+            *dst++ = encode[a >> 2];
+            *dst++ = encode[(k1 | a << 4) & 0x3F];
+            *dst++ = encode[k2];
+            *dst++ = encode[EncodePad];
+        }
+    }
+    return (length + 2) / 3 * 4;
+}
+
+SkBase64::Error SkBase64::decode(const char* src, size_t len) {
+    Error err = decode(src, len, false);
+    SkASSERT(err == kNoError);
+    if (err != kNoError)
+        return err;
+    fData = new char[fLength];  // should use sk_malloc/sk_free
+    decode(src, len, true);
+    return kNoError;
+}
+
+#ifdef SK_SUPPORT_UNITTEST
+void SkBase64::UnitTest() {
+    signed char all[256];
+    for (int index = 0; index < 256; index++)
+        all[index] = (signed char) (index + 1);
+    for (int offset = 0; offset < 6; offset++) {
+        size_t length = 256 - offset;
+        size_t encodeLength = Encode(all + offset, length, NULL);
+        char* src = (char*)sk_malloc_throw(encodeLength + 1);
+        Encode(all + offset, length, src);
+        src[encodeLength] = '\0';
+        SkBase64 tryMe;
+        tryMe.decode(src, encodeLength);
+        SkASSERT(length == tryMe.fLength);
+        SkASSERT(strcmp((const char*) (all + offset), tryMe.fData) == 0);
+        sk_free(src);
+        delete[] tryMe.fData;
+    }
+}
+#endif
+
+
diff --git a/src/animator/SkBase64.h b/src/animator/SkBase64.h
new file mode 100644
index 0000000..12ccf8c
--- /dev/null
+++ b/src/animator/SkBase64.h
@@ -0,0 +1,47 @@
+/* libs/graphics/animator/SkBase64.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 SkBase64_DEFINED
+#define SkBase64_DEFINED
+
+#include "SkTypes.h"
+
+struct SkBase64 {
+public:
+    enum Error {
+        kNoError,
+        kPadError,
+        kBadCharError
+    };
+
+    SkBase64();
+    Error decode(const char* src, size_t length);
+    char* getData() { return fData; }
+    static size_t Encode(const void* src, size_t length, void* dest);
+
+#ifdef SK_SUPPORT_UNITTEST
+    static void UnitTest();
+#endif
+private:
+    Error decode(const void* srcPtr, size_t length, bool writeDestination);
+
+    size_t fLength;
+    char* fData;
+    friend class SkImage;
+};
+
+#endif // SkBase64_DEFINED
diff --git a/src/animator/SkBoundable.cpp b/src/animator/SkBoundable.cpp
new file mode 100644
index 0000000..f2125ea
--- /dev/null
+++ b/src/animator/SkBoundable.cpp
@@ -0,0 +1,64 @@
+/* libs/graphics/animator/SkBoundable.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 "SkBoundable.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+
+SkBoundable::SkBoundable() {
+    clearBounds();
+    fBounds.fTop = 0;
+    fBounds.fRight = 0;
+    fBounds.fBottom = 0;
+}
+
+void SkBoundable::clearBounder() {
+    fBounds.fLeft = 0x7fff;
+}
+
+void SkBoundable::getBounds(SkRect* rect) {
+    SkASSERT(rect);
+    if (fBounds.fLeft == (int16_t)0x8000U) {
+        INHERITED::getBounds(rect);
+        return;
+    }
+    rect->fLeft = SkIntToScalar(fBounds.fLeft);
+    rect->fTop = SkIntToScalar(fBounds.fTop);
+    rect->fRight = SkIntToScalar(fBounds.fRight);
+    rect->fBottom = SkIntToScalar(fBounds.fBottom);
+}
+
+void SkBoundable::enableBounder() {
+    fBounds.fLeft = 0;
+}
+
+
+SkBoundableAuto::SkBoundableAuto(SkBoundable* boundable, 
+        SkAnimateMaker& maker) : fBoundable(boundable), fMaker(maker) {
+    if (fBoundable->hasBounds()) {
+        fMaker.fCanvas->setBounder(&maker.fDisplayList);
+        fMaker.fDisplayList.fBounds.setEmpty();
+    }
+}
+
+SkBoundableAuto::~SkBoundableAuto() {
+    if (fBoundable->hasBounds() == false)
+        return;
+    fMaker.fCanvas->setBounder(NULL);
+    fBoundable->setBounds(fMaker.fDisplayList.fBounds);
+}
+
diff --git a/src/animator/SkBoundable.h b/src/animator/SkBoundable.h
new file mode 100644
index 0000000..67f5a12
--- /dev/null
+++ b/src/animator/SkBoundable.h
@@ -0,0 +1,50 @@
+/* libs/graphics/animator/SkBoundable.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 SkBoundable_DEFINED
+#define SkBoundable_DEFINED
+
+#include "SkDrawable.h"
+#include "SkRect.h"
+
+class SkBoundable : public SkDrawable {
+public:
+    SkBoundable();
+    virtual void clearBounder();
+    virtual void enableBounder();
+    virtual void getBounds(SkRect* );
+    bool hasBounds() { return fBounds.fLeft != (int16_t)0x8000U; }
+    void setBounds(SkIRect& bounds) { fBounds = bounds; }
+protected:
+    void clearBounds() { fBounds.fLeft = (int16_t) SkToU16(0x8000); }; // mark bounds as unset
+    SkIRect fBounds;
+private:
+    typedef SkDrawable INHERITED;
+};
+
+class SkBoundableAuto {
+public:
+    SkBoundableAuto(SkBoundable* boundable, SkAnimateMaker& maker);
+    ~SkBoundableAuto();
+private:
+    SkBoundable* fBoundable;
+    SkAnimateMaker& fMaker;
+    SkBoundableAuto& operator= (const SkBoundableAuto& );
+};
+
+#endif // SkBoundable_DEFINED
+
diff --git a/src/animator/SkBuildCondensedInfo.cpp b/src/animator/SkBuildCondensedInfo.cpp
new file mode 100644
index 0000000..5e4eedc
--- /dev/null
+++ b/src/animator/SkBuildCondensedInfo.cpp
@@ -0,0 +1,292 @@
+/* libs/graphics/animator/SkBuildCondensedInfo.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 "SkTypes.h"
+#if defined SK_BUILD_CONDENSED
+#include "SkMemberInfo.h"
+#if SK_USE_CONDENSED_INFO == 1 
+#error "SK_USE_CONDENSED_INFO must be zero to build condensed info"
+#endif
+#if !defined SK_BUILD_FOR_WIN32
+#error "SK_BUILD_FOR_WIN32 must be defined to build condensed info"
+#endif
+#include "SkDisplayType.h"
+#include "SkIntArray.h"
+#include <stdio.h>
+
+SkTDMemberInfoArray gInfos;
+SkTDIntArray gInfosCounts;
+SkTDDisplayTypesArray gInfosTypeIDs;
+SkTDMemberInfoArray gUnknowns;
+SkTDIntArray gUnknownsCounts;
+
+static void AddInfo(SkDisplayTypes type, const SkMemberInfo* info, int infoCount) {
+    SkASSERT(gInfos[type] == NULL);
+    gInfos[type] = info;
+    gInfosCounts[type] = infoCount;
+    *gInfosTypeIDs.append() = type;
+    size_t allStrs = 0;
+    for (int inner = 0; inner < infoCount; inner++) {
+        SkASSERT(info[inner].fCount < 256);
+        int offset = (int) info[inner].fOffset;
+        SkASSERT(offset < 128 && offset > -129);
+        SkASSERT(allStrs < 256);
+        if (info[inner].fType == SkType_BaseClassInfo) {
+            const SkMemberInfo* innerInfo = (const SkMemberInfo*) info[inner].fName;
+            if (gUnknowns.find(innerInfo) == -1) {
+                *gUnknowns.append() = innerInfo;
+                *gUnknownsCounts.append() = info[inner].fCount;
+            }
+        }
+        if (info[inner].fType != SkType_BaseClassInfo && info[inner].fName)
+            allStrs += strlen(info[inner].fName);
+        allStrs += 1;
+        SkASSERT(info[inner].fType < 256);
+    }
+}
+
+static void WriteInfo(FILE* condensed, const SkMemberInfo* info, int infoCount,
+            const char* typeName, bool draw, bool display) {
+    fprintf(condensed, "static const char g%sStrings[] = \n", typeName);
+    int inner;
+    // write strings
+    for (inner = 0; inner < infoCount; inner++) {
+        const char* name = (info[inner].fType != SkType_BaseClassInfo && info[inner].fName) ?
+            info[inner].fName : "";
+        const char* zero = inner < infoCount - 1 ? "\\0" : "";
+        fprintf(condensed, "\t\"%s%s\"\n", name, zero);
+    }
+    fprintf(condensed, ";\n\nstatic const SkMemberInfo g%s", draw ? "Draw" : display ? "Display" : "");
+    fprintf(condensed, "%sInfo[] = {", typeName);
+    size_t nameOffset = 0;
+    // write info tables
+    for (inner = 0; inner < infoCount; inner++) {
+        size_t offset = info[inner].fOffset;
+        if (info[inner].fType == SkType_BaseClassInfo) {
+            offset = (size_t) gInfos.find((const SkMemberInfo* ) info[inner].fName);
+            SkASSERT((int) offset >= 0);
+            offset = gInfosTypeIDs.find((SkDisplayTypes) offset);
+            SkASSERT((int) offset >= 0);
+        }
+        fprintf(condensed, "\n\t{%d, %d, %d, %d}", nameOffset, offset,
+            info[inner].fType, info[inner].fCount);
+        if (inner < infoCount - 1)
+            putc(',', condensed);
+        if (info[inner].fType != SkType_BaseClassInfo && info[inner].fName)
+            nameOffset += strlen(info[inner].fName);
+        nameOffset += 1;
+    }
+    fprintf(condensed, "\n};\n\n");
+}
+
+static void Get3DName(char* scratch, const char* name) {
+    if (strncmp("skia3d:", name, sizeof("skia3d:") - 1) == 0) {
+        strcpy(scratch, "3D_");
+        scratch[3]= name[7] & ~0x20;
+        strcpy(&scratch[4], &name[8]);
+    } else {
+        scratch[0] = name[0] & ~0x20;
+        strcpy(&scratch[1], &name[1]);
+    }
+}
+
+int type_compare(const void* a, const void* b) {
+    SkDisplayTypes first = *(SkDisplayTypes*) a;
+    SkDisplayTypes second = *(SkDisplayTypes*) b;
+    return first < second ? -1 : first == second ? 0 : 1;
+}
+
+void SkDisplayType::BuildCondensedInfo(SkAnimateMaker* maker) {
+    gInfos.setCount(kNumberOfTypes);
+    memset(gInfos.begin(), 0, sizeof(gInfos[0]) * kNumberOfTypes);
+    gInfosCounts.setCount(kNumberOfTypes);
+    memset(gInfosCounts.begin(), -1, sizeof(gInfosCounts[0]) * kNumberOfTypes);
+    // check to see if it is condensable
+    int index, infoCount;
+    for (index = 0; index < kTypeNamesSize; index++) {
+        const SkMemberInfo* info = GetMembers(maker, gTypeNames[index].fType, &infoCount);
+        if (info == NULL)
+            continue;
+        AddInfo(gTypeNames[index].fType, info, infoCount);
+    }
+    const SkMemberInfo* extraInfo = 
+        SkDisplayType::GetMembers(maker, SkType_3D_Point, &infoCount);
+    AddInfo(SkType_Point, extraInfo, infoCount);
+    AddInfo(SkType_3D_Point, extraInfo, infoCount);
+//  int baseInfos = gInfos.count();
+    do {
+        SkTDMemberInfoArray oldRefs = gUnknowns;
+        SkTDIntArray oldRefCounts = gUnknownsCounts;
+        gUnknowns.reset();
+        gUnknownsCounts.reset();
+        for (index = 0; index < oldRefs.count(); index++) {
+            const SkMemberInfo* info = oldRefs[index];
+            if (gInfos.find(info) == -1) {
+                int typeIndex = 0;
+                for (; typeIndex < kNumberOfTypes; typeIndex++) {
+                    const SkMemberInfo* temp = SkDisplayType::GetMembers(
+                        maker, (SkDisplayTypes) typeIndex, NULL);
+                    if (temp == info)
+                        break;
+                }
+                SkASSERT(typeIndex < kNumberOfTypes);
+                AddInfo((SkDisplayTypes) typeIndex, info, oldRefCounts[index]);
+            }
+        }
+    } while (gUnknowns.count() > 0);
+    qsort(gInfosTypeIDs.begin(), gInfosTypeIDs.count(), sizeof(gInfosTypeIDs[0]), &type_compare);
+#ifdef SK_DEBUG
+    FILE* condensed = fopen("../../src/animator/SkCondensedDebug.cpp", "w+");
+    fprintf(condensed, "#include \"SkTypes.h\"\n");
+    fprintf(condensed, "#ifdef SK_DEBUG\n");
+#else
+    FILE* condensed = fopen("../../src/animator/SkCondensedRelease.cpp", "w+");
+    fprintf(condensed, "#include \"SkTypes.h\"\n");
+    fprintf(condensed, "#ifdef SK_RELEASE\n");
+#endif
+    // write header
+    fprintf(condensed, "// This file was automatically generated.\n");
+    fprintf(condensed, "// To change it, edit the file with the matching debug info.\n");
+    fprintf(condensed, "// Then execute SkDisplayType::BuildCondensedInfo() to "
+        "regenerate this file.\n\n");
+    // write name of memberInfo
+    int typeNameIndex = 0;
+    int unknown = 1;
+    for (index = 0; index < gInfos.count(); index++) {
+        const SkMemberInfo* info = gInfos[index];
+        if (info == NULL)
+            continue;
+        char scratch[64];
+        bool drawPrefix, displayPrefix;
+        while (gTypeNames[typeNameIndex].fType < index)
+            typeNameIndex++;
+        if (gTypeNames[typeNameIndex].fType == index) {
+            Get3DName(scratch, gTypeNames[typeNameIndex].fName);
+            drawPrefix = gTypeNames[typeNameIndex].fDrawPrefix;
+            displayPrefix = gTypeNames[typeNameIndex].fDisplayPrefix;
+        } else {
+            sprintf(scratch, "Unknown%d", unknown++);
+            drawPrefix = displayPrefix = false;
+        }
+        WriteInfo(condensed, info, gInfosCounts[index], scratch, drawPrefix, displayPrefix);
+    }
+    // write array of table pointers
+//  start here;
+    fprintf(condensed, "static const SkMemberInfo* const gInfoTables[] = {");
+    typeNameIndex = 0;
+    unknown = 1;
+    for (index = 0; index < gInfos.count(); index++) {
+        const SkMemberInfo* info = gInfos[index];
+        if (info == NULL)
+            continue;
+        char scratch[64];
+        bool drawPrefix, displayPrefix;
+        while (gTypeNames[typeNameIndex].fType < index)
+            typeNameIndex++;
+        if (gTypeNames[typeNameIndex].fType == index) {
+            Get3DName(scratch, gTypeNames[typeNameIndex].fName);
+            drawPrefix = gTypeNames[typeNameIndex].fDrawPrefix;
+            displayPrefix = gTypeNames[typeNameIndex].fDisplayPrefix;
+        } else {
+            sprintf(scratch, "Unknown%d", unknown++);
+            drawPrefix = displayPrefix = false;
+        }
+        fprintf(condensed, "\n\tg");
+        if (drawPrefix)
+            fprintf(condensed, "Draw");
+        if (displayPrefix)
+            fprintf(condensed, "Display");
+        fprintf(condensed, "%sInfo", scratch);
+        if (index < gInfos.count() - 1)
+                putc(',', condensed);
+    }
+    fprintf(condensed, "\n};\n\n");
+    // write the array of number of entries in the info table
+    fprintf(condensed, "static const unsigned char gInfoCounts[] = {\n\t");
+    int written = 0;
+    for (index = 0; index < gInfosCounts.count(); index++) {
+        int count = gInfosCounts[index];
+        if (count < 0)
+            continue;
+        if (written > 0)
+            putc(',', condensed);
+        if (written % 20 == 19)
+            fprintf(condensed, "\n\t");
+        fprintf(condensed, "%d",count);
+        written++;
+    }
+    fprintf(condensed, "\n};\n\n");
+    // write array of type ids table entries correspond to
+    fprintf(condensed, "static const unsigned char gTypeIDs[] = {\n\t");
+    int typeIDCount = 0;
+    typeNameIndex = 0;
+    unknown = 1;
+    for (index = 0; index < gInfosCounts.count(); index++) {
+        const SkMemberInfo* info = gInfos[index];
+        if (info == NULL)
+            continue;
+        typeIDCount++;
+        char scratch[64];
+        while (gTypeNames[typeNameIndex].fType < index)
+            typeNameIndex++;
+        if (gTypeNames[typeNameIndex].fType == index) {
+            Get3DName(scratch, gTypeNames[typeNameIndex].fName);
+        } else
+            sprintf(scratch, "Unknown%d", unknown++);
+        fprintf(condensed, "%d%c // %s\n\t", index, 
+            index < gInfosCounts.count() ? ',' : ' ', scratch);
+    }
+    fprintf(condensed, "\n};\n\n");
+    fprintf(condensed, "static const int kTypeIDs = %d;\n\n", typeIDCount);
+    // write the array of string pointers
+    fprintf(condensed, "static const char* const gInfoNames[] = {");
+    typeNameIndex = 0;
+    unknown = 1;
+    written = 0;
+    for (index = 0; index < gInfosCounts.count(); index++) {
+        const SkMemberInfo* info = gInfos[index];
+        if (info == NULL)
+            continue;
+        if (written > 0)
+                putc(',', condensed);
+        written++;
+        fprintf(condensed, "\n\tg");
+        char scratch[64];
+        while (gTypeNames[typeNameIndex].fType < index)
+            typeNameIndex++;
+        if (gTypeNames[typeNameIndex].fType == index) {
+            Get3DName(scratch, gTypeNames[typeNameIndex].fName);
+        } else
+            sprintf(scratch, "Unknown%d", unknown++);
+        fprintf(condensed, "%sStrings", scratch);
+    }
+    fprintf(condensed, "\n};\n\n");
+    fprintf(condensed, "#endif\n");
+    fclose(condensed);
+    gInfos.reset();
+    gInfosCounts.reset();
+    gInfosTypeIDs.reset();
+    gUnknowns.reset();
+    gUnknownsCounts.reset();
+}
+
+#elif defined SK_DEBUG
+#include "SkDisplayType.h"
+void SkDisplayType::BuildCondensedInfo(SkAnimateMaker* ) {}
+#endif
+
+
diff --git a/src/animator/SkCondensedDebug.cpp b/src/animator/SkCondensedDebug.cpp
new file mode 100644
index 0000000..e7b7ff1
--- /dev/null
+++ b/src/animator/SkCondensedDebug.cpp
@@ -0,0 +1,1397 @@
+/* libs/graphics/animator/SkCondensedDebug.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 "SkTypes.h"
+#ifndef SK_BUILD_FOR_UNIX
+#ifdef SK_DEBUG
+// This file was automatically generated.
+// To change it, edit the file with the matching debug info.
+// Then execute SkDisplayType::BuildCondensedInfo() to regenerate this file.
+
+static const char gMathStrings[] = 
+    "E\0"
+    "LN10\0"
+    "LN2\0"
+    "LOG10E\0"
+    "LOG2E\0"
+    "PI\0"
+    "SQRT1_2\0"
+    "SQRT2\0"
+    "abs\0"
+    "acos\0"
+    "asin\0"
+    "atan\0"
+    "atan2\0"
+    "ceil\0"
+    "cos\0"
+    "exp\0"
+    "floor\0"
+    "log\0"
+    "max\0"
+    "min\0"
+    "pow\0"
+    "random\0"
+    "round\0"
+    "sin\0"
+    "sqrt\0"
+    "tan"
+;
+
+static const SkMemberInfo gMathInfo[] = {
+    {0, -1, 67, 98},
+    {2, -2, 67, 98},
+    {7, -3, 67, 98},
+    {11, -4, 67, 98},
+    {18, -5, 67, 98},
+    {24, -6, 67, 98},
+    {27, -7, 67, 98},
+    {35, -8, 67, 98},
+    {41, -1, 66, 98},
+    {45, -2, 66, 98},
+    {50, -3, 66, 98},
+    {55, -4, 66, 98},
+    {60, -5, 66, 98},
+    {66, -6, 66, 98},
+    {71, -7, 66, 98},
+    {75, -8, 66, 98},
+    {79, -9, 66, 98},
+    {85, -10, 66, 98},
+    {89, -11, 66, 98},
+    {93, -12, 66, 98},
+    {97, -13, 66, 98},
+    {101, -14, 66, 98},
+    {108, -15, 66, 98},
+    {114, -16, 66, 98},
+    {118, -17, 66, 98},
+    {123, -18, 66, 98}
+};
+
+static const char gAddStrings[] = 
+    "inPlace\0"
+    "offset\0"
+    "use\0"
+    "where"
+;
+
+static const SkMemberInfo gAddInfo[] = {
+    {0, 16, 26, 1},
+    {8, 20, 96, 1},
+    {15, 24, 37, 1},
+    {19, 28, 37, 1}
+};
+
+static const char gAddCircleStrings[] = 
+    "\0"
+    "radius\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gAddCircleInfo[] = {
+    {0, 3, 18, 1},
+    {1, 24, 98, 1},
+    {8, 28, 98, 1},
+    {10, 32, 98, 1}
+};
+
+static const char gUnknown1Strings[] = 
+    "direction"
+;
+
+static const SkMemberInfo gUnknown1Info[] = {
+    {0, 20, 75, 1}
+};
+
+static const char gAddOvalStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gAddOvalInfo[] = {
+    {0, 6, 18, 5}
+};
+
+static const char gAddPathStrings[] = 
+    "matrix\0"
+    "path"
+;
+
+static const SkMemberInfo gAddPathInfo[] = {
+    {0, 20, 65, 1},
+    {7, 24, 74, 1}
+};
+
+static const char gAddRectangleStrings[] = 
+    "\0"
+    "bottom\0"
+    "left\0"
+    "right\0"
+    "top"
+;
+
+static const SkMemberInfo gAddRectangleInfo[] = {
+    {0, 3, 18, 1},
+    {1, 36, 98, 1},
+    {8, 24, 98, 1},
+    {13, 32, 98, 1},
+    {19, 28, 98, 1}
+};
+
+static const char gAddRoundRectStrings[] = 
+    "\0"
+    "rx\0"
+    "ry"
+;
+
+static const SkMemberInfo gAddRoundRectInfo[] = {
+    {0, 6, 18, 5},
+    {1, 40, 98, 1},
+    {4, 44, 98, 1}
+};
+
+static const char gUnknown2Strings[] = 
+    "begin\0"
+    "blend\0"
+    "dur\0"
+    "dynamic\0"
+    "field\0"
+    "formula\0"
+    "from\0"
+    "mirror\0"
+    "repeat\0"
+    "reset\0"
+    "target\0"
+    "to\0"
+    "values"
+;
+
+static const SkMemberInfo gUnknown2Info[] = {
+    {0, 16, 71, 1},
+    {6, 20, 119, 98},
+    {12, 36, 71, 1},
+    {16, -1, 67, 26},
+    {24, 40, 108, 2},
+    {30, 48, 40, 2},
+    {38, 56, 40, 2},
+    {43, -2, 67, 26},
+    {50, 64, 98, 1},
+    {57, -3, 67, 26},
+    {63, 68, 40, 2},
+    {70, 76, 40, 2},
+    {73, -4, 67, 40}
+};
+
+static const char gAnimateFieldStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gAnimateFieldInfo[] = {
+    {0, 8, 18, 13}
+};
+
+static const char gApplyStrings[] = 
+    "animator\0"
+    "begin\0"
+    "dontDraw\0"
+    "dynamicScope\0"
+    "interval\0"
+    "mode\0"
+    "pickup\0"
+    "restore\0"
+    "scope\0"
+    "step\0"
+    "steps\0"
+    "time\0"
+    "transition"
+;
+
+static const SkMemberInfo gApplyInfo[] = {
+    {0, -1, 67, 10},
+    {9, 16, 71, 1},
+    {15, 20, 26, 1},
+    {24, 24, 108, 2},
+    {37, 32, 71, 1},
+    {46, 36, 13, 1},
+    {51, 40, 26, 1},
+    {58, 44, 26, 1},
+    {66, 48, 37, 1},
+    {72, -2, 67, 96},
+    {77, 52, 96, 1},
+    {83, -3, 67, 71},
+    {88, 56, 14, 1}
+};
+
+static const char gUnknown3Strings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gUnknown3Info[] = {
+    {0, 48, 98, 1},
+    {2, 52, 98, 1}
+};
+
+static const char gBitmapStrings[] = 
+    "\0"
+    "erase\0"
+    "format\0"
+    "height\0"
+    "rowBytes\0"
+    "width"
+;
+
+static const SkMemberInfo gDrawBitmapInfo[] = {
+    {0, 11, 18, 2},
+    {1, -1, 67, 15},
+    {7, 56, 21, 1},
+    {14, 60, 96, 1},
+    {21, 64, 96, 1},
+    {30, 68, 96, 1}
+};
+
+static const char gBitmapShaderStrings[] = 
+    "\0"
+    "filterType\0"
+    "image"
+;
+
+static const SkMemberInfo gDrawBitmapShaderInfo[] = {
+    {0, 67, 18, 2},
+    {1, 28, 47, 1},
+    {12, 32, 17, 1}
+};
+
+static const char gBlurStrings[] = 
+    "blurStyle\0"
+    "radius"
+;
+
+static const SkMemberInfo gDrawBlurInfo[] = {
+    {0, 24, 63, 1},
+    {10, 20, 98, 1}
+};
+
+static const char gBoundsStrings[] = 
+    "\0"
+    "inval"
+;
+
+static const SkMemberInfo gDisplayBoundsInfo[] = {
+    {0, 58, 18, 7},
+    {1, 44, 26, 1}
+};
+
+static const char gClipStrings[] = 
+    "path\0"
+    "rectangle"
+;
+
+static const SkMemberInfo gDrawClipInfo[] = {
+    {0, 20, 74, 1},
+    {5, 16, 91, 1}
+};
+
+static const char gColorStrings[] = 
+    "alpha\0"
+    "blue\0"
+    "color\0"
+    "green\0"
+    "hue\0"
+    "red\0"
+    "saturation\0"
+    "value"
+;
+
+static const SkMemberInfo gDrawColorInfo[] = {
+    {0, -1, 67, 98},
+    {6, -2, 67, 98},
+    {11, 20, 15, 1},
+    {17, -3, 67, 98},
+    {23, -4, 67, 98},
+    {27, -5, 67, 98},
+    {31, -6, 67, 98},
+    {42, -7, 67, 98}
+};
+
+static const char gCubicToStrings[] = 
+    "x1\0"
+    "x2\0"
+    "x3\0"
+    "y1\0"
+    "y2\0"
+    "y3"
+;
+
+static const SkMemberInfo gCubicToInfo[] = {
+    {0, 20, 98, 1},
+    {3, 28, 98, 1},
+    {6, 36, 98, 1},
+    {9, 24, 98, 1},
+    {12, 32, 98, 1},
+    {15, 40, 98, 1}
+};
+
+static const char gDashStrings[] = 
+    "intervals\0"
+    "phase"
+;
+
+static const SkMemberInfo gDashInfo[] = {
+    {0, 20, 119, 98},
+    {10, 36, 98, 1}
+};
+
+static const char gDataStrings[] = 
+    "\0"
+    "name"
+;
+
+static const SkMemberInfo gDataInfo[] = {
+    {0, 33, 18, 3},
+    {1, 32, 108, 2}
+};
+
+static const char gDiscreteStrings[] = 
+    "deviation\0"
+    "segLength"
+;
+
+static const SkMemberInfo gDiscreteInfo[] = {
+    {0, 20, 98, 1},
+    {10, 24, 98, 1}
+};
+
+static const char gDrawToStrings[] = 
+    "drawOnce\0"
+    "use"
+;
+
+static const SkMemberInfo gDrawToInfo[] = {
+    {0, 72, 26, 1},
+    {9, 76, 19, 1}
+};
+
+static const char gDumpStrings[] = 
+    "displayList\0"
+    "eventList\0"
+    "events\0"
+    "groups\0"
+    "name\0"
+    "posts"
+;
+
+static const SkMemberInfo gDumpInfo[] = {
+    {0, 16, 26, 1},
+    {12, 20, 26, 1},
+    {22, 24, 26, 1},
+    {29, 36, 26, 1},
+    {36, 28, 108, 2},
+    {41, 40, 26, 1}
+};
+
+static const char gEmbossStrings[] = 
+    "ambient\0"
+    "direction\0"
+    "radius\0"
+    "specular"
+;
+
+static const SkMemberInfo gDrawEmbossInfo[] = {
+    {0, -1, 67, 98},
+    {8, 20, 119, 98},
+    {18, 36, 98, 1},
+    {25, -2, 67, 98}
+};
+
+static const char gEventStrings[] = 
+    "code\0"
+    "disable\0"
+    "key\0"
+    "keys\0"
+    "kind\0"
+    "target\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gDisplayEventInfo[] = {
+    {0, 16, 43, 1},
+    {5, 20, 26, 1},
+    {13, -1, 67, 108},
+    {17, -2, 67, 108},
+    {22, 24, 44, 1},
+    {27, 28, 108, 2},
+    {34, 36, 98, 1},
+    {36, 40, 98, 1}
+};
+
+static const char gFromPathStrings[] = 
+    "mode\0"
+    "offset\0"
+    "path"
+;
+
+static const SkMemberInfo gFromPathInfo[] = {
+    {0, 20, 49, 1},
+    {5, 24, 98, 1},
+    {12, 28, 74, 1}
+};
+
+static const char gUnknown4Strings[] = 
+    "\0"
+    "offsets\0"
+    "unitMapper"
+;
+
+static const SkMemberInfo gUnknown4Info[] = {
+    {0, 67, 18, 2},
+    {1, 28, 119, 98},
+    {9, 44, 108, 2}
+};
+
+static const char gGStrings[] = 
+    "condition\0"
+    "enableCondition"
+;
+
+static const SkMemberInfo gGInfo[] = {
+    {0, 16, 40, 2},
+    {10, 24, 40, 2}
+};
+
+static const char gHitClearStrings[] = 
+    "targets"
+;
+
+static const SkMemberInfo gHitClearInfo[] = {
+    {0, 16, 119, 36}
+};
+
+static const char gHitTestStrings[] = 
+    "bullets\0"
+    "hits\0"
+    "targets\0"
+    "value"
+;
+
+static const SkMemberInfo gHitTestInfo[] = {
+    {0, 16, 119, 36},
+    {8, 32, 119, 96},
+    {13, 48, 119, 36},
+    {21, 64, 26, 1}
+};
+
+static const char gImageStrings[] = 
+    "\0"
+    "base64\0"
+    "src"
+;
+
+static const SkMemberInfo gImageInfo[] = {
+    {0, 11, 18, 2},
+    {1, 56, 16, 2},
+    {8, 64, 108, 2}
+};
+
+static const char gIncludeStrings[] = 
+    "src"
+;
+
+static const SkMemberInfo gIncludeInfo[] = {
+    {0, 16, 108, 2}
+};
+
+static const char gInputStrings[] = 
+    "s32\0"
+    "scalar\0"
+    "string"
+;
+
+static const SkMemberInfo gInputInfo[] = {
+    {0, 16, 96, 1},
+    {4, 20, 98, 1},
+    {11, 24, 108, 2}
+};
+
+static const char gLineStrings[] = 
+    "x1\0"
+    "x2\0"
+    "y1\0"
+    "y2"
+;
+
+static const SkMemberInfo gLineInfo[] = {
+    {0, 24, 98, 1},
+    {3, 28, 98, 1},
+    {6, 32, 98, 1},
+    {9, 36, 98, 1}
+};
+
+static const char gLineToStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gLineToInfo[] = {
+    {0, 20, 98, 1},
+    {2, 24, 98, 1}
+};
+
+static const char gLinearGradientStrings[] = 
+    "\0"
+    "points"
+;
+
+static const SkMemberInfo gLinearGradientInfo[] = {
+    {0, 27, 18, 3},
+    {1, 88, 77, 4}
+};
+
+static const char gMatrixStrings[] = 
+    "matrix\0"
+    "perspectX\0"
+    "perspectY\0"
+    "rotate\0"
+    "scale\0"
+    "scaleX\0"
+    "scaleY\0"
+    "skewX\0"
+    "skewY\0"
+    "translate\0"
+    "translateX\0"
+    "translateY"
+;
+
+static const SkMemberInfo gDrawMatrixInfo[] = {
+    {0, 16, 119, 98},
+    {7, -1, 67, 98},
+    {17, -2, 67, 98},
+    {27, -3, 67, 98},
+    {34, -4, 67, 98},
+    {40, -5, 67, 98},
+    {47, -6, 67, 98},
+    {54, -7, 67, 98},
+    {60, -8, 67, 98},
+    {66, -9, 67, 77},
+    {76, -10, 67, 98},
+    {87, -11, 67, 98}
+};
+
+static const char gMoveStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gMoveInfo[] = {
+    {0, 1, 18, 4}
+};
+
+static const char gMoveToStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gMoveToInfo[] = {
+    {0, 20, 98, 1},
+    {2, 24, 98, 1}
+};
+
+static const char gMovieStrings[] = 
+    "src"
+;
+
+static const SkMemberInfo gMovieInfo[] = {
+    {0, 16, 108, 2}
+};
+
+static const char gOvalStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gOvalInfo[] = {
+    {0, 58, 18, 7}
+};
+
+static const char gPaintStrings[] = 
+    "antiAlias\0"
+    "ascent\0"
+    "color\0"
+    "descent\0"
+    "filterType\0"
+    "linearText\0"
+    "maskFilter\0"
+    "measureText\0"
+    "pathEffect\0"
+    "shader\0"
+    "strikeThru\0"
+    "stroke\0"
+    "strokeCap\0"
+    "strokeJoin\0"
+    "strokeMiter\0"
+    "strokeWidth\0"
+    "style\0"
+    "textAlign\0"
+    "textScaleX\0"
+    "textSize\0"
+    "textSkewX\0"
+    "textTracking\0"
+    "typeface\0"
+    "underline\0"
+    "xfermode"
+;
+
+static const SkMemberInfo gDrawPaintInfo[] = {
+    {0, 16, 26, 1},
+    {10, -1, 67, 98},
+    {17, 20, 31, 1},
+    {23, -2, 67, 98},
+    {31, 24, 47, 1},
+    {42, 28, 26, 1},
+    {53, 32, 62, 1},
+    {64, -1, 66, 98},
+    {76, 36, 76, 1},
+    {87, 40, 102, 1},
+    {94, 44, 26, 1},
+    {105, 48, 26, 1},
+    {112, 52, 27, 1},
+    {122, 56, 58, 1},
+    {133, 60, 98, 1},
+    {145, 64, 98, 1},
+    {157, 68, 109, 1},
+    {163, 72, 9, 1},
+    {173, 76, 98, 1},
+    {184, 80, 98, 1},
+    {193, 84, 98, 1},
+    {203, 88, 98, 1},
+    {216, 92, 120, 1},
+    {225, 96, 26, 1},
+    {235, 100, 121, 1}
+};
+
+static const char gPathStrings[] = 
+    "d\0"
+    "fillType\0"
+    "length"
+;
+
+static const SkMemberInfo gDrawPathInfo[] = {
+    {0, 52, 108, 2},
+    {2, -1, 67, 46},
+    {11, -2, 67, 98}
+};
+
+static const char gUnknown5Strings[] = 
+    "x\0"
+    "y\0"
+    "z"
+;
+
+static const SkMemberInfo gUnknown5Info[] = {
+    {0, 0, 98, 1},
+    {2, 4, 98, 1},
+    {4, 8, 98, 1}
+};
+
+static const char gPointStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gDrawPointInfo[] = {
+    {0, 16, 98, 1},
+    {2, 20, 98, 1}
+};
+
+static const char gPolyToPolyStrings[] = 
+    "destination\0"
+    "source"
+;
+
+static const SkMemberInfo gPolyToPolyInfo[] = {
+    {0, 24, 80, 1},
+    {12, 20, 80, 1}
+};
+
+static const char gPolygonStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gPolygonInfo[] = {
+    {0, 48, 18, 1}
+};
+
+static const char gPolylineStrings[] = 
+    "points"
+;
+
+static const SkMemberInfo gPolylineInfo[] = {
+    {0, 88, 119, 98}
+};
+
+static const char gPostStrings[] = 
+    "delay\0"
+    "initialized\0"
+    "mode\0"
+    "sink\0"
+    "target\0"
+    "type"
+;
+
+static const SkMemberInfo gPostInfo[] = {
+    {0, 16, 71, 1},
+    {6, 20, 26, 1},
+    {18, 24, 45, 1},
+    {23, -1, 67, 108},
+    {28, -2, 67, 108},
+    {35, -3, 67, 108}
+};
+
+static const char gQuadToStrings[] = 
+    "x1\0"
+    "x2\0"
+    "y1\0"
+    "y2"
+;
+
+static const SkMemberInfo gQuadToInfo[] = {
+    {0, 20, 98, 1},
+    {3, 28, 98, 1},
+    {6, 24, 98, 1},
+    {9, 32, 98, 1}
+};
+
+static const char gRCubicToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRCubicToInfo[] = {
+    {0, 18, 18, 6}
+};
+
+static const char gRLineToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRLineToInfo[] = {
+    {0, 35, 18, 2}
+};
+
+static const char gRMoveToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRMoveToInfo[] = {
+    {0, 39, 18, 2}
+};
+
+static const char gRQuadToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRQuadToInfo[] = {
+    {0, 50, 18, 4}
+};
+
+static const char gRadialGradientStrings[] = 
+    "\0"
+    "center\0"
+    "radius"
+;
+
+static const SkMemberInfo gRadialGradientInfo[] = {
+    {0, 27, 18, 3},
+    {1, 88, 77, 2},
+    {8, 96, 98, 1}
+};
+
+static const char gRandomStrings[] = 
+    "blend\0"
+    "max\0"
+    "min\0"
+    "random\0"
+    "seed"
+;
+
+static const SkMemberInfo gDisplayRandomInfo[] = {
+    {0, 16, 98, 1},
+    {6, 24, 98, 1},
+    {10, 20, 98, 1},
+    {14, 1, 67, 98},
+    {21, -2, 67, 96}
+};
+
+static const char gRectToRectStrings[] = 
+    "destination\0"
+    "source"
+;
+
+static const SkMemberInfo gRectToRectInfo[] = {
+    {0, 24, 91, 1},
+    {12, 20, 91, 1}
+};
+
+static const char gRectangleStrings[] = 
+    "bottom\0"
+    "height\0"
+    "left\0"
+    "needsRedraw\0"
+    "right\0"
+    "top\0"
+    "width"
+;
+
+static const SkMemberInfo gRectangleInfo[] = {
+    {0, 36, 98, 1},
+    {7, -1, 67, 98},
+    {14, 24, 98, 1},
+    {19, -2, 67, 26},
+    {31, 32, 98, 1},
+    {37, 28, 98, 1},
+    {41, -3, 67, 98}
+};
+
+static const char gRemoveStrings[] = 
+    "offset\0"
+    "where"
+;
+
+static const SkMemberInfo gRemoveInfo[] = {
+    {0, 20, 96, 1},
+    {7, 28, 37, 1}
+};
+
+static const char gReplaceStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gReplaceInfo[] = {
+    {0, 1, 18, 4}
+};
+
+static const char gRotateStrings[] = 
+    "center\0"
+    "degrees"
+;
+
+static const SkMemberInfo gRotateInfo[] = {
+    {0, 24, 77, 2},
+    {7, 20, 98, 1}
+};
+
+static const char gRoundRectStrings[] = 
+    "\0"
+    "rx\0"
+    "ry"
+;
+
+static const SkMemberInfo gRoundRectInfo[] = {
+    {0, 58, 18, 7},
+    {1, 44, 98, 1},
+    {4, 48, 98, 1}
+};
+
+static const char gS32Strings[] = 
+    "value"
+;
+
+static const SkMemberInfo gS32Info[] = {
+    {0, 16, 96, 1}
+};
+
+static const char gScalarStrings[] = 
+    "value"
+;
+
+static const SkMemberInfo gScalarInfo[] = {
+    {0, 16, 98, 1}
+};
+
+static const char gScaleStrings[] = 
+    "center\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gScaleInfo[] = {
+    {0, 28, 77, 2},
+    {7, 20, 98, 1},
+    {9, 24, 98, 1}
+};
+
+static const char gSetStrings[] = 
+    "begin\0"
+    "dur\0"
+    "dynamic\0"
+    "field\0"
+    "formula\0"
+    "reset\0"
+    "target\0"
+    "to"
+;
+
+static const SkMemberInfo gSetInfo[] = {
+    {0, 16, 71, 1},
+    {6, 36, 71, 1},
+    {10, -1, 67, 26},
+    {18, 40, 108, 2},
+    {24, 48, 40, 2},
+    {32, -3, 67, 26},
+    {38, 68, 40, 2},
+    {45, 76, 40, 2}
+};
+
+static const char gShaderStrings[] = 
+    "matrix\0"
+    "tileMode"
+;
+
+static const SkMemberInfo gShaderInfo[] = {
+    {0, 20, 65, 1},
+    {7, 24, 116, 1}
+};
+
+static const char gSkewStrings[] = 
+    "center\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gSkewInfo[] = {
+    {0, 28, 77, 2},
+    {7, 20, 98, 1},
+    {9, 24, 98, 1}
+};
+
+static const char g3D_CameraStrings[] = 
+    "axis\0"
+    "hackHeight\0"
+    "hackWidth\0"
+    "location\0"
+    "observer\0"
+    "patch\0"
+    "zenith"
+;
+
+static const SkMemberInfo g3D_CameraInfo[] = {
+    {0, 36, 106, 3},
+    {5, 20, 98, 1},
+    {16, 16, 98, 1},
+    {26, 24, 106, 3},
+    {35, 60, 106, 3},
+    {44, 108, 105, 1},
+    {50, 48, 106, 3}
+};
+
+static const char g3D_PatchStrings[] = 
+    "origin\0"
+    "rotateDegrees\0"
+    "u\0"
+    "v"
+;
+
+static const SkMemberInfo g3D_PatchInfo[] = {
+    {0, 40, 106, 3},
+    {7, -1, 66, 98},
+    {21, 16, 106, 3},
+    {23, 28, 106, 3}
+};
+
+static const char gUnknown6Strings[] = 
+    "x\0"
+    "y\0"
+    "z"
+;
+
+static const SkMemberInfo gUnknown6Info[] = {
+    {0, 0, 98, 1},
+    {2, 4, 98, 1},
+    {4, 8, 98, 1}
+};
+
+static const char gSnapshotStrings[] = 
+    "filename\0"
+    "quality\0"
+    "sequence\0"
+    "type"
+;
+
+static const SkMemberInfo gSnapshotInfo[] = {
+    {0, 16, 108, 2},
+    {9, 24, 98, 1},
+    {17, 28, 26, 1},
+    {26, 32, 20, 1}
+};
+
+static const char gStringStrings[] = 
+    "length\0"
+    "slice\0"
+    "value"
+;
+
+static const SkMemberInfo gStringInfo[] = {
+    {0, -1, 67, 96},
+    {7, -1, 66, 108},
+    {13, 16, 108, 2}
+};
+
+static const char gTextStrings[] = 
+    "length\0"
+    "text\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gTextInfo[] = {
+    {0, -1, 67, 96},
+    {7, 24, 108, 2},
+    {12, 32, 98, 1},
+    {14, 36, 98, 1}
+};
+
+static const char gTextBoxStrings[] = 
+    "\0"
+    "mode\0"
+    "spacingAdd\0"
+    "spacingAlign\0"
+    "spacingMul\0"
+    "text"
+;
+
+static const SkMemberInfo gTextBoxInfo[] = {
+    {0, 58, 18, 7},
+    {1, 60, 113, 1},
+    {6, 56, 98, 1},
+    {17, 64, 112, 1},
+    {30, 52, 98, 1},
+    {41, 44, 108, 2}
+};
+
+static const char gTextOnPathStrings[] = 
+    "offset\0"
+    "path\0"
+    "text"
+;
+
+static const SkMemberInfo gTextOnPathInfo[] = {
+    {0, 24, 98, 1},
+    {7, 28, 74, 1},
+    {12, 32, 110, 1}
+};
+
+static const char gTextToPathStrings[] = 
+    "path\0"
+    "text"
+;
+
+static const SkMemberInfo gTextToPathInfo[] = {
+    {0, 16, 74, 1},
+    {5, 20, 110, 1}
+};
+
+static const char gTranslateStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gTranslateInfo[] = {
+    {0, 20, 98, 1},
+    {2, 24, 98, 1}
+};
+
+static const char gTypedArrayStrings[] = 
+    "length\0"
+    "values"
+;
+
+static const SkMemberInfo gTypedArrayInfo[] = {
+    {0, -1, 67, 96},
+    {7, 16, 119, 0}
+};
+
+static const char gTypefaceStrings[] = 
+    "fontName"
+;
+
+static const SkMemberInfo gTypefaceInfo[] = {
+    {0, 20, 108, 2}
+};
+
+static const SkMemberInfo* const gInfoTables[] = {
+    gMathInfo,
+    gAddInfo,
+    gAddCircleInfo,
+    gUnknown1Info,
+    gAddOvalInfo,
+    gAddPathInfo,
+    gAddRectangleInfo,
+    gAddRoundRectInfo,
+    gUnknown2Info,
+    gAnimateFieldInfo,
+    gApplyInfo,
+    gUnknown3Info,
+    gDrawBitmapInfo,
+    gDrawBitmapShaderInfo,
+    gDrawBlurInfo,
+    gDisplayBoundsInfo,
+    gDrawClipInfo,
+    gDrawColorInfo,
+    gCubicToInfo,
+    gDashInfo,
+    gDataInfo,
+    gDiscreteInfo,
+    gDrawToInfo,
+    gDumpInfo,
+    gDrawEmbossInfo,
+    gDisplayEventInfo,
+    gFromPathInfo,
+    gUnknown4Info,
+    gGInfo,
+    gHitClearInfo,
+    gHitTestInfo,
+    gImageInfo,
+    gIncludeInfo,
+    gInputInfo,
+    gLineInfo,
+    gLineToInfo,
+    gLinearGradientInfo,
+    gDrawMatrixInfo,
+    gMoveInfo,
+    gMoveToInfo,
+    gMovieInfo,
+    gOvalInfo,
+    gDrawPaintInfo,
+    gDrawPathInfo,
+    gUnknown5Info,
+    gDrawPointInfo,
+    gPolyToPolyInfo,
+    gPolygonInfo,
+    gPolylineInfo,
+    gPostInfo,
+    gQuadToInfo,
+    gRCubicToInfo,
+    gRLineToInfo,
+    gRMoveToInfo,
+    gRQuadToInfo,
+    gRadialGradientInfo,
+    gDisplayRandomInfo,
+    gRectToRectInfo,
+    gRectangleInfo,
+    gRemoveInfo,
+    gReplaceInfo,
+    gRotateInfo,
+    gRoundRectInfo,
+    gS32Info,
+    gScalarInfo,
+    gScaleInfo,
+    gSetInfo,
+    gShaderInfo,
+    gSkewInfo,
+    g3D_CameraInfo,
+    g3D_PatchInfo,
+    gUnknown6Info,
+    gSnapshotInfo,
+    gStringInfo,
+    gTextInfo,
+    gTextBoxInfo,
+    gTextOnPathInfo,
+    gTextToPathInfo,
+    gTranslateInfo,
+    gTypedArrayInfo,
+    gTypefaceInfo,
+};
+
+static const unsigned char gInfoCounts[] = {
+    26,4,4,1,1,2,5,3,13,1,13,2,6,3,2,2,2,8,6,
+    2,2,2,2,6,4,8,3,3,2,1,4,3,1,3,4,2,2,12,1,
+    2,1,1,25,3,3,2,2,1,1,6,4,1,1,1,1,3,5,2,7,
+    2,1,2,3,1,1,3,8,2,3,7,4,3,4,3,4,6,3,2,2,
+    2,1
+};
+
+static const unsigned char gTypeIDs[] = {
+    1, // Math
+    2, // Add
+    3, // AddCircle
+    4, // Unknown1
+    5, // AddOval
+    6, // AddPath
+    7, // AddRectangle
+    8, // AddRoundRect
+    10, // Unknown2
+    11, // AnimateField
+    12, // Apply
+    17, // Unknown3
+    19, // Bitmap
+    22, // BitmapShader
+    23, // Blur
+    25, // Bounds
+    29, // Clip
+    31, // Color
+    32, // CubicTo
+    33, // Dash
+    34, // Data
+    35, // Discrete
+    38, // DrawTo
+    39, // Dump
+    41, // Emboss
+    42, // Event
+    48, // FromPath
+    51, // Unknown4
+    52, // G
+    53, // HitClear
+    54, // HitTest
+    55, // Image
+    56, // Include
+    57, // Input
+    59, // Line
+    60, // LineTo
+    61, // LinearGradient
+    65, // Matrix
+    68, // Move
+    69, // MoveTo
+    70, // Movie
+    72, // Oval
+    73, // Paint
+    74, // Path
+    77, // Unknown5
+    78, // Point
+    79, // PolyToPoly
+    80, // Polygon
+    81, // Polyline
+    82, // Post
+    83, // QuadTo
+    84, // RCubicTo
+    85, // RLineTo
+    86, // RMoveTo
+    87, // RQuadTo
+    88, // RadialGradient
+    89, // Random
+    90, // RectToRect
+    91, // Rectangle
+    92, // Remove
+    93, // Replace
+    94, // Rotate
+    95, // RoundRect
+    96, // S32
+    98, // Scalar
+    99, // Scale
+    101, // Set
+    102, // Shader
+    103, // Skew
+    104, // 3D_Camera
+    105, // 3D_Patch
+    106, // Unknown6
+    107, // Snapshot
+    108, // String
+    110, // Text
+    111, // TextBox
+    114, // TextOnPath
+    115, // TextToPath
+    117, // Translate
+    119, // TypedArray
+    120, // Typeface
+    
+};
+
+static const int kTypeIDs = 81;
+
+static const char* const gInfoNames[] = {
+    gMathStrings,
+    gAddStrings,
+    gAddCircleStrings,
+    gUnknown1Strings,
+    gAddOvalStrings,
+    gAddPathStrings,
+    gAddRectangleStrings,
+    gAddRoundRectStrings,
+    gUnknown2Strings,
+    gAnimateFieldStrings,
+    gApplyStrings,
+    gUnknown3Strings,
+    gBitmapStrings,
+    gBitmapShaderStrings,
+    gBlurStrings,
+    gBoundsStrings,
+    gClipStrings,
+    gColorStrings,
+    gCubicToStrings,
+    gDashStrings,
+    gDataStrings,
+    gDiscreteStrings,
+    gDrawToStrings,
+    gDumpStrings,
+    gEmbossStrings,
+    gEventStrings,
+    gFromPathStrings,
+    gUnknown4Strings,
+    gGStrings,
+    gHitClearStrings,
+    gHitTestStrings,
+    gImageStrings,
+    gIncludeStrings,
+    gInputStrings,
+    gLineStrings,
+    gLineToStrings,
+    gLinearGradientStrings,
+    gMatrixStrings,
+    gMoveStrings,
+    gMoveToStrings,
+    gMovieStrings,
+    gOvalStrings,
+    gPaintStrings,
+    gPathStrings,
+    gUnknown5Strings,
+    gPointStrings,
+    gPolyToPolyStrings,
+    gPolygonStrings,
+    gPolylineStrings,
+    gPostStrings,
+    gQuadToStrings,
+    gRCubicToStrings,
+    gRLineToStrings,
+    gRMoveToStrings,
+    gRQuadToStrings,
+    gRadialGradientStrings,
+    gRandomStrings,
+    gRectToRectStrings,
+    gRectangleStrings,
+    gRemoveStrings,
+    gReplaceStrings,
+    gRotateStrings,
+    gRoundRectStrings,
+    gS32Strings,
+    gScalarStrings,
+    gScaleStrings,
+    gSetStrings,
+    gShaderStrings,
+    gSkewStrings,
+    g3D_CameraStrings,
+    g3D_PatchStrings,
+    gUnknown6Strings,
+    gSnapshotStrings,
+    gStringStrings,
+    gTextStrings,
+    gTextBoxStrings,
+    gTextOnPathStrings,
+    gTextToPathStrings,
+    gTranslateStrings,
+    gTypedArrayStrings,
+    gTypefaceStrings
+};
+
+#endif
+#endif
+
+
diff --git a/src/animator/SkCondensedRelease.cpp b/src/animator/SkCondensedRelease.cpp
new file mode 100644
index 0000000..a493cb3
--- /dev/null
+++ b/src/animator/SkCondensedRelease.cpp
@@ -0,0 +1,1374 @@
+/* libs/graphics/animator/SkCondensedRelease.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 "SkTypes.h"
+#ifndef SK_BUILD_FOR_UNIX
+#ifdef SK_RELEASE
+// This file was automatically generated.
+// To change it, edit the file with the matching debug info.
+// Then execute SkDisplayType::BuildCondensedInfo() to regenerate this file.
+
+static const char gMathStrings[] = 
+    "E\0"
+    "LN10\0"
+    "LN2\0"
+    "LOG10E\0"
+    "LOG2E\0"
+    "PI\0"
+    "SQRT1_2\0"
+    "SQRT2\0"
+    "abs\0"
+    "acos\0"
+    "asin\0"
+    "atan\0"
+    "atan2\0"
+    "ceil\0"
+    "cos\0"
+    "exp\0"
+    "floor\0"
+    "log\0"
+    "max\0"
+    "min\0"
+    "pow\0"
+    "random\0"
+    "round\0"
+    "sin\0"
+    "sqrt\0"
+    "tan"
+;
+
+static const SkMemberInfo gMathInfo[] = {
+    {0, -1, 67, 98},
+    {2, -2, 67, 98},
+    {7, -3, 67, 98},
+    {11, -4, 67, 98},
+    {18, -5, 67, 98},
+    {24, -6, 67, 98},
+    {27, -7, 67, 98},
+    {35, -8, 67, 98},
+    {41, -1, 66, 98},
+    {45, -2, 66, 98},
+    {50, -3, 66, 98},
+    {55, -4, 66, 98},
+    {60, -5, 66, 98},
+    {66, -6, 66, 98},
+    {71, -7, 66, 98},
+    {75, -8, 66, 98},
+    {79, -9, 66, 98},
+    {85, -10, 66, 98},
+    {89, -11, 66, 98},
+    {93, -12, 66, 98},
+    {97, -13, 66, 98},
+    {101, -14, 66, 98},
+    {108, -15, 66, 98},
+    {114, -16, 66, 98},
+    {118, -17, 66, 98},
+    {123, -18, 66, 98}
+};
+
+static const char gAddStrings[] = 
+    "inPlace\0"
+    "offset\0"
+    "use\0"
+    "where"
+;
+
+static const SkMemberInfo gAddInfo[] = {
+    {0, 4, 26, 1},
+    {8, 8, 96, 1},
+    {15, 12, 37, 1},
+    {19, 16, 37, 1}
+};
+
+static const char gAddCircleStrings[] = 
+    "\0"
+    "radius\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gAddCircleInfo[] = {
+    {0, 3, 18, 1},
+    {1, 12, 98, 1},
+    {8, 16, 98, 1},
+    {10, 20, 98, 1}
+};
+
+static const char gUnknown1Strings[] = 
+    "direction"
+;
+
+static const SkMemberInfo gUnknown1Info[] = {
+    {0, 8, 75, 1}
+};
+
+static const char gAddOvalStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gAddOvalInfo[] = {
+    {0, 6, 18, 5}
+};
+
+static const char gAddPathStrings[] = 
+    "matrix\0"
+    "path"
+;
+
+static const SkMemberInfo gAddPathInfo[] = {
+    {0, 8, 65, 1},
+    {7, 12, 74, 1}
+};
+
+static const char gAddRectangleStrings[] = 
+    "\0"
+    "bottom\0"
+    "left\0"
+    "right\0"
+    "top"
+;
+
+static const SkMemberInfo gAddRectangleInfo[] = {
+    {0, 3, 18, 1},
+    {1, 24, 98, 1},
+    {8, 12, 98, 1},
+    {13, 20, 98, 1},
+    {19, 16, 98, 1}
+};
+
+static const char gAddRoundRectStrings[] = 
+    "\0"
+    "rx\0"
+    "ry"
+;
+
+static const SkMemberInfo gAddRoundRectInfo[] = {
+    {0, 6, 18, 5},
+    {1, 28, 98, 1},
+    {4, 32, 98, 1}
+};
+
+static const char gUnknown2Strings[] = 
+    "begin\0"
+    "blend\0"
+    "dur\0"
+    "dynamic\0"
+    "field\0"
+    "formula\0"
+    "from\0"
+    "mirror\0"
+    "repeat\0"
+    "reset\0"
+    "target\0"
+    "to\0"
+    "values"
+;
+
+static const SkMemberInfo gUnknown2Info[] = {
+    {0, 4, 71, 1},
+    {6, 8, 119, 98},
+    {12, 16, 71, 1},
+    {16, -1, 67, 26},
+    {24, 20, 108, 1},
+    {30, 24, 40, 1},
+    {38, 28, 40, 1},
+    {43, -2, 67, 26},
+    {50, 32, 98, 1},
+    {57, -3, 67, 26},
+    {63, 36, 40, 1},
+    {70, 40, 40, 1},
+    {73, -4, 67, 40}
+};
+
+static const char gAnimateFieldStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gAnimateFieldInfo[] = {
+    {0, 8, 18, 13}
+};
+
+static const char gApplyStrings[] = 
+    "animator\0"
+    "begin\0"
+    "dontDraw\0"
+    "dynamicScope\0"
+    "interval\0"
+    "mode\0"
+    "pickup\0"
+    "restore\0"
+    "scope\0"
+    "step\0"
+    "steps\0"
+    "time\0"
+    "transition"
+;
+
+static const SkMemberInfo gApplyInfo[] = {
+    {0, -1, 67, 10},
+    {9, 4, 71, 1},
+    {15, 8, 26, 1},
+    {24, 12, 108, 1},
+    {37, 16, 71, 1},
+    {46, 20, 13, 1},
+    {51, 24, 26, 1},
+    {58, 28, 26, 1},
+    {66, 32, 37, 1},
+    {72, -2, 67, 96},
+    {77, 36, 96, 1},
+    {83, -3, 67, 71},
+    {88, 40, 14, 1}
+};
+
+static const char gUnknown3Strings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gUnknown3Info[] = {
+    {0, 36, 98, 1},
+    {2, 40, 98, 1}
+};
+
+static const char gBitmapStrings[] = 
+    "\0"
+    "erase\0"
+    "format\0"
+    "height\0"
+    "rowBytes\0"
+    "width"
+;
+
+static const SkMemberInfo gDrawBitmapInfo[] = {
+    {0, 11, 18, 2},
+    {1, -1, 67, 15},
+    {7, 44, 21, 1},
+    {14, 48, 96, 1},
+    {21, 52, 96, 1},
+    {30, 56, 96, 1}
+};
+
+static const char gBitmapShaderStrings[] = 
+    "\0"
+    "filterType\0"
+    "image"
+;
+
+static const SkMemberInfo gDrawBitmapShaderInfo[] = {
+    {0, 66, 18, 2},
+    {1, 16, 47, 1},
+    {12, 20, 17, 1}
+};
+
+static const char gBlurStrings[] = 
+    "blurStyle\0"
+    "radius"
+;
+
+static const SkMemberInfo gDrawBlurInfo[] = {
+    {0, 12, 63, 1},
+    {10, 8, 98, 1}
+};
+
+static const char gBoundsStrings[] = 
+    "\0"
+    "inval"
+;
+
+static const SkMemberInfo gDisplayBoundsInfo[] = {
+    {0, 57, 18, 7},
+    {1, 32, 26, 1}
+};
+
+static const char gClipStrings[] = 
+    "path\0"
+    "rectangle"
+;
+
+static const SkMemberInfo gDrawClipInfo[] = {
+    {0, 8, 74, 1},
+    {5, 4, 91, 1}
+};
+
+static const char gColorStrings[] = 
+    "alpha\0"
+    "blue\0"
+    "color\0"
+    "green\0"
+    "hue\0"
+    "red\0"
+    "saturation\0"
+    "value"
+;
+
+static const SkMemberInfo gDrawColorInfo[] = {
+    {0, -1, 67, 98},
+    {6, -2, 67, 98},
+    {11, 8, 15, 1},
+    {17, -3, 67, 98},
+    {23, -4, 67, 98},
+    {27, -5, 67, 98},
+    {31, -6, 67, 98},
+    {42, -7, 67, 98}
+};
+
+static const char gCubicToStrings[] = 
+    "x1\0"
+    "x2\0"
+    "x3\0"
+    "y1\0"
+    "y2\0"
+    "y3"
+;
+
+static const SkMemberInfo gCubicToInfo[] = {
+    {0, 8, 98, 1},
+    {3, 16, 98, 1},
+    {6, 24, 98, 1},
+    {9, 12, 98, 1},
+    {12, 20, 98, 1},
+    {15, 28, 98, 1}
+};
+
+static const char gDashStrings[] = 
+    "intervals\0"
+    "phase"
+;
+
+static const SkMemberInfo gDashInfo[] = {
+    {0, 8, 119, 98},
+    {10, 16, 98, 1}
+};
+
+static const char gDataStrings[] = 
+    "\0"
+    "name"
+;
+
+static const SkMemberInfo gDataInfo[] = {
+    {0, 32, 18, 3},
+    {1, 16, 108, 1}
+};
+
+static const char gDiscreteStrings[] = 
+    "deviation\0"
+    "segLength"
+;
+
+static const SkMemberInfo gDiscreteInfo[] = {
+    {0, 8, 98, 1},
+    {10, 12, 98, 1}
+};
+
+static const char gDrawToStrings[] = 
+    "drawOnce\0"
+    "use"
+;
+
+static const SkMemberInfo gDrawToInfo[] = {
+    {0, 36, 26, 1},
+    {9, 40, 19, 1}
+};
+
+static const char gEmbossStrings[] = 
+    "ambient\0"
+    "direction\0"
+    "radius\0"
+    "specular"
+;
+
+static const SkMemberInfo gDrawEmbossInfo[] = {
+    {0, -1, 67, 98},
+    {8, 8, 119, 98},
+    {18, 16, 98, 1},
+    {25, -2, 67, 98}
+};
+
+static const char gEventStrings[] = 
+    "code\0"
+    "disable\0"
+    "key\0"
+    "keys\0"
+    "kind\0"
+    "target\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gDisplayEventInfo[] = {
+    {0, 4, 43, 1},
+    {5, 8, 26, 1},
+    {13, -1, 67, 108},
+    {17, -2, 67, 108},
+    {22, 12, 44, 1},
+    {27, 16, 108, 1},
+    {34, 20, 98, 1},
+    {36, 24, 98, 1}
+};
+
+static const char gFromPathStrings[] = 
+    "mode\0"
+    "offset\0"
+    "path"
+;
+
+static const SkMemberInfo gFromPathInfo[] = {
+    {0, 8, 49, 1},
+    {5, 12, 98, 1},
+    {12, 16, 74, 1}
+};
+
+static const char gUnknown4Strings[] = 
+    "\0"
+    "offsets\0"
+    "unitMapper"
+;
+
+static const SkMemberInfo gUnknown4Info[] = {
+    {0, 66, 18, 2},
+    {1, 16, 119, 98},
+    {9, 24, 108, 1}
+};
+
+static const char gGStrings[] = 
+    "condition\0"
+    "enableCondition"
+;
+
+static const SkMemberInfo gGInfo[] = {
+    {0, 4, 40, 1},
+    {10, 8, 40, 1}
+};
+
+static const char gHitClearStrings[] = 
+    "targets"
+;
+
+static const SkMemberInfo gHitClearInfo[] = {
+    {0, 4, 119, 36}
+};
+
+static const char gHitTestStrings[] = 
+    "bullets\0"
+    "hits\0"
+    "targets\0"
+    "value"
+;
+
+static const SkMemberInfo gHitTestInfo[] = {
+    {0, 4, 119, 36},
+    {8, 12, 119, 96},
+    {13, 20, 119, 36},
+    {21, 28, 26, 1}
+};
+
+static const char gImageStrings[] = 
+    "\0"
+    "base64\0"
+    "src"
+;
+
+static const SkMemberInfo gImageInfo[] = {
+    {0, 11, 18, 2},
+    {1, 44, 16, 2},
+    {8, 52, 108, 1}
+};
+
+static const char gIncludeStrings[] = 
+    "src"
+;
+
+static const SkMemberInfo gIncludeInfo[] = {
+    {0, 4, 108, 1}
+};
+
+static const char gInputStrings[] = 
+    "s32\0"
+    "scalar\0"
+    "string"
+;
+
+static const SkMemberInfo gInputInfo[] = {
+    {0, 4, 96, 1},
+    {4, 8, 98, 1},
+    {11, 12, 108, 1}
+};
+
+static const char gLineStrings[] = 
+    "x1\0"
+    "x2\0"
+    "y1\0"
+    "y2"
+;
+
+static const SkMemberInfo gLineInfo[] = {
+    {0, 12, 98, 1},
+    {3, 16, 98, 1},
+    {6, 20, 98, 1},
+    {9, 24, 98, 1}
+};
+
+static const char gLineToStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gLineToInfo[] = {
+    {0, 8, 98, 1},
+    {2, 12, 98, 1}
+};
+
+static const char gLinearGradientStrings[] = 
+    "\0"
+    "points"
+;
+
+static const SkMemberInfo gLinearGradientInfo[] = {
+    {0, 26, 18, 3},
+    {1, 48, 77, 4}
+};
+
+static const char gMatrixStrings[] = 
+    "matrix\0"
+    "perspectX\0"
+    "perspectY\0"
+    "rotate\0"
+    "scale\0"
+    "scaleX\0"
+    "scaleY\0"
+    "skewX\0"
+    "skewY\0"
+    "translate\0"
+    "translateX\0"
+    "translateY"
+;
+
+static const SkMemberInfo gDrawMatrixInfo[] = {
+    {0, 4, 119, 98},
+    {7, -1, 67, 98},
+    {17, -2, 67, 98},
+    {27, -3, 67, 98},
+    {34, -4, 67, 98},
+    {40, -5, 67, 98},
+    {47, -6, 67, 98},
+    {54, -7, 67, 98},
+    {60, -8, 67, 98},
+    {66, -9, 67, 77},
+    {76, -10, 67, 98},
+    {87, -11, 67, 98}
+};
+
+static const char gMoveStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gMoveInfo[] = {
+    {0, 1, 18, 4}
+};
+
+static const char gMoveToStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gMoveToInfo[] = {
+    {0, 8, 98, 1},
+    {2, 12, 98, 1}
+};
+
+static const char gMovieStrings[] = 
+    "src"
+;
+
+static const SkMemberInfo gMovieInfo[] = {
+    {0, 4, 108, 1}
+};
+
+static const char gOvalStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gOvalInfo[] = {
+    {0, 57, 18, 7}
+};
+
+static const char gPaintStrings[] = 
+    "antiAlias\0"
+    "ascent\0"
+    "color\0"
+    "descent\0"
+    "filterType\0"
+    "linearText\0"
+    "maskFilter\0"
+    "measureText\0"
+    "pathEffect\0"
+    "shader\0"
+    "strikeThru\0"
+    "stroke\0"
+    "strokeCap\0"
+    "strokeJoin\0"
+    "strokeMiter\0"
+    "strokeWidth\0"
+    "style\0"
+    "textAlign\0"
+    "textScaleX\0"
+    "textSize\0"
+    "textSkewX\0"
+    "textTracking\0"
+    "typeface\0"
+    "underline\0"
+    "xfermode"
+;
+
+static const SkMemberInfo gDrawPaintInfo[] = {
+    {0, 4, 26, 1},
+    {10, -1, 67, 98},
+    {17, 8, 31, 1},
+    {23, -2, 67, 98},
+    {31, 12, 47, 1},
+    {42, 16, 26, 1},
+    {53, 20, 62, 1},
+    {64, -1, 66, 98},
+    {76, 24, 76, 1},
+    {87, 28, 102, 1},
+    {94, 32, 26, 1},
+    {105, 36, 26, 1},
+    {112, 40, 27, 1},
+    {122, 44, 58, 1},
+    {133, 48, 98, 1},
+    {145, 52, 98, 1},
+    {157, 56, 109, 1},
+    {163, 60, 9, 1},
+    {173, 64, 98, 1},
+    {184, 68, 98, 1},
+    {193, 72, 98, 1},
+    {203, 76, 98, 1},
+    {216, 80, 120, 1},
+    {225, 84, 26, 1},
+    {235, 88, 121, 1}
+};
+
+static const char gPathStrings[] = 
+    "d\0"
+    "fillType\0"
+    "length"
+;
+
+static const SkMemberInfo gDrawPathInfo[] = {
+    {0, 32, 108, 1},
+    {2, -1, 67, 46},
+    {11, -2, 67, 98}
+};
+
+static const char gUnknown5Strings[] = 
+    "x\0"
+    "y\0"
+    "z"
+;
+
+static const SkMemberInfo gUnknown5Info[] = {
+    {0, 0, 98, 1},
+    {2, 4, 98, 1},
+    {4, 8, 98, 1}
+};
+
+static const char gPointStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gDrawPointInfo[] = {
+    {0, 4, 98, 1},
+    {2, 8, 98, 1}
+};
+
+static const char gPolyToPolyStrings[] = 
+    "destination\0"
+    "source"
+;
+
+static const SkMemberInfo gPolyToPolyInfo[] = {
+    {0, 12, 80, 1},
+    {12, 8, 80, 1}
+};
+
+static const char gPolygonStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gPolygonInfo[] = {
+    {0, 47, 18, 1}
+};
+
+static const char gPolylineStrings[] = 
+    "points"
+;
+
+static const SkMemberInfo gPolylineInfo[] = {
+    {0, 56, 119, 98}
+};
+
+static const char gPostStrings[] = 
+    "delay\0"
+    "initialized\0"
+    "mode\0"
+    "sink\0"
+    "target\0"
+    "type"
+;
+
+static const SkMemberInfo gPostInfo[] = {
+    {0, 4, 71, 1},
+    {6, 8, 26, 1},
+    {18, 12, 45, 1},
+    {23, -1, 67, 108},
+    {28, -2, 67, 108},
+    {35, -3, 67, 108}
+};
+
+static const char gQuadToStrings[] = 
+    "x1\0"
+    "x2\0"
+    "y1\0"
+    "y2"
+;
+
+static const SkMemberInfo gQuadToInfo[] = {
+    {0, 8, 98, 1},
+    {3, 16, 98, 1},
+    {6, 12, 98, 1},
+    {9, 20, 98, 1}
+};
+
+static const char gRCubicToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRCubicToInfo[] = {
+    {0, 18, 18, 6}
+};
+
+static const char gRLineToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRLineToInfo[] = {
+    {0, 34, 18, 2}
+};
+
+static const char gRMoveToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRMoveToInfo[] = {
+    {0, 38, 18, 2}
+};
+
+static const char gRQuadToStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gRQuadToInfo[] = {
+    {0, 49, 18, 4}
+};
+
+static const char gRadialGradientStrings[] = 
+    "\0"
+    "center\0"
+    "radius"
+;
+
+static const SkMemberInfo gRadialGradientInfo[] = {
+    {0, 26, 18, 3},
+    {1, 48, 77, 2},
+    {8, 56, 98, 1}
+};
+
+static const char gRandomStrings[] = 
+    "blend\0"
+    "max\0"
+    "min\0"
+    "random\0"
+    "seed"
+;
+
+static const SkMemberInfo gDisplayRandomInfo[] = {
+    {0, 4, 98, 1},
+    {6, 12, 98, 1},
+    {10, 8, 98, 1},
+    {14, 1, 67, 98},
+    {21, -2, 67, 96}
+};
+
+static const char gRectToRectStrings[] = 
+    "destination\0"
+    "source"
+;
+
+static const SkMemberInfo gRectToRectInfo[] = {
+    {0, 12, 91, 1},
+    {12, 8, 91, 1}
+};
+
+static const char gRectangleStrings[] = 
+    "bottom\0"
+    "height\0"
+    "left\0"
+    "needsRedraw\0"
+    "right\0"
+    "top\0"
+    "width"
+;
+
+static const SkMemberInfo gRectangleInfo[] = {
+    {0, 24, 98, 1},
+    {7, -1, 67, 98},
+    {14, 12, 98, 1},
+    {19, -2, 67, 26},
+    {31, 20, 98, 1},
+    {37, 16, 98, 1},
+    {41, -3, 67, 98}
+};
+
+static const char gRemoveStrings[] = 
+    "offset\0"
+    "where"
+;
+
+static const SkMemberInfo gRemoveInfo[] = {
+    {0, 8, 96, 1},
+    {7, 16, 37, 1}
+};
+
+static const char gReplaceStrings[] = 
+    ""
+;
+
+static const SkMemberInfo gReplaceInfo[] = {
+    {0, 1, 18, 4}
+};
+
+static const char gRotateStrings[] = 
+    "center\0"
+    "degrees"
+;
+
+static const SkMemberInfo gRotateInfo[] = {
+    {0, 12, 77, 2},
+    {7, 8, 98, 1}
+};
+
+static const char gRoundRectStrings[] = 
+    "\0"
+    "rx\0"
+    "ry"
+;
+
+static const SkMemberInfo gRoundRectInfo[] = {
+    {0, 57, 18, 7},
+    {1, 32, 98, 1},
+    {4, 36, 98, 1}
+};
+
+static const char gS32Strings[] = 
+    "value"
+;
+
+static const SkMemberInfo gS32Info[] = {
+    {0, 4, 96, 1}
+};
+
+static const char gScalarStrings[] = 
+    "value"
+;
+
+static const SkMemberInfo gScalarInfo[] = {
+    {0, 4, 98, 1}
+};
+
+static const char gScaleStrings[] = 
+    "center\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gScaleInfo[] = {
+    {0, 16, 77, 2},
+    {7, 8, 98, 1},
+    {9, 12, 98, 1}
+};
+
+static const char gSetStrings[] = 
+    "begin\0"
+    "dur\0"
+    "dynamic\0"
+    "field\0"
+    "formula\0"
+    "reset\0"
+    "target\0"
+    "to"
+;
+
+static const SkMemberInfo gSetInfo[] = {
+    {0, 4, 71, 1},
+    {6, 16, 71, 1},
+    {10, -1, 67, 26},
+    {18, 20, 108, 1},
+    {24, 24, 40, 1},
+    {32, -3, 67, 26},
+    {38, 36, 40, 1},
+    {45, 40, 40, 1}
+};
+
+static const char gShaderStrings[] = 
+    "matrix\0"
+    "tileMode"
+;
+
+static const SkMemberInfo gShaderInfo[] = {
+    {0, 8, 65, 1},
+    {7, 12, 116, 1}
+};
+
+static const char gSkewStrings[] = 
+    "center\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gSkewInfo[] = {
+    {0, 16, 77, 2},
+    {7, 8, 98, 1},
+    {9, 12, 98, 1}
+};
+
+static const char g3D_CameraStrings[] = 
+    "axis\0"
+    "hackHeight\0"
+    "hackWidth\0"
+    "location\0"
+    "observer\0"
+    "patch\0"
+    "zenith"
+;
+
+static const SkMemberInfo g3D_CameraInfo[] = {
+    {0, 24, 106, 3},
+    {5, 8, 98, 1},
+    {16, 4, 98, 1},
+    {26, 12, 106, 3},
+    {35, 48, 106, 3},
+    {44, 96, 105, 1},
+    {50, 36, 106, 3}
+};
+
+static const char g3D_PatchStrings[] = 
+    "origin\0"
+    "rotateDegrees\0"
+    "u\0"
+    "v"
+;
+
+static const SkMemberInfo g3D_PatchInfo[] = {
+    {0, 28, 106, 3},
+    {7, -1, 66, 98},
+    {21, 4, 106, 3},
+    {23, 16, 106, 3}
+};
+
+static const char gUnknown6Strings[] = 
+    "x\0"
+    "y\0"
+    "z"
+;
+
+static const SkMemberInfo gUnknown6Info[] = {
+    {0, 0, 98, 1},
+    {2, 4, 98, 1},
+    {4, 8, 98, 1}
+};
+
+static const char gSnapshotStrings[] = 
+    "filename\0"
+    "quality\0"
+    "sequence\0"
+    "type"
+;
+
+static const SkMemberInfo gSnapshotInfo[] = {
+    {0, 4, 108, 1},
+    {9, 8, 98, 1},
+    {17, 12, 26, 1},
+    {26, 16, 20, 1}
+};
+
+static const char gStringStrings[] = 
+    "length\0"
+    "slice\0"
+    "value"
+;
+
+static const SkMemberInfo gStringInfo[] = {
+    {0, -1, 67, 96},
+    {7, -1, 66, 108},
+    {13, 4, 108, 1}
+};
+
+static const char gTextStrings[] = 
+    "length\0"
+    "text\0"
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gTextInfo[] = {
+    {0, -1, 67, 96},
+    {7, 12, 108, 1},
+    {12, 16, 98, 1},
+    {14, 20, 98, 1}
+};
+
+static const char gTextBoxStrings[] = 
+    "\0"
+    "mode\0"
+    "spacingAdd\0"
+    "spacingAlign\0"
+    "spacingMul\0"
+    "text"
+;
+
+static const SkMemberInfo gTextBoxInfo[] = {
+    {0, 57, 18, 7},
+    {1, 44, 113, 1},
+    {6, 40, 98, 1},
+    {17, 48, 112, 1},
+    {30, 36, 98, 1},
+    {41, 32, 108, 1}
+};
+
+static const char gTextOnPathStrings[] = 
+    "offset\0"
+    "path\0"
+    "text"
+;
+
+static const SkMemberInfo gTextOnPathInfo[] = {
+    {0, 12, 98, 1},
+    {7, 16, 74, 1},
+    {12, 20, 110, 1}
+};
+
+static const char gTextToPathStrings[] = 
+    "path\0"
+    "text"
+;
+
+static const SkMemberInfo gTextToPathInfo[] = {
+    {0, 4, 74, 1},
+    {5, 8, 110, 1}
+};
+
+static const char gTranslateStrings[] = 
+    "x\0"
+    "y"
+;
+
+static const SkMemberInfo gTranslateInfo[] = {
+    {0, 8, 98, 1},
+    {2, 12, 98, 1}
+};
+
+static const char gTypedArrayStrings[] = 
+    "length\0"
+    "values"
+;
+
+static const SkMemberInfo gTypedArrayInfo[] = {
+    {0, -1, 67, 96},
+    {7, 4, 119, 0}
+};
+
+static const char gTypefaceStrings[] = 
+    "fontName"
+;
+
+static const SkMemberInfo gTypefaceInfo[] = {
+    {0, 8, 108, 1}
+};
+
+static const SkMemberInfo* const gInfoTables[] = {
+    gMathInfo,
+    gAddInfo,
+    gAddCircleInfo,
+    gUnknown1Info,
+    gAddOvalInfo,
+    gAddPathInfo,
+    gAddRectangleInfo,
+    gAddRoundRectInfo,
+    gUnknown2Info,
+    gAnimateFieldInfo,
+    gApplyInfo,
+    gUnknown3Info,
+    gDrawBitmapInfo,
+    gDrawBitmapShaderInfo,
+    gDrawBlurInfo,
+    gDisplayBoundsInfo,
+    gDrawClipInfo,
+    gDrawColorInfo,
+    gCubicToInfo,
+    gDashInfo,
+    gDataInfo,
+    gDiscreteInfo,
+    gDrawToInfo,
+    gDrawEmbossInfo,
+    gDisplayEventInfo,
+    gFromPathInfo,
+    gUnknown4Info,
+    gGInfo,
+    gHitClearInfo,
+    gHitTestInfo,
+    gImageInfo,
+    gIncludeInfo,
+    gInputInfo,
+    gLineInfo,
+    gLineToInfo,
+    gLinearGradientInfo,
+    gDrawMatrixInfo,
+    gMoveInfo,
+    gMoveToInfo,
+    gMovieInfo,
+    gOvalInfo,
+    gDrawPaintInfo,
+    gDrawPathInfo,
+    gUnknown5Info,
+    gDrawPointInfo,
+    gPolyToPolyInfo,
+    gPolygonInfo,
+    gPolylineInfo,
+    gPostInfo,
+    gQuadToInfo,
+    gRCubicToInfo,
+    gRLineToInfo,
+    gRMoveToInfo,
+    gRQuadToInfo,
+    gRadialGradientInfo,
+    gDisplayRandomInfo,
+    gRectToRectInfo,
+    gRectangleInfo,
+    gRemoveInfo,
+    gReplaceInfo,
+    gRotateInfo,
+    gRoundRectInfo,
+    gS32Info,
+    gScalarInfo,
+    gScaleInfo,
+    gSetInfo,
+    gShaderInfo,
+    gSkewInfo,
+    g3D_CameraInfo,
+    g3D_PatchInfo,
+    gUnknown6Info,
+    gSnapshotInfo,
+    gStringInfo,
+    gTextInfo,
+    gTextBoxInfo,
+    gTextOnPathInfo,
+    gTextToPathInfo,
+    gTranslateInfo,
+    gTypedArrayInfo,
+    gTypefaceInfo,
+};
+
+static const unsigned char gInfoCounts[] = {
+    26,4,4,1,1,2,5,3,13,1,13,2,6,3,2,2,2,8,6,
+    2,2,2,2,4,8,3,3,2,1,4,3,1,3,4,2,2,12,1,2,
+    1,1,25,3,3,2,2,1,1,6,4,1,1,1,1,3,5,2,7,2,
+    1,2,3,1,1,3,8,2,3,7,4,3,4,3,4,6,3,2,2,2,
+    1
+};
+
+static const unsigned char gTypeIDs[] = {
+    1, // Math
+    2, // Add
+    3, // AddCircle
+    4, // Unknown1
+    5, // AddOval
+    6, // AddPath
+    7, // AddRectangle
+    8, // AddRoundRect
+    10, // Unknown2
+    11, // AnimateField
+    12, // Apply
+    17, // Unknown3
+    19, // Bitmap
+    22, // BitmapShader
+    23, // Blur
+    25, // Bounds
+    29, // Clip
+    31, // Color
+    32, // CubicTo
+    33, // Dash
+    34, // Data
+    35, // Discrete
+    38, // DrawTo
+    41, // Emboss
+    42, // Event
+    48, // FromPath
+    51, // Unknown4
+    52, // G
+    53, // HitClear
+    54, // HitTest
+    55, // Image
+    56, // Include
+    57, // Input
+    59, // Line
+    60, // LineTo
+    61, // LinearGradient
+    65, // Matrix
+    68, // Move
+    69, // MoveTo
+    70, // Movie
+    72, // Oval
+    73, // Paint
+    74, // Path
+    77, // Unknown5
+    78, // Point
+    79, // PolyToPoly
+    80, // Polygon
+    81, // Polyline
+    82, // Post
+    83, // QuadTo
+    84, // RCubicTo
+    85, // RLineTo
+    86, // RMoveTo
+    87, // RQuadTo
+    88, // RadialGradient
+    89, // Random
+    90, // RectToRect
+    91, // Rectangle
+    92, // Remove
+    93, // Replace
+    94, // Rotate
+    95, // RoundRect
+    96, // S32
+    98, // Scalar
+    99, // Scale
+    101, // Set
+    102, // Shader
+    103, // Skew
+    104, // 3D_Camera
+    105, // 3D_Patch
+    106, // Unknown6
+    107, // Snapshot
+    108, // String
+    110, // Text
+    111, // TextBox
+    114, // TextOnPath
+    115, // TextToPath
+    117, // Translate
+    119, // TypedArray
+    120, // Typeface
+    
+};
+
+static const int kTypeIDs = 80;
+
+static const char* const gInfoNames[] = {
+    gMathStrings,
+    gAddStrings,
+    gAddCircleStrings,
+    gUnknown1Strings,
+    gAddOvalStrings,
+    gAddPathStrings,
+    gAddRectangleStrings,
+    gAddRoundRectStrings,
+    gUnknown2Strings,
+    gAnimateFieldStrings,
+    gApplyStrings,
+    gUnknown3Strings,
+    gBitmapStrings,
+    gBitmapShaderStrings,
+    gBlurStrings,
+    gBoundsStrings,
+    gClipStrings,
+    gColorStrings,
+    gCubicToStrings,
+    gDashStrings,
+    gDataStrings,
+    gDiscreteStrings,
+    gDrawToStrings,
+    gEmbossStrings,
+    gEventStrings,
+    gFromPathStrings,
+    gUnknown4Strings,
+    gGStrings,
+    gHitClearStrings,
+    gHitTestStrings,
+    gImageStrings,
+    gIncludeStrings,
+    gInputStrings,
+    gLineStrings,
+    gLineToStrings,
+    gLinearGradientStrings,
+    gMatrixStrings,
+    gMoveStrings,
+    gMoveToStrings,
+    gMovieStrings,
+    gOvalStrings,
+    gPaintStrings,
+    gPathStrings,
+    gUnknown5Strings,
+    gPointStrings,
+    gPolyToPolyStrings,
+    gPolygonStrings,
+    gPolylineStrings,
+    gPostStrings,
+    gQuadToStrings,
+    gRCubicToStrings,
+    gRLineToStrings,
+    gRMoveToStrings,
+    gRQuadToStrings,
+    gRadialGradientStrings,
+    gRandomStrings,
+    gRectToRectStrings,
+    gRectangleStrings,
+    gRemoveStrings,
+    gReplaceStrings,
+    gRotateStrings,
+    gRoundRectStrings,
+    gS32Strings,
+    gScalarStrings,
+    gScaleStrings,
+    gSetStrings,
+    gShaderStrings,
+    gSkewStrings,
+    g3D_CameraStrings,
+    g3D_PatchStrings,
+    gUnknown6Strings,
+    gSnapshotStrings,
+    gStringStrings,
+    gTextStrings,
+    gTextBoxStrings,
+    gTextOnPathStrings,
+    gTextToPathStrings,
+    gTranslateStrings,
+    gTypedArrayStrings,
+    gTypefaceStrings
+};
+#endif
+#endif
+
diff --git a/src/animator/SkDisplayAdd.cpp b/src/animator/SkDisplayAdd.cpp
new file mode 100644
index 0000000..8a97a06
--- /dev/null
+++ b/src/animator/SkDisplayAdd.cpp
@@ -0,0 +1,254 @@
+/* libs/graphics/animator/SkDisplayAdd.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 "SkDisplayAdd.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayApply.h"
+#include "SkDisplayList.h"
+#include "SkDrawable.h"
+#include "SkDrawGroup.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAdd::fInfo[] = {
+    SK_MEMBER(mode, AddMode),
+    SK_MEMBER(offset, Int),
+    SK_MEMBER(use, Drawable),
+    SK_MEMBER(where, Drawable)
+};
+
+#endif
+
+// start here;
+// add onEndElement to turn where string into f_Where
+// probably need new SkAnimateMaker::resolve flavor that takes
+// where="id", where="event-target" or not-specified
+// offset="#" (implements before, after, and index if no 'where')
+
+DEFINE_GET_MEMBER(SkAdd);
+
+SkAdd::SkAdd() : mode(kMode_indirect), 
+    offset(SK_MaxS32), use(NULL), where(NULL) {
+}
+
+SkDisplayable* SkAdd::deepCopy(SkAnimateMaker* maker) {
+    SkDrawable* saveUse = use;
+    SkDrawable* saveWhere = where;
+    use = NULL;
+    where = NULL;
+    SkAdd* copy = (SkAdd*) INHERITED::deepCopy(maker);
+    copy->use = use = saveUse;
+    copy->where = where = saveWhere;
+    return copy;
+}
+
+bool SkAdd::draw(SkAnimateMaker& maker) {
+    SkASSERT(use);
+    SkASSERT(use->isDrawable());
+    if (mode == kMode_indirect)
+        use->draw(maker);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkAdd::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpAttrs(maker);
+    if (where)
+        SkDebugf("where=\"%s\" ", where->id);
+    if (mode == kMode_immediate)
+        SkDebugf("mode=\"immediate\" ");
+    SkDebugf(">\n");
+    SkDisplayList::fIndent += 4;
+    int save = SkDisplayList::fDumpIndex;
+    if (use)    //just in case
+        use->dump(maker);
+    SkDisplayList::fIndent -= 4;
+    SkDisplayList::fDumpIndex = save;
+    dumpEnd(maker);
+}
+#endif
+
+bool SkAdd::enable(SkAnimateMaker& maker ) {
+    SkDisplayTypes type = getType();
+    SkDisplayList& displayList = maker.fDisplayList;
+    SkTDDrawableArray* parentList = displayList.getDrawList();
+    if (type == SkType_Add) {
+        if (use == NULL) // not set in apply yet
+            return true;
+    }
+    bool skipAddToParent = true;
+    SkASSERT(type != SkType_Replace || where);
+    SkTDDrawableArray* grandList SK_INIT_TO_AVOID_WARNING;
+    SkGroup* parentGroup = NULL;
+    SkGroup* thisGroup = NULL;
+    int index = where ? displayList.findGroup(where, &parentList, &parentGroup,
+        &thisGroup, &grandList) : 0;
+    if (index < 0)
+        return true;
+    int max = parentList->count();
+    if (where == NULL && type == SkType_Move)
+        index = max;
+    if (offset != SK_MaxS32) {
+        index += offset;
+        if (index > max) {
+            maker.setErrorCode(SkDisplayXMLParserError::kIndexOutOfRange);
+            return true;    // caller should not add
+        }
+    }
+    if (offset < 0 && where == NULL)
+        index += max + 1;
+    switch (type) {
+        case SkType_Add:
+            if (offset == SK_MaxS32 && where == NULL) {
+                if (use->isDrawable()) {
+                    skipAddToParent = mode == kMode_immediate;
+                    if (skipAddToParent) {
+                        if (where == NULL) {
+                            SkTDDrawableArray* useParentList;
+                            index = displayList.findGroup(this, &useParentList, &parentGroup,
+                                &thisGroup, &grandList);
+                            if (index >= 0) {
+                                parentGroup->markCopySize(index);
+                                parentGroup->markCopySet(index);
+                                useParentList->begin()[index] = use;
+                                break;
+                            }                               
+                        }
+                        *parentList->append() = use;
+                    }
+                }
+                break;
+            } else {
+                if (thisGroup)
+                    thisGroup->markCopySize(index);
+                *parentList->insert(index) = use;
+                if (thisGroup)
+                    thisGroup->markCopySet(index);
+                if (use->isApply())
+                    ((SkApply*) use)->setEmbedded();
+            }
+            break;
+        case SkType_Move: {
+            int priorLocation = parentList->find(use);
+            if (priorLocation < 0)
+                break;
+            *parentList->insert(index) = use;
+            if (index < priorLocation)
+                priorLocation++;
+            parentList->remove(priorLocation);
+            } break;
+        case SkType_Remove: {
+            SkDisplayable* old = (*parentList)[index];
+            if (((SkRemove*)(this))->fDelete) {
+                delete old;
+                goto noHelperNeeded;
+            }
+            for (int inner = 0; inner < maker.fChildren.count(); inner++) {
+                SkDisplayable* child = maker.fChildren[inner];
+                if (child == old || child->contains(old))
+                    goto noHelperNeeded;
+            }
+            if (maker.fHelpers.find(old) < 0)
+                maker.helperAdd(old);
+noHelperNeeded:
+            parentList->remove(index);
+            } break;
+        case SkType_Replace:
+            if (thisGroup) {
+                thisGroup->markCopySize(index);
+                if (thisGroup->markedForDelete(index)) {
+                    SkDisplayable* old = (*parentList)[index];
+                    if (maker.fHelpers.find(old) < 0)
+                        maker.helperAdd(old);
+                }
+            }
+            (*parentList)[index] = use;
+            if (thisGroup)
+                thisGroup->markCopySet(index);
+            break;
+        default:
+            SkASSERT(0);
+    }
+    if (type == SkType_Remove)
+        return true;
+    if (use->hasEnable())
+        use->enable(maker);
+    return skipAddToParent; // append if indirect: *parentList->append() = this;
+}
+
+bool SkAdd::hasEnable() const {
+    return true;
+}
+
+void SkAdd::initialize() {
+    if (use)
+        use->initialize();
+}
+
+bool SkAdd::isDrawable() const {
+    return getType() == SkType_Add && mode == kMode_indirect && offset == SK_MaxS32 &&
+        where == NULL && use != NULL && use->isDrawable();
+}
+
+//SkDisplayable* SkAdd::resolveTarget(SkAnimateMaker& maker) {
+//  return use;
+//}
+
+
+bool SkClear::enable(SkAnimateMaker& maker ) {
+    SkDisplayList& displayList = maker.fDisplayList;
+    displayList.clear();
+    return true;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkMove::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkMove);
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRemove::fInfo[] = {
+    SK_MEMBER_ALIAS(delete, fDelete, Boolean),  // !!! experimental
+    SK_MEMBER(offset, Int),
+    SK_MEMBER(where, Drawable)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRemove);
+
+SkRemove::SkRemove() : fDelete(false) {
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkReplace::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkReplace);
+
diff --git a/src/animator/SkDisplayAdd.h b/src/animator/SkDisplayAdd.h
new file mode 100644
index 0000000..0f3edc9
--- /dev/null
+++ b/src/animator/SkDisplayAdd.h
@@ -0,0 +1,81 @@
+/* libs/graphics/animator/SkDisplayAdd.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 SkDisplayAdd_DEFINED
+#define SkDisplayAdd_DEFINED
+
+#include "SkDrawable.h"
+#include "SkMemberInfo.h"
+
+class SkAdd : public SkDrawable {
+    DECLARE_MEMBER_INFO(Add);
+    SkAdd();
+    
+    enum Mode {
+        kMode_indirect,
+        kMode_immediate
+    };
+    
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool enable(SkAnimateMaker& );
+    virtual bool hasEnable() const;
+    virtual void initialize();
+    virtual bool isDrawable() const;
+protected:
+//  struct _A {
+        Mode mode;
+        int32_t offset;
+        SkDrawable* use;
+        SkDrawable* where;  // if NULL, offset becomes index
+//  } A;
+private:
+    typedef SkDrawable INHERITED;
+};
+
+class SkClear : public SkDisplayable {
+    virtual bool enable(SkAnimateMaker& );
+};
+
+class SkMove : public SkAdd {
+    DECLARE_MEMBER_INFO(Move);
+private:
+    typedef SkAdd INHERITED;
+};
+
+class SkRemove : public SkAdd {
+    DECLARE_MEMBER_INFO(Remove);
+    SkRemove();
+protected:
+    SkBool fDelete;
+private:
+    friend class SkAdd;
+    typedef SkAdd INHERITED;
+};
+
+class SkReplace : public SkAdd {
+    DECLARE_MEMBER_INFO(Replace);
+private:
+    typedef SkAdd INHERITED;
+};
+
+#endif // SkDisplayAdd_DEFINED
+
+
diff --git a/src/animator/SkDisplayApply.cpp b/src/animator/SkDisplayApply.cpp
new file mode 100644
index 0000000..b9e65f7
--- /dev/null
+++ b/src/animator/SkDisplayApply.cpp
@@ -0,0 +1,814 @@
+/* libs/graphics/animator/SkDisplayApply.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 "SkDisplayApply.h"
+#include "SkAnimateActive.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateSet.h"
+#include "SkAnimatorScript.h"
+#include "SkDisplayType.h"
+#include "SkDrawGroup.h"
+#include "SkParse.h"
+#include "SkScript.h"
+#include "SkSystemEventTypes.h"
+#ifdef SK_DEBUG
+#include "SkTime.h"
+#endif
+#include <ctype.h>
+
+enum SkApply_Properties {
+    SK_PROPERTY(animator),
+    SK_PROPERTY(step),
+    SK_PROPERTY(steps),
+    SK_PROPERTY(time)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+// if no attibutes, enclosed displayable is both scope & target
+// only if both scope & target are specified, or if target and enclosed displayable, are scope and target different
+const SkMemberInfo SkApply::fInfo[] = {
+    SK_MEMBER_PROPERTY(animator, Animate),
+    SK_MEMBER(begin, MSec),
+    SK_MEMBER(dontDraw, Boolean),
+    SK_MEMBER(dynamicScope, String),
+    SK_MEMBER(interval, MSec),  // recommended redraw interval
+    SK_MEMBER(mode, ApplyMode),
+#if 0
+    SK_MEMBER(pickup, Boolean),
+#endif
+    SK_MEMBER(restore, Boolean),
+    SK_MEMBER(scope, Drawable), // thing that scopes animation (unnamed enclosed displayable goes here)
+    SK_MEMBER_PROPERTY(step, Int),
+    SK_MEMBER_PROPERTY(steps, Int),
+    SK_MEMBER_PROPERTY(time, MSec),
+    SK_MEMBER(transition, ApplyTransition)
+};
+
+#endif 
+
+DEFINE_GET_MEMBER(SkApply);
+
+SkApply::SkApply() : begin(0), dontDraw(false), interval((SkMSec) -1), mode((Mode) -1), /*pickup(false), */
+    restore(false), scope(NULL), steps(-1), transition((Transition) -1), fActive(NULL), /*fCurrentScope(NULL),*/
+    fLastTime(0), fAppended(false), fContainsScope(false), fDeleteScope(false), fEmbedded(false), 
+    fEnabled(false), fEnabling(false) {
+}
+
+SkApply::~SkApply() {
+    for (SkDrawable** curPtr = fScopes.begin(); curPtr < fScopes.end(); curPtr++)
+        delete *curPtr;
+    if (fDeleteScope)
+        delete scope;
+    // !!! caller must call maker.removeActive(fActive)
+    delete fActive;
+}
+
+void SkApply::activate(SkAnimateMaker& maker) {
+    if (fActive != NULL) {
+        if (fActive->fDrawIndex == 0 && fActive->fDrawMax == 0)
+            return; // if only one use, nothing more to do
+        if (restore == false)
+            return; // all share same state, regardless of instance number
+        bool save = fActive->initializeSave();
+        fActive->fixInterpolator(save);
+    } else {
+        fActive = new SkActive(*this, maker);
+        fActive->init();
+        maker.appendActive(fActive);
+        if (restore) {
+            fActive->initializeSave();
+            int animators = fAnimators.count();
+            for (int index = 0; index < animators; index++)
+                fActive->saveInterpolatorValues(index);
+        }
+    }
+}
+
+void SkApply::append(SkApply* apply) {
+    if (fActive == NULL)
+        return;
+    int oldCount = fActive->fAnimators.count();
+    fActive->append(apply);
+    if (restore) {
+        fActive->appendSave(oldCount);
+        int newCount = fActive->fAnimators.count();
+        for (int index = oldCount; index < newCount; index++)
+            fActive->saveInterpolatorValues(index);
+    }
+}
+
+void SkApply::applyValues(int animatorIndex, SkOperand* values, int count,
+     SkDisplayTypes valuesType, SkMSec time) 
+{
+    SkAnimateBase* animator = fActive->fAnimators[animatorIndex];
+    const SkMemberInfo * info = animator->fFieldInfo;
+    SkASSERT(animator);
+    SkASSERT(info != NULL);
+    SkDisplayTypes type = (SkDisplayTypes) info->fType;
+    SkDisplayable* target = getTarget(animator);
+    if (animator->hasExecute() || type == SkType_MemberFunction || type == SkType_MemberProperty) {
+        SkDisplayable* executor = animator->hasExecute() ? animator : target;
+        if (type != SkType_MemberProperty) {
+            SkTDArray<SkScriptValue> typedValues;
+            for (int index = 0; index < count; index++) {
+                SkScriptValue temp;
+                temp.fType = valuesType;
+                temp.fOperand = values[index];
+                *typedValues.append() = temp;
+            }
+            executor->executeFunction(target, info->functionIndex(), typedValues, info->getType(), NULL);
+        } else {
+            SkScriptValue scriptValue;
+            scriptValue.fOperand = values[0];
+            scriptValue.fType = info->getType();
+            target->setProperty(info->propertyIndex(), scriptValue);
+        }
+    } else {
+        SkTypedArray converted;
+        if (type == SkType_ARGB) {
+            if (count == 4) {
+                // !!! assert that it is SkType_Float ?
+                animator->packARGB(&values->fScalar, count, &converted);
+                values = converted.begin();
+                count = converted.count();
+            } else
+                SkASSERT(count == 1);
+        }
+//      SkASSERT(type == SkType_ARGB || type == SkType_String ||info->isSettable());
+        if (type == SkType_String || type == SkType_DynamicString)
+            info->setString(target, values->fString);
+        else if (type == SkType_Drawable || type == SkType_Displayable)
+            target->setReference(info, values->fDisplayable);
+        else
+            info->setValue(target, values, count);
+    } 
+}
+
+bool SkApply::contains(SkDisplayable* child) {
+    for (SkDrawable** curPtr = fScopes.begin(); curPtr < fScopes.end(); curPtr++) {
+        if (*curPtr == child || (*curPtr)->contains(child))
+            return true;
+    }
+    return fDeleteScope && scope == child;
+}
+
+SkDisplayable* SkApply::deepCopy(SkAnimateMaker* maker) {
+    SkDrawable* saveScope = scope;
+    scope = NULL;
+    SkApply* result = (SkApply*) INHERITED::deepCopy(maker);
+    result->scope = scope = saveScope;
+    SkAnimateBase** end = fAnimators.end();
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < end; animPtr++) {
+        SkAnimateBase* anim = (SkAnimateBase*) (*animPtr)->deepCopy(maker);
+        *result->fAnimators.append() = anim;
+        maker->helperAdd(anim);
+    }
+    return result;
+}
+
+void SkApply::disable() {
+    //!!! this is the right thing to do, but has bad side effects because of other problems
+    // currently, if an apply is in a g and scopes a statement in another g, it ends up as members
+    // of both containers. The disabling here incorrectly disables both instances
+    // maybe the fEnabled flag needs to be moved to the fActive data so that both 
+    // instances are not affected.
+//  fEnabled = false;
+}
+
+bool SkApply::draw(SkAnimateMaker& maker) {
+    if (scope ==NULL)
+        return false;
+    if (scope->isApply() || scope->isDrawable() == false)
+        return false;
+    if (fEnabled == false)
+        enable(maker);
+    SkASSERT(scope);
+    activate(maker);
+    if (mode == kMode_immediate)
+        return fActive->draw();
+    bool result = interpolate(maker, maker.getInTime());
+    if (dontDraw == false) {
+//      if (scope->isDrawable())
+            result |= scope->draw(maker);
+    }
+    if (restore) {
+        for (int index = 0; index < fActive->fAnimators.count(); index++)
+            endSave(index);
+        fActive->advance();
+    }
+    return result;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkApply::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    if (dynamicScope.isEmpty() == false)
+        SkDebugf("dynamicScope=\"%s\" ", dynamicScope.c_str());
+    if (dontDraw)
+        SkDebugf("dontDraw=\"true\" ");
+    if (begin != 0) //perhaps we want this no matter what?
+        SkDebugf("begin=\"%g\" ", (float) begin/1000.0f);   //is this correct?
+    if (interval != (SkMSec) -1)
+        SkDebugf("interval=\"%g\" ", (float) interval/1000.0f);
+    if (steps != -1)
+        SkDebugf("steps=\"%d\" ", steps);
+    if (restore)
+        SkDebugf("restore=\"true\" ");
+    if (transition == kTransition_reverse)
+        SkDebugf("transition=\"reverse\" ");
+    if (mode == kMode_immediate) {
+        SkDebugf("mode=\"immediate\" ");
+    }
+    else if (mode == kMode_create) {
+        SkDebugf("mode=\"create\" ");
+    }
+    bool closedYet = false;
+    SkDisplayList::fIndent += 4;
+    int save = SkDisplayList::fDumpIndex;
+    if (scope) {
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        scope->dump(maker);
+    }
+    int index;
+//  if (fActive) {
+        for (index = 0; index < fAnimators.count(); index++) {
+            if (closedYet == false) {
+                SkDebugf(">\n");
+                closedYet = true;
+            }
+            SkAnimateBase* animator = fAnimators[index];
+            animator->dump(maker);
+//      }
+    }
+    SkDisplayList::fIndent -= 4;
+    SkDisplayList::fDumpIndex = save;
+    if (closedYet)
+        dumpEnd(maker);
+    else
+        SkDebugf("/>\n");
+}
+#endif
+
+bool SkApply::enable(SkAnimateMaker& maker) {
+    fEnabled = true;
+    bool initialized = fActive != NULL;
+    if (dynamicScope.size() > 0)
+        enableDynamic(maker);
+    if (maker.fError.hasError()) 
+        return false;
+    int animators = fAnimators.count();
+    int index;
+    for (index = 0; index < animators; index++) {
+        SkAnimateBase* animator = fAnimators[index];
+        animator->fStart = maker.fEnableTime;
+        animator->fResetPending = animator->fReset;
+    }
+    if (scope && scope->isApply())
+        ((SkApply*) scope)->setEmbedded();
+/*  if (mode == kMode_once) {
+        if (scope) {
+            activate(maker);
+            interpolate(maker, maker.fEnableTime);
+            inactivate(maker);
+        }
+        return true;
+    }*/
+    if ((mode == kMode_immediate || mode == kMode_create) && scope == NULL)
+        return false;   // !!! error?
+    bool enableMe = scope && (scope->hasEnable() || scope->isApply() || scope->isDrawable() == false);
+    if (mode == kMode_immediate && enableMe || mode == kMode_create)
+        activate(maker);    // for non-drawables like post, prime them here
+    if (mode == kMode_immediate && enableMe)
+        fActive->enable();
+    if (mode == kMode_create && scope != NULL) {
+        enableCreate(maker);
+        return true;
+    }
+    if (mode == kMode_immediate) {
+        return scope->isApply() || scope->isDrawable() == false;
+    }
+    refresh(maker);
+    SkDisplayList& displayList = maker.fDisplayList;
+    SkDrawable* drawable;
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    SkString debugOut;
+    SkMSec time = maker.getAppTime();
+    debugOut.appendS32(time - maker.fDebugTimeBase);
+    debugOut.append(" apply enable id=");
+    debugOut.append(_id);
+    debugOut.append("; start=");
+    debugOut.appendS32(maker.fEnableTime - maker.fDebugTimeBase);
+    SkDebugf("%s\n", debugOut.c_str());
+#endif
+    if (scope == NULL || scope->isApply() || scope->getType() == SkType_Movie || scope->isDrawable() == false) {
+        activate(maker);    // for non-drawables like post, prime them here
+        if (initialized) {
+            append(this);
+        }
+        fEnabling = true;
+        interpolate(maker, maker.fEnableTime);
+        fEnabling = false;
+        if (scope != NULL && dontDraw == false)
+            scope->enable(maker);
+        return true;
+    } else if (initialized && restore == false)
+        append(this);
+#if 0
+    bool wasActive = inactivate(maker); // start fresh
+    if (wasActive) {
+        activate(maker);
+        interpolate(maker, maker.fEnableTime);
+        return true;
+    }
+#endif
+//  start here;
+    // now that one apply might embed another, only the parent apply should replace the scope 
+    // or get appended to the display list
+    // similarly, an apply added by an add immediate has already been located in the display list
+    // and should not get moved or added again here
+    if (fEmbedded) {
+        return false;   // already added to display list by embedder
+    }
+    drawable = (SkDrawable*) scope;
+    SkTDDrawableArray* parentList;
+    SkTDDrawableArray* grandList;
+    SkGroup* parentGroup;
+    SkGroup* thisGroup;
+    int old = displayList.findGroup(drawable, &parentList, &parentGroup, &thisGroup, &grandList);
+    if (old < 0)
+        goto append;
+    else if (fContainsScope) {
+        if ((*parentList)[old] != this || restore == true) {
+append:
+            if (parentGroup)
+                parentGroup->markCopySize(old);
+            if (parentList->count() < 10000) {
+                fAppended = true;
+                *parentList->append() = this;
+            } else
+                maker.setErrorCode(SkDisplayXMLParserError::kDisplayTreeTooDeep);
+            old = -1;
+        } else
+            reset();
+    } else {
+        SkASSERT(old < parentList->count());
+        if ((*parentList)[old]->isApply()) {
+            SkApply* apply = (SkApply*) (*parentList)[old];
+            if (apply != this && apply->fActive == NULL)
+                apply->activate(maker);
+            apply->append(this);
+            parentGroup = NULL;
+        } else {
+            if (parentGroup)
+                parentGroup->markCopySize(old);
+            SkDrawable** newApplyLocation = &(*parentList)[old];
+            SkGroup* pGroup;
+            int oldApply = displayList.findGroup(this, &parentList, &pGroup, &thisGroup, &grandList);
+            if (oldApply >= 0) {
+                (*parentList)[oldApply] = (SkDrawable*) SkDisplayType::CreateInstance(&maker, SkType_Apply);
+                parentGroup = NULL;
+                fDeleteScope = true;
+            }
+            *newApplyLocation = this;
+        }
+    }
+    if (parentGroup) {
+        parentGroup->markCopySet(old);
+        fDeleteScope = dynamicScope.size() == 0;
+    }
+    return true;
+}
+
+void SkApply::enableCreate(SkAnimateMaker& maker) {
+    SkString newID;
+    for (int step = 0; step <= steps; step++) {
+        fLastTime = step * SK_MSec1;
+        bool success = maker.computeID(scope, this, &newID);
+        if (success == false)
+            return;
+        if (maker.find(newID.c_str(), NULL))
+            continue;
+        SkApply* copy = (SkApply*) deepCopy(&maker); // work on copy of animator state
+        if (mode == kMode_create)
+            copy->mode = (Mode) -1;
+        SkDrawable* copyScope = copy->scope = (SkDrawable*) scope->deepCopy(&maker);
+        *fScopes.append() = copyScope;
+        if (copyScope->resolveIDs(maker, scope, this)) {
+            step = steps; // quit
+            goto next; // resolveIDs failed
+        }
+        if (newID.size() > 0) 
+            maker.setID(copyScope, newID);
+        if (copy->resolveIDs(maker, this, this)) { // fix up all fields, including target
+            step = steps; // quit
+            goto next; // resolveIDs failed
+        }
+        copy->activate(maker);
+        copy->interpolate(maker, step * SK_MSec1);
+        maker.removeActive(copy->fActive);
+    next:
+        delete copy;
+    }
+}
+
+void SkApply::enableDynamic(SkAnimateMaker& maker) {
+    SkASSERT(mode != kMode_create); // create + dynamic are not currently compatible
+    SkDisplayable* newScope;
+    bool success = SkAnimatorScript::EvaluateDisplayable(maker, this, dynamicScope.c_str(),
+        &newScope);
+    if (success && scope != newScope) {
+        SkTDDrawableArray* pList, * gList;
+        SkGroup* pGroup = NULL, * found = NULL;
+        int old = maker.fDisplayList.findGroup(scope, &pList, &pGroup, &found, &gList);
+        if (pList && old >= 0 && (*pList)[old]->isApply() && (*pList)[old] != this) {
+            if (fAppended == false) {
+                if (found != NULL) {
+                    SkDisplayable* oldChild = (*pList)[old];
+                    if (oldChild->isApply() && found->copySet(old)) {
+                        found->markCopyClear(old);
+    //                  delete oldChild;
+                    }
+                }
+                (*pList)[old] = scope;
+            } else
+                pList->remove(old);
+        }
+        scope = (SkDrawable*) newScope;
+        onEndElement(maker);
+    }
+    maker.removeActive(fActive);
+    delete fActive;
+    fActive = NULL;
+}
+
+void SkApply::endSave(int index) {
+    SkAnimateBase* animate = fActive->fAnimators[index];
+    const SkMemberInfo* info = animate->fFieldInfo;
+    SkDisplayTypes type = (SkDisplayTypes) info->fType;
+    if (type == SkType_MemberFunction)
+        return;
+    SkDisplayable* target = getTarget(animate);
+    size_t size = info->getSize(target);
+    int count = (int) (size / sizeof(SkScalar));
+    int activeIndex = fActive->fDrawIndex + index;
+    SkOperand* last = new SkOperand[count];
+    SkAutoTDelete<SkOperand> autoLast(last);
+    if (type != SkType_MemberProperty) {
+        info->getValue(target, last, count);
+        SkOperand* saveOperand = fActive->fSaveRestore[activeIndex];
+        if (saveOperand)
+            info->setValue(target, fActive->fSaveRestore[activeIndex], count);
+    } else {
+        SkScriptValue scriptValue;
+        bool success = target->getProperty(info->propertyIndex(), &scriptValue);
+        SkASSERT(success = true);
+        last[0] = scriptValue.fOperand;
+        scriptValue.fOperand = fActive->fSaveRestore[activeIndex][0];
+        target->setProperty(info->propertyIndex(), scriptValue);
+    }
+    SkOperand* save = fActive->fSaveRestore[activeIndex];
+    if (save)
+        memcpy(save, last, count * sizeof(SkOperand));
+}
+
+bool SkApply::getProperty(int index, SkScriptValue* value) const {
+    switch (index) {
+        case SK_PROPERTY(step):
+            value->fType = SkType_Int;
+            value->fOperand.fS32 = fLastTime / SK_MSec1;
+            break;
+        case SK_PROPERTY(steps):
+            value->fType = SkType_Int;
+            value->fOperand.fS32 = steps;
+            break;
+        case SK_PROPERTY(time):
+            value->fType = SkType_MSec;
+            value->fOperand.fS32 = fLastTime;
+            break;
+        default:
+    //      SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+void SkApply::getStep(SkScriptValue* value) {
+    getProperty(SK_PROPERTY(step), value);
+}
+
+SkDrawable* SkApply::getTarget(SkAnimateBase* animate) {
+    if (animate->fTargetIsScope == false || mode != kMode_create)
+        return animate->fTarget;
+    return scope;
+}
+
+bool SkApply::hasDelayedAnimator() const {
+    SkAnimateBase** animEnd = fAnimators.end();
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < animEnd; animPtr++) {
+        SkAnimateBase* animator = *animPtr;
+        if (animator->fDelayed) 
+            return true;
+    }
+    return false;
+}
+
+bool SkApply::hasEnable() const {
+    return true;
+}
+
+bool SkApply::inactivate(SkAnimateMaker& maker) {
+    if (fActive == NULL)
+        return false;
+    maker.removeActive(fActive);
+    delete fActive;
+    fActive = NULL;
+    return true;
+}
+
+#ifdef SK_DEBUG
+SkMSec lastTime = (SkMSec) -1;
+#endif
+
+bool SkApply::interpolate(SkAnimateMaker& maker, SkMSec rawTime) {
+    if (fActive == NULL)
+        return false;
+    bool result = false;
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    SkMSec time = maker.getAppTime();
+    if (lastTime == (SkMSec) -1)
+        lastTime = rawTime - 1;
+    if (fActive != NULL && 
+        strcmp(id, "a3") == 0 && rawTime > lastTime) {
+        lastTime += 1000;
+        SkString debugOut;
+        debugOut.appendS32(time - maker.fDebugTimeBase);
+        debugOut.append(" apply id=");
+        debugOut.append(_id);
+        debugOut.append("; ");
+        debugOut.append(fActive->fAnimators[0]->_id);
+        debugOut.append("=");
+        debugOut.appendS32(rawTime - fActive->fState[0].fStartTime);
+        debugOut.append(")");
+        SkDebugf("%s\n", debugOut.c_str());
+    }
+#endif
+    fActive->start();
+    if (restore)
+        fActive->initializeSave();
+    int animators = fActive->fAnimators.count();
+    for (int inner = 0; inner < animators; inner++) {
+        SkAnimateBase* animate = fActive->fAnimators[inner];
+        if (animate->fChanged) {
+            animate->fChanged = false;
+            animate->fStart = rawTime;
+    //      SkTypedArray values;
+    //      int count = animate->fValues.count();
+    //      values.setCount(count);
+    //      memcpy(values.begin(), animate->fValues.begin(), sizeof(SkOperand) * count);
+            animate->onEndElement(maker);
+    //      if (memcmp(values.begin(), animate->fValues.begin(), sizeof(SkOperand) * count) != 0) {
+                fActive->append(this);
+                fActive->start();
+    //      }
+        }
+        SkMSec time = fActive->getTime(rawTime, inner);
+        SkActive::SkState& state = fActive->fState[inner];
+        if (SkMSec_LT(rawTime, state.fStartTime)) {
+            if (fEnabling) {
+                animate->fDelayed = true;
+                maker.delayEnable(this, state.fStartTime);
+            }
+            continue;
+        } else
+            animate->fDelayed = false;
+        SkMSec innerTime = fLastTime = state.getRelativeTime(time);
+        if (restore) 
+            fActive->restoreInterpolatorValues(inner);
+        if (animate->fReset) {
+            if (transition != SkApply::kTransition_reverse) {
+                if (SkMSec_LT(state.fBegin + state.fDuration, innerTime)) {
+                    if (animate->fResetPending) {
+                        innerTime = 0;
+                        animate->fResetPending = false;
+                    } else
+                        continue;
+                }
+            } else if (innerTime == 0) {
+                    if (animate->fResetPending) {
+                        innerTime = state.fBegin + state.fDuration;
+                        animate->fResetPending = false;
+                    } else
+                        continue;
+            }
+        }
+        int count = animate->components();
+        SkAutoSTMalloc<16, SkOperand> values(count);
+        SkInterpolatorBase::Result interpResult = fActive->fInterpolators[inner]->timeToValues(
+            innerTime, values.get());
+        result |= (interpResult != SkInterpolatorBase::kFreezeEnd_Result);
+        if ((transition != SkApply::kTransition_reverse && interpResult == SkInterpolatorBase::kFreezeEnd_Result ||
+                transition == SkApply::kTransition_reverse && fLastTime == 0) && state.fUnpostedEndEvent) {
+//          SkDEBUGF(("interpolate: post on end\n"));
+            state.fUnpostedEndEvent = false;
+            maker.postOnEnd(animate, state.fBegin + state.fDuration);
+            maker.fAdjustedStart = 0;    // !!! left over from synchronizing animation days, undoubtably out of date (and broken)
+        }
+        if (animate->formula.size() > 0) {
+            if (fLastTime > animate->dur)
+                fLastTime = animate->dur;
+            SkTypedArray formulaValues;
+            formulaValues.setCount(count);
+            bool success = animate->fFieldInfo->setValue(maker, &formulaValues, 0, 0, NULL, 
+                animate->getValuesType(), animate->formula);
+            SkASSERT(success);
+            if (restore)
+                save(inner); // save existing value
+            applyValues(inner, formulaValues.begin(), count, animate->getValuesType(), innerTime);
+        } else {
+            if (restore)
+                save(inner); // save existing value
+            applyValues(inner, values.get(), count, animate->getValuesType(), innerTime);
+        }
+    }
+    return result;
+}
+
+void SkApply::initialize() {
+    if (scope == NULL)
+        return;
+    if (scope->isApply() || scope->isDrawable() == false)
+        return;
+    scope->initialize();
+}
+
+void SkApply::onEndElement(SkAnimateMaker& maker) 
+{
+    SkDrawable* scopePtr = scope;
+    while (scopePtr && scopePtr->isApply()) {
+        SkApply* scopedApply = (SkApply*) scopePtr;
+        if (scopedApply->scope == this) {
+            maker.setErrorCode(SkDisplayXMLParserError::kApplyScopesItself);
+            return;
+        }
+        scopePtr = scopedApply->scope;
+    }
+    if (mode == kMode_create)
+        return;
+    if (scope != NULL && steps >= 0 && scope->isApply() == false && scope->isDrawable())
+        scope->setSteps(steps);
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < fAnimators.end(); animPtr++) {
+        SkAnimateBase* anim = *animPtr;
+        //for reusing apply statements with dynamic scope
+        if (anim->fTarget == NULL || anim->fTargetIsScope) {
+            anim->fTargetIsScope = true;
+            if (scope)
+                anim->fTarget = scope;
+            else
+                anim->setTarget(maker);
+            anim->onEndElement(maker);  // allows animate->fFieldInfo to be set
+        }
+        if (scope != NULL && steps >= 0 && anim->fTarget != scope && anim->fTarget->isDrawable())
+            anim->fTarget->setSteps(steps);
+    }
+}
+
+const SkMemberInfo* SkApply::preferredChild(SkDisplayTypes type) {
+    SkASSERT(SkDisplayType::IsAnimate(type) == false);
+    fContainsScope = true;
+    return getMember("scope"); // !!! cwap! need to refer to member through enum like kScope instead
+}
+
+void SkApply::refresh(SkAnimateMaker& maker) {
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < fAnimators.end(); animPtr++) {
+        SkAnimateBase* animate = *animPtr;
+        animate->onEndElement(maker);
+    }
+    if (fActive)
+        fActive->resetInterpolators();
+}
+
+void SkApply::reset() {
+    if (fActive)
+        fActive->resetState();
+}
+
+bool SkApply::resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply) { //   replace to/formula strings in animators of the form xxx.step with the step value, if xxx.step is in scope
+    if (resolveField(maker, apply, &dynamicScope) == false)
+        return true;    // failed
+    SkAnimateBase** endPtr = fAnimators.end();
+    SkAnimateBase** origPtr = ((SkApply*) original)->fAnimators.begin();
+    for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < endPtr; ) {
+        SkAnimateBase* animator = *animPtr++;
+        maker.resolveID(animator, *origPtr++);
+        if (resolveField(maker, this, &animator->target) == false)
+            return true;
+        if (resolveField(maker, this, &animator->from) == false)
+            return true;
+        if (resolveField(maker, this, &animator->to) == false)
+            return true;
+        if (resolveField(maker, this, &animator->formula) == false)
+            return true;
+    }
+//  setEmbedded();
+    onEndElement(maker);
+    return false; // succeeded
+}
+
+bool SkApply::resolveField(SkAnimateMaker& maker, SkDisplayable* parent, SkString* str) {
+    const char* script = str->c_str();
+    if (str->startsWith("#string:") == false)
+        return true;
+    script += sizeof("#string:") - 1;
+    return SkAnimatorScript::EvaluateString(maker, this, parent, script, str);
+}
+
+void SkApply::save(int index) {
+    SkAnimateBase* animate = fActive->fAnimators[index];
+    const SkMemberInfo * info = animate->fFieldInfo;
+    SkDisplayable* target = getTarget(animate);
+//  if (animate->hasExecute())
+//      info = animate->getResolvedInfo();
+    SkDisplayTypes type = (SkDisplayTypes) info->fType;
+    if (type == SkType_MemberFunction)
+        return; // nothing to save
+    size_t size = info->getSize(target);
+    int count = (int) (size / sizeof(SkScalar));
+    bool useLast = true;
+// !!! this all may be unneeded, at least in the dynamic case ??
+    int activeIndex = fActive->fDrawIndex + index;
+    SkTDOperandArray last;
+    if (fActive->fSaveRestore[activeIndex] == NULL) {
+        fActive->fSaveRestore[activeIndex] = new SkOperand[count];
+        useLast = false;
+    } else {
+        last.setCount(count);
+        memcpy(last.begin(), fActive->fSaveRestore[activeIndex], count * sizeof(SkOperand));
+    }
+    if (type != SkType_MemberProperty) {
+        info->getValue(target, fActive->fSaveRestore[activeIndex], count);
+        if (useLast)
+            info->setValue(target, last.begin(), count);
+    } else {
+        SkScriptValue scriptValue;
+        bool success = target->getProperty(info->propertyIndex(), &scriptValue);
+        SkASSERT(success == true);
+        SkASSERT(scriptValue.fType == SkType_Float);
+        fActive->fSaveRestore[activeIndex][0] = scriptValue.fOperand;
+        if (useLast) {
+            SkScriptValue scriptValue;
+            scriptValue.fType = type;
+            scriptValue.fOperand = last[0];
+            target->setProperty(info->propertyIndex(), scriptValue);
+        }
+    }
+// !!!  end of unneeded
+}
+
+bool SkApply::setProperty(int index, SkScriptValue& scriptValue) {
+    switch (index) {
+        case SK_PROPERTY(animator): {
+            SkAnimateBase* animate = (SkAnimateBase*) scriptValue.fOperand.fDisplayable;
+            SkASSERT(animate->isAnimate());
+            *fAnimators.append() = animate;
+            return true; 
+        }
+        case SK_PROPERTY(steps):
+            steps = scriptValue.fOperand.fS32;
+            if (fActive)
+                fActive->setSteps(steps);
+            return true;
+    }
+    return false;
+}
+
+void SkApply::setSteps(int _steps) { 
+    steps = _steps; 
+}
+
+#ifdef SK_DEBUG
+void SkApply::validate() {
+    if (fActive)
+        fActive->validate();
+}
+#endif
+
+
+
diff --git a/src/animator/SkDisplayApply.h b/src/animator/SkDisplayApply.h
new file mode 100644
index 0000000..d51e467
--- /dev/null
+++ b/src/animator/SkDisplayApply.h
@@ -0,0 +1,116 @@
+/* libs/graphics/animator/SkDisplayApply.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 SkDisplayApply_DEFINED
+#define SkDisplayApply_DEFINED
+
+#include "SkAnimateBase.h"
+#include "SkDrawable.h"
+#include "SkIntArray.h"
+
+class SkActive;
+
+class SkApply : public SkDrawable {
+    DECLARE_MEMBER_INFO(Apply);
+public:
+
+    SkApply();
+    virtual ~SkApply();
+
+    enum Transition {
+        kTransition_normal,
+        kTransition_reverse
+    };
+    
+    enum Mode {
+        kMode_create,
+        kMode_immediate,
+        //kMode_once
+    };
+    void activate(SkAnimateMaker& );
+    void append(SkApply* apply);
+    void appendActive(SkActive* );
+    void applyValues(int animatorIndex, SkOperand* values, int count,
+        SkDisplayTypes , SkMSec time);
+    virtual bool contains(SkDisplayable*);
+//  void createActive(SkAnimateMaker& );
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    void disable();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool enable(SkAnimateMaker& );
+    void enableCreate(SkAnimateMaker& );
+    void enableDynamic(SkAnimateMaker& );
+    void endSave(int index);
+    Mode getMode() { return mode; }
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    SkDrawable* getScope() { return scope; }
+    void getStep(SkScriptValue* );
+    SkDrawable* getTarget(SkAnimateBase* );
+    bool hasDelayedAnimator() const;
+    virtual bool hasEnable() const;
+    bool inactivate(SkAnimateMaker& maker);
+    virtual void initialize(); 
+    bool interpolate(SkAnimateMaker& , SkMSec time);
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual const SkMemberInfo* preferredChild(SkDisplayTypes type);
+    void refresh(SkAnimateMaker& );
+    void reset();
+    virtual bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* );
+    bool resolveField(SkAnimateMaker& , SkDisplayable* parent, SkString* str);
+    void save(int index);
+    void setEmbedded() { fEmbedded = true; }
+    virtual bool setProperty(int index, SkScriptValue& );
+    virtual void setSteps(int _steps);
+//  virtual void setTime(SkMSec time);
+#ifdef SK_DEBUG
+    virtual void validate();
+#endif
+private:
+    SkMSec begin;
+    SkBool dontDraw;
+    SkString dynamicScope;
+    SkMSec interval;
+    Mode mode;
+#if 0
+    SkBool pickup;
+#endif
+    SkBool restore;
+    SkDrawable* scope;
+    int32_t steps;
+    Transition transition;
+    SkActive* fActive;
+    SkTDAnimateArray fAnimators;
+//  SkDrawable* fCurrentScope;
+    SkMSec fLastTime;   // used only to return script property time
+    SkTDDrawableArray fScopes;
+    SkBool fAppended : 1;
+    SkBool fContainsScope : 1;
+    SkBool fDeleteScope : 1;
+    SkBool fEmbedded : 1;
+    SkBool fEnabled : 1;
+    SkBool fEnabling : 1; // set if calling interpolate from enable
+    friend class SkActive;
+    friend class SkDisplayList;
+    typedef SkDrawable INHERITED;
+};
+
+#endif // SkDisplayApply_DEFINED
+
+
diff --git a/src/animator/SkDisplayBounds.cpp b/src/animator/SkDisplayBounds.cpp
new file mode 100644
index 0000000..d0499ce
--- /dev/null
+++ b/src/animator/SkDisplayBounds.cpp
@@ -0,0 +1,54 @@
+/* libs/graphics/animator/SkDisplayBounds.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 "SkDisplayBounds.h"
+#include "SkAnimateMaker.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayBounds::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(inval, Boolean)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayBounds);
+
+SkDisplayBounds::SkDisplayBounds() : inval(false) {
+}
+
+bool SkDisplayBounds::draw(SkAnimateMaker& maker) {
+    maker.fDisplayList.fUnionBounds = SkToBool(inval);
+    maker.fDisplayList.fDrawBounds = false;
+    fBounds.setEmpty();
+    bool result = INHERITED::draw(maker);
+    maker.fDisplayList.fUnionBounds = false;
+    maker.fDisplayList.fDrawBounds = true;
+    if (inval && fBounds.isEmpty() == false) {
+        SkIRect& rect = maker.fDisplayList.fInvalBounds;
+        maker.fDisplayList.fHasUnion = true;
+        if (rect.isEmpty())
+            rect = fBounds;
+        else 
+            rect.join(fBounds);
+    }
+    return result;
+}
+
+
+
diff --git a/src/animator/SkDisplayBounds.h b/src/animator/SkDisplayBounds.h
new file mode 100644
index 0000000..6fcd09c
--- /dev/null
+++ b/src/animator/SkDisplayBounds.h
@@ -0,0 +1,33 @@
+/* libs/graphics/animator/SkDisplayBounds.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 SkDisplayBounds_DEFINED
+#define SkDisplayBounds_DEFINED
+
+#include "SkDrawRectangle.h"
+
+class SkDisplayBounds : public SkDrawRect {
+    DECLARE_DISPLAY_MEMBER_INFO(Bounds);
+    SkDisplayBounds();
+    virtual bool draw(SkAnimateMaker& );
+private:
+    SkBool inval;
+    typedef SkDrawRect INHERITED;
+};
+
+#endif // SkDisplayBounds_DEFINED
+
diff --git a/src/animator/SkDisplayEvent.cpp b/src/animator/SkDisplayEvent.cpp
new file mode 100644
index 0000000..6253cdf
--- /dev/null
+++ b/src/animator/SkDisplayEvent.cpp
@@ -0,0 +1,339 @@
+/* libs/graphics/animator/SkDisplayEvent.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 "SkDisplayEvent.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayApply.h"
+#include "SkDisplayInput.h"
+#include "SkDisplayList.h"
+#ifdef SK_DEBUG
+#include "SkDump.h"
+#endif
+#include "SkEvent.h"
+#include "SkDisplayInput.h"
+#include "SkKey.h"
+#include "SkMetaData.h"
+#include "SkScript.h"
+#include "SkUtils.h"
+
+enum SkDisplayEvent_Properties {
+    SK_PROPERTY(key),
+    SK_PROPERTY(keys)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayEvent::fInfo[] = {
+    SK_MEMBER(code, EventCode),
+    SK_MEMBER(disable, Boolean),
+    SK_MEMBER_PROPERTY(key, String), // a single key (also last key pressed)
+    SK_MEMBER_PROPERTY(keys, String), // a single key or dash-delimited range of keys
+    SK_MEMBER(kind, EventKind),
+    SK_MEMBER(target, String),
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayEvent);
+
+SkDisplayEvent::SkDisplayEvent() : code((SkKey) -1), disable(false),
+    kind(kUser), x(0), y(0), fLastCode((SkKey) -1), fMax((SkKey) -1), fTarget(NULL) {
+}
+
+SkDisplayEvent::~SkDisplayEvent() {
+    deleteMembers();
+}
+
+bool SkDisplayEvent::add(SkAnimateMaker& , SkDisplayable* child) { 
+    *fChildren.append() = child; 
+    return true; 
+}
+
+bool SkDisplayEvent::contains(SkDisplayable* match) {
+    for (int index = 0; index < fChildren.count(); index++) {
+        if (fChildren[index] == match || fChildren[index]->contains(match))
+            return true;
+    }
+    return false;
+}
+
+SkDisplayable* SkDisplayEvent::contains(const SkString& match) {
+    for (int index = 0; index < fChildren.count(); index++) {
+        SkDisplayable* child = fChildren[index];
+        if (child->contains(match))
+            return child;
+    }
+    return NULL;
+}
+
+void SkDisplayEvent::deleteMembers() {
+    for (int index = 0; index < fChildren.count(); index++) {
+        SkDisplayable* evt = fChildren[index];
+        delete evt;
+    }
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayEvent::dumpEvent(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkString str;
+    SkDump::GetEnumString(SkType_EventKind, kind, &str);
+    SkDebugf("kind=\"%s\" ", str.c_str());
+    if (kind == SkDisplayEvent::kKeyPress || kind == SkDisplayEvent::kKeyPressUp) {
+        if (code >= 0)
+            SkDump::GetEnumString(SkType_EventCode, code, &str);
+        else
+            str.set("none");
+        SkDebugf("code=\"%s\" ", str.c_str());
+    }
+    if (kind == SkDisplayEvent::kKeyChar) {
+        if (fMax != (SkKey) -1 && fMax != code)
+            SkDebugf("keys=\"%c - %c\" ", code, fMax);
+        else
+            SkDebugf("key=\"%c\" ", code);
+    }
+    if (fTarget != NULL) {
+        SkDebugf("target=\"%s\" ", fTarget->id);
+    }
+    if (kind >= SkDisplayEvent::kMouseDown && kind <= SkDisplayEvent::kMouseUp) {
+#ifdef SK_CAN_USE_FLOAT
+        SkDebugf("x=\"%g\" y=\"%g\" ", SkScalarToFloat(x), SkScalarToFloat(y));
+#else
+        SkDebugf("x=\"%x\" y=\"%x\" ", x, y);
+#endif
+    }
+    if (disable)
+        SkDebugf("disable=\"true\" ");
+    SkDebugf("/>\n");
+}
+#endif
+
+bool SkDisplayEvent::enableEvent(SkAnimateMaker& maker) 
+{
+    maker.fActiveEvent = this;
+    if (fChildren.count() == 0)
+        return false;
+    if (disable)
+        return false;
+#ifdef SK_DUMP_ENABLED
+    if (maker.fDumpEvents) {
+        SkDebugf("enable: ");
+        dumpEvent(&maker);
+    }
+#endif
+    SkDisplayList& displayList = maker.fDisplayList;
+    for (int index = 0; index < fChildren.count(); index++) {
+        SkDisplayable* displayable = fChildren[index];
+        if (displayable->isGroup()) {
+            SkTDDrawableArray* parentList = displayList.getDrawList();
+            *parentList->append() = (SkDrawable*) displayable;  // make it findable before children are enabled
+        }
+        if (displayable->enable(maker))
+            continue;
+        if (maker.hasError()) 
+            return true;
+        if (displayable->isDrawable() == false)
+            return true;    // error
+        SkDrawable* drawable = (SkDrawable*) displayable;
+        SkTDDrawableArray* parentList = displayList.getDrawList();
+        *parentList->append() = drawable;
+    }
+    return false;
+}
+
+bool SkDisplayEvent::getProperty(int index, SkScriptValue* value) const {
+    switch (index) {
+        case SK_PROPERTY(key):
+        case SK_PROPERTY(keys): {
+            value->fType = SkType_String;
+            char scratch[8];
+            SkKey convert = index == SK_PROPERTY(keys) ? code : fLastCode;
+            size_t size = convert > 0 ? SkUTF8_FromUnichar(convert, scratch) : 0;
+            fKeyString.set(scratch, size);
+            value->fOperand.fString = &fKeyString;
+            if (index != SK_PROPERTY(keys) || fMax == (SkKey) -1 || fMax == code)
+                break;
+            value->fOperand.fString->append("-");
+            size = SkUTF8_FromUnichar(fMax, scratch);
+            value->fOperand.fString->append(scratch, size);
+            } break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+void SkDisplayEvent::onEndElement(SkAnimateMaker& maker)
+{
+    if (kind == kUser)
+        return;
+    maker.fEvents.addEvent(this);
+    if (kind == kOnEnd) {
+        bool found = maker.find(target.c_str(), &fTarget);
+        SkASSERT(found);
+        SkASSERT(fTarget && fTarget->isAnimate());
+        SkAnimateBase* animate = (SkAnimateBase*) fTarget;
+        animate->setHasEndEvent();
+    }
+}
+
+void SkDisplayEvent::populateInput(SkAnimateMaker& maker, const SkEvent& fEvent) {
+    const SkMetaData& meta = fEvent.getMetaData();
+    SkMetaData::Iter iter(meta);
+    SkMetaData::Type    type;
+    int number;
+    const char* name;
+    while ((name = iter.next(&type, &number)) != NULL) {
+        if (name[0] == '\0')
+            continue;
+        SkDisplayable* displayable;
+        SkInput* input;
+        for (int index = 0; index < fChildren.count(); index++) {
+            displayable = fChildren[index];
+            if (displayable->getType() != SkType_Input)
+                continue;
+            input = (SkInput*) displayable;
+            if (input->name.equals(name))
+                goto found;
+        }
+        if (!maker.find(name, &displayable) || displayable->getType() != SkType_Input)
+            continue;
+        input = (SkInput*) displayable;
+    found:
+        switch (type) {
+            case SkMetaData::kS32_Type:
+                meta.findS32(name, &input->fInt);
+                break;
+            case SkMetaData::kScalar_Type:
+                meta.findScalar(name, &input->fFloat);
+                break;
+            case SkMetaData::kPtr_Type:
+                SkASSERT(0);
+                break; // !!! not handled for now
+            case SkMetaData::kString_Type:
+                input->string.set(meta.findString(name));
+                break;
+            default:
+                SkASSERT(0);
+        }
+    }
+    // re-evaluate all animators that may have built their values from input strings
+    for (SkDisplayable** childPtr = fChildren.begin(); childPtr < fChildren.end(); childPtr++) {
+        SkDisplayable* displayable = *childPtr;
+        if (displayable->isApply() == false)
+            continue;
+        SkApply* apply = (SkApply*) displayable;
+        apply->refresh(maker);
+    }
+}
+
+bool SkDisplayEvent::setProperty(int index, SkScriptValue& value) {
+    SkASSERT(index == SK_PROPERTY(key) || index == SK_PROPERTY(keys));
+    SkASSERT(value.fType == SkType_String);
+    SkString* string = value.fOperand.fString;
+    const char* chars = string->c_str();
+    int count = SkUTF8_CountUnichars(chars);
+    SkASSERT(count >= 1);
+    code = (SkKey) SkUTF8_NextUnichar(&chars);
+    fMax = code;
+    SkASSERT(count == 1 || index == SK_PROPERTY(keys));
+    if (--count > 0) {
+        SkASSERT(*chars == '-');
+        chars++;
+        fMax = (SkKey) SkUTF8_NextUnichar(&chars);
+        SkASSERT(fMax >= code);
+    }
+    return true;
+}
+
+#ifdef ANDROID
+
+#include "SkMetaData.h"
+#include "SkParse.h"
+#include "SkTextBox.h"
+#include "SkXMLWriter.h"
+
+void SkMetaData::setPtr(char const*, void* ) {}
+void SkMetaData::setS32(char const*, int ) {}
+bool SkEventSink::doEvent(SkEvent const& ) { return false; }
+bool SkXMLParser::parse(SkStream& ) { return false; }
+SkXMLParserError::SkXMLParserError( ) {}
+void SkEvent::setType(char const*, unsigned long ) {}
+bool SkEvent::PostTime(SkEvent*, unsigned int, unsigned int ) { return false; }
+SkEvent::SkEvent(char const* ) {}
+SkEvent::SkEvent(SkEvent const& ) {}
+SkEvent::SkEvent( ) {}
+SkEvent::~SkEvent( ) {}
+bool SkEventSink::onQuery(SkEvent* ) { return false; }
+SkEventSink::SkEventSink( ) {}
+SkEventSink::~SkEventSink( ) {}
+bool SkXMLParser::parse(char const*, unsigned long ) { return false; }
+bool SkXMLParser::parse(SkDOM const&, SkDOMNode const* ) { return false; }
+bool SkEvent::Post(SkEvent*, unsigned int, unsigned int ) { return false; }
+void SkParse::UnitTest( ) {}
+const char* SkMetaData::findString(char const*) const {return 0;}
+bool SkMetaData::findPtr(char const*, void**) const {return false;}
+bool SkMetaData::findS32(char const*, int*) const {return false;}
+bool SkEvent::isType(char const*, unsigned long) const { return false; }
+void SkMetaData::setString(char const*, char const* ) {}
+const char* SkParse::FindNamedColor(char const*, unsigned long, unsigned int* ) {return false; }
+const char* SkMetaData::Iter::next(SkMetaData::Type*, int* ) { return false; }
+SkMetaData::Iter::Iter(SkMetaData const& ) {}
+bool SkMetaData::findScalar(char const*, int*) const {return false;}
+void SkMetaData::reset( ) {}
+void SkEvent::setType(SkString const& ) {}
+bool SkMetaData::findBool(char const*, bool*) const {return false;}
+void SkEvent::getType(SkString*) const {}
+bool SkXMLParser::endElement(char const* ) { return false; }
+bool SkXMLParser::addAttribute(char const*, char const* ) { return false;}
+bool SkXMLParser::startElement(char const* ) { return false;}
+bool SkXMLParser::text(char const*, int ) { return false;}
+bool SkXMLParser::onText(char const*, int ) { return false;}
+SkXMLParser::SkXMLParser(SkXMLParserError* ) {}
+SkXMLParser::~SkXMLParser( ) {}
+SkXMLParserError::~SkXMLParserError( ) {}
+void SkXMLParserError::getErrorString(SkString*) const {}
+void SkTextBox::setSpacing(int, int ) {}
+void SkTextBox::setSpacingAlign(SkTextBox::SpacingAlign ) {}
+void SkTextBox::draw(SkCanvas*, char const*, unsigned long, SkPaint const& ) {}
+void SkTextBox::setBox(SkRect const& ) {}
+void SkTextBox::setMode(SkTextBox::Mode ) {}
+SkTextBox::SkTextBox( ) {}
+void SkMetaData::setScalar(char const*, int ) {}
+const char* SkParse::FindScalar(char const*, int* ) {return 0; }
+const char* SkParse::FindScalars(char const*, int*, int ) {return 0; }
+const char* SkParse::FindHex(char const*, unsigned int* ) {return 0; }
+const char* SkParse::FindS32(char const*, int* ) {return 0; }
+void SkXMLWriter::addAttribute(char const*, char const* ) {}
+void SkXMLWriter::startElement(char const* ) {}
+void SkXMLWriter::doEnd(SkXMLWriter::Elem* ) {}
+SkXMLWriter::Elem* SkXMLWriter::getEnd( ) { return 0; }
+bool SkXMLWriter::doStart(char const*, unsigned long ) { return false; }
+SkXMLWriter::SkXMLWriter(bool ) {}
+SkXMLWriter::~SkXMLWriter( ) {}
+SkMetaData::SkMetaData() {}
+SkMetaData::~SkMetaData() {}
+bool SkEventSink::onEvent(SkEvent const&) {return false;}
+bool SkXMLParser::onEndElement(char const*) {return false;}
+bool SkXMLParser::onAddAttribute(char const*, char const*) {return false;}
+bool SkXMLParser::onStartElement(char const*) {return false;}
+void SkXMLWriter::writeHeader() {}
+
+#endif
diff --git a/src/animator/SkDisplayEvent.h b/src/animator/SkDisplayEvent.h
new file mode 100644
index 0000000..837cfec
--- /dev/null
+++ b/src/animator/SkDisplayEvent.h
@@ -0,0 +1,75 @@
+/* libs/graphics/animator/SkDisplayEvent.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 SkDisplayEvent_DEFINED
+#define SkDisplayEvent_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkIntArray.h"
+#include "SkKey.h"
+
+class SkEvent;
+
+class SkDisplayEvent : public SkDisplayable {
+    DECLARE_DISPLAY_MEMBER_INFO(Event);
+    enum Kind {
+        kNo_kind,
+        kKeyChar,
+        kKeyPress,
+        kKeyPressUp,    //i assume the order here is intended to match with skanimatorscript.cpp
+        kMouseDown,
+        kMouseDrag,
+        kMouseMove,
+        kMouseUp,
+        kOnEnd,
+        kOnload,
+        kUser
+    };
+    SkDisplayEvent();
+    virtual ~SkDisplayEvent();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool contains(SkDisplayable*);
+    virtual SkDisplayable* contains(const SkString& );
+#ifdef SK_DEBUG
+    void dumpEvent(SkAnimateMaker* );
+#endif
+    bool enableEvent(SkAnimateMaker& );
+    virtual bool getProperty(int index, SkScriptValue* ) const;
+    virtual void onEndElement(SkAnimateMaker& maker);
+    void populateInput(SkAnimateMaker& , const SkEvent& fEvent);
+    virtual bool setProperty(int index, SkScriptValue& );
+protected:
+    SkKey code;
+    SkBool disable;
+    Kind kind;
+    SkString target;
+    SkScalar x;
+    SkScalar y;
+    SkTDDisplayableArray fChildren;
+    mutable SkString fKeyString;
+    SkKey fLastCode; // last key to trigger this event
+    SkKey fMax; // if the code expresses a range
+    SkDisplayable* fTarget; // used by onEnd
+private:
+    void deleteMembers();
+    friend class SkEvents;
+    typedef SkDisplayable INHERITED;
+};
+
+#endif // SkDisplayEvent_DEFINED
+
diff --git a/src/animator/SkDisplayEvents.cpp b/src/animator/SkDisplayEvents.cpp
new file mode 100644
index 0000000..e6a01ba
--- /dev/null
+++ b/src/animator/SkDisplayEvents.cpp
@@ -0,0 +1,121 @@
+/* libs/graphics/animator/SkDisplayEvents.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 "SkDisplayEvents.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimator.h"
+#include "SkDisplayEvent.h"
+#include "SkDisplayMovie.h"
+#include "SkDrawable.h"
+#ifdef SK_DEBUG
+#include "SkDump.h"
+#endif
+
+SkEventState::SkEventState() : fCode(0), fDisable(false), fDisplayable(0), fX(0), fY(0) {
+}
+
+SkEvents::SkEvents() {
+}
+
+SkEvents::~SkEvents() {
+}
+
+bool SkEvents::doEvent(SkAnimateMaker& maker, SkDisplayEvent::Kind kind, SkEventState* state) {
+/*#ifdef SK_DUMP_ENABLED
+    if (maker.fDumpEvents) {
+        SkDebugf("doEvent: ");
+        SkString str;
+        SkDump::GetEnumString(SkType_EventKind, kind, &str);
+        SkDebugf("kind=%s ", str.c_str());
+        if (state && state->fDisplayable)
+            state->fDisplayable->SkDisplayable::dump(&maker);
+        else
+            SkDebugf("\n");
+    }
+#endif*/
+    bool handled = false;
+    SkDisplayable** firstMovie = maker.fMovies.begin();
+    SkDisplayable** endMovie = maker.fMovies.end();
+    for (SkDisplayable** ptr = firstMovie; ptr < endMovie; ptr++) {
+        SkDisplayMovie* movie = (SkDisplayMovie*) *ptr;
+        if (kind != SkDisplayEvent::kOnload)
+            movie->doEvent(kind, state);
+    }
+    SkDisplayable* displayable = state ? state->fDisplayable : NULL;
+    int keyCode = state ? state->fCode : 0;
+    int count = fEvents.count();
+    for (int index = 0; index < count; index++) {
+        SkDisplayEvent* evt = fEvents[index];
+        if (evt->disable)
+            continue;
+        if (evt->kind != kind)
+            continue;
+        if (evt->code != (SkKey) -1) {
+            if ((int) evt->code > keyCode || (int) (evt->fMax != (SkKey) -1 ? evt->fMax : evt->code) < keyCode)
+                continue;
+            evt->fLastCode = (SkKey) keyCode;
+        }
+        if (evt->fTarget != NULL && evt->fTarget != displayable)
+            continue;
+        if (state == NULL || state->fDisable == 0) {
+            if (kind >= SkDisplayEvent::kMouseDown && kind <= SkDisplayEvent::kMouseUp) {
+                evt->x = state->fX;
+                evt->y = state->fY;
+            }
+            if (evt->enableEvent(maker))
+                fError = true;
+        }
+        handled = true;
+    }
+    return handled;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkEvents::dump(SkAnimateMaker& maker) {
+    int index;
+    SkTDDrawableArray& drawArray = maker.fDisplayList.fDrawList;
+    int count = drawArray.count();
+    for (index = 0; index < count; index++) {
+        SkDrawable* drawable = drawArray[index];
+        drawable->dumpEvents(); 
+    }
+    count = fEvents.count();
+    for (index = 0; index < count; index++) {
+        SkDisplayEvent* evt = fEvents[index];
+        evt->dumpEvent(&maker);
+    }
+}
+#endif
+
+// currently this only removes onLoad events
+void SkEvents::removeEvent(SkDisplayEvent::Kind kind, SkEventState* state) {
+    int keyCode = state ? state->fCode : 0;
+    SkDisplayable* displayable = state ? state->fDisplayable : NULL;
+    for (SkDisplayEvent** evtPtr = fEvents.begin(); evtPtr < fEvents.end(); evtPtr++) {
+        SkDisplayEvent* evt = *evtPtr;
+        if (evt->kind != kind)
+            continue;
+        if (evt->code != (SkKey) -1) {
+            if ((int) evt->code > keyCode || (int) (evt->fMax != (SkKey) -1 ? evt->fMax : evt->code) < keyCode)
+                continue;
+        }
+        if (evt->fTarget != NULL && evt->fTarget != displayable)
+            continue;
+        int index = fEvents.find(evt);
+        fEvents.remove(index);
+    }
+}
diff --git a/src/animator/SkDisplayEvents.h b/src/animator/SkDisplayEvents.h
new file mode 100644
index 0000000..73721d4
--- /dev/null
+++ b/src/animator/SkDisplayEvents.h
@@ -0,0 +1,51 @@
+/* libs/graphics/animator/SkDisplayEvents.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 SkDisplayEvents_DEFINED
+#define SkDisplayEvents_DEFINED
+
+#include "SkEvent.h"
+#include "SkDisplayEvent.h"
+
+struct SkEventState {
+    SkEventState();
+    int fCode;
+    SkBool fDisable;
+    SkDisplayable* fDisplayable;
+    SkScalar fX;
+    SkScalar fY;
+};
+
+class SkEvents {
+public:
+    SkEvents();
+    ~SkEvents();
+    void addEvent(SkDisplayEvent* evt) { *fEvents.append() = evt; }
+    bool doEvent(SkAnimateMaker& , SkDisplayEvent::Kind , SkEventState* );
+#ifdef SK_DUMP_ENABLED
+    void dump(SkAnimateMaker& );
+#endif
+    void reset() { fEvents.reset(); }
+    void removeEvent(SkDisplayEvent::Kind kind, SkEventState* );
+private:
+    SkTDDisplayEventArray fEvents;
+    SkBool fError;
+    friend class SkDisplayXMLParser;
+};
+
+#endif // SkDisplayEvents_DEFINED
+
diff --git a/src/animator/SkDisplayInclude.cpp b/src/animator/SkDisplayInclude.cpp
new file mode 100644
index 0000000..26b1e69
--- /dev/null
+++ b/src/animator/SkDisplayInclude.cpp
@@ -0,0 +1,67 @@
+/* libs/graphics/animator/SkDisplayInclude.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 "SkDisplayInclude.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimator.h"
+
+#if 0
+#undef SK_MEMBER
+#define SK_MEMBER(_member, _type) \
+    { #_member, SK_OFFSETOF(BASE_CLASS::_A, _member), SkType_##_type, \
+    sizeof(((BASE_CLASS::_A*) 0)->_member) / sizeof(SkScalar) }
+#endif
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkInclude::fInfo[] = {
+    SK_MEMBER(src, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkInclude);
+
+//SkInclude::SkInclude() {
+//  src.init();
+//}
+
+//SkInclude::~SkInclude() {
+//  src.unref();
+//}
+
+bool SkInclude::enable(SkAnimateMaker & ) {
+    return true;
+}
+
+bool SkInclude::hasEnable() const {
+    return true;
+}
+
+void SkInclude::onEndElement(SkAnimateMaker& maker) {
+    maker.fInInclude = true;
+    if (src.size() == 0 || maker.decodeURI(src.c_str()) == false) {
+        if (maker.getErrorCode() != SkXMLParserError::kNoError || maker.getNativeCode() != -1) {
+            maker.setInnerError(&maker, src);
+            maker.setErrorCode(SkDisplayXMLParserError::kInInclude);
+        } else {
+            maker.setErrorNoun(src);
+            maker.setErrorCode(SkDisplayXMLParserError::kIncludeNameUnknownOrMissing);
+        }
+    }
+    maker.fInInclude = false;
+}
diff --git a/src/animator/SkDisplayInclude.h b/src/animator/SkDisplayInclude.h
new file mode 100644
index 0000000..81ad76a
--- /dev/null
+++ b/src/animator/SkDisplayInclude.h
@@ -0,0 +1,34 @@
+/* libs/graphics/animator/SkDisplayInclude.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 SkDisplayInclude_DEFINED
+#define SkDisplayInclude_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+
+class SkInclude : public SkDisplayable {
+    DECLARE_MEMBER_INFO(Include);
+    virtual void onEndElement(SkAnimateMaker & );
+    virtual bool enable(SkAnimateMaker & );
+    virtual bool hasEnable() const;
+protected:
+    SkString src;
+};
+
+#endif // SkDisplayInclude_DEFINED
+
diff --git a/src/animator/SkDisplayInput.cpp b/src/animator/SkDisplayInput.cpp
new file mode 100644
index 0000000..3688930
--- /dev/null
+++ b/src/animator/SkDisplayInput.cpp
@@ -0,0 +1,63 @@
+/* libs/graphics/animator/SkDisplayInput.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 "SkDisplayInput.h"
+
+enum SkInput_Properties {
+    SK_PROPERTY(initialized)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkInput::fInfo[] = {
+    SK_MEMBER_ALIAS(float, fFloat, Float),
+    SK_MEMBER_PROPERTY(initialized, Boolean),
+    SK_MEMBER_ALIAS(int, fInt, Int),
+    SK_MEMBER(name, String),
+    SK_MEMBER(string, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkInput);
+
+SkInput::SkInput() : fInt((int) SK_NaN32), fFloat(SK_ScalarNaN) {}
+
+SkDisplayable* SkInput::contains(const SkString& string) {
+    return string.equals(name) ? this : NULL;
+}
+
+bool SkInput::enable(SkAnimateMaker & ) {
+    return true;
+}
+
+bool SkInput::getProperty(int index, SkScriptValue* value) const {
+    switch (index) {
+        case SK_PROPERTY(initialized):
+            value->fType = SkType_Boolean;
+            value->fOperand.fS32 = fInt != (int) SK_NaN32 ||
+                SkScalarIsNaN(fFloat) == false || string.size() > 0;
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+ 
+bool SkInput::hasEnable() const {
+    return true;
+}
diff --git a/src/animator/SkDisplayInput.h b/src/animator/SkDisplayInput.h
new file mode 100644
index 0000000..ac7c7c1
--- /dev/null
+++ b/src/animator/SkDisplayInput.h
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkDisplayInput.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 SkDisplayInput_DEFINED
+#define SkDisplayInput_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+
+class SkInput : public SkDisplayable {
+    DECLARE_MEMBER_INFO(Input);
+    SkInput();
+    virtual SkDisplayable* contains(const SkString& );
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool enable(SkAnimateMaker & );
+    virtual bool hasEnable() const;
+protected:
+    SkString name;
+    int32_t  fInt;
+    SkScalar fFloat;
+    SkString string;
+private:
+    friend class SkDisplayEvent;
+    friend class SkPost;
+};
+
+#endif // SkDisplayInput_DEFINED
+
diff --git a/src/animator/SkDisplayList.cpp b/src/animator/SkDisplayList.cpp
new file mode 100644
index 0000000..57f29f7
--- /dev/null
+++ b/src/animator/SkDisplayList.cpp
@@ -0,0 +1,168 @@
+/* libs/graphics/animator/SkDisplayList.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 "SkDisplayList.h"
+#include "SkAnimateActive.h"
+#include "SkAnimateBase.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayApply.h"
+#include "SkDrawable.h"
+#include "SkDrawGroup.h"
+#include "SkDrawMatrix.h"
+#include "SkInterpolator.h"
+#include "SkTime.h"
+
+SkDisplayList::SkDisplayList() : fDrawBounds(true), fUnionBounds(false), fInTime(0) {
+}
+
+SkDisplayList::~SkDisplayList() {
+}
+
+void SkDisplayList::append(SkActive* active) {
+    *fActiveList.append() = active;
+}
+
+bool SkDisplayList::draw(SkAnimateMaker& maker, SkMSec inTime) {
+    validate();
+    fInTime = inTime;
+    bool result = false;
+    fInvalBounds.setEmpty();
+    if (fDrawList.count()) {
+        for (SkActive** activePtr = fActiveList.begin(); activePtr < fActiveList.end(); activePtr++) {
+            SkActive* active = *activePtr;
+            active->reset();
+        }
+        for (int index = 0; index < fDrawList.count(); index++) {
+            SkDrawable* draw = fDrawList[index];
+            draw->initialize(); // allow matrices to reset themselves
+            SkASSERT(draw->isDrawable());
+            validate();
+            result |= draw->draw(maker);
+        }
+    }
+    validate();
+    return result;
+}
+
+int SkDisplayList::findGroup(SkDrawable* match, SkTDDrawableArray** list,
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) { 
+    *parent = NULL;
+    *list = &fDrawList;
+    *grandList = &fDrawList;
+    return SearchForMatch(match, list, parent, found, grandList);
+}
+
+void SkDisplayList::hardReset() {
+    fDrawList.reset();
+    fActiveList.reset();
+}
+
+bool SkDisplayList::onIRect(const SkIRect& r) {
+    fBounds = r;
+    return fDrawBounds;
+}
+
+int SkDisplayList::SearchForMatch(SkDrawable* match, SkTDDrawableArray** list,
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) { 
+    *found = NULL;
+    for (int index = 0; index < (*list)->count(); index++) {
+        SkDrawable* draw = (**list)[index];
+        if (draw == match)
+            return index;
+        if (draw->isApply()) {
+            SkApply* apply = (SkApply*) draw;
+            if (apply->scope == match)
+                return index;
+            if (apply->scope->isGroup() && SearchGroupForMatch(apply->scope, match, list, parent, found, grandList, index))
+                return index;
+            if (apply->mode == SkApply::kMode_create) {
+                for (SkDrawable** ptr = apply->fScopes.begin(); ptr < apply->fScopes.end(); ptr++) {
+                    SkDrawable* scope = *ptr;
+                    if (scope == match)
+                        return index;
+                    //perhaps should call SearchGroupForMatch here as well (on scope)
+                }
+            } 
+        }
+        if (draw->isGroup() && SearchGroupForMatch(draw, match, list, parent, found, grandList, index)) 
+            return index;
+        
+    }
+    return -1;
+}
+
+bool SkDisplayList::SearchGroupForMatch(SkDrawable* draw, SkDrawable* match, SkTDDrawableArray** list,
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList, int &index) {
+            SkGroup* group = (SkGroup*) draw;
+            if (group->getOriginal() == match)
+                return true;
+            SkTDDrawableArray* saveList = *list;
+            int groupIndex = group->findGroup(match, list, parent, found, grandList);
+            if (groupIndex >= 0) {
+                *found = group;
+                index = groupIndex;
+                return true;
+            }
+            *list = saveList;
+            return false;
+        }
+
+void SkDisplayList::reset() {
+    for (int index = 0; index < fDrawList.count(); index++) {
+        SkDrawable* draw = fDrawList[index];
+        if (draw->isApply() == false)
+            continue;
+        SkApply* apply = (SkApply*) draw;
+        apply->reset();
+    }           
+}
+
+void SkDisplayList::remove(SkActive* active) {
+    int index = fActiveList.find(active);
+    SkASSERT(index >= 0);
+    fActiveList.remove(index);  // !!! could use shuffle instead
+    SkASSERT(fActiveList.find(active) < 0);
+}
+
+#ifdef SK_DUMP_ENABLED
+int SkDisplayList::fDumpIndex;
+int SkDisplayList::fIndent;
+
+void SkDisplayList::dump(SkAnimateMaker* maker) {
+    fIndent = 0;
+    dumpInner(maker);
+}
+
+void SkDisplayList::dumpInner(SkAnimateMaker* maker) {
+    for (int index = 0; index < fDrawList.count(); index++) {
+        fDumpIndex = index;
+        fDrawList[fDumpIndex]->dump(maker);
+    }
+}
+
+#endif
+
+#ifdef SK_DEBUG
+void SkDisplayList::validate() {
+    for (int index = 0; index < fDrawList.count(); index++) {
+        SkDrawable* draw = fDrawList[index];
+        draw->validate();
+    }
+}
+#endif
+
+
diff --git a/src/animator/SkDisplayList.h b/src/animator/SkDisplayList.h
new file mode 100644
index 0000000..3743e35
--- /dev/null
+++ b/src/animator/SkDisplayList.h
@@ -0,0 +1,79 @@
+/* libs/graphics/animator/SkDisplayList.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 SkDisplayList_DEFINED
+#define SkDisplayList_DEFINED
+
+#include "SkOperand.h"
+#include "SkIntArray.h"
+#include "SkBounder.h"
+#include "SkRect.h"
+
+class SkAnimateMaker;
+class SkActive;
+class SkApply;
+class SkDrawable;
+class SkGroup;
+
+class SkDisplayList : public SkBounder {
+public:
+    SkDisplayList();
+    virtual ~SkDisplayList();
+    void append(SkActive* );
+    void clear() { fDrawList.reset(); }
+    int count() { return fDrawList.count(); }
+    bool draw(SkAnimateMaker& , SkMSec time);
+#ifdef SK_DUMP_ENABLED
+    void dump(SkAnimateMaker* maker);
+    void dumpInner(SkAnimateMaker* maker);
+    static int fIndent;
+    static int fDumpIndex;
+#endif
+    int findGroup(SkDrawable* match, SkTDDrawableArray** list, 
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList); 
+    SkDrawable* get(int index) { return fDrawList[index]; }
+    SkMSec getTime() { return fInTime; }
+    SkTDDrawableArray* getDrawList() { return &fDrawList; }
+    void hardReset();
+    virtual bool onIRect(const SkIRect& r);
+    void reset();
+    void remove(SkActive* );
+#ifdef SK_DEBUG
+    void validate();
+#else
+    void validate() {}
+#endif
+    static int SearchForMatch(SkDrawable* match, SkTDDrawableArray** list,
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList);
+    static bool SearchGroupForMatch(SkDrawable* draw, SkDrawable* match, 
+        SkTDDrawableArray** list, SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList, 
+        int &index);
+public:
+    SkIRect fBounds;
+    SkIRect fInvalBounds;
+    bool fDrawBounds;
+    bool fHasUnion;
+    bool fUnionBounds;
+private:
+    SkTDDrawableArray fDrawList;
+    SkTDActiveArray fActiveList;
+    SkMSec fInTime;
+    friend class SkEvents;
+};
+
+#endif // SkDisplayList_DEFINED
+
diff --git a/src/animator/SkDisplayMath.cpp b/src/animator/SkDisplayMath.cpp
new file mode 100644
index 0000000..66bd1f8
--- /dev/null
+++ b/src/animator/SkDisplayMath.cpp
@@ -0,0 +1,248 @@
+/* libs/graphics/animator/SkDisplayMath.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 "SkDisplayMath.h"
+
+enum SkDisplayMath_Properties {
+    SK_PROPERTY(E),
+    SK_PROPERTY(LN10),
+    SK_PROPERTY(LN2),
+    SK_PROPERTY(LOG10E),
+    SK_PROPERTY(LOG2E),
+    SK_PROPERTY(PI),
+    SK_PROPERTY(SQRT1_2),
+    SK_PROPERTY(SQRT2)
+};
+
+const SkScalar SkDisplayMath::gConstants[] = {
+#ifdef SK_SCALAR_IS_FLOAT
+    2.718281828f,   // E
+    2.302585093f,   // LN10
+    0.693147181f,   // LN2
+    0.434294482f,   // LOG10E
+    1.442695041f,   // LOG2E
+    3.141592654f,   // PI
+    0.707106781f,   // SQRT1_2
+    1.414213562f        // SQRT2 
+#else
+    0x2B7E1,    // E
+    0x24D76,    // LN10
+    0xB172,     // LN2
+    0x6F2E,     // LOG10E
+    0x17154,    // LOG2E
+    0x3243F,    // PI
+    0xB505,     // SQRT1_2
+    0x16A0A // SQRT2
+#endif
+};
+
+enum SkDisplayMath_Functions {
+    SK_FUNCTION(abs),
+    SK_FUNCTION(acos),
+    SK_FUNCTION(asin),
+    SK_FUNCTION(atan),
+    SK_FUNCTION(atan2),
+    SK_FUNCTION(ceil),
+    SK_FUNCTION(cos),
+    SK_FUNCTION(exp),
+    SK_FUNCTION(floor),
+    SK_FUNCTION(log),
+    SK_FUNCTION(max),
+    SK_FUNCTION(min),
+    SK_FUNCTION(pow),
+    SK_FUNCTION(random),
+    SK_FUNCTION(round),
+    SK_FUNCTION(sin),
+    SK_FUNCTION(sqrt),
+    SK_FUNCTION(tan)
+};
+
+const SkFunctionParamType SkDisplayMath::fFunctionParameters[] = {
+    (SkFunctionParamType) SkType_Float, // abs
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // acos
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // asin
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // atan
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // atan2
+    (SkFunctionParamType) SkType_Float,
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // ceil
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // cos
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // exp
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // floor
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // log
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Array, // max
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Array, // min
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // pow
+    (SkFunctionParamType) SkType_Float,
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // random
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // round
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // sin
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // sqrt
+    (SkFunctionParamType) 0,
+    (SkFunctionParamType) SkType_Float, // tan
+    (SkFunctionParamType) 0
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayMath::fInfo[] = {
+    SK_MEMBER_PROPERTY(E, Float),
+    SK_MEMBER_PROPERTY(LN10, Float),
+    SK_MEMBER_PROPERTY(LN2, Float),
+    SK_MEMBER_PROPERTY(LOG10E, Float),
+    SK_MEMBER_PROPERTY(LOG2E, Float),
+    SK_MEMBER_PROPERTY(PI, Float),
+    SK_MEMBER_PROPERTY(SQRT1_2, Float),
+    SK_MEMBER_PROPERTY(SQRT2, Float),
+    SK_MEMBER_FUNCTION(abs, Float),
+    SK_MEMBER_FUNCTION(acos, Float),
+    SK_MEMBER_FUNCTION(asin, Float),
+    SK_MEMBER_FUNCTION(atan, Float),
+    SK_MEMBER_FUNCTION(atan2, Float),
+    SK_MEMBER_FUNCTION(ceil, Float),
+    SK_MEMBER_FUNCTION(cos, Float),
+    SK_MEMBER_FUNCTION(exp, Float),
+    SK_MEMBER_FUNCTION(floor, Float),
+    SK_MEMBER_FUNCTION(log, Float),
+    SK_MEMBER_FUNCTION(max, Float),
+    SK_MEMBER_FUNCTION(min, Float),
+    SK_MEMBER_FUNCTION(pow, Float),
+    SK_MEMBER_FUNCTION(random, Float),
+    SK_MEMBER_FUNCTION(round, Float),
+    SK_MEMBER_FUNCTION(sin, Float),
+    SK_MEMBER_FUNCTION(sqrt, Float),
+    SK_MEMBER_FUNCTION(tan, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayMath);
+
+void SkDisplayMath::executeFunction(SkDisplayable* target, int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* scriptValue) {
+    if (scriptValue == NULL)
+        return;
+    SkASSERT(target == this);
+    SkScriptValue* array = parameters.begin();
+    SkScriptValue* end = parameters.end();
+    SkScalar input = parameters[0].fOperand.fScalar;
+    SkScalar scalarResult;
+    switch (index) {
+        case SK_FUNCTION(abs):
+            scalarResult = SkScalarAbs(input); 
+            break;
+        case SK_FUNCTION(acos):
+            scalarResult = SkScalarACos(input);
+            break;
+        case SK_FUNCTION(asin):
+            scalarResult = SkScalarASin(input);
+            break;
+        case SK_FUNCTION(atan):
+            scalarResult = SkScalarATan2(input, SK_Scalar1);
+            break;
+        case SK_FUNCTION(atan2):
+            scalarResult = SkScalarATan2(input, parameters[1].fOperand.fScalar);
+            break;
+        case SK_FUNCTION(ceil):
+            scalarResult = SkIntToScalar(SkScalarCeil(input)); 
+            break;
+        case SK_FUNCTION(cos):
+            scalarResult = SkScalarCos(input);
+            break;
+        case SK_FUNCTION(exp):
+            scalarResult = SkScalarExp(input);
+            break;
+        case SK_FUNCTION(floor):
+            scalarResult = SkIntToScalar(SkScalarFloor(input)); 
+            break;
+        case SK_FUNCTION(log):
+            scalarResult = SkScalarLog(input);
+            break;
+        case SK_FUNCTION(max):
+            scalarResult = -SK_ScalarMax;
+            while (array < end) {
+                scalarResult = SkMaxScalar(scalarResult, array->fOperand.fScalar);
+                array++;
+            }
+            break;
+        case SK_FUNCTION(min):
+            scalarResult = SK_ScalarMax;
+            while (array < end) {
+                scalarResult = SkMinScalar(scalarResult, array->fOperand.fScalar);
+                array++;
+            }
+            break;
+        case SK_FUNCTION(pow):
+            // not the greatest -- but use x^y = e^(y * ln(x))
+            scalarResult = SkScalarLog(input);
+            scalarResult = SkScalarMul(parameters[1].fOperand.fScalar, scalarResult);
+            scalarResult = SkScalarExp(scalarResult);
+            break;
+        case SK_FUNCTION(random):
+            scalarResult = fRandom.nextUScalar1();
+            break;
+        case SK_FUNCTION(round):
+            scalarResult = SkIntToScalar(SkScalarRound(input)); 
+            break;
+        case SK_FUNCTION(sin):
+            scalarResult = SkScalarSin(input);
+            break;
+        case SK_FUNCTION(sqrt): {
+            SkASSERT(parameters.count() == 1);
+            SkASSERT(type == SkType_Float);
+            scalarResult = SkScalarSqrt(input); 
+            } break;
+        case SK_FUNCTION(tan):
+            scalarResult = SkScalarTan(input);
+            break;
+        default:
+            SkASSERT(0);
+            scalarResult = SK_ScalarNaN;
+    }
+    scriptValue->fOperand.fScalar = scalarResult;
+    scriptValue->fType = SkType_Float;
+}
+
+const SkFunctionParamType* SkDisplayMath::getFunctionsParameters() {
+    return fFunctionParameters;
+}
+
+bool SkDisplayMath::getProperty(int index, SkScriptValue* value) const {
+    if ((unsigned)index < SK_ARRAY_COUNT(gConstants)) {
+        value->fOperand.fScalar = gConstants[index];
+        value->fType = SkType_Float;
+        return true;
+    }
+    SkASSERT(0);
+    return false;
+}
diff --git a/src/animator/SkDisplayMath.h b/src/animator/SkDisplayMath.h
new file mode 100644
index 0000000..de26319
--- /dev/null
+++ b/src/animator/SkDisplayMath.h
@@ -0,0 +1,40 @@
+/* libs/graphics/animator/SkDisplayMath.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 SkDisplayMath_DEFINED
+#define SkDisplayMath_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkRandom.h"
+
+class SkDisplayMath : public SkDisplayable {
+    DECLARE_DISPLAY_MEMBER_INFO(Math);
+    virtual void executeFunction(SkDisplayable* , int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* );
+    virtual const SkFunctionParamType* getFunctionsParameters();
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+private:
+    mutable SkRandom fRandom;
+    static const SkScalar gConstants[];
+    static const SkFunctionParamType fFunctionParameters[];
+
+};
+    
+#endif // SkDisplayMath_DEFINED
+
diff --git a/src/animator/SkDisplayMovie.cpp b/src/animator/SkDisplayMovie.cpp
new file mode 100644
index 0000000..ed74329
--- /dev/null
+++ b/src/animator/SkDisplayMovie.cpp
@@ -0,0 +1,138 @@
+/* libs/graphics/animator/SkDisplayMovie.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 "SkDisplayMovie.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayMovie::fInfo[] = {
+    SK_MEMBER(src, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayMovie);
+
+SkDisplayMovie::SkDisplayMovie() : fDecodedSuccessfully(false), fLoaded(false), fMovieBuilt(false) {
+    fMovie.fMaker->fInMovie = true;
+}
+
+SkDisplayMovie::~SkDisplayMovie() {
+}
+
+void SkDisplayMovie::buildMovie() {
+    if (fMovieBuilt)
+        return;
+    SkAnimateMaker* movieMaker = fMovie.fMaker;
+    SkAnimateMaker* parentMaker = movieMaker->fParentMaker;
+    if (src.size() == 0 || parentMaker == NULL)
+        return;
+    movieMaker->fPrefix.set(parentMaker->fPrefix);
+    fDecodedSuccessfully = fMovie.fMaker->decodeURI(src.c_str());
+    if (fDecodedSuccessfully == false) {
+
+        if (movieMaker->getErrorCode() != SkXMLParserError::kNoError || movieMaker->getNativeCode() != -1) {
+            movieMaker->setInnerError(parentMaker, src);
+            parentMaker->setErrorCode(SkDisplayXMLParserError::kInMovie);
+        } else {
+            parentMaker->setErrorNoun(src);
+            parentMaker->setErrorCode(SkDisplayXMLParserError::kMovieNameUnknownOrMissing);
+        }
+    }
+    fMovieBuilt = true;
+}
+
+SkDisplayable* SkDisplayMovie::deepCopy(SkAnimateMaker* maker) {
+    SkDisplayMovie* copy = (SkDisplayMovie*) INHERITED::deepCopy(maker);
+    copy->fMovie.fMaker->fParentMaker = fMovie.fMaker->fParentMaker;
+    copy->fMovie.fMaker->fHostEventSinkID = fMovie.fMaker->fHostEventSinkID;
+    copy->fMovieBuilt = false;
+    *fMovie.fMaker->fParentMaker->fMovies.append() = copy;
+    return copy;
+}
+
+void SkDisplayMovie::dirty() {
+    buildMovie();
+}
+
+bool SkDisplayMovie::doEvent(SkDisplayEvent::Kind kind, SkEventState* state) {
+    if (fLoaded == false)
+        return false;
+    fMovie.fMaker->fEnableTime = fMovie.fMaker->fParentMaker->fEnableTime;
+    return fMovie.fMaker->fEvents.doEvent(*fMovie.fMaker, kind, state);
+}
+
+bool SkDisplayMovie::draw(SkAnimateMaker& maker) {
+    if (fDecodedSuccessfully == false)
+        return false;
+    if (fLoaded == false)
+        enable(maker);
+    maker.fCanvas->save();
+    SkPaint local = SkPaint(*maker.fPaint);
+    bool result = fMovie.draw(maker.fCanvas, &local, 
+        maker.fDisplayList.getTime()) != SkAnimator::kNotDifferent;
+    maker.fDisplayList.fInvalBounds.join(fMovie.fMaker->fDisplayList.fInvalBounds);
+    maker.fCanvas->restore();
+    return result;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayMovie::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("src=\"%s\"/>\n",  src.c_str());
+    SkAnimateMaker* movieMaker = fMovie.fMaker;
+    SkDisplayList::fIndent += 4;
+    movieMaker->fDisplayList.dumpInner(movieMaker);
+    SkDisplayList::fIndent -= 4;
+    dumpEnd(maker);
+}
+
+void SkDisplayMovie::dumpEvents() {
+    fMovie.fMaker->fEvents.dump(*fMovie.fMaker);
+}
+#endif
+
+bool SkDisplayMovie::enable(SkAnimateMaker& maker) {
+    if (fDecodedSuccessfully == false)
+        return false;
+    SkAnimateMaker* movieMaker = fMovie.fMaker;
+    movieMaker->fEvents.doEvent(*movieMaker, SkDisplayEvent::kOnload, NULL);
+    movieMaker->fEvents.removeEvent(SkDisplayEvent::kOnload, NULL);
+    fLoaded = true;
+    movieMaker->fLoaded = true;
+    return false;
+}
+
+bool SkDisplayMovie::hasEnable() const {
+    return true;
+}
+
+void SkDisplayMovie::onEndElement(SkAnimateMaker& maker) {
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    fMovie.fMaker->fDebugTimeBase = maker.fDebugTimeBase;
+#endif
+    fMovie.fMaker->fPrefix.set(maker.fPrefix);
+    fMovie.fMaker->fHostEventSinkID = maker.fHostEventSinkID;
+    fMovie.fMaker->fParentMaker = &maker;
+    buildMovie();
+    *maker.fMovies.append() = this;
+}
+
+
diff --git a/src/animator/SkDisplayMovie.h b/src/animator/SkDisplayMovie.h
new file mode 100644
index 0000000..2b8a893
--- /dev/null
+++ b/src/animator/SkDisplayMovie.h
@@ -0,0 +1,60 @@
+/* libs/graphics/animator/SkDisplayMovie.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 SkDisplayMovie_DEFINED
+#define SkDisplayMovie_DEFINED
+
+#include "SkAnimator.h"
+#include "SkDrawable.h"
+#include "SkMemberInfo.h"
+
+struct SkEventState;
+
+class SkDisplayMovie : public SkDrawable {
+    DECLARE_DISPLAY_MEMBER_INFO(Movie);
+    SkDisplayMovie();
+    virtual ~SkDisplayMovie();
+    void buildMovie();
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual void dirty();
+    bool doEvent(const SkEvent& evt) {
+        return fLoaded && fMovie.doEvent(evt);
+    }
+    virtual bool doEvent(SkDisplayEvent::Kind , SkEventState* state );
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+    virtual void dumpEvents();
+#endif
+    virtual bool enable(SkAnimateMaker& );
+    const SkAnimator* getAnimator() const { return &fMovie; }
+    virtual bool hasEnable() const;
+    virtual void onEndElement(SkAnimateMaker& );
+protected:
+    SkString src;
+    SkAnimator fMovie;
+    SkBool8 fDecodedSuccessfully;
+    SkBool8 fLoaded;
+    SkBool8 fMovieBuilt;
+    friend class SkAnimateMaker;
+    friend class SkPost;
+private:
+    typedef SkDrawable INHERITED;
+};
+
+#endif // SkDisplayMovie_DEFINED
+
diff --git a/src/animator/SkDisplayNumber.cpp b/src/animator/SkDisplayNumber.cpp
new file mode 100644
index 0000000..3bb96d7
--- /dev/null
+++ b/src/animator/SkDisplayNumber.cpp
@@ -0,0 +1,67 @@
+/* libs/graphics/animator/SkDisplayNumber.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 "SkDisplayNumber.h"
+
+enum SkDisplayNumber_Properties {
+    SK_PROPERTY(MAX_VALUE),
+    SK_PROPERTY(MIN_VALUE),
+    SK_PROPERTY(NEGATIVE_INFINITY),
+    SK_PROPERTY(NaN),
+    SK_PROPERTY(POSITIVE_INFINITY)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayNumber::fInfo[] = {
+    SK_MEMBER_PROPERTY(MAX_VALUE, Float),
+    SK_MEMBER_PROPERTY(MIN_VALUE, Float),
+    SK_MEMBER_PROPERTY(NEGATIVE_INFINITY, Float),
+    SK_MEMBER_PROPERTY(NaN, Float),
+    SK_MEMBER_PROPERTY(POSITIVE_INFINITY, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayNumber);
+
+bool SkDisplayNumber::getProperty(int index, SkScriptValue* value) const {
+    SkScalar constant;
+    switch (index) {
+        case SK_PROPERTY(MAX_VALUE):
+            constant = SK_ScalarMax;
+        break;
+        case SK_PROPERTY(MIN_VALUE):
+            constant = SK_ScalarMin;
+        break;
+        case SK_PROPERTY(NEGATIVE_INFINITY):
+            constant = -SK_ScalarInfinity;
+        break;
+        case SK_PROPERTY(NaN):
+            constant = SK_ScalarNaN;
+        break;
+        case SK_PROPERTY(POSITIVE_INFINITY):
+            constant = SK_ScalarInfinity;
+        break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    value->fOperand.fScalar = constant;
+    value->fType = SkType_Float;
+    return true;
+}
diff --git a/src/animator/SkDisplayNumber.h b/src/animator/SkDisplayNumber.h
new file mode 100644
index 0000000..515c35b
--- /dev/null
+++ b/src/animator/SkDisplayNumber.h
@@ -0,0 +1,30 @@
+/* libs/graphics/animator/SkDisplayNumber.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 SkDisplayNumber_DEFINED
+#define SkDisplayNumber_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+
+class SkDisplayNumber : public SkDisplayable {
+    DECLARE_DISPLAY_MEMBER_INFO(Number);
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+private:
+};
+    
+#endif // SkDisplayNumber_DEFINED
diff --git a/src/animator/SkDisplayPost.cpp b/src/animator/SkDisplayPost.cpp
new file mode 100644
index 0000000..17e7c76
--- /dev/null
+++ b/src/animator/SkDisplayPost.cpp
@@ -0,0 +1,315 @@
+/* libs/graphics/animator/SkDisplayPost.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 "SkDisplayPost.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimator.h"
+#include "SkDisplayMovie.h"
+#include "SkPostParts.h"
+#include "SkScript.h"
+#ifdef SK_DEBUG
+#include "SkDump.h"
+#include "SkTime.h"
+#endif
+
+enum SkPost_Properties {
+    SK_PROPERTY(target),
+    SK_PROPERTY(type)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkPost::fInfo[] = {
+    SK_MEMBER(delay, MSec),
+//  SK_MEMBER(initialized, Boolean),
+    SK_MEMBER(mode, EventMode),
+    SK_MEMBER(sink, String),
+    SK_MEMBER_PROPERTY(target, String),
+    SK_MEMBER_PROPERTY(type, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkPost);
+
+SkPost::SkPost() : delay(0), /*initialized(SkBool(-1)), */ mode(kImmediate), fMaker(NULL),
+    fSinkID(0), fTargetMaker(NULL), fChildHasID(false), fDirty(false) {
+}
+
+SkPost::~SkPost() {
+    for (SkData** part = fParts.begin(); part < fParts.end();  part++)
+        delete *part;
+}
+
+bool SkPost::add(SkAnimateMaker& , SkDisplayable* child) {
+    SkASSERT(child && child->isData());
+    SkData* part = (SkData*) child;
+    *fParts.append() = part;
+    return true;
+}
+
+bool SkPost::childrenNeedDisposing() const { 
+    return false; 
+}
+
+void SkPost::dirty() { 
+    fDirty = true; 
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkPost::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkString* eventType = new SkString();
+    fEvent.getType(eventType);
+    if (eventType->equals("user")) {
+        const char* target = fEvent.findString("id");
+        SkDebugf("target=\"%s\" ", target);
+    }
+    else
+        SkDebugf("type=\"%s\" ", eventType->c_str());
+    delete eventType;
+    
+    if (delay > 0) {
+#ifdef SK_CAN_USE_FLOAT
+        SkDebugf("delay=\"%g\" ", SkScalarToFloat(SkScalarDiv(delay, 1000)));
+#else
+        SkDebugf("delay=\"%x\" ", SkScalarDiv(delay, 1000));
+#endif
+    }
+//  if (initialized == false)
+//      SkDebugf("(uninitialized) ");
+    SkString string;
+    SkDump::GetEnumString(SkType_EventMode, mode, &string);
+    if (!string.equals("immediate"))
+        SkDebugf("mode=\"%s\" ", string.c_str());
+    // !!! could enhance this to search through make hierarchy to show name of sink
+    if (sink.size() > 0) {
+        SkDebugf("sink=\"%s\" sinkID=\"%d\" ", sink.c_str(), fSinkID);
+    } else if (fSinkID != maker->getAnimator()->getSinkID() && fSinkID != 0) {
+        SkDebugf("sinkID=\"%d\" ", fSinkID);
+    }
+    const SkMetaData& meta = fEvent.getMetaData();
+    SkMetaData::Iter iter(meta);
+    SkMetaData::Type    type;
+    int number;
+    const char* name;
+    bool closedYet = false;
+    SkDisplayList::fIndent += 4;
+    //this seems to work, but kinda hacky
+    //for some reason the last part is id, which i don't want
+    //and the parts seem to be in the reverse order from the one in which we find the 
+    //data itself
+    //SkData** ptr = fParts.end();
+    //SkData* data;
+    //const char* ID;
+    while ((name = iter.next(&type, &number)) != NULL) {
+        //ptr--;
+        if (strcmp(name, "id") == 0)
+            continue;
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        //data = *ptr;
+        //if (data->id)
+        //    ID = data->id;
+        //else
+        //    ID = "";
+        SkDebugf("%*s<data name=\"%s\" ", SkDisplayList::fIndent, "", name);
+        switch (type) {
+            case SkMetaData::kS32_Type: {
+                int32_t s32;
+                meta.findS32(name, &s32);
+                SkDebugf("int=\"%d\" ", s32);
+                } break;
+            case SkMetaData::kScalar_Type: {
+                SkScalar scalar;
+                meta.findScalar(name, &scalar);
+#ifdef SK_CAN_USE_FLOAT
+                SkDebugf("float=\"%g\" ", SkScalarToFloat(scalar));
+#else
+                SkDebugf("float=\"%x\" ", scalar);
+#endif
+                } break;
+            case SkMetaData::kString_Type:
+                SkDebugf("string=\"%s\" ", meta.findString(name));
+                break;
+            case SkMetaData::kPtr_Type: {//when do we have a pointer
+                    void* ptr;
+                    meta.findPtr(name, &ptr);
+                    SkDebugf("0x%08x ", ptr);
+                } break;
+            case SkMetaData::kBool_Type: {
+                bool boolean;
+                meta.findBool(name, &boolean);
+                SkDebugf("boolean=\"%s\" ", boolean ? "true " : "false ");
+                } break;
+            default:
+                break;
+        }
+        SkDebugf("/>\n");
+        //ptr++;
+/*      perhaps this should only be done in the case of a pointer?
+        SkDisplayable* displayable;
+        if (maker->find(name, &displayable))
+            displayable->dump(maker);
+        else
+            SkDebugf("\n");*/
+    }
+    SkDisplayList::fIndent -= 4;
+    if (closedYet)
+        dumpEnd(maker);
+    else
+        SkDebugf("/>\n");
+
+}
+#endif
+
+bool SkPost::enable(SkAnimateMaker& maker ) {
+    if (maker.hasError())
+        return true;
+    if (fDirty) {
+        if (sink.size() > 0)
+            findSinkID();
+        if (fChildHasID) {
+            SkString preserveID(fEvent.findString("id"));
+            fEvent.getMetaData().reset();
+            if (preserveID.size() > 0)
+                fEvent.setString("id", preserveID);
+            for (SkData** part = fParts.begin(); part < fParts.end();  part++) {
+                if ((*part)->add())
+                    maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingDataToPost);
+            }
+        }
+        fDirty = false;
+    }
+#ifdef SK_DUMP_ENABLED
+    if (maker.fDumpPosts) {
+        SkDebugf("post enable: ");
+        dump(&maker);
+    }
+#if defined SK_DEBUG_ANIMATION_TIMING
+    SkString debugOut;
+    SkMSec time = maker.getAppTime();
+    debugOut.appendS32(time - maker.fDebugTimeBase);
+    debugOut.append(" post id=");
+    debugOut.append(_id);
+    debugOut.append(" enable=");
+    debugOut.appendS32(maker.fEnableTime - maker.fDebugTimeBase);
+    debugOut.append(" delay=");
+    debugOut.appendS32(delay);
+#endif
+#endif
+//  SkMSec adjustedDelay = maker.adjustDelay(maker.fEnableTime, delay);
+    SkMSec futureTime = maker.fEnableTime + delay;
+    fEvent.setFast32(futureTime);
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+    debugOut.append(" future=");
+    debugOut.appendS32(futureTime - maker.fDebugTimeBase);
+    SkDebugf("%s\n", debugOut.c_str());
+#endif
+    SkEventSinkID targetID = fSinkID;
+    bool isAnimatorEvent = true;
+    SkAnimator* anim = maker.getAnimator();
+    if (targetID == 0) {
+        isAnimatorEvent = fEvent.findString("id") != NULL;
+        if (isAnimatorEvent) 
+            targetID = anim->getSinkID();
+        else if (maker.fHostEventSinkID)
+            targetID = maker.fHostEventSinkID;
+        else
+            return true;
+    } else
+        anim = fTargetMaker->getAnimator();
+    if (delay == 0) {
+        if (isAnimatorEvent && mode == kImmediate)
+            fTargetMaker->doEvent(fEvent);
+        else
+            anim->onEventPost(new SkEvent(fEvent), targetID);
+    } else
+        anim->onEventPostTime(new SkEvent(fEvent), targetID, futureTime);
+    return true;
+}
+
+void SkPost::findSinkID() {
+    // get the next delimiter '.' if any
+    fTargetMaker = fMaker;
+    const char* ch = sink.c_str();
+    do {
+        const char* end = strchr(ch, '.');
+        size_t len = end ? end - ch : strlen(ch);
+        SkDisplayable* displayable = NULL;
+        if (SK_LITERAL_STR_EQUAL("parent", ch, len)) {
+            if (fTargetMaker->fParentMaker)
+                fTargetMaker = fTargetMaker->fParentMaker;
+            else {
+                fTargetMaker->setErrorCode(SkDisplayXMLParserError::kNoParentAvailable);
+                return;
+            }
+        } else {
+            fTargetMaker->find(ch, len, &displayable);
+            if (displayable == NULL || displayable->getType() != SkType_Movie) {
+                fTargetMaker->setErrorCode(SkDisplayXMLParserError::kExpectedMovie);
+                return;
+            }
+            SkDisplayMovie* movie = (SkDisplayMovie*) displayable;
+            fTargetMaker = movie->fMovie.fMaker;
+        }
+        if (end == NULL)
+            break;
+        ch = ++end;
+    } while (true);
+    SkAnimator* anim = fTargetMaker->getAnimator();
+    fSinkID = anim->getSinkID();
+}
+ 
+bool SkPost::hasEnable() const {
+    return true;
+}
+
+void SkPost::onEndElement(SkAnimateMaker& maker) {
+    fTargetMaker = fMaker = &maker;
+    if (fChildHasID == false) {
+        for (SkData** part = fParts.begin(); part < fParts.end();  part++)
+            delete *part;
+        fParts.reset();
+    }
+}
+
+void SkPost::setChildHasID() { 
+    fChildHasID = true; 
+}
+
+bool SkPost::setProperty(int index, SkScriptValue& value) {
+    SkASSERT(value.fType == SkType_String);
+    SkString* string = value.fOperand.fString;
+    switch(index) {
+        case SK_PROPERTY(target): {
+            fEvent.setType("user");
+            fEvent.setString("id", *string);
+            mode = kImmediate;
+            } break;
+        case SK_PROPERTY(type):
+            fEvent.setType(*string);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
diff --git a/src/animator/SkDisplayPost.h b/src/animator/SkDisplayPost.h
new file mode 100644
index 0000000..4b75b07
--- /dev/null
+++ b/src/animator/SkDisplayPost.h
@@ -0,0 +1,67 @@
+/* libs/graphics/animator/SkDisplayPost.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 SkDisplayPost_DEFINED
+#define SkDisplayPost_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkEvent.h"
+#include "SkEventSink.h"
+#include "SkMemberInfo.h"
+#include "SkIntArray.h"
+
+class SkData;
+class SkAnimateMaker;
+
+class SkPost : public SkDisplayable {
+    DECLARE_MEMBER_INFO(Post);
+    enum Mode {
+        kDeferred,
+        kImmediate
+    };
+    SkPost();
+    virtual ~SkPost();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool childrenNeedDisposing() const;
+    virtual void dirty();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool enable(SkAnimateMaker& );
+    virtual bool hasEnable() const;
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual void setChildHasID();
+    virtual bool setProperty(int index, SkScriptValue& );
+protected:
+    SkMSec delay;
+    SkString sink;
+//  SkBool initialized;
+    Mode mode;
+    SkEvent fEvent;
+    SkAnimateMaker* fMaker;
+    SkTDDataArray fParts;
+    SkEventSinkID fSinkID;
+    SkAnimateMaker* fTargetMaker;
+    SkBool8 fChildHasID;
+    SkBool8 fDirty;
+private:
+    void findSinkID();
+    friend class SkData;
+    typedef SkDisplayable INHERITED;
+};
+
+#endif //SkDisplayPost_DEFINED
diff --git a/src/animator/SkDisplayRandom.cpp b/src/animator/SkDisplayRandom.cpp
new file mode 100644
index 0000000..ab078cd
--- /dev/null
+++ b/src/animator/SkDisplayRandom.cpp
@@ -0,0 +1,80 @@
+/* libs/graphics/animator/SkDisplayRandom.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 "SkDisplayRandom.h"
+#include "SkInterpolator.h"
+
+enum SkDisplayRandom_Properties {
+    SK_PROPERTY(random),
+    SK_PROPERTY(seed)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayRandom::fInfo[] = {
+    SK_MEMBER(blend, Float),
+    SK_MEMBER(max, Float),
+    SK_MEMBER(min, Float),
+    SK_MEMBER_DYNAMIC_PROPERTY(random, Float),
+    SK_MEMBER_PROPERTY(seed, Int)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayRandom);
+
+SkDisplayRandom::SkDisplayRandom() : blend(0), min(0), max(SK_Scalar1) {
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayRandom::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+#ifdef SK_CAN_USE_FLOAT
+    SkDebugf("min=\"%g\" ", SkScalarToFloat(min));
+    SkDebugf("max=\"%g\" ", SkScalarToFloat(max));
+    SkDebugf("blend=\"%g\" ", SkScalarToFloat(blend));    
+#else
+    SkDebugf("min=\"%x\" ", min);
+    SkDebugf("max=\"%x\" ", max);
+    SkDebugf("blend=\"%x\" ", blend);    
+#endif
+    SkDebugf("/>\n");
+}
+#endif
+
+bool SkDisplayRandom::getProperty(int index, SkScriptValue* value) const {
+    switch(index) {
+        case SK_PROPERTY(random): {
+            SkScalar random = fRandom.nextUScalar1();
+            SkScalar relativeT = SkUnitCubicInterp(random, SK_Scalar1 - blend, 0, 0, SK_Scalar1 - blend);
+            value->fOperand.fScalar = min + SkScalarMul(max - min, relativeT);
+            value->fType = SkType_Float;
+            return true;
+        }
+        default:
+            SkASSERT(0);
+    }
+    return false;
+}
+
+bool SkDisplayRandom::setProperty(int index, SkScriptValue& value) {
+    SkASSERT(index == SK_PROPERTY(seed));
+    SkASSERT(value.fType == SkType_Int);
+    fRandom.setSeed(value.fOperand.fS32);
+    return true;
+}
+
diff --git a/src/animator/SkDisplayRandom.h b/src/animator/SkDisplayRandom.h
new file mode 100644
index 0000000..b40907e
--- /dev/null
+++ b/src/animator/SkDisplayRandom.h
@@ -0,0 +1,49 @@
+/* libs/graphics/animator/SkDisplayRandom.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 SkDisplayRandom_DEFINED
+#define SkDisplayRandom_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkRandom.h"
+
+#ifdef min
+#undef min
+#endif
+
+#ifdef max
+#undef max
+#endif
+
+class SkDisplayRandom : public SkDisplayable {
+    DECLARE_DISPLAY_MEMBER_INFO(Random);
+    SkDisplayRandom();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool setProperty(int index, SkScriptValue& );
+private:
+    SkScalar blend;
+    SkScalar min;
+    SkScalar max;
+    mutable SkRandom fRandom;
+};
+
+#endif // SkDisplayRandom_DEFINED
+
diff --git a/src/animator/SkDisplayScreenplay.cpp b/src/animator/SkDisplayScreenplay.cpp
new file mode 100644
index 0000000..58180e7
--- /dev/null
+++ b/src/animator/SkDisplayScreenplay.cpp
@@ -0,0 +1,30 @@
+/* libs/graphics/animator/SkDisplayScreenplay.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 "SkDisplayScreenplay.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayScreenplay::fInfo[] = {
+    SK_MEMBER(time, MSec)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayScreenplay);
+
+
diff --git a/src/animator/SkDisplayScreenplay.h b/src/animator/SkDisplayScreenplay.h
new file mode 100644
index 0000000..cb8103d
--- /dev/null
+++ b/src/animator/SkDisplayScreenplay.h
@@ -0,0 +1,29 @@
+/* libs/graphics/animator/SkDisplayScreenplay.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 SkDisplayScreenplay_DEFINED
+#define SkDisplayScreenplay_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+
+class SkDisplayScreenplay : public SkDisplayable {
+    DECLARE_DISPLAY_MEMBER_INFO(Screenplay);
+    SkMSec time;
+};
+
+#endif // SkDisplayScreenplay_DEFINED
diff --git a/src/animator/SkDisplayType.cpp b/src/animator/SkDisplayType.cpp
new file mode 100644
index 0000000..90fcd89
--- /dev/null
+++ b/src/animator/SkDisplayType.cpp
@@ -0,0 +1,774 @@
+/* libs/graphics/animator/SkDisplayType.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 "SkDisplayType.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateSet.h"
+#include "SkDisplayAdd.h"
+#include "SkDisplayApply.h"
+#include "SkDisplayBounds.h"
+#include "SkDisplayEvent.h"
+#include "SkDisplayInclude.h"
+#ifdef SK_DEBUG
+#include "SkDisplayList.h"
+#endif
+#include "SkDisplayMath.h"
+#include "SkDisplayMovie.h"
+#include "SkDisplayNumber.h"
+#include "SkDisplayPost.h"
+#include "SkDisplayRandom.h"
+#include "SkDisplayTypes.h"
+#include "SkDraw3D.h"
+#include "SkDrawBitmap.h"
+#include "SkDrawClip.h"
+#include "SkDrawDash.h"
+#include "SkDrawDiscrete.h"
+#include "SkDrawEmboss.h"
+#include "SkDrawFull.h"
+#include "SkDrawGradient.h"
+#include "SkDrawLine.h"
+#include "SkDrawMatrix.h"
+#include "SkDrawOval.h"
+#include "SkDrawPaint.h"
+#include "SkDrawPath.h"
+#include "SkDrawPoint.h"
+#include "SkDrawSaveLayer.h"
+#include "SkDrawText.h"
+#include "SkDrawTextBox.h"
+#include "SkDrawTo.h"
+#include "SkDrawTransparentShader.h"
+#include "SkDump.h"
+#include "SkExtras.h"
+#include "SkHitClear.h"
+#include "SkHitTest.h"
+#include "SkMatrixParts.h"
+#include "SkPathParts.h"
+#include "SkPostParts.h"
+#include "SkSnapshot.h"
+#include "SkTextOnPath.h"
+#include "SkTextToPath.h"
+#include "SkTSearch.h"
+
+#define CASE_NEW(_class) \
+    case SkType_##_class: result = new Sk##_class(); break
+#define CASE_DRAW_NEW(_class) \
+    case SkType_##_class: result = new SkDraw##_class(); break
+#define CASE_DISPLAY_NEW(_class) \
+    case SkType_##_class: result = new SkDisplay##_class(); break
+#ifdef SK_DEBUG
+    #define CASE_DEBUG_RETURN_NIL(_class) \
+        case SkType_##_class: return NULL
+#else
+    #define CASE_DEBUG_RETURN_NIL(_class)
+#endif
+    
+
+SkDisplayTypes SkDisplayType::gNewTypes = kNumberOfTypes;
+
+SkDisplayable* SkDisplayType::CreateInstance(SkAnimateMaker* maker, SkDisplayTypes type) {
+    SkDisplayable* result = NULL;
+    switch (type) {
+        // unknown
+        CASE_DISPLAY_NEW(Math);
+        CASE_DISPLAY_NEW(Number);
+        CASE_NEW(Add);
+        CASE_NEW(AddCircle);
+        // addgeom
+        CASE_DEBUG_RETURN_NIL(AddMode);
+        CASE_NEW(AddOval);
+        CASE_NEW(AddPath);
+        CASE_NEW(AddRect);
+        CASE_NEW(AddRoundRect);
+        CASE_DEBUG_RETURN_NIL(Align);
+        CASE_NEW(Animate);
+        // animatebase
+        CASE_NEW(Apply);
+        CASE_DEBUG_RETURN_NIL(ApplyMode);
+        CASE_DEBUG_RETURN_NIL(ApplyTransition);
+        CASE_DISPLAY_NEW(Array);
+        // argb
+        // base64
+        // basebitmap
+        // baseclassinfo
+        CASE_DRAW_NEW(Bitmap);
+        // bitmapencoding
+        // bitmapformat
+        CASE_DRAW_NEW(BitmapShader);
+        CASE_DRAW_NEW(Blur);
+        CASE_DISPLAY_NEW(Boolean);
+        // boundable
+        CASE_DISPLAY_NEW(Bounds);
+        CASE_DEBUG_RETURN_NIL(Cap);
+        CASE_NEW(Clear);
+        CASE_DRAW_NEW(Clip);
+        CASE_NEW(Close);
+        CASE_DRAW_NEW(Color);
+        CASE_NEW(CubicTo);
+        CASE_NEW(Dash);
+        CASE_NEW(Data);
+        CASE_NEW(Discrete);
+        // displayable
+        // drawable
+        CASE_NEW(DrawTo);
+        CASE_NEW(Dump);
+        // dynamicstring
+        CASE_DRAW_NEW(Emboss);
+        CASE_DISPLAY_NEW(Event);
+        CASE_DEBUG_RETURN_NIL(EventCode);
+        CASE_DEBUG_RETURN_NIL(EventKind);
+        CASE_DEBUG_RETURN_NIL(EventMode);
+        // filltype
+        // filtertype
+        CASE_DISPLAY_NEW(Float);
+        CASE_NEW(FromPath);
+        CASE_DEBUG_RETURN_NIL(FromPathMode);
+        CASE_NEW(Full);
+        // gradient
+        CASE_NEW(Group);
+        CASE_NEW(HitClear);
+        CASE_NEW(HitTest);
+        CASE_NEW(Image);
+        CASE_NEW(Include);
+        CASE_NEW(Input);
+        CASE_DISPLAY_NEW(Int);
+        CASE_DEBUG_RETURN_NIL(Join);
+        CASE_NEW(Line);
+        CASE_NEW(LineTo);
+        CASE_NEW(LinearGradient);
+        CASE_DRAW_NEW(MaskFilter);
+        CASE_DEBUG_RETURN_NIL(MaskFilterBlurStyle);
+        // maskfilterlight
+        CASE_DRAW_NEW(Matrix);
+        // memberfunction
+        // memberproperty
+        CASE_NEW(Move);
+        CASE_NEW(MoveTo);
+        CASE_DISPLAY_NEW(Movie);
+        // msec
+        CASE_NEW(Oval);
+        CASE_DRAW_NEW(Paint);
+        CASE_DRAW_NEW(Path);
+        // pathdirection
+        CASE_DRAW_NEW(PathEffect);
+        // point
+        CASE_NEW(DrawPoint);
+        CASE_NEW(PolyToPoly);
+        CASE_NEW(Polygon);
+        CASE_NEW(Polyline);
+        CASE_NEW(Post);
+        CASE_NEW(QuadTo);
+        CASE_NEW(RCubicTo);
+        CASE_NEW(RLineTo);
+        CASE_NEW(RMoveTo);
+        CASE_NEW(RQuadTo);
+        CASE_NEW(RadialGradient);
+        CASE_DISPLAY_NEW(Random);
+        CASE_DRAW_NEW(Rect);
+        CASE_NEW(RectToRect);
+        CASE_NEW(Remove);
+        CASE_NEW(Replace);
+        CASE_NEW(Rotate);
+        CASE_NEW(RoundRect);
+        CASE_NEW(Save);
+        CASE_NEW(SaveLayer);
+        CASE_NEW(Scale);
+        // screenplay
+        CASE_NEW(Set);
+        CASE_DRAW_NEW(Shader);
+        CASE_NEW(Skew);
+        CASE_NEW(3D_Camera);
+        CASE_NEW(3D_Patch);
+        // 3dpoint
+        CASE_NEW(Snapshot);
+        CASE_DISPLAY_NEW(String);
+        // style
+        CASE_NEW(Text);
+        CASE_DRAW_NEW(TextBox);
+        // textboxalign
+        // textboxmode
+        CASE_NEW(TextOnPath);
+        CASE_NEW(TextToPath);
+        CASE_DEBUG_RETURN_NIL(TileMode);
+        CASE_NEW(Translate);
+        CASE_DRAW_NEW(TransparentShader);
+        CASE_DRAW_NEW(Typeface);
+        CASE_DEBUG_RETURN_NIL(Xfermode);
+        default:
+            SkExtras** end = maker->fExtras.end();
+            for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) {
+                if ((result = (*extraPtr)->createInstance(type)) != NULL)
+                    return result;
+            }
+            SkASSERT(0);
+    }
+    return result;
+}
+
+#undef CASE_NEW
+#undef CASE_DRAW_NEW
+#undef CASE_DISPLAY_NEW
+
+#if SK_USE_CONDENSED_INFO == 0
+
+#define CASE_GET_INFO(_class) case SkType_##_class: \
+    info = Sk##_class::fInfo; infoCount = Sk##_class::fInfoCount; break
+#define CASE_GET_DRAW_INFO(_class) case SkType_##_class: \
+    info = SkDraw##_class::fInfo; infoCount = SkDraw##_class::fInfoCount; break
+#define CASE_GET_DISPLAY_INFO(_class) case SkType_##_class: \
+    info = SkDisplay##_class::fInfo; infoCount = SkDisplay##_class::fInfoCount; \
+    break
+
+const SkMemberInfo* SkDisplayType::GetMembers(SkAnimateMaker* maker, 
+        SkDisplayTypes type, int* infoCountPtr) {
+    const SkMemberInfo* info = NULL;
+    int infoCount = 0;
+    switch (type) {
+        // unknown
+        CASE_GET_DISPLAY_INFO(Math);
+        CASE_GET_DISPLAY_INFO(Number);
+        CASE_GET_INFO(Add);
+        CASE_GET_INFO(AddCircle);
+        CASE_GET_INFO(AddGeom);
+        // addmode
+        CASE_GET_INFO(AddOval);
+        CASE_GET_INFO(AddPath);
+        CASE_GET_INFO(AddRect);
+        CASE_GET_INFO(AddRoundRect);
+        // align
+        CASE_GET_INFO(Animate);
+        CASE_GET_INFO(AnimateBase);
+        CASE_GET_INFO(Apply);
+        // applymode
+        // applytransition
+        CASE_GET_DISPLAY_INFO(Array);
+        // argb
+        // base64
+        CASE_GET_INFO(BaseBitmap);
+        // baseclassinfo
+        CASE_GET_DRAW_INFO(Bitmap);
+        // bitmapencoding
+        // bitmapformat
+        CASE_GET_DRAW_INFO(BitmapShader);
+        CASE_GET_DRAW_INFO(Blur);
+        CASE_GET_DISPLAY_INFO(Boolean);
+        // boundable
+        CASE_GET_DISPLAY_INFO(Bounds);
+        // cap
+        // clear
+        CASE_GET_DRAW_INFO(Clip);
+        // close
+        CASE_GET_DRAW_INFO(Color);
+        CASE_GET_INFO(CubicTo);
+        CASE_GET_INFO(Dash);
+        CASE_GET_INFO(Data);
+        CASE_GET_INFO(Discrete);
+        // displayable
+        // drawable
+        CASE_GET_INFO(DrawTo);
+        CASE_GET_INFO(Dump);
+        // dynamicstring
+        CASE_GET_DRAW_INFO(Emboss);
+        CASE_GET_DISPLAY_INFO(Event);
+        // eventcode
+        // eventkind
+        // eventmode
+        // filltype
+        // filtertype
+        CASE_GET_DISPLAY_INFO(Float);
+        CASE_GET_INFO(FromPath);
+        // frompathmode
+        // full
+        CASE_GET_INFO(Gradient);
+        CASE_GET_INFO(Group);
+        CASE_GET_INFO(HitClear);
+        CASE_GET_INFO(HitTest);
+        CASE_GET_INFO(Image);
+        CASE_GET_INFO(Include);
+        CASE_GET_INFO(Input);
+        CASE_GET_DISPLAY_INFO(Int);
+        // join
+        CASE_GET_INFO(Line);
+        CASE_GET_INFO(LineTo);
+        CASE_GET_INFO(LinearGradient);
+        // maskfilter
+        // maskfilterblurstyle
+        // maskfilterlight
+        CASE_GET_DRAW_INFO(Matrix);
+        // memberfunction
+        // memberproperty
+        CASE_GET_INFO(Move);
+        CASE_GET_INFO(MoveTo);
+        CASE_GET_DISPLAY_INFO(Movie);
+        // msec
+        CASE_GET_INFO(Oval);
+        CASE_GET_DRAW_INFO(Path);
+        CASE_GET_DRAW_INFO(Paint);
+        // pathdirection
+        // patheffect
+        case SkType_Point: info = Sk_Point::fInfo; infoCount = Sk_Point::fInfoCount; break; // no virtual flavor
+        CASE_GET_INFO(DrawPoint); // virtual flavor
+        CASE_GET_INFO(PolyToPoly);
+        CASE_GET_INFO(Polygon);
+        CASE_GET_INFO(Polyline);
+        CASE_GET_INFO(Post);
+        CASE_GET_INFO(QuadTo);
+        CASE_GET_INFO(RCubicTo);
+        CASE_GET_INFO(RLineTo);
+        CASE_GET_INFO(RMoveTo);
+        CASE_GET_INFO(RQuadTo);
+        CASE_GET_INFO(RadialGradient);
+        CASE_GET_DISPLAY_INFO(Random);
+        CASE_GET_DRAW_INFO(Rect);
+        CASE_GET_INFO(RectToRect);
+        CASE_GET_INFO(Remove);
+        CASE_GET_INFO(Replace);
+        CASE_GET_INFO(Rotate);
+        CASE_GET_INFO(RoundRect);
+        CASE_GET_INFO(Save);
+        CASE_GET_INFO(SaveLayer);
+        CASE_GET_INFO(Scale);
+        // screenplay
+        CASE_GET_INFO(Set);
+        CASE_GET_DRAW_INFO(Shader);
+        CASE_GET_INFO(Skew);
+        CASE_GET_INFO(3D_Camera);
+        CASE_GET_INFO(3D_Patch);
+        CASE_GET_INFO(3D_Point);
+        CASE_GET_INFO(Snapshot);
+        CASE_GET_DISPLAY_INFO(String);
+        // style
+        CASE_GET_INFO(Text);
+        CASE_GET_DRAW_INFO(TextBox);
+        // textboxalign
+        // textboxmode
+        CASE_GET_INFO(TextOnPath);
+        CASE_GET_INFO(TextToPath);
+        // tilemode
+        CASE_GET_INFO(Translate);
+        // transparentshader
+        CASE_GET_DRAW_INFO(Typeface);
+        // xfermode
+        // knumberoftypes
+        default: 
+            if (maker) {
+                SkExtras** end = maker->fExtras.end();
+                for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) {
+                    if ((info = (*extraPtr)->getMembers(type, infoCountPtr)) != NULL)
+                        return info;
+                }
+            }
+            return NULL;
+    }
+    if (infoCountPtr)
+        *infoCountPtr = infoCount;
+    return info;
+}
+
+const SkMemberInfo* SkDisplayType::GetMember(SkAnimateMaker* maker, 
+        SkDisplayTypes type, const char** matchPtr ) {
+    int infoCount;
+    const SkMemberInfo* info = GetMembers(maker, type, &infoCount);
+    info = SkMemberInfo::Find(info, infoCount, matchPtr);
+//  SkASSERT(info);
+    return info;
+}
+
+#undef CASE_GET_INFO
+#undef CASE_GET_DRAW_INFO
+#undef CASE_GET_DISPLAY_INFO
+
+#endif // SK_USE_CONDENSED_INFO == 0
+
+#if defined SK_DEBUG || defined SK_BUILD_CONDENSED
+    #define DRAW_NAME(_name, _type) {_name, _type, true, false }
+    #define DISPLAY_NAME(_name, _type) {_name, _type, false, true }
+    #define INIT_BOOL_FIELDS    , false, false
+#else
+    #define DRAW_NAME(_name, _type) {_name, _type }
+    #define DISPLAY_NAME(_name, _type) {_name, _type }
+    #define INIT_BOOL_FIELDS
+#endif
+
+const TypeNames gTypeNames[] = {
+    // unknown
+    { "Math", SkType_Math                       INIT_BOOL_FIELDS },
+    { "Number", SkType_Number                   INIT_BOOL_FIELDS },
+    { "add", SkType_Add                         INIT_BOOL_FIELDS },
+    { "addCircle", SkType_AddCircle             INIT_BOOL_FIELDS },
+    // addgeom
+    // addmode
+    { "addOval", SkType_AddOval                 INIT_BOOL_FIELDS },
+    { "addPath", SkType_AddPath                 INIT_BOOL_FIELDS },
+    { "addRect", SkType_AddRect                 INIT_BOOL_FIELDS },
+    { "addRoundRect", SkType_AddRoundRect       INIT_BOOL_FIELDS },
+    // align
+    { "animate", SkType_Animate                 INIT_BOOL_FIELDS },
+    // animateBase
+    { "apply", SkType_Apply                     INIT_BOOL_FIELDS },
+    // applymode
+    // applytransition
+    { "array", SkType_Array                     INIT_BOOL_FIELDS },
+    // argb
+    // base64
+    // basebitmap
+    // baseclassinfo
+    DRAW_NAME("bitmap", SkType_Bitmap),
+    // bitmapencoding
+    // bitmapformat
+    DRAW_NAME("bitmapShader", SkType_BitmapShader),
+    DRAW_NAME("blur", SkType_Blur),
+    { "boolean", SkType_Boolean                 INIT_BOOL_FIELDS },
+    // boundable
+    DISPLAY_NAME("bounds", SkType_Bounds),
+    // cap
+    { "clear", SkType_Clear                     INIT_BOOL_FIELDS },
+    DRAW_NAME("clip", SkType_Clip),
+    { "close", SkType_Close                     INIT_BOOL_FIELDS },
+    DRAW_NAME("color", SkType_Color),
+    { "cubicTo", SkType_CubicTo                 INIT_BOOL_FIELDS },
+    { "dash", SkType_Dash                       INIT_BOOL_FIELDS },
+    { "data", SkType_Data                       INIT_BOOL_FIELDS },
+    { "discrete", SkType_Discrete               INIT_BOOL_FIELDS },
+    // displayable
+    // drawable
+    { "drawTo", SkType_DrawTo                   INIT_BOOL_FIELDS },
+    { "dump", SkType_Dump                       INIT_BOOL_FIELDS },
+    // dynamicstring
+    DRAW_NAME("emboss", SkType_Emboss),
+    DISPLAY_NAME("event", SkType_Event),
+    // eventcode
+    // eventkind
+    // eventmode
+    // filltype
+    // filtertype
+    { "float", SkType_Float                     INIT_BOOL_FIELDS },
+    { "fromPath", SkType_FromPath               INIT_BOOL_FIELDS },
+    // frompathmode
+    { "full", SkType_Full                       INIT_BOOL_FIELDS },
+    // gradient
+    { "group", SkType_Group                     INIT_BOOL_FIELDS },
+    { "hitClear", SkType_HitClear               INIT_BOOL_FIELDS },
+    { "hitTest", SkType_HitTest                 INIT_BOOL_FIELDS },
+    { "image", SkType_Image                     INIT_BOOL_FIELDS },
+    { "include", SkType_Include                 INIT_BOOL_FIELDS },
+    { "input", SkType_Input                     INIT_BOOL_FIELDS },
+    { "int", SkType_Int                         INIT_BOOL_FIELDS },
+    // join
+    { "line", SkType_Line                       INIT_BOOL_FIELDS },
+    { "lineTo", SkType_LineTo                   INIT_BOOL_FIELDS },
+    { "linearGradient", SkType_LinearGradient   INIT_BOOL_FIELDS },
+    { "maskFilter", SkType_MaskFilter           INIT_BOOL_FIELDS },
+    // maskfilterblurstyle
+    // maskfilterlight
+    DRAW_NAME("matrix", SkType_Matrix),
+    // memberfunction
+    // memberproperty
+    { "move", SkType_Move                       INIT_BOOL_FIELDS },
+    { "moveTo", SkType_MoveTo                   INIT_BOOL_FIELDS },
+    { "movie", SkType_Movie                     INIT_BOOL_FIELDS },
+    // msec
+    { "oval", SkType_Oval                       INIT_BOOL_FIELDS },
+    DRAW_NAME("paint", SkType_Paint),
+    DRAW_NAME("path", SkType_Path),
+    // pathdirection
+    { "pathEffect", SkType_PathEffect           INIT_BOOL_FIELDS },
+    // point
+    DRAW_NAME("point", SkType_DrawPoint),
+    { "polyToPoly", SkType_PolyToPoly           INIT_BOOL_FIELDS },
+    { "polygon", SkType_Polygon                 INIT_BOOL_FIELDS },
+    { "polyline", SkType_Polyline               INIT_BOOL_FIELDS },
+    { "post", SkType_Post                       INIT_BOOL_FIELDS },
+    { "quadTo", SkType_QuadTo                   INIT_BOOL_FIELDS },
+    { "rCubicTo", SkType_RCubicTo               INIT_BOOL_FIELDS },
+    { "rLineTo", SkType_RLineTo                 INIT_BOOL_FIELDS },
+    { "rMoveTo", SkType_RMoveTo                 INIT_BOOL_FIELDS },
+    { "rQuadTo", SkType_RQuadTo                 INIT_BOOL_FIELDS },
+    { "radialGradient", SkType_RadialGradient   INIT_BOOL_FIELDS },
+    DISPLAY_NAME("random", SkType_Random),
+    { "rect", SkType_Rect                       INIT_BOOL_FIELDS },
+    { "rectToRect", SkType_RectToRect           INIT_BOOL_FIELDS },
+    { "remove", SkType_Remove                   INIT_BOOL_FIELDS },
+    { "replace", SkType_Replace                 INIT_BOOL_FIELDS },
+    { "rotate", SkType_Rotate                   INIT_BOOL_FIELDS },
+    { "roundRect", SkType_RoundRect             INIT_BOOL_FIELDS },
+    { "save", SkType_Save                       INIT_BOOL_FIELDS },
+    { "saveLayer", SkType_SaveLayer             INIT_BOOL_FIELDS },
+    { "scale", SkType_Scale                     INIT_BOOL_FIELDS },
+    // screenplay
+    { "set", SkType_Set                         INIT_BOOL_FIELDS },
+    { "shader", SkType_Shader                   INIT_BOOL_FIELDS },
+    { "skew", SkType_Skew                       INIT_BOOL_FIELDS },
+    { "skia3d:camera", SkType_3D_Camera         INIT_BOOL_FIELDS },
+    { "skia3d:patch", SkType_3D_Patch           INIT_BOOL_FIELDS },
+    // point
+    { "snapshot", SkType_Snapshot               INIT_BOOL_FIELDS },
+    { "string", SkType_String                   INIT_BOOL_FIELDS },
+    // style
+    { "text", SkType_Text                       INIT_BOOL_FIELDS },
+    { "textBox", SkType_TextBox                 INIT_BOOL_FIELDS },
+    // textboxalign
+    // textboxmode
+    { "textOnPath", SkType_TextOnPath           INIT_BOOL_FIELDS },
+    { "textToPath", SkType_TextToPath           INIT_BOOL_FIELDS },
+    // tilemode
+    { "translate", SkType_Translate             INIT_BOOL_FIELDS },
+    DRAW_NAME("transparentShader", SkType_TransparentShader),
+    { "typeface", SkType_Typeface               INIT_BOOL_FIELDS }
+    // xfermode
+    // knumberoftypes
+};
+
+const int kTypeNamesSize = SK_ARRAY_COUNT(gTypeNames);
+
+SkDisplayTypes SkDisplayType::Find(SkAnimateMaker* maker, const SkMemberInfo* match) {
+    for (int index = 0; index < kTypeNamesSize; index++) {
+        SkDisplayTypes type = gTypeNames[index].fType;
+        const SkMemberInfo* info = SkDisplayType::GetMembers(maker, type, NULL);
+        if (info == match)
+            return type;
+    }
+    return (SkDisplayTypes) -1;
+}
+
+// !!! optimize this by replacing function with a byte-sized lookup table
+SkDisplayTypes SkDisplayType::GetParent(SkAnimateMaker* maker, SkDisplayTypes base) {
+    if (base == SkType_Group || base == SkType_Save || base == SkType_SaveLayer)        //!!! cheat a little until we have a lookup table
+        return SkType_Displayable;
+    if (base == SkType_Set)
+        return SkType_Animate;  // another cheat until we have a lookup table
+    const SkMemberInfo* info = GetMembers(maker, base, NULL); // get info for this type
+    SkASSERT(info);
+    if (info->fType != SkType_BaseClassInfo)
+        return SkType_Unknown; // if no base, done
+    // !!! could change SK_MEMBER_INHERITED macro to take type, stuff in offset, so that 
+    // this (and table builder) could know type without the following steps:
+    const SkMemberInfo* inherited = info->getInherited();
+    SkDisplayTypes result = (SkDisplayTypes) (SkType_Unknown + 1);
+    for (; result <= SkType_Xfermode; result = (SkDisplayTypes) (result + 1)) {
+        const SkMemberInfo* match = GetMembers(maker, result, NULL);
+        if (match == inherited)
+            break;
+    }
+    SkASSERT(result <= SkType_Xfermode);
+    return result;
+}
+
+SkDisplayTypes SkDisplayType::GetType(SkAnimateMaker* maker, const char match[], size_t len ) {
+    int index = SkStrSearch(&gTypeNames[0].fName, kTypeNamesSize, match, 
+        len, sizeof(gTypeNames[0]));
+    if (index >= 0 && index < kTypeNamesSize)
+        return gTypeNames[index].fType;
+    SkExtras** end = maker->fExtras.end();
+    for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) {
+        SkDisplayTypes result = (*extraPtr)->getType(match, len);
+        if (result != SkType_Unknown)
+            return result;
+    }
+    return (SkDisplayTypes) -1;
+}
+
+bool SkDisplayType::IsEnum(SkAnimateMaker* , SkDisplayTypes type) {
+    switch (type) {
+        case SkType_AddMode:
+        case SkType_Align:
+        case SkType_ApplyMode:
+        case SkType_ApplyTransition:
+        case SkType_BitmapEncoding:
+        case SkType_BitmapFormat:
+        case SkType_Boolean:
+        case SkType_Cap:
+        case SkType_EventCode:
+        case SkType_EventKind:
+        case SkType_EventMode:
+        case SkType_FillType:
+        case SkType_FilterType:
+        case SkType_FontStyle:
+        case SkType_FromPathMode:
+        case SkType_Join:
+        case SkType_MaskFilterBlurStyle:
+        case SkType_PathDirection:
+        case SkType_Style:
+        case SkType_TextBoxAlign:
+        case SkType_TextBoxMode:
+        case SkType_TileMode:
+        case SkType_Xfermode:
+            return true;
+        default:    // to avoid warnings
+            break;
+    }
+    return false;
+}
+
+bool SkDisplayType::IsDisplayable(SkAnimateMaker* , SkDisplayTypes type) {
+    switch (type) {
+        case SkType_Add:
+        case SkType_AddCircle:
+        case SkType_AddOval:
+        case SkType_AddPath:
+        case SkType_AddRect:
+        case SkType_AddRoundRect:
+        case SkType_Animate:
+        case SkType_AnimateBase:
+        case SkType_Apply:
+        case SkType_BaseBitmap:
+        case SkType_Bitmap:
+        case SkType_BitmapShader:
+        case SkType_Blur:
+        case SkType_Clear:
+        case SkType_Clip:
+        case SkType_Close:
+        case SkType_Color:
+        case SkType_CubicTo:
+        case SkType_Dash:
+        case SkType_Data:
+        case SkType_Discrete:
+        case SkType_Displayable:
+        case SkType_Drawable:
+        case SkType_DrawTo:
+        case SkType_Emboss:
+        case SkType_Event:
+        case SkType_FromPath:
+        case SkType_Full:
+        case SkType_Group:
+        case SkType_Image:
+        case SkType_Input:
+        case SkType_Line:
+        case SkType_LineTo:
+        case SkType_LinearGradient:
+        case SkType_Matrix:
+        case SkType_Move:
+        case SkType_MoveTo:
+        case SkType_Movie:
+        case SkType_Oval:
+        case SkType_Paint:
+        case SkType_Path:
+        case SkType_PolyToPoly:
+        case SkType_Polygon:
+        case SkType_Polyline:
+        case SkType_Post:
+        case SkType_QuadTo:
+        case SkType_RCubicTo:
+        case SkType_RLineTo:
+        case SkType_RMoveTo:
+        case SkType_RQuadTo:
+        case SkType_RadialGradient:
+        case SkType_Random:
+        case SkType_Rect:
+        case SkType_RectToRect:
+        case SkType_Remove:
+        case SkType_Replace:
+        case SkType_Rotate:
+        case SkType_RoundRect:
+        case SkType_Save:
+        case SkType_SaveLayer:
+        case SkType_Scale:
+        case SkType_Set:
+        case SkType_Shader:
+        case SkType_Skew:
+        case SkType_3D_Camera:
+        case SkType_3D_Patch:
+        case SkType_Snapshot:
+        case SkType_Text:
+        case SkType_TextBox:
+        case SkType_TextOnPath:
+        case SkType_TextToPath:
+        case SkType_Translate:
+        case SkType_TransparentShader:
+            return true;
+        default:    // to avoid warnings
+            break;
+    }
+    return false;
+}
+
+bool SkDisplayType::IsStruct(SkAnimateMaker* , SkDisplayTypes type) {
+    switch (type) {
+        case SkType_Point:
+        case SkType_3D_Point:
+            return true;
+        default:    // to avoid warnings
+            break;
+    }
+    return false;
+}
+
+
+SkDisplayTypes SkDisplayType::RegisterNewType() {
+    gNewTypes = (SkDisplayTypes) (gNewTypes + 1);
+    return gNewTypes;
+}
+
+
+
+#ifdef SK_DEBUG
+const char* SkDisplayType::GetName(SkAnimateMaker* maker, SkDisplayTypes type) {
+    for (int index = 0; index < kTypeNamesSize - 1; index++) {
+        if (gTypeNames[index].fType == type)
+            return gTypeNames[index].fName;
+    }
+    SkExtras** end = maker->fExtras.end();
+    for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) {
+        const char* result = (*extraPtr)->getName(type);
+        if (result != NULL)
+            return result;
+    }
+    return NULL;
+}
+#endif
+
+#ifdef SK_SUPPORT_UNITTEST
+void SkDisplayType::UnitTest() {
+    SkAnimator animator;
+    SkAnimateMaker* maker = animator.fMaker;
+    int index;
+    for (index = 0; index < kTypeNamesSize - 1; index++) {
+        SkASSERT(strcmp(gTypeNames[index].fName, gTypeNames[index + 1].fName) < 0);
+        SkASSERT(gTypeNames[index].fType < gTypeNames[index + 1].fType);
+    }
+    for (index = 0; index < kTypeNamesSize; index++) {
+        SkDisplayable* test = CreateInstance(maker, gTypeNames[index].fType);
+        if (test == NULL)
+            continue;
+#if defined _WIN32 && _MSC_VER >= 1300  && defined _INC_CRTDBG // only on windows, only if using "crtdbg.h"
+    // we know that crtdbg puts 0xfdfdfdfd at the end of the block
+    // look for unitialized memory, signature 0xcdcdcdcd prior to that
+        int* start = (int*) test;
+        while (*start != 0xfdfdfdfd) {
+            SkASSERT(*start != 0xcdcdcdcd);
+            start++;
+        }
+#endif
+        delete test;
+    }
+    for (index = 0; index < kTypeNamesSize; index++) {
+        int infoCount;
+        const SkMemberInfo* info = GetMembers(maker, gTypeNames[index].fType, &infoCount);
+        if (info == NULL)
+            continue;
+#if SK_USE_CONDENSED_INFO == 0
+        for (int inner = 0; inner < infoCount - 1; inner++) {
+            if (info[inner].fType == SkType_BaseClassInfo)
+                continue;
+            SkASSERT(strcmp(info[inner].fName, info[inner + 1].fName) < 0);
+        }
+#endif
+    }
+#if defined SK_DEBUG || defined SK_BUILD_CONDENSED
+    BuildCondensedInfo(maker);
+#endif
+}
+#endif
diff --git a/src/animator/SkDisplayType.h b/src/animator/SkDisplayType.h
new file mode 100644
index 0000000..a29b285
--- /dev/null
+++ b/src/animator/SkDisplayType.h
@@ -0,0 +1,216 @@
+/* libs/graphics/animator/SkDisplayType.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 SkDisplayType_DEFINED
+#define SkDisplayType_DEFINED
+
+#include "SkMath.h"
+#include "SkScalar.h"
+
+#ifdef SK_DEBUG
+    #ifdef SK_CAN_USE_FLOAT
+        #define SK_DUMP_ENABLED
+    #endif
+    #ifdef SK_BUILD_FOR_MAC
+        #define SK_FIND_LEAKS
+    #endif
+#endif
+
+#define SK_LITERAL_STR_EQUAL(str, token, len) (sizeof(str) - 1 == len \
+    && strncmp(str, token, sizeof(str) - 1) == 0)
+
+class SkAnimateMaker;
+class SkDisplayable;
+struct SkMemberInfo;
+
+enum SkDisplayTypes {
+    SkType_Unknown,
+    SkType_Math, // for ecmascript compatible Math functions and constants
+    SkType_Number,  // for for ecmascript compatible Number functions and constants
+    SkType_Add,
+    SkType_AddCircle,
+    SkType_AddGeom,
+    SkType_AddMode,
+    SkType_AddOval,
+    SkType_AddPath,
+    SkType_AddRect, // path part
+    SkType_AddRoundRect,
+    SkType_Align,
+    SkType_Animate,
+    SkType_AnimateBase, // base type for animate, set
+    SkType_Apply,
+    SkType_ApplyMode,
+    SkType_ApplyTransition,
+    SkType_Array,
+    SkType_ARGB,
+    SkType_Base64,
+    SkType_BaseBitmap,
+    SkType_BaseClassInfo,
+    SkType_Bitmap,
+    SkType_BitmapEncoding,
+    SkType_BitmapFormat,
+    SkType_BitmapShader,
+    SkType_Blur,
+    SkType_Boolean, // can have values -1 (uninitialized), 0, 1
+    SkType_Boundable,
+    SkType_Bounds,
+    SkType_Cap,
+    SkType_Clear,
+    SkType_Clip,
+    SkType_Close,
+    SkType_Color,
+    SkType_CubicTo,
+    SkType_Dash,
+    SkType_Data,
+    SkType_Discrete,
+    SkType_Displayable,
+    SkType_Drawable,
+    SkType_DrawTo,
+    SkType_Dump,
+    SkType_DynamicString,   // evaluate at draw time
+    SkType_Emboss,
+    SkType_Event,
+    SkType_EventCode,
+    SkType_EventKind,
+    SkType_EventMode,
+    SkType_FillType,
+    SkType_FilterType,
+    SkType_Float,
+    SkType_FontStyle,
+    SkType_FromPath,
+    SkType_FromPathMode,
+    SkType_Full,
+    SkType_Gradient,
+    SkType_Group,
+    SkType_HitClear,
+    SkType_HitTest,
+    SkType_Image,
+    SkType_Include,
+    SkType_Input,
+    SkType_Int,
+    SkType_Join,
+    SkType_Line, // simple line primitive
+    SkType_LineTo, // used as part of path construction
+    SkType_LinearGradient,
+    SkType_MaskFilter,
+    SkType_MaskFilterBlurStyle,
+    SkType_MaskFilterLight,
+    SkType_Matrix,
+    SkType_MemberFunction,
+    SkType_MemberProperty,
+    SkType_Move,
+    SkType_MoveTo,
+    SkType_Movie,
+    SkType_MSec,
+    SkType_Oval,
+    SkType_Paint,
+    SkType_Path,
+    SkType_PathDirection,
+    SkType_PathEffect,
+    SkType_Point,   // used inside other structures, no vtable
+    SkType_DrawPoint, // used to draw points, has a vtable
+    SkType_PolyToPoly,
+    SkType_Polygon,
+    SkType_Polyline,
+    SkType_Post,
+    SkType_QuadTo,
+    SkType_RCubicTo,
+    SkType_RLineTo,
+    SkType_RMoveTo,
+    SkType_RQuadTo,
+    SkType_RadialGradient,
+    SkType_Random,
+    SkType_Rect,
+    SkType_RectToRect,
+    SkType_Remove,
+    SkType_Replace,
+    SkType_Rotate,
+    SkType_RoundRect,
+    SkType_Save,
+    SkType_SaveLayer,
+    SkType_Scale,
+    SkType_Screenplay,
+    SkType_Set,
+    SkType_Shader,
+    SkType_Skew,
+    SkType_3D_Camera,
+    SkType_3D_Patch,
+    SkType_3D_Point,
+    SkType_Snapshot,
+    SkType_String,  // pointer to SkString
+    SkType_Style,
+    SkType_Text,
+    SkType_TextBox,
+    SkType_TextBoxAlign,
+    SkType_TextBoxMode,
+    SkType_TextOnPath,
+    SkType_TextToPath,
+    SkType_TileMode,
+    SkType_Translate,
+    SkType_TransparentShader,
+    SkType_Typeface,
+    SkType_Xfermode,
+    kNumberOfTypes
+};
+
+struct TypeNames {
+    const char* fName;
+    SkDisplayTypes fType;
+#if defined SK_DEBUG || defined SK_BUILD_CONDENSED
+    bool fDrawPrefix;
+    bool fDisplayPrefix;
+#endif
+};
+
+#ifdef SK_DEBUG
+typedef SkDisplayTypes SkFunctionParamType;
+#else
+typedef unsigned char SkFunctionParamType;
+#endif
+
+extern const TypeNames gTypeNames[];
+extern const int kTypeNamesSize;
+
+class SkDisplayType {
+public:
+    static SkDisplayTypes Find(SkAnimateMaker* , const SkMemberInfo* );
+    static const SkMemberInfo* GetMember(SkAnimateMaker* , SkDisplayTypes , const char** );
+    static const SkMemberInfo* GetMembers(SkAnimateMaker* , SkDisplayTypes , int* infoCountPtr);
+    static SkDisplayTypes GetParent(SkAnimateMaker* , SkDisplayTypes );
+    static bool IsDisplayable(SkAnimateMaker* , SkDisplayTypes );
+    static bool IsEnum(SkAnimateMaker* , SkDisplayTypes );
+    static bool IsStruct(SkAnimateMaker* , SkDisplayTypes );
+    static SkDisplayTypes RegisterNewType();
+    static SkDisplayTypes Resolve(const char[] , const SkMemberInfo** );
+#ifdef SK_DEBUG
+    static bool IsAnimate(SkDisplayTypes type ) { return type == SkType_Animate || 
+        type == SkType_Set; }
+    static const char* GetName(SkAnimateMaker* , SkDisplayTypes );
+#endif
+#ifdef SK_SUPPORT_UNITTEST
+    static void UnitTest();
+#endif
+#if defined SK_DEBUG || defined SK_BUILD_CONDENSED
+    static void BuildCondensedInfo(SkAnimateMaker* );
+#endif
+    static SkDisplayTypes GetType(SkAnimateMaker* , const char[] , size_t len);
+    static SkDisplayable* CreateInstance(SkAnimateMaker* , SkDisplayTypes );
+private:
+    static SkDisplayTypes gNewTypes;
+};
+
+#endif // SkDisplayType_DEFINED
diff --git a/src/animator/SkDisplayTypes.cpp b/src/animator/SkDisplayTypes.cpp
new file mode 100644
index 0000000..b5bc573
--- /dev/null
+++ b/src/animator/SkDisplayTypes.cpp
@@ -0,0 +1,229 @@
+/* libs/graphics/animator/SkDisplayTypes.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 "SkDisplayTypes.h"
+#include "SkAnimateBase.h"
+
+bool SkDisplayDepend::canContainDependents() const {
+    return true; 
+}
+
+void SkDisplayDepend::dirty() {
+    SkDisplayable** last = fDependents.end();
+    for (SkDisplayable** depPtr = fDependents.begin(); depPtr < last; depPtr++) {
+        SkAnimateBase* animate = (SkAnimateBase* ) *depPtr;
+        animate->setChanged(true);
+    }
+}
+
+// Boolean
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayBoolean::fInfo[] = {
+    SK_MEMBER(value, Boolean)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayBoolean);
+
+SkDisplayBoolean::SkDisplayBoolean() : value(false) {
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayBoolean::dump(SkAnimateMaker* maker){
+    dumpBase(maker);
+    SkDebugf("value=\"%s\" />\n", value ? "true" : "false");
+}
+#endif
+
+// int32_t
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayInt::fInfo[] = {
+    SK_MEMBER(value, Int)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayInt);
+
+SkDisplayInt::SkDisplayInt() : value(0) {
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayInt::dump(SkAnimateMaker* maker){
+    dumpBase(maker);
+    SkDebugf("value=\"%d\" />\n", value);
+}
+#endif
+
+// SkScalar
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayFloat::fInfo[] = {
+    SK_MEMBER(value, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayFloat);
+
+SkDisplayFloat::SkDisplayFloat() : value(0) {
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayFloat::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+#ifdef SK_CAN_USE_FLOAT
+    SkDebugf("value=\"%g\" />\n", SkScalarToFloat(value));
+#else
+    SkDebugf("value=\"%x\" />\n", value);
+#endif
+}
+#endif
+
+// SkString
+enum SkDisplayString_Functions {
+    SK_FUNCTION(slice)
+};
+
+enum SkDisplayString_Properties {
+    SK_PROPERTY(length)
+};
+
+const SkFunctionParamType SkDisplayString::fFunctionParameters[] = {
+    (SkFunctionParamType) SkType_Int,   // slice
+    (SkFunctionParamType) SkType_Int,
+    (SkFunctionParamType) 0
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayString::fInfo[] = {
+    SK_MEMBER_PROPERTY(length, Int),
+    SK_MEMBER_FUNCTION(slice, String),
+    SK_MEMBER(value, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayString);
+
+SkDisplayString::SkDisplayString() {
+}
+
+SkDisplayString::SkDisplayString(SkString& copyFrom) : value(copyFrom) {
+}
+
+void SkDisplayString::executeFunction(SkDisplayable* target, int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* scriptValue) {
+    if (scriptValue == NULL)
+        return;
+    SkASSERT(target == this);
+    switch (index) {
+        case SK_FUNCTION(slice):
+            scriptValue->fType = SkType_String;
+            SkASSERT(parameters[0].fType == SkType_Int);
+            int start =  parameters[0].fOperand.fS32;
+            if (start < 0)
+                start = (int) (value.size() - start);
+            int end = (int) value.size();
+            if (parameters.count() > 1) {
+                SkASSERT(parameters[1].fType == SkType_Int);
+                end = parameters[1].fOperand.fS32;
+            }
+            //if (end >= 0 && end < (int) value.size())
+            if (end >= 0 && end <= (int) value.size())
+                scriptValue->fOperand.fString = new SkString(&value.c_str()[start], end - start);
+            else
+                scriptValue->fOperand.fString = new SkString(value);
+        break;
+    }
+}
+
+const SkFunctionParamType* SkDisplayString::getFunctionsParameters() {
+    return fFunctionParameters;
+}
+
+bool SkDisplayString::getProperty(int index, SkScriptValue* scriptValue) const {
+    switch (index) { 
+        case SK_PROPERTY(length):
+            scriptValue->fType = SkType_Int;
+            scriptValue->fOperand.fS32 = (int32_t) value.size();
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+
+// SkArray
+#if 0   // !!! reason enough to qualify enum with class name or move typedArray into its own file
+enum SkDisplayArray_Properties {
+    SK_PROPERTY(length)
+};
+#endif
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDisplayArray::fInfo[] = {
+    SK_MEMBER_PROPERTY(length, Int),
+    SK_MEMBER_ARRAY(values, Unknown)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDisplayArray);
+
+SkDisplayArray::SkDisplayArray() {
+}
+
+SkDisplayArray::SkDisplayArray(SkTypedArray& copyFrom) : values(copyFrom) {
+
+}
+
+SkDisplayArray::~SkDisplayArray() {
+    if (values.getType() == SkType_String) {
+        for (int index = 0; index < values.count(); index++)
+            delete values[index].fString;
+        return;
+    }
+    if (values.getType() == SkType_Array) {
+        for (int index = 0; index < values.count(); index++)
+            delete values[index].fArray;
+    }
+}
+
+bool SkDisplayArray::getProperty(int index, SkScriptValue* value) const {
+    switch (index) { 
+        case SK_PROPERTY(length):
+            value->fType = SkType_Int;
+            value->fOperand.fS32 = values.count();
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+
+
diff --git a/src/animator/SkDisplayTypes.h b/src/animator/SkDisplayTypes.h
new file mode 100644
index 0000000..0ac57ba
--- /dev/null
+++ b/src/animator/SkDisplayTypes.h
@@ -0,0 +1,115 @@
+/* libs/graphics/animator/SkDisplayTypes.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 SkDisplayTypes_DEFINED
+#define SkDisplayTypes_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkTypedArray.h"
+
+class SkOpArray; // compiled script experiment
+
+
+class SkDisplayDepend : public SkDisplayable {
+public:
+    virtual bool canContainDependents() const;
+    void addDependent(SkDisplayable* displayable) {
+        if (fDependents.find(displayable) < 0)
+            *fDependents.append() = displayable;
+    }
+    virtual void dirty();
+private:
+    SkTDDisplayableArray fDependents;
+    typedef SkDisplayable INHERITED;
+};
+
+class SkDisplayBoolean : public SkDisplayDepend {
+    DECLARE_DISPLAY_MEMBER_INFO(Boolean);
+    SkDisplayBoolean();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    SkBool value;
+    friend class SkAnimatorScript;
+    friend class SkAnimatorScript_Box;
+    friend class SkAnimatorScript_Unbox;
+    typedef SkDisplayDepend INHERITED;
+};
+
+class SkDisplayInt : public SkDisplayDepend {
+    DECLARE_DISPLAY_MEMBER_INFO(Int);
+    SkDisplayInt();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+private:
+    int32_t value;
+    friend class SkAnimatorScript;
+    friend class SkAnimatorScript_Box;
+    friend class SkAnimatorScript_Unbox;
+    typedef SkDisplayDepend INHERITED;
+};
+
+class SkDisplayFloat : public SkDisplayDepend {
+    DECLARE_DISPLAY_MEMBER_INFO(Float);
+    SkDisplayFloat();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+private:
+    SkScalar value;
+    friend class SkAnimatorScript;
+    friend class SkAnimatorScript_Box;
+    friend class SkAnimatorScript_Unbox;
+    typedef SkDisplayDepend INHERITED;
+};
+
+class SkDisplayString : public SkDisplayDepend {
+    DECLARE_DISPLAY_MEMBER_INFO(String);
+    SkDisplayString();
+    SkDisplayString(SkString& );
+    virtual void executeFunction(SkDisplayable* , int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* );
+    virtual const SkFunctionParamType* getFunctionsParameters();
+    virtual bool getProperty(int index, SkScriptValue* ) const;
+    SkString value;
+private:
+    static const SkFunctionParamType fFunctionParameters[];
+};
+
+class SkDisplayArray : public SkDisplayDepend {
+    DECLARE_DISPLAY_MEMBER_INFO(Array);
+    SkDisplayArray();
+    SkDisplayArray(SkTypedArray& );
+    SkDisplayArray(SkOpArray& ); // compiled script experiment
+    virtual ~SkDisplayArray();
+    virtual bool getProperty(int index, SkScriptValue* ) const;
+private:
+    SkTypedArray values;
+    friend class SkAnimator;
+    friend class SkAnimatorScript;
+    friend class SkAnimatorScript2;
+    friend class SkAnimatorScript_Unbox;
+    friend class SkDisplayable;
+    friend struct SkMemberInfo;
+    friend class SkScriptEngine;
+};
+
+#endif // SkDisplayTypes_DEFINED
+
diff --git a/src/animator/SkDisplayXMLParser.cpp b/src/animator/SkDisplayXMLParser.cpp
new file mode 100644
index 0000000..d2775e8
--- /dev/null
+++ b/src/animator/SkDisplayXMLParser.cpp
@@ -0,0 +1,318 @@
+/* libs/graphics/animator/SkDisplayXMLParser.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 "SkDisplayXMLParser.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayApply.h"
+#include "SkUtils.h"
+#ifdef SK_DEBUG
+#include "SkTime.h"
+#endif
+
+static char const* const gErrorStrings[] = {
+    "unknown error ",
+    "apply scopes itself",
+    "display tree too deep (circular reference?) ",
+    "element missing parent ",
+    "element type not allowed in parent ",
+    "error adding <data> to <post> ",
+    "error adding to <matrix> ",
+    "error adding to <paint> ",
+    "error adding to <path> ",
+    "error in attribute value ",
+    "error in script ",
+    "expected movie in sink attribute ",
+    "field not in target ",
+    "number of offsets in gradient must match number of colors",
+    "no offset in gradient may be greater than one",
+    "last offset in gradient must be one",
+    "offsets in gradient must be increasing",
+    "first offset in gradient must be zero",
+    "gradient attribute \"points\" must have length of four", 
+    "in include ",
+    "in movie ",
+    "include name unknown or missing ",
+    "index out of range ",
+    "movie name unknown or missing ",
+    "no parent available to resolve sink attribute ",
+    "parent element can't contain ",
+    "saveLayer must specify a bounds",
+    "target id not found ",
+    "unexpected type "
+};
+
+SkDisplayXMLParserError::~SkDisplayXMLParserError() {
+}
+
+void SkDisplayXMLParserError::getErrorString(SkString* str) const {
+    if (fCode > kUnknownError)
+        str->set(gErrorStrings[fCode - kUnknownError]);
+    else
+        str->reset();
+    INHERITED::getErrorString(str);
+}
+
+void SkDisplayXMLParserError::setInnerError(SkAnimateMaker* parent, const SkString& src) {
+    SkString inner;
+    getErrorString(&inner);
+    inner.prepend(": ");
+    inner.prependS32(getLineNumber());
+    inner.prepend(", line ");
+    inner.prepend(src);
+    parent->setErrorNoun(inner);
+}
+
+
+SkDisplayXMLParser::SkDisplayXMLParser(SkAnimateMaker& maker)
+    : INHERITED(&maker.fError), fMaker(maker), fInInclude(maker.fInInclude), 
+        fInSkia(maker.fInInclude), fCurrDisplayable(NULL)
+{
+}
+
+SkDisplayXMLParser::~SkDisplayXMLParser() {
+    if (fCurrDisplayable && fMaker.fChildren.find(fCurrDisplayable) < 0)
+        delete fCurrDisplayable;
+    for (Parent* parPtr = fParents.begin() + 1; parPtr < fParents.end(); parPtr++) {
+        SkDisplayable* displayable = parPtr->fDisplayable;
+        if (displayable == fCurrDisplayable)
+            continue;
+        SkASSERT(fMaker.fChildren.find(displayable) < 0);
+        if (fMaker.fHelpers.find(displayable) < 0)
+            delete displayable;
+    }
+}
+
+
+
+bool SkDisplayXMLParser::onAddAttribute(const char name[], const char value[]) {
+    return onAddAttributeLen(name, value, strlen(value));
+}
+
+bool SkDisplayXMLParser::onAddAttributeLen(const char attrName[], const char attrValue[], 
+                                        size_t attrValueLen)
+{
+    if (fCurrDisplayable == NULL)    // this signals we should ignore attributes for this element
+        return strncmp(attrName, "xmlns", sizeof("xmlns") - 1) != 0;
+    SkDisplayable*  displayable = fCurrDisplayable;
+    SkDisplayTypes  type = fCurrType;
+
+    if (strcmp(attrName, "id") == 0) {
+        if (fMaker.find(attrValue, attrValueLen, NULL)) {
+            fError->setNoun(attrValue, attrValueLen);
+            fError->setCode(SkXMLParserError::kDuplicateIDs);
+            return true;
+        }
+#ifdef SK_DEBUG
+        displayable->_id.set(attrValue, attrValueLen);
+        displayable->id = displayable->_id.c_str();
+#endif
+        fMaker.idsSet(attrValue, attrValueLen, displayable);
+        int parentIndex = fParents.count() - 1;
+        if (parentIndex > 0) {
+            SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable;
+            parent->setChildHasID();
+        }
+        return false;
+    }
+    const char* name = attrName;
+    const SkMemberInfo* info = SkDisplayType::GetMember(&fMaker, type, &name);
+    if (info == NULL) {
+        fError->setNoun(name);
+        fError->setCode(SkXMLParserError::kUnknownAttributeName);
+        return true;
+    }
+    if (info->setValue(fMaker, NULL, 0, info->getCount(), displayable, info->getType(), attrValue,
+            attrValueLen))
+        return false;
+    if (fMaker.fError.hasError()) {
+        fError->setNoun(attrValue, attrValueLen);
+        return true;
+    }
+    SkDisplayable* ref = NULL;
+    if (fMaker.find(attrValue, attrValueLen, &ref) == false) {
+        ref = fMaker.createInstance(attrValue, attrValueLen);
+        if (ref == NULL) {
+            fError->setNoun(attrValue, attrValueLen);
+            fError->setCode(SkXMLParserError::kErrorInAttributeValue);
+            return true;
+        } else
+            fMaker.helperAdd(ref);
+    }
+    if (info->fType != SkType_MemberProperty) {
+        fError->setNoun(name);
+        fError->setCode(SkXMLParserError::kUnknownAttributeName);
+        return true;
+    }
+    SkScriptValue scriptValue;
+    scriptValue.fOperand.fDisplayable = ref;
+    scriptValue.fType = ref->getType();
+    displayable->setProperty(info->propertyIndex(), scriptValue);
+    return false;
+}
+
+bool SkDisplayXMLParser::onEndElement(const char elem[])
+{
+    int parentIndex = fParents.count() - 1;
+    if (parentIndex >= 0) {
+        Parent& container = fParents[parentIndex];
+        SkDisplayable* displayable = container.fDisplayable;
+        fMaker.fEndDepth = parentIndex;
+        displayable->onEndElement(fMaker);
+        if (fMaker.fError.hasError()) 
+            return true;
+        if (parentIndex > 0) {
+            SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable;
+            bool result = parent->add(fMaker, displayable);
+            if (fMaker.hasError()) 
+                return true;
+            if (result == false) {
+                int infoCount;
+                const SkMemberInfo* info = 
+                    SkDisplayType::GetMembers(&fMaker, fParents[parentIndex - 1].fType, &infoCount);
+                const SkMemberInfo* foundInfo;
+                if ((foundInfo = searchContainer(info, infoCount)) != NULL) {
+                    parent->setReference(foundInfo, displayable);
+        //          if (displayable->isHelper() == false)
+                        fMaker.helperAdd(displayable);
+                } else {
+                    fMaker.setErrorCode(SkDisplayXMLParserError::kElementTypeNotAllowedInParent);
+                    return true;
+                }
+            }
+            if (parent->childrenNeedDisposing())
+                delete displayable;
+        }
+        fParents.remove(parentIndex);
+    }
+    fCurrDisplayable = NULL;
+    if (fInInclude == false && strcasecmp(elem, "screenplay") == 0) {
+        if (fMaker.fInMovie == false) {
+            fMaker.fEnableTime = fMaker.getAppTime();
+#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
+            if (fMaker.fDebugTimeBase == (SkMSec) -1)
+                fMaker.fDebugTimeBase = fMaker.fEnableTime;
+            SkString debugOut;
+            SkMSec time = fMaker.getAppTime();
+            debugOut.appendS32(time - fMaker.fDebugTimeBase);
+            debugOut.append(" onLoad enable=");
+            debugOut.appendS32(fMaker.fEnableTime - fMaker.fDebugTimeBase);
+            SkDebugf("%s\n", debugOut.c_str());
+#endif
+            fMaker.fEvents.doEvent(fMaker, SkDisplayEvent::kOnload, NULL);
+            if (fMaker.fError.hasError()) 
+                return true;
+            fMaker.fEvents.removeEvent(SkDisplayEvent::kOnload, NULL);
+
+        }
+        fInSkia = false;
+    }
+    return false;
+}
+
+bool SkDisplayXMLParser::onStartElement(const char name[])
+{
+    return onStartElementLen(name, strlen(name));
+}
+
+bool SkDisplayXMLParser::onStartElementLen(const char name[], size_t len) {
+    fCurrDisplayable = NULL; // init so we'll ignore attributes if we exit early
+
+    if (strncasecmp(name, "screenplay", len) == 0) {
+        fInSkia = true;
+        if (fInInclude == false)
+            fMaker.idsSet(name, len, &fMaker.fScreenplay);
+        return false;
+    }
+    if (fInSkia == false)
+        return false;
+
+    SkDisplayable* displayable = fMaker.createInstance(name, len);
+    if (displayable == NULL) {
+        fError->setNoun(name, len);
+        fError->setCode(SkXMLParserError::kUnknownElement);
+        return true;
+    }
+    SkDisplayTypes type = displayable->getType();
+    Parent record = { displayable, type };
+    *fParents.append() = record;
+    if (fParents.count() == 1)
+        fMaker.childrenAdd(displayable);
+    else {
+        Parent* parent = fParents.end() - 2;
+        if (displayable->setParent(parent->fDisplayable)) {
+            fError->setNoun(name, len);
+            getError()->setCode(SkDisplayXMLParserError::kParentElementCantContain);
+            return true;
+        }
+    }
+
+    // set these for subsequent calls to addAttribute()
+    fCurrDisplayable = displayable;
+    fCurrType = type;
+    return false;
+}
+
+const SkMemberInfo* SkDisplayXMLParser::searchContainer(const SkMemberInfo* infoBase,
+                                                         int infoCount) {
+    const SkMemberInfo* bestDisplayable = NULL;
+    const SkMemberInfo* lastResort = NULL;
+    for (int index = 0; index < infoCount; index++) {
+        const SkMemberInfo* info = &infoBase[index];
+        if (info->fType == SkType_BaseClassInfo) {
+            const SkMemberInfo* inherited = info->getInherited();
+            const SkMemberInfo* result = searchContainer(inherited, info->fCount);
+            if (result != NULL)
+                return result;
+            continue;
+        }
+        Parent* container = fParents.end() - 1;
+        SkDisplayTypes type = (SkDisplayTypes) info->fType;
+        if (type == SkType_MemberProperty) 
+            type = info->propertyType();
+        SkDisplayTypes containerType = container->fType;
+        if (type == containerType && (type == SkType_Rect || type == SkType_Polygon ||
+            type == SkType_Array || type == SkType_Int || type == SkType_Bitmap))
+            goto rectNext;
+        while (type != containerType) {
+            if (containerType == SkType_Displayable)
+                goto next;
+            containerType = SkDisplayType::GetParent(&fMaker, containerType);
+            if (containerType == SkType_Unknown)
+                goto next;
+        }
+        return info;
+next:
+        if (type == SkType_Drawable || type == SkType_Displayable && 
+            container->fDisplayable->isDrawable()) {
+rectNext:
+            if (fParents.count() > 1) {
+                Parent* parent = fParents.end() - 2;
+                if (info == parent->fDisplayable->preferredChild(type))
+                    bestDisplayable = info;
+                else
+                    lastResort = info;
+            }
+        }
+    }
+    if (bestDisplayable)
+        return bestDisplayable;
+    if (lastResort)
+        return lastResort;
+    return NULL;
+}
+
+
diff --git a/src/animator/SkDisplayXMLParser.h b/src/animator/SkDisplayXMLParser.h
new file mode 100644
index 0000000..2c2bec1
--- /dev/null
+++ b/src/animator/SkDisplayXMLParser.h
@@ -0,0 +1,101 @@
+/* libs/graphics/animator/SkDisplayXMLParser.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 SkDisplayXMLParser_DEFINED
+#define SkDisplayXMLParser_DEFINED
+
+#include "SkIntArray.h"
+#include "SkTDict.h"
+#include "SkDisplayType.h"
+#include "SkXMLParser.h"
+
+class SkAnimateMaker;
+class SkDisplayable;
+
+class SkDisplayXMLParserError : public SkXMLParserError {
+public:
+    enum ErrorCode {
+        kApplyScopesItself = kUnknownError + 1,
+        kDisplayTreeTooDeep,
+        kElementMissingParent,
+        kElementTypeNotAllowedInParent,
+        kErrorAddingDataToPost,
+        kErrorAddingToMatrix,
+        kErrorAddingToPaint,
+        kErrorAddingToPath,
+        kErrorInAttributeValue,
+        kErrorInScript,
+        kExpectedMovie,
+        kFieldNotInTarget,
+        kGradientOffsetsDontMatchColors,
+        kGradientOffsetsMustBeNoMoreThanOne,
+        kGradientOffsetsMustEndWithOne,
+        kGradientOffsetsMustIncrease,
+        kGradientOffsetsMustStartWithZero,
+        kGradientPointsLengthMustBeFour,
+        kInInclude,
+        kInMovie,
+        kIncludeNameUnknownOrMissing,
+        kIndexOutOfRange,
+        kMovieNameUnknownOrMissing,
+        kNoParentAvailable,
+        kParentElementCantContain,
+        kSaveLayerNeedsBounds,
+        kTargetIDNotFound,
+        kUnexpectedType
+    };
+    virtual ~SkDisplayXMLParserError();
+    virtual void getErrorString(SkString* str) const;
+    void setCode(ErrorCode code) { INHERITED::setCode((INHERITED::ErrorCode) code); }
+    void setInnerError(SkAnimateMaker* maker, const SkString& str);
+    typedef SkXMLParserError INHERITED;
+    friend class SkDisplayXMLParser;
+};
+
+class SkDisplayXMLParser : public SkXMLParser {
+public:
+    SkDisplayXMLParser(SkAnimateMaker& maker);
+    virtual ~SkDisplayXMLParser();
+protected:
+    virtual bool onAddAttribute(const char name[], const char value[]);
+    bool onAddAttributeLen(const char name[], const char value[], size_t len);
+    virtual bool onEndElement(const char elem[]);
+    virtual bool onStartElement(const char elem[]);
+    bool onStartElementLen(const char elem[], size_t len);
+private:
+    struct Parent {
+        SkDisplayable* fDisplayable;
+        SkDisplayTypes fType;
+    };
+    SkTDArray<Parent> fParents;
+    SkDisplayXMLParser& operator= (const SkDisplayXMLParser& );
+    SkDisplayXMLParserError* getError() { return (SkDisplayXMLParserError*) fError; }
+    const SkMemberInfo* searchContainer(const SkMemberInfo* ,
+        int infoCount);
+    SkAnimateMaker& fMaker;
+    SkBool fInInclude;
+    SkBool fInSkia;
+    // local state between onStartElement and onAddAttribute
+    SkDisplayable*  fCurrDisplayable;
+    SkDisplayTypes  fCurrType;
+    friend class SkXMLAnimatorWriter;
+    typedef SkXMLParser INHERITED;
+};
+
+#endif // SkDisplayXMLParser_DEFINED
+
+
diff --git a/src/animator/SkDisplayable.cpp b/src/animator/SkDisplayable.cpp
new file mode 100644
index 0000000..e50e1ab
--- /dev/null
+++ b/src/animator/SkDisplayable.cpp
@@ -0,0 +1,566 @@
+/* libs/graphics/animator/SkDisplayable.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 "SkDisplayable.h"
+#include "SkDisplayApply.h"
+#include "SkParse.h"
+#ifdef SK_DEBUG
+#include "SkDisplayList.h"
+#endif
+#include "SkDisplayTypes.h"
+
+#ifdef SK_FIND_LEAKS
+// int SkDisplayable::fAllocationCount;
+SkTDDisplayableArray SkDisplayable::fAllocations;
+#endif
+
+#ifdef SK_DEBUG
+SkDisplayable::SkDisplayable() { 
+    id = _id.c_str();
+#ifdef SK_FIND_LEAKS
+    // fAllocationCount++;
+    *fAllocations.append() = this;
+#endif
+}
+#endif
+
+SkDisplayable::~SkDisplayable() {
+#ifdef SK_FIND_LEAKS
+    //  fAllocationCount--;
+    int index = fAllocations.find(this);
+    SkASSERT(index >= 0);
+    fAllocations.remove(index);
+#endif
+}
+
+bool SkDisplayable::add(SkAnimateMaker& , SkDisplayable* child) {
+    return false; 
+}
+
+//void SkDisplayable::apply(SkAnimateMaker& , const SkMemberInfo* , 
+//      SkDisplayable* , SkScalar [], int count) { 
+//  SkASSERT(0); 
+//}
+
+bool SkDisplayable::canContainDependents() const {
+    return false; 
+}
+ 
+bool SkDisplayable::childrenNeedDisposing() const { 
+    return false; 
+}
+
+void SkDisplayable::clearBounder() {
+}
+
+bool SkDisplayable::contains(SkDisplayable* ) {
+    return false;
+}
+
+SkDisplayable* SkDisplayable::contains(const SkString& ) {
+    return NULL;
+}
+
+SkDisplayable* SkDisplayable::deepCopy(SkAnimateMaker* maker) {
+    SkDisplayTypes type = getType();
+    if (type == SkType_Unknown) {
+        SkASSERT(0);
+        return NULL;
+    }
+    SkDisplayable* copy = SkDisplayType::CreateInstance(maker, type);
+    int index = -1;
+    int propIndex = 0;
+    const SkMemberInfo* info;
+    do {
+        info = copy->getMember(++index);
+        if (info == NULL)
+            break;
+        if (info->fType == SkType_MemberProperty) {
+            SkScriptValue value;
+            if (getProperty(propIndex, &value))
+                copy->setProperty(propIndex, value);
+            propIndex++;
+            continue;
+        }
+        if (info->fType == SkType_MemberFunction)
+            continue;
+        if (info->fType == SkType_Array) {
+            SkTDOperandArray* array = (SkTDOperandArray*) info->memberData(this);
+            int arrayCount;
+            if (array == NULL || (arrayCount = array->count()) == 0)
+                continue;
+            SkTDOperandArray* copyArray = (SkTDOperandArray*) info->memberData(copy);
+            copyArray->setCount(arrayCount);
+            SkDisplayTypes elementType;
+            if (type == SkType_Array) {
+                SkDisplayArray* dispArray = (SkDisplayArray*) this;
+                elementType = dispArray->values.getType();
+            } else
+                elementType = info->arrayType();
+            size_t elementSize = SkMemberInfo::GetSize(elementType);
+            size_t byteSize = elementSize * arrayCount;
+            memcpy(copyArray->begin(), array->begin(), byteSize);
+            continue;
+        }
+        if (SkDisplayType::IsDisplayable(maker, info->fType)) {
+            SkDisplayable** displayable = (SkDisplayable**) info->memberData(this);
+            if (*displayable == NULL || *displayable == (SkDisplayable*) -1)
+                continue;
+            SkDisplayable* deeper = (*displayable)->deepCopy(maker);
+            info->setMemberData(copy, deeper, sizeof(deeper));
+            continue;
+        }
+        if (info->fType == SkType_String || info->fType == SkType_DynamicString) {
+            SkString* string;
+            info->getString(this, &string);
+            info->setString(copy, string);
+            continue;
+        }
+        void* data = info->memberData(this);
+        size_t size = SkMemberInfo::GetSize(info->fType);
+        info->setMemberData(copy, data, size);
+    } while (true);
+    copy->dirty();
+    return copy;
+}
+
+void SkDisplayable::dirty() {
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDisplayable::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+#if SK_USE_CONDENSED_INFO == 0
+    this->dumpAttrs(maker);
+    this->dumpChildren(maker);
+#endif
+}
+
+void SkDisplayable::dumpAttrs(SkAnimateMaker* maker) {
+    SkDisplayTypes type = getType();
+    if (type == SkType_Unknown) {
+        //SkDebugf("/>\n");
+        return;
+    }
+    SkDisplayable* blankCopy = SkDisplayType::CreateInstance(maker, type);
+
+    int index = -1;
+    int propIndex = 0;
+    const SkMemberInfo* info;
+    const SkMemberInfo* blankInfo;
+    SkScriptValue value;
+    SkScriptValue blankValue;
+    SkOperand values[2];
+    SkOperand blankValues[2];
+    do {
+        info = this->getMember(++index);
+        if (NULL == info) {
+            //SkDebugf("\n");
+            break;
+        }
+        if (SkType_MemberProperty == info->fType) {
+            if (getProperty(propIndex, &value)) {
+                blankCopy->getProperty(propIndex, &blankValue);
+                //last two are dummies
+                dumpValues(info, value.fType, value.fOperand, blankValue.fOperand, value.fOperand, blankValue.fOperand);
+                }
+            
+            propIndex++;
+            continue;
+        }
+        if (SkDisplayType::IsDisplayable(maker, info->fType)) {
+            continue;
+        }
+        
+        if (info->fType == SkType_MemberFunction)
+            continue;
+            
+            
+        if (info->fType == SkType_Array) {
+            SkTDOperandArray* array = (SkTDOperandArray*) info->memberData(this);
+            int arrayCount;
+            if (array == NULL || (arrayCount = array->count()) == 0)
+                continue;
+            SkDisplayTypes elementType;
+            if (type == SkType_Array) {
+                SkDisplayArray* dispArray = (SkDisplayArray*) this;
+                elementType = dispArray->values.getType();
+            } else
+                elementType = info->arrayType();
+            bool firstElem = true;
+            SkDebugf("%s=\"[", info->fName);
+            for (SkOperand* op = array->begin(); op < array->end(); op++) {
+                if (!firstElem) SkDebugf(",");
+                switch (elementType) {
+                        case SkType_Displayable:
+                            SkDebugf("%s", op->fDisplayable->id);
+                            break;
+                        case SkType_Int:                            
+                            SkDebugf("%d", op->fS32);
+                            break;
+                        case SkType_Float:
+#ifdef SK_CAN_USE_FLOAT
+                            SkDebugf("%g", SkScalarToFloat(op->fScalar));
+#else
+                            SkDebugf("%x", op->fScalar);
+#endif
+                            break;
+                        case SkType_String:
+                        case SkType_DynamicString:    
+                            SkDebugf("%s", op->fString->c_str());
+                            break;
+                        default:
+                            break;
+                }
+                firstElem = false;
+            }
+            SkDebugf("]\" ");
+            continue;
+        }
+        
+        if (info->fType == SkType_String || info->fType == SkType_DynamicString) {
+            SkString* string;
+            info->getString(this, &string);
+            if (string->isEmpty() == false)
+                SkDebugf("%s=\"%s\"\t", info->fName, string->c_str()); 
+            continue;
+        }
+        
+        
+        blankInfo = blankCopy->getMember(index);
+        int i = info->fCount;
+        info->getValue(this, values, i);
+        blankInfo->getValue(blankCopy, blankValues, i);
+        dumpValues(info, info->fType, values[0], blankValues[0], values[1], blankValues[1]);
+    } while (true);
+    delete blankCopy;
+}
+
+void SkDisplayable::dumpBase(SkAnimateMaker* maker) {
+    SkDisplayTypes type = getType();
+    const char* elementName = "(unknown)";
+    if (type != SkType_Unknown && type != SkType_Screenplay)
+        elementName = SkDisplayType::GetName(maker, type);
+    SkDebugf("%*s", SkDisplayList::fIndent, "");
+    if (SkDisplayList::fDumpIndex != 0 && SkDisplayList::fIndent == 0)
+        SkDebugf("%d: ", SkDisplayList::fDumpIndex);
+    SkDebugf("<%s ", elementName);
+    if (strcmp(id,"") != 0)
+        SkDebugf("id=\"%s\" ", id);
+}
+
+void SkDisplayable::dumpChildren(SkAnimateMaker* maker, bool closedAngle) {
+    
+    int index = -1;
+    const SkMemberInfo* info;
+    index = -1;
+    SkDisplayList::fIndent += 4;
+    do {
+        info = this->getMember(++index);
+        if (NULL == info) {
+            break;
+        }
+        if (SkDisplayType::IsDisplayable(maker, info->fType)) {
+            SkDisplayable** displayable = (SkDisplayable**) info->memberData(this);
+            if (*displayable == NULL || *displayable == (SkDisplayable*) -1)
+                continue;
+            if (closedAngle == false) {
+                SkDebugf(">\n");
+                closedAngle = true;
+            }
+            (*displayable)->dump(maker);
+        }
+    } while (true);
+    SkDisplayList::fIndent -= 4;
+    if (closedAngle)
+        dumpEnd(maker);
+    else
+        SkDebugf("/>\n");
+}
+
+void SkDisplayable::dumpEnd(SkAnimateMaker* maker) {
+    SkDisplayTypes type = getType();
+    const char* elementName = "(unknown)";
+    if (type != SkType_Unknown && type != SkType_Screenplay)
+        elementName = SkDisplayType::GetName(maker, type);
+    SkDebugf("%*s", SkDisplayList::fIndent, "");
+    SkDebugf("</%s>\n", elementName);
+}
+
+void SkDisplayable::dumpEvents() {
+}
+
+void SkDisplayable::dumpValues(const SkMemberInfo* info, SkDisplayTypes type, SkOperand op, SkOperand blankOp,
+    SkOperand op2, SkOperand blankOp2) {
+    switch (type) {
+    case SkType_BitmapEncoding:
+        switch (op.fS32) {
+            case 0 : SkDebugf("type=\"jpeg\" ");
+                break;
+            case 1 : SkDebugf("type=\"png\" ");
+                break;
+            default: SkDebugf("type=\"UNDEFINED\" ");
+        }
+        break;
+    //should make this a separate case in dump attrs, rather than make dump values have a larger signature
+    case SkType_Point:
+        if (op.fScalar != blankOp.fScalar || op2.fScalar != blankOp.fScalar) {
+#ifdef SK_CAN_USE_FLOAT
+            SkDebugf("%s=\"[%g,%g]\" ", info->fName, SkScalarToFloat(op.fScalar), SkScalarToFloat(op2.fScalar));
+#else
+            SkDebugf("%s=\"[%x,%x]\" ", info->fName, op.fScalar, op2.fScalar);
+#endif
+        }
+        break;
+    case SkType_FromPathMode:
+        switch (op.fS32) {
+            case 0:
+                //don't want to print anything for 0, just adding it to remove it from default:
+                break;
+            case 1:
+                SkDebugf("%s=\"%s\" ", info->fName, "angle");
+                break;
+            case 2:
+                SkDebugf("%s=\"%s\" ", info->fName, "position");
+                break;
+            default:
+                SkDebugf("%s=\"INVALID\" ", info->fName);
+        }
+        break;
+    case SkType_MaskFilterBlurStyle:
+        switch (op.fS32) {
+            case 0:
+                break;
+            case 1:
+                SkDebugf("%s=\"%s\" ", info->fName, "solid");
+                break;
+            case 2:
+                SkDebugf("%s=\"%s\" ", info->fName, "outer");
+                break;
+            case 3:
+                SkDebugf("%s=\"%s\" ", info->fName, "inner");
+                break;
+            default:
+                SkDebugf("%s=\"INVALID\" ", info->fName);
+        }
+        break;
+    case SkType_FilterType:
+        if (op.fS32 == 1)
+            SkDebugf("%s=\"%s\" ", info->fName, "bilinear");
+        break;
+    case SkType_PathDirection:
+        SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "cw" : "ccw");
+        break;
+    case SkType_FillType:
+        SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "winding" : "evenOdd");
+        break;
+    case SkType_TileMode:
+        //correct to look at the S32?
+        if (op.fS32 != blankOp.fS32) 
+            SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "clamp" : op.fS32 == 1 ? "repeat" : "mirror");
+        break;
+    case SkType_Boolean:
+        if (op.fS32 != blankOp.fS32)
+            SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "false" : "true");
+        break;
+    case SkType_Int:
+        if (op.fS32 != blankOp.fS32)
+            SkDebugf(" %s=\"%d\"  ", info->fName, op.fS32);
+        break;
+    case SkType_Float:
+        if (op.fScalar != blankOp.fScalar) { //or /65536?
+#ifdef SK_CAN_USE_FLOAT
+            SkDebugf("%s=\"%g\"  ", info->fName, SkScalarToFloat(op.fScalar));
+#else
+            SkDebugf("%s=\"%x\"  ", info->fName, op.fScalar);
+#endif
+        }
+        break;
+    case SkType_String:
+    case SkType_DynamicString:
+        if (op.fString->size() > 0) 
+            SkDebugf("%s=\"%s\" ", info->fName, op.fString->c_str());
+        break;
+    case SkType_MSec:
+        if (op.fS32 != blankOp.fS32) {
+#ifdef SK_CAN_USE_FLOAT
+            SkDebugf(" %s=\"%g\"  ", info->fName, SkScalarToFloat(SkScalarDiv(op.fS32, 1000)));
+#else
+            SkDebugf(" %s=\"%x\"  ", info->fName, SkScalarDiv(op.fS32, 1000));
+#endif
+        }
+    default:
+        SkDebugf("");
+    }    
+}
+
+#endif
+
+bool SkDisplayable::enable( SkAnimateMaker& ) { 
+    return false;
+}
+
+void SkDisplayable::enableBounder() {
+}
+
+void SkDisplayable::executeFunction(SkDisplayable* , int index, 
+        SkTDArray<SkScriptValue>& , SkDisplayTypes, SkScriptValue*  ) {
+    SkASSERT(0); 
+}
+
+void SkDisplayable::executeFunction(SkDisplayable* target, 
+        const SkMemberInfo* info, SkTypedArray* values, SkScriptValue* value) {
+    SkTDArray<SkScriptValue> typedValues;
+    for (SkOperand* op = values->begin(); op < values->end(); op++) {
+        SkScriptValue temp;
+        temp.fType = values->getType();
+        temp.fOperand = *op;
+        *typedValues.append() = temp;
+    }
+    executeFunction(target, info->functionIndex(), typedValues, info->getType(), value);
+}
+
+void SkDisplayable::executeFunction2(SkDisplayable* , int index, 
+        SkOpArray* params, SkDisplayTypes, SkOperand2*  ) {
+    SkASSERT(0); 
+}
+
+void SkDisplayable::getBounds(SkRect* rect) {
+    SkASSERT(rect);
+    rect->fLeft = rect->fTop = SK_ScalarMax;
+    rect->fRight= rect->fBottom = -SK_ScalarMax;
+}
+
+const SkFunctionParamType* SkDisplayable::getFunctionsParameters() {
+    return NULL;
+}
+
+const SkMemberInfo* SkDisplayable::getMember(int index) { 
+    return NULL; 
+}
+
+const SkMemberInfo* SkDisplayable::getMember(const char name[]) { 
+    return NULL; 
+}
+
+const SkFunctionParamType* SkDisplayable::getParameters(const SkMemberInfo* info, 
+        int* paramCount) {
+    const SkFunctionParamType* params = getFunctionsParameters();
+    SkASSERT(params != NULL);
+    int funcIndex = info->functionIndex();
+    // !!! eventually break traversing params into an external function (maybe this whole function)
+    int index = funcIndex;
+    int offset = 0;
+    while (--index >= 0) {
+        while (params[offset] != 0)
+            offset++;
+        offset++;
+    }
+    int count = 0;
+    while (params[offset] != 0) {
+        count++;
+        offset++;
+    }
+    *paramCount = count;
+    return &params[offset - count];
+}
+
+SkDisplayable* SkDisplayable::getParent() const {
+    return NULL;
+}
+
+bool SkDisplayable::getProperty(int index, SkScriptValue* ) const {
+//  SkASSERT(0); 
+    return false; 
+}
+
+bool SkDisplayable::getProperty2(int index, SkOperand2* value) const {
+    SkASSERT(0); 
+    return false; 
+}
+
+SkDisplayTypes SkDisplayable::getType() const { 
+    return SkType_Unknown; 
+}
+
+bool SkDisplayable::hasEnable() const {
+    return false;
+}
+
+bool SkDisplayable::isDrawable() const {
+    return false; 
+}
+
+void SkDisplayable::onEndElement(SkAnimateMaker& ) {}
+
+const SkMemberInfo* SkDisplayable::preferredChild(SkDisplayTypes type) {
+    return NULL;
+}
+
+bool SkDisplayable::resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply) {
+    return false;
+}
+
+//SkDisplayable* SkDisplayable::resolveTarget(SkAnimateMaker& ) { 
+//  return this; 
+//}
+
+void SkDisplayable::setChildHasID() {
+}
+
+bool SkDisplayable::setParent(SkDisplayable* ) {
+    return false;
+}
+
+bool SkDisplayable::setProperty(int index, SkScriptValue& ) {
+    //SkASSERT(0); 
+    return false; 
+}
+
+void SkDisplayable::setReference(const SkMemberInfo* info, SkDisplayable* displayable) {
+    if (info->fType == SkType_MemberProperty) {
+        SkScriptValue scriptValue;
+        scriptValue.fOperand.fDisplayable = displayable;
+        scriptValue.fType = displayable->getType();
+        setProperty(info->propertyIndex(), scriptValue);
+    } else if (info->fType == SkType_Array) {
+        SkASSERT(displayable->getType() == SkType_Array);
+        SkDisplayArray* dispArray = (SkDisplayArray*) displayable;
+        SkTDScalarArray* array = (SkTDScalarArray* ) info->memberData(this);
+        array->setCount(dispArray->values.count());
+        memcpy(array->begin(), dispArray->values.begin(), dispArray->values.count() * sizeof(int));
+        //
+
+        // !!! need a way for interpreter engine to own array
+        // !!! probably need to replace all scriptable arrays with single bigger array
+        // that has operand and type on every element -- or
+        // when array is dirtied, need to get parent to reparse to local array
+    } else {
+        void* storage = info->memberData(this);
+        memcpy(storage, &displayable, sizeof(SkDisplayable*));
+    }
+// !!! unclear why displayable is dirtied here
+// if this is called, this breaks fromPath.xml
+//  displayable->dirty();
+}
+
+#ifdef SK_DEBUG
+void SkDisplayable::validate() {
+}
+#endif
+
+
diff --git a/src/animator/SkDisplayable.h b/src/animator/SkDisplayable.h
new file mode 100644
index 0000000..cbc96de
--- /dev/null
+++ b/src/animator/SkDisplayable.h
@@ -0,0 +1,120 @@
+/* libs/graphics/animator/SkDisplayable.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 SkDisplayable_DEFINED
+#define SkDisplayable_DEFINED
+
+#include "SkOperand.h"
+#ifdef SK_DEBUG
+#include "SkString.h"
+#endif
+#include "SkIntArray.h"
+#include "SkRect.h"
+#include "SkTDArray.h"
+
+class SkAnimateMaker;
+class SkApply;
+class SkEvents;
+struct SkMemberInfo;
+struct SkScriptValue;
+class SkOpArray; // compiled scripting experiment
+union SkOperand2; // compiled scripting experiment
+
+class SkDisplayable {
+public:
+#ifdef SK_DEBUG
+    SkDisplayable();
+#endif
+    virtual ~SkDisplayable();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool canContainDependents() const;
+    virtual bool childrenNeedDisposing() const;
+    virtual void clearBounder();
+    virtual bool contains(SkDisplayable* );
+    virtual SkDisplayable* contains(const SkString& );
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual void dirty();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+    void dumpAttrs(SkAnimateMaker* );
+    void dumpBase(SkAnimateMaker* );
+    void dumpChildren(SkAnimateMaker* maker, bool closedAngle = false );
+    void dumpEnd(SkAnimateMaker* );
+    virtual void dumpEvents();
+#endif
+    virtual bool enable( SkAnimateMaker& );
+    virtual void enableBounder();
+    virtual void executeFunction(SkDisplayable* , int functionIndex, 
+        SkTDArray<SkScriptValue>& , SkDisplayTypes , SkScriptValue* ); 
+    void executeFunction(SkDisplayable* , const SkMemberInfo* , 
+        SkTypedArray* , SkScriptValue* ); 
+    virtual void executeFunction2(SkDisplayable* , int functionIndex, 
+        SkOpArray* params , SkDisplayTypes , SkOperand2* ); // compiled scripting experiment
+    virtual void getBounds(SkRect* );
+    virtual const SkFunctionParamType* getFunctionsParameters();
+    virtual const SkMemberInfo* getMember(int index);
+    virtual const SkMemberInfo* getMember(const char name[]);
+    const SkFunctionParamType* getParameters(const SkMemberInfo* info, 
+        int* paramCount);
+    virtual SkDisplayable* getParent() const;
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool getProperty2(int index, SkOperand2* value) const;    // compiled scripting experiment
+    virtual SkDisplayTypes getType() const;
+    virtual bool hasEnable() const;
+    bool isAnimate() const { 
+        SkDisplayTypes type = getType(); 
+        return type == SkType_Animate || type == SkType_Set; }
+    bool isApply() const { return getType() == SkType_Apply; }
+    bool isColor() const { return getType() == SkType_Color; }
+    virtual bool isDrawable() const;
+    bool isGroup() const { return getType() == SkType_Group || 
+        getType() == SkType_Save || getType() == SkType_DrawTo ||
+        getType() == SkType_SaveLayer; }
+    bool isMatrix() const { return getType() == SkType_Matrix; }
+    virtual bool isPaint() const { return getType() == SkType_Paint; }
+    virtual bool isPath() const { return false; }
+    bool isPost() const { return getType() == SkType_Post; }
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual const SkMemberInfo* preferredChild(SkDisplayTypes type);
+    virtual bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* );
+    virtual void setChildHasID();
+    virtual bool setParent(SkDisplayable* );
+    virtual bool setProperty(int index, SkScriptValue& );
+    void setReference(const SkMemberInfo* info, SkDisplayable* ref);
+#ifdef SK_DEBUG
+    bool isData() const { return getType() == SkType_Data; };
+    bool isEvent() const { return getType() == SkType_Event; }
+    virtual bool isMatrixPart() const { return false; }
+    bool isPatch() const { return getType() == SkType_3D_Patch; }
+    virtual bool isPaintPart() const { return false; }
+    virtual bool isPathPart() const { return false; }
+    virtual void validate();
+    SkString _id;
+    const char* id;
+//  static int fAllocationCount;
+    static SkTDDisplayableArray fAllocations;
+#else
+    void validate() {}
+#endif
+#ifdef SK_DUMP_ENABLED
+private:
+    void dumpValues(const SkMemberInfo* info, SkDisplayTypes type, SkOperand op, SkOperand blankOp,
+        SkOperand op2, SkOperand blankOp2);
+#endif
+};
+
+#endif // SkDisplayable_DEFINED
diff --git a/src/animator/SkDraw3D.cpp b/src/animator/SkDraw3D.cpp
new file mode 100644
index 0000000..137de9f
--- /dev/null
+++ b/src/animator/SkDraw3D.cpp
@@ -0,0 +1,117 @@
+/* libs/graphics/animator/SkDraw3D.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 "SkDraw3D.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkTypedArray.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo Sk3D_Point::fInfo[] = {
+    SK_MEMBER_ALIAS(x, fPoint.fX, Float),
+    SK_MEMBER_ALIAS(y, fPoint.fY, Float),
+    SK_MEMBER_ALIAS(z, fPoint.fZ, Float)
+};
+
+#endif
+
+DEFINE_NO_VIRTUALS_GET_MEMBER(Sk3D_Point);
+
+Sk3D_Point::Sk3D_Point() {
+    fPoint.set(0, 0, 0);    
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo Sk3D_Camera::fInfo[] = {
+    SK_MEMBER_ALIAS(axis, fCamera.fAxis, 3D_Point),
+    SK_MEMBER(hackHeight, Float),
+    SK_MEMBER(hackWidth, Float),
+    SK_MEMBER_ALIAS(location, fCamera.fLocation, 3D_Point),
+    SK_MEMBER_ALIAS(observer, fCamera.fObserver, 3D_Point),
+    SK_MEMBER(patch, 3D_Patch),
+    SK_MEMBER_ALIAS(zenith, fCamera.fZenith, 3D_Point),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(Sk3D_Camera);
+
+Sk3D_Camera::Sk3D_Camera() : hackWidth(0), hackHeight(0), patch(NULL) {
+}
+
+Sk3D_Camera::~Sk3D_Camera() {
+}
+
+bool Sk3D_Camera::draw(SkAnimateMaker& maker) {
+    fCamera.update();
+    SkMatrix matrix;
+    fCamera.patchToMatrix(patch->fPatch, &matrix);
+    matrix.preTranslate(hackWidth / 2, -hackHeight / 2);
+    matrix.postTranslate(hackWidth / 2, hackHeight / 2);
+    maker.fCanvas->concat(matrix);
+    return false;
+}
+
+
+enum Sk3D_Patch_Functions {
+    SK_FUNCTION(rotateDegrees)
+};
+
+const SkFunctionParamType Sk3D_Patch::fFunctionParameters[] = {
+    (SkFunctionParamType) SkType_Float,
+    (SkFunctionParamType) SkType_Float,
+    (SkFunctionParamType) SkType_Float,
+    (SkFunctionParamType) 0 // terminator for parameter list (there may be multiple parameter lists)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo Sk3D_Patch::fInfo[] = {
+    SK_MEMBER_ALIAS(origin, fPatch.fOrigin, 3D_Point),
+    SK_MEMBER_FUNCTION(rotateDegrees, Float),
+    SK_MEMBER_ALIAS(u, fPatch.fU, 3D_Point),
+    SK_MEMBER_ALIAS(v, fPatch.fV, 3D_Point)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(Sk3D_Patch);
+
+void Sk3D_Patch::executeFunction(SkDisplayable* target, int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* ) {
+    SkASSERT(target == this);
+    switch (index) {
+        case SK_FUNCTION(rotateDegrees):
+            SkASSERT(parameters.count() == 3);
+            SkASSERT(type == SkType_Float);
+            fPatch.rotateDegrees(parameters[0].fOperand.fScalar, 
+                parameters[1].fOperand.fScalar, parameters[2].fOperand.fScalar);
+            break;
+        default:
+            SkASSERT(0);
+    }
+}
+
+const SkFunctionParamType* Sk3D_Patch::getFunctionsParameters() {
+    return fFunctionParameters;
+}
+
+
+
diff --git a/src/animator/SkDraw3D.h b/src/animator/SkDraw3D.h
new file mode 100644
index 0000000..5ab61cd
--- /dev/null
+++ b/src/animator/SkDraw3D.h
@@ -0,0 +1,59 @@
+/* libs/graphics/animator/SkDraw3D.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 SkDraw3D_DEFINED
+#define SkDraw3D_DEFINED
+
+#include "SkCamera.h"
+#include "SkDrawable.h"
+#include "SkMemberInfo.h"
+
+class Sk3D_Patch;
+
+struct Sk3D_Point {
+    DECLARE_NO_VIRTUALS_MEMBER_INFO(3D_Point);
+    Sk3D_Point();
+private:
+    SkPoint3D fPoint;
+};
+
+class Sk3D_Camera : public SkDrawable {
+    DECLARE_MEMBER_INFO(3D_Camera);
+    Sk3D_Camera();
+    virtual ~Sk3D_Camera();
+    virtual bool draw(SkAnimateMaker& );
+private:
+    SkScalar hackWidth;
+    SkScalar hackHeight;
+    SkCamera3D fCamera;
+    Sk3D_Patch* patch;
+};
+
+class Sk3D_Patch : public SkDisplayable {
+    DECLARE_MEMBER_INFO(3D_Patch);
+private:
+    virtual void executeFunction(SkDisplayable* , int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* );
+    virtual const SkFunctionParamType* getFunctionsParameters();
+    SkPatch3D  fPatch;
+    static const SkFunctionParamType fFunctionParameters[];
+    friend class Sk3D_Camera;
+};
+
+#endif // SkDraw3D_DEFINED
+
diff --git a/src/animator/SkDrawBitmap.cpp b/src/animator/SkDrawBitmap.cpp
new file mode 100644
index 0000000..25355be
--- /dev/null
+++ b/src/animator/SkDrawBitmap.cpp
@@ -0,0 +1,206 @@
+/* libs/graphics/animator/SkDrawBitmap.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 "SkDrawBitmap.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkPaint.h"
+#include "SkStream.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkBaseBitmap::fInfo[] = {
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkBaseBitmap);
+
+SkBaseBitmap::SkBaseBitmap() : x(0), y(0) {
+}
+
+SkBaseBitmap::~SkBaseBitmap() {
+}
+
+bool SkBaseBitmap::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawBitmap(fBitmap, x, y, maker.fPaint);
+    return false;
+}
+
+enum SkDrawBitmap_Properties {
+    SK_PROPERTY(erase)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawBitmap::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER_PROPERTY(erase, ARGB),
+    SK_MEMBER(format, BitmapFormat),
+    SK_MEMBER(height, Int),
+    SK_MEMBER(rowBytes, Int),
+    SK_MEMBER(width, Int),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawBitmap);
+
+SkDrawBitmap::SkDrawBitmap() : format((SkBitmap::Config) -1), height(-1), 
+    rowBytes(0),    width(-1), fColor(0), fColorSet(false) {
+}
+
+SkDrawBitmap::~SkDrawBitmap() {
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawBitmap::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpAttrs(maker);
+    if (fColorSet)
+        SkDebugf("erase=\"argb(%d,%d,%d,%d)\" ", SkColorGetA(fColor)/255, SkColorGetR(fColor),
+            SkColorGetG(fColor), SkColorGetB(fColor));
+    if (rowBytes > 0)
+        SkDebugf("rowBytes=\"%d\" ", rowBytes);
+    const char* formatName;
+    switch (format) {
+        case 0: formatName = "none"; break;
+        case 1: formatName = "A1"; break;
+        case 2: formatName = "A8"; break;
+        case 3: formatName = "Index8"; break;
+        case 4: formatName = "RGB16"; break;
+        case 5: formatName = "RGB32"; break;
+    }
+    SkDebugf("format=\"%s\" />\n", formatName);
+}
+#endif
+
+void SkDrawBitmap::onEndElement(SkAnimateMaker& maker) {
+    SkASSERT(format != (SkBitmap::Config) -1);
+    SkASSERT(width != -1);
+    SkASSERT(height != -1);
+    SkASSERT(rowBytes >= 0);
+    fBitmap.setConfig((SkBitmap::Config) format, width, height, rowBytes);
+    fBitmap.allocPixels();
+    if (fColorSet)
+        fBitmap.eraseColor(fColor);
+}
+
+bool SkDrawBitmap::setProperty(int index, SkScriptValue& value)
+{
+    switch (index) {
+        case SK_PROPERTY(erase):
+            SkASSERT(value.fType == SkType_ARGB);
+            fColor = value.fOperand.fS32;
+            fColorSet = true;
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+
+enum SkImage_Properties {
+    SK_PROPERTY(height),
+    SK_PROPERTY(width)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkImage::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(base64, Base64),
+    SK_MEMBER_PROPERTY(height, Int),
+    SK_MEMBER(src, String),
+    SK_MEMBER_PROPERTY(width, Int)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkImage);
+
+SkImage::SkImage() : fDirty(true), fUriBase(NULL) {
+    base64.fData = NULL;
+    base64.fLength = 0;
+}
+
+SkImage::~SkImage() {
+    delete[] base64.fData; 
+}
+
+SkDisplayable* SkImage::deepCopy(SkAnimateMaker* maker) {
+    SkDisplayable* copy = INHERITED::deepCopy(maker);
+    ((SkImage*) copy)->fUriBase = ((SkImage*) this)->fUriBase;
+    return copy;
+}
+
+void SkImage::dirty() {
+    fDirty = true;
+}
+
+bool SkImage::draw(SkAnimateMaker& maker) {
+    if (fDirty) 
+        resolve();
+    return INHERITED::draw(maker);
+}
+
+bool SkImage::getProperty(int index, SkScriptValue* value) const {
+    if (fDirty) 
+        resolve();
+    switch (index) {
+        case SK_PROPERTY(height):
+            value->fOperand.fS32 = fBitmap.height();
+            break;
+        case SK_PROPERTY(width):
+            value->fOperand.fS32 = fBitmap.width();
+            break;
+    default:
+        SkASSERT(0);
+        return false;
+    }
+    value->fType = SkType_Int;
+    return true;
+}
+
+void SkImage::onEndElement(SkAnimateMaker& maker) {
+    fUriBase = maker.fPrefix.c_str();
+}
+
+void SkImage::resolve() {
+    fDirty = false;
+    if (base64.fData) {
+        fBitmap.reset();
+        SkImageDecoder::DecodeMemory(base64.fData, base64.fLength, &fBitmap);
+    } else if (src.size()) {
+        if (fLast.equals(src))
+            return;
+        fLast.set(src);
+        fBitmap.reset();
+        
+        //SkStream* stream = SkStream::GetURIStream(fUriBase, src.c_str());
+        SkStream* stream = new SkFILEStream(src.c_str());
+
+        SkAutoTDelete<SkStream> autoDel(stream);
+        SkImageDecoder::DecodeStream(stream, &fBitmap);
+    }   
+}
diff --git a/src/animator/SkDrawBitmap.h b/src/animator/SkDrawBitmap.h
new file mode 100644
index 0000000..f846193
--- /dev/null
+++ b/src/animator/SkDrawBitmap.h
@@ -0,0 +1,82 @@
+/* libs/graphics/animator/SkDrawBitmap.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 SkDrawBitmap_DEFINED
+#define SkDrawBitmap_DEFINED
+
+#include "SkBoundable.h"
+#include "SkBase64.h"
+#include "SkBitmap.h"
+// #include "SkImageDecoder.h"
+#include "SkMemberInfo.h"
+
+class SkBaseBitmap : public SkBoundable {
+    DECLARE_MEMBER_INFO(BaseBitmap);
+    SkBaseBitmap();
+    virtual ~SkBaseBitmap();
+    virtual bool draw(SkAnimateMaker& );
+protected:
+    SkBitmap fBitmap;
+    SkScalar x;
+    SkScalar y;
+private:
+    friend class SkDrawTo;
+    friend class SkDrawBitmapShader;
+    typedef SkBoundable INHERITED;
+};
+
+class SkDrawBitmap : public SkBaseBitmap {
+    DECLARE_DRAW_MEMBER_INFO(Bitmap);
+    SkDrawBitmap();
+    virtual ~SkDrawBitmap();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual bool setProperty(int index, SkScriptValue& value);
+protected:
+    int /*SkBitmap::Config*/ format;
+    int32_t height;
+    int32_t rowBytes;
+    int32_t width;
+    SkColor fColor;
+    SkBool fColorSet;
+    typedef SkBaseBitmap INHERITED;
+};
+
+class SkImage : public SkBaseBitmap {
+    DECLARE_MEMBER_INFO(Image);
+    SkImage();
+    virtual ~SkImage();
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual void dirty();
+    virtual bool draw(SkAnimateMaker& );
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual void onEndElement(SkAnimateMaker& maker);
+private:
+    void resolve() const { (const_cast<SkImage*>(this))->resolve(); }
+    void resolve();
+protected:
+    SkBase64 base64;
+    SkString src;
+    SkString fLast; // cache of src so that stream isn't unnecessarily decoded 
+    SkBool fDirty;
+    const char* fUriBase;
+    typedef SkBaseBitmap INHERITED;
+};
+
+#endif // SkDrawBitmap_DEFINED
diff --git a/src/animator/SkDrawBlur.cpp b/src/animator/SkDrawBlur.cpp
new file mode 100644
index 0000000..0ebabce
--- /dev/null
+++ b/src/animator/SkDrawBlur.cpp
@@ -0,0 +1,40 @@
+/* libs/graphics/animator/SkDrawBlur.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 "SkDrawBlur.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawBlur::fInfo[] = {
+    SK_MEMBER(blurStyle, MaskFilterBlurStyle),
+    SK_MEMBER(radius, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawBlur);
+
+SkDrawBlur::SkDrawBlur() : radius(-1), 
+    blurStyle(SkBlurMaskFilter::kNormal_BlurStyle) {
+}
+
+SkMaskFilter* SkDrawBlur::getMaskFilter() {
+    if (radius < 0)
+        return NULL;
+    return SkBlurMaskFilter::Create(radius, (SkBlurMaskFilter::BlurStyle) blurStyle);
+}
+
diff --git a/src/animator/SkDrawBlur.h b/src/animator/SkDrawBlur.h
new file mode 100644
index 0000000..14ceba0
--- /dev/null
+++ b/src/animator/SkDrawBlur.h
@@ -0,0 +1,34 @@
+/* libs/graphics/animator/SkDrawBlur.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 SkDrawBlur_DEFINED
+#define SkDrawBlur_DEFINED
+
+#include "SkPaintParts.h"
+#include "SkBlurMaskFilter.h"
+
+class SkDrawBlur : public SkDrawMaskFilter {
+    DECLARE_DRAW_MEMBER_INFO(Blur);
+    SkDrawBlur();
+    virtual SkMaskFilter* getMaskFilter();
+protected:
+    SkScalar radius;
+    int /*SkBlurMaskFilter::BlurStyle*/ blurStyle;
+};
+
+#endif // SkDrawBlur_DEFINED
+
diff --git a/src/animator/SkDrawClip.cpp b/src/animator/SkDrawClip.cpp
new file mode 100644
index 0000000..1c01c6a
--- /dev/null
+++ b/src/animator/SkDrawClip.cpp
@@ -0,0 +1,48 @@
+/* libs/graphics/animator/SkDrawClip.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 "SkDrawClip.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkDrawRectangle.h"
+#include "SkDrawPath.h"
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawClip::fInfo[] = {
+    SK_MEMBER(path, Path),
+    SK_MEMBER(rect, Rect)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawClip);
+
+SkDrawClip::SkDrawClip() : rect(NULL), path(NULL) {
+}
+
+bool SkDrawClip::draw(SkAnimateMaker& maker ) {
+    if (rect != NULL)
+        maker.fCanvas->clipRect(rect->fRect);
+    else {
+        SkASSERT(path != NULL);
+        maker.fCanvas->clipPath(path->fPath);
+    }
+    return false;
+}
+
diff --git a/src/animator/SkDrawClip.h b/src/animator/SkDrawClip.h
new file mode 100644
index 0000000..2fed99a
--- /dev/null
+++ b/src/animator/SkDrawClip.h
@@ -0,0 +1,37 @@
+/* libs/graphics/animator/SkDrawClip.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 SkDrawClip_DEFINED
+#define SkDrawClip_DEFINED
+
+#include "SkDrawable.h"
+#include "SkMemberInfo.h"
+#include "SkRegion.h"
+
+class SkDrawPath;
+class SkDrawRect;
+
+class SkDrawClip : public SkDrawable {
+    DECLARE_DRAW_MEMBER_INFO(Clip);
+    SkDrawClip();
+    virtual bool draw(SkAnimateMaker& );
+private:
+    SkDrawRect* rect;
+    SkDrawPath* path;
+};
+
+#endif // SkDrawClip_DEFINED
diff --git a/src/animator/SkDrawColor.cpp b/src/animator/SkDrawColor.cpp
new file mode 100644
index 0000000..009ca10
--- /dev/null
+++ b/src/animator/SkDrawColor.cpp
@@ -0,0 +1,278 @@
+/* libs/graphics/animator/SkDrawColor.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 "SkDrawColor.h"
+#ifdef SK_DEBUG
+#include "SkDisplayList.h"
+#endif
+#include "SkDrawPaint.h"
+#include "SkParse.h"
+#include "SkScript.h"
+
+enum HSV_Choice {
+    kGetHue,
+    kGetSaturation,
+    kGetValue
+};
+
+static SkScalar RGB_to_HSV(SkColor color, HSV_Choice choice) {
+    SkScalar red = SkIntToScalar(SkColorGetR(color));
+    SkScalar green = SkIntToScalar(SkColorGetG(color));
+    SkScalar blue = SkIntToScalar(SkColorGetB(color));
+    SkScalar min = SkMinScalar(SkMinScalar(red, green), blue);
+    SkScalar value = SkMaxScalar(SkMaxScalar(red, green), blue);
+    if (choice == kGetValue)
+        return value/255;
+    SkScalar delta = value - min;
+    SkScalar saturation = value == 0 ? 0 : SkScalarDiv(delta, value);
+    if (choice == kGetSaturation)
+        return saturation;
+    SkScalar hue;
+    if (saturation == 0)
+        hue = 0;
+    else {
+        SkScalar part60 = SkScalarDiv(60 * SK_Scalar1, delta);
+        if (red == value) {
+            hue = SkScalarMul(green - blue, part60);
+            if (hue < 0)
+                hue += 360 * SK_Scalar1;
+        }
+        else if (green == value)
+            hue = 120 * SK_Scalar1 + SkScalarMul(blue - red, part60);
+        else  // blue == value
+            hue = 240 * SK_Scalar1 + SkScalarMul(red - green, part60);
+    }
+    SkASSERT(choice == kGetHue);
+    return hue;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable 'red', etc. may be used without having been initialized
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+static SkColor HSV_to_RGB(SkColor color, HSV_Choice choice, SkScalar hsv) {
+    SkScalar hue = choice == kGetHue ? hsv : RGB_to_HSV(color, kGetHue);
+    SkScalar saturation = choice == kGetSaturation ? hsv : RGB_to_HSV(color, kGetSaturation);
+    SkScalar value = choice == kGetValue ? hsv : RGB_to_HSV(color, kGetValue);
+    value *= 255;
+    SkScalar red SK_INIT_TO_AVOID_WARNING;
+    SkScalar green SK_INIT_TO_AVOID_WARNING;
+    SkScalar blue SK_INIT_TO_AVOID_WARNING;
+    if (saturation == 0)    // color is on black-and-white center line
+        red = green = blue = value;
+    else {
+        //SkScalar fraction = SkScalarMod(hue, 60 * SK_Scalar1);
+        int sextant = SkScalarFloor(hue / 60);
+        SkScalar fraction = hue / 60 - SkIntToScalar(sextant);
+        SkScalar p = SkScalarMul(value , SK_Scalar1 - saturation);
+        SkScalar q = SkScalarMul(value, SK_Scalar1 - SkScalarMul(saturation, fraction));
+        SkScalar t = SkScalarMul(value, SK_Scalar1 - 
+            SkScalarMul(saturation, SK_Scalar1 - fraction));
+        switch (sextant % 6) {
+            case 0: red = value; green = t; blue = p; break;
+            case 1: red = q; green = value; blue = p; break;
+            case 2: red = p; green = value; blue = t; break;
+            case 3: red = p; green = q; blue = value; break;
+            case 4: red = t;  green = p; blue = value; break;
+            case 5: red = value; green = p; blue = q; break;
+        }
+    }
+    //used to say SkToU8((U8CPU) red) etc
+    return SkColorSetARGB(SkColorGetA(color), SkScalarRound(red), 
+        SkScalarRound(green), SkScalarRound(blue));
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  
+#pragma warning ( pop )
+#endif
+
+enum SkDrawColor_Properties {
+    SK_PROPERTY(alpha),
+    SK_PROPERTY(blue),
+    SK_PROPERTY(green),
+    SK_PROPERTY(hue),
+    SK_PROPERTY(red),
+    SK_PROPERTY(saturation),
+    SK_PROPERTY(value)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawColor::fInfo[] = {
+    SK_MEMBER_PROPERTY(alpha, Float),
+    SK_MEMBER_PROPERTY(blue, Float),
+    SK_MEMBER(color, ARGB),
+    SK_MEMBER_PROPERTY(green, Float),
+    SK_MEMBER_PROPERTY(hue, Float),
+    SK_MEMBER_PROPERTY(red, Float),
+    SK_MEMBER_PROPERTY(saturation, Float),
+    SK_MEMBER_PROPERTY(value, Float),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawColor);
+
+SkDrawColor::SkDrawColor() : fDirty(false) { 
+    color = SK_ColorBLACK; 
+    fHue = fSaturation = fValue = SK_ScalarNaN;
+}
+
+bool SkDrawColor::add() {
+    if (fPaint->color != NULL)
+        return true; // error (probably color in paint as attribute as well)
+    fPaint->color = this;
+    fPaint->fOwnsColor = true;
+    return false;
+}
+
+SkDisplayable* SkDrawColor::deepCopy(SkAnimateMaker* maker) {
+    SkDrawColor* copy = new SkDrawColor();
+    copy->color = color;
+    copy->fHue = fHue;
+    copy->fSaturation = fSaturation;
+    copy->fValue = fValue;
+    copy->fDirty = fDirty;
+    return copy;
+}
+
+void SkDrawColor::dirty(){
+    fDirty = true;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawColor::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("alpha=\"%d\" red=\"%d\" green=\"%d\" blue=\"%d\" />\n",  
+        SkColorGetA(color)/255, SkColorGetR(color),
+        SkColorGetG(color), SkColorGetB(color));
+}
+#endif
+
+SkColor SkDrawColor::getColor() { 
+    if (fDirty) {
+        if (SkScalarIsNaN(fValue) == false)
+            color = HSV_to_RGB(color, kGetValue, fValue);
+        if (SkScalarIsNaN(fSaturation) == false)
+            color = HSV_to_RGB(color, kGetSaturation, fSaturation);
+        if (SkScalarIsNaN(fHue) == false)
+            color = HSV_to_RGB(color, kGetHue, fHue);
+        fDirty = false;
+    }
+    return color; 
+}
+
+SkDisplayable* SkDrawColor::getParent() const {
+    return fPaint;
+}
+
+bool SkDrawColor::getProperty(int index, SkScriptValue* value) const {
+    value->fType = SkType_Float;
+    SkScalar result;
+    switch(index) {
+        case SK_PROPERTY(alpha):
+            result = SkIntToScalar(SkColorGetA(color)) / 255;
+            break;
+        case SK_PROPERTY(blue):
+            result = SkIntToScalar(SkColorGetB(color));
+            break;
+        case SK_PROPERTY(green):
+            result = SkIntToScalar(SkColorGetG(color));
+            break;
+        case SK_PROPERTY(hue):
+            result = RGB_to_HSV(color, kGetHue);
+            break;
+        case SK_PROPERTY(red):
+            result = SkIntToScalar(SkColorGetR(color));
+            break;
+        case SK_PROPERTY(saturation):
+            result = RGB_to_HSV(color, kGetSaturation);
+            break;
+        case SK_PROPERTY(value):
+            result = RGB_to_HSV(color, kGetValue);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    value->fOperand.fScalar = result;
+    return true;
+}
+
+void SkDrawColor::onEndElement(SkAnimateMaker& maker){
+    fDirty = true;
+}
+
+bool SkDrawColor::setParent(SkDisplayable* parent) {
+    SkASSERT(parent != NULL);
+    if (parent->getType() == SkType_LinearGradient || parent->getType() == SkType_RadialGradient)
+        return false;
+    if (parent->isPaint() == false)
+        return true;
+    fPaint = (SkDrawPaint*) parent;
+    return false;
+}
+
+bool SkDrawColor::setProperty(int index, SkScriptValue& value) {
+    SkASSERT(value.fType == SkType_Float);
+    SkScalar scalar = value.fOperand.fScalar;
+    switch (index) {
+        case SK_PROPERTY(alpha):
+            uint8_t alpha;
+        #ifdef SK_SCALAR_IS_FLOAT
+            alpha = scalar == SK_Scalar1 ? 255 : SkToU8((U8CPU) (scalar * 256));
+        #else
+            alpha = SkToU8((scalar - (scalar >= SK_ScalarHalf)) >> 8);
+        #endif
+            color = SkColorSetARGB(alpha, SkColorGetR(color), 
+                SkColorGetG(color), SkColorGetB(color));
+            break;
+        case SK_PROPERTY(blue):
+            scalar = SkScalarClampMax(scalar, 255 * SK_Scalar1);
+            color = SkColorSetARGB(SkColorGetA(color), SkColorGetR(color), 
+                SkColorGetG(color), SkToU8((U8CPU) scalar));
+            break;
+        case SK_PROPERTY(green):
+            scalar = SkScalarClampMax(scalar, 255 * SK_Scalar1);
+            color = SkColorSetARGB(SkColorGetA(color), SkColorGetR(color), 
+                SkToU8((U8CPU) scalar), SkColorGetB(color));
+            break;
+        case SK_PROPERTY(hue):
+            fHue = scalar;//RGB_to_HSV(color, kGetHue);
+            fDirty = true;
+            break;
+        case SK_PROPERTY(red):
+            scalar = SkScalarClampMax(scalar, 255 * SK_Scalar1);
+            color = SkColorSetARGB(SkColorGetA(color), SkToU8((U8CPU) scalar), 
+                SkColorGetG(color), SkColorGetB(color));
+        break;
+        case SK_PROPERTY(saturation):
+            fSaturation = scalar;//RGB_to_HSV(color, kGetSaturation);
+            fDirty = true;
+            break;
+        case SK_PROPERTY(value):
+            fValue = scalar;//RGB_to_HSV(color, kGetValue);
+            fDirty = true;
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
diff --git a/src/animator/SkDrawColor.h b/src/animator/SkDrawColor.h
new file mode 100644
index 0000000..89b87cc
--- /dev/null
+++ b/src/animator/SkDrawColor.h
@@ -0,0 +1,50 @@
+/* libs/graphics/animator/SkDrawColor.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 SkDrawColor_DEFINED
+#define SkDrawColor_DEFINED
+
+#include "SkPaintParts.h"
+#include "SkColor.h"
+
+class SkDrawColor : public SkPaintPart {
+    DECLARE_DRAW_MEMBER_INFO(Color);
+    SkDrawColor();
+    virtual bool add();
+    virtual void dirty();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    SkColor getColor();
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual SkDisplayable* getParent() const;
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual bool setParent(SkDisplayable* parent);
+    virtual bool setProperty(int index, SkScriptValue&);
+protected:
+    SkColor color;
+    SkScalar fHue;
+    SkScalar fSaturation;
+    SkScalar fValue;
+    SkBool fDirty;
+private:
+    friend class SkGradient;
+    typedef SkPaintPart INHERITED;
+};
+
+#endif // SkDrawColor_DEFINED
diff --git a/src/animator/SkDrawDash.cpp b/src/animator/SkDrawDash.cpp
new file mode 100644
index 0000000..bdac9cd
--- /dev/null
+++ b/src/animator/SkDrawDash.cpp
@@ -0,0 +1,44 @@
+/* libs/graphics/animator/SkDrawDash.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 "SkDrawDash.h"
+#include "SkDashPathEffect.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDash::fInfo[] = {
+    SK_MEMBER_ARRAY(intervals, Float),
+    SK_MEMBER(phase, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDash);
+
+SkDash::SkDash() : phase(0) {
+}
+
+SkDash::~SkDash() {
+}
+
+SkPathEffect* SkDash::getPathEffect() {
+    int count = intervals.count();
+    if (count == 0)
+        return NULL;
+    return new SkDashPathEffect(intervals.begin(), count, phase);
+}
+
diff --git a/src/animator/SkDrawDash.h b/src/animator/SkDrawDash.h
new file mode 100644
index 0000000..c2108c1
--- /dev/null
+++ b/src/animator/SkDrawDash.h
@@ -0,0 +1,35 @@
+/* libs/graphics/animator/SkDrawDash.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 SkDrawDash_DEFINED
+#define SkDrawDash_DEFINED
+
+#include "SkPaintParts.h"
+#include "SkIntArray.h"
+
+class SkDash : public SkDrawPathEffect {
+    DECLARE_MEMBER_INFO(Dash);
+    SkDash();
+    virtual ~SkDash();
+    virtual SkPathEffect* getPathEffect();
+private:
+    SkTDScalarArray intervals;
+    SkScalar phase;
+};
+
+#endif // SkDrawDash_DEFINED
+
diff --git a/src/animator/SkDrawDiscrete.cpp b/src/animator/SkDrawDiscrete.cpp
new file mode 100644
index 0000000..e1089c2
--- /dev/null
+++ b/src/animator/SkDrawDiscrete.cpp
@@ -0,0 +1,43 @@
+/* libs/graphics/animator/SkDrawDiscrete.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 "SkDrawDiscrete.h"
+#include "SkAnimateMaker.h"
+#include "SkPaint.h"
+#include "SkDiscretePathEffect.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDiscrete::fInfo[] = {
+    SK_MEMBER(deviation, Float),
+    SK_MEMBER(segLength, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDiscrete);
+
+SkDiscrete::SkDiscrete() : deviation(0), segLength(0) {
+}
+
+SkPathEffect* SkDiscrete::getPathEffect() {
+    if (deviation <= 0 || segLength <= 0)
+        return NULL;
+    else
+        return new SkDiscretePathEffect(segLength, deviation);
+}
+
diff --git a/src/animator/SkDrawDiscrete.h b/src/animator/SkDrawDiscrete.h
new file mode 100644
index 0000000..78485ee
--- /dev/null
+++ b/src/animator/SkDrawDiscrete.h
@@ -0,0 +1,32 @@
+/* libs/graphics/animator/SkDrawDiscrete.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 SkDrawDiscrete_DEFINED
+#define SkDrawDiscrete_DEFINED
+
+#include "SkPaintParts.h"
+
+class SkDiscrete : public SkDrawPathEffect {
+    DECLARE_MEMBER_INFO(Discrete);
+    SkDiscrete();
+    virtual SkPathEffect* getPathEffect();
+private:
+    SkScalar deviation;
+    SkScalar segLength;
+};
+
+#endif //SkDrawDiscrete_DEFINED
diff --git a/src/animator/SkDrawEmboss.cpp b/src/animator/SkDrawEmboss.cpp
new file mode 100644
index 0000000..77f6dfd
--- /dev/null
+++ b/src/animator/SkDrawEmboss.cpp
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkDrawEmboss.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 "SkDrawEmboss.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawEmboss::fInfo[] = {
+    SK_MEMBER(ambient, Float),
+    SK_MEMBER_ARRAY(direction, Float),
+    SK_MEMBER(radius, Float),
+    SK_MEMBER(specular, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawEmboss);
+
+SkDrawEmboss::SkDrawEmboss() : radius(-1) { 
+    direction.setCount(3);
+}
+
+SkMaskFilter* SkDrawEmboss::getMaskFilter() {
+    if (radius < 0 || direction.count() !=3)
+        return NULL;
+    return SkBlurMaskFilter::CreateEmboss(direction.begin(), ambient, specular, radius);
+}
+
diff --git a/src/animator/SkDrawEmboss.h b/src/animator/SkDrawEmboss.h
new file mode 100644
index 0000000..fe6911b
--- /dev/null
+++ b/src/animator/SkDrawEmboss.h
@@ -0,0 +1,33 @@
+/* libs/graphics/animator/SkDrawEmboss.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 SkDrawEmboss_DEFINED
+#define SkDrawEmboss_DEFINED
+
+#include "SkDrawBlur.h"
+
+class SkDrawEmboss : public SkDrawMaskFilter {
+    DECLARE_DRAW_MEMBER_INFO(Emboss);
+    SkDrawEmboss();
+    virtual SkMaskFilter* getMaskFilter();
+protected:
+    SkTDScalarArray direction;
+    SkScalar radius, ambient, specular;
+};
+
+#endif // SkDrawEmboss_DEFINED
+
diff --git a/src/animator/SkDrawExtraPathEffect.cpp b/src/animator/SkDrawExtraPathEffect.cpp
new file mode 100644
index 0000000..4cca738
--- /dev/null
+++ b/src/animator/SkDrawExtraPathEffect.cpp
@@ -0,0 +1,519 @@
+/* libs/graphics/animator/SkDrawExtraPathEffect.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 "SkDrawExtraPathEffect.h"
+#include "SkDrawPath.h"
+#include "Sk1DPathEffect.h"
+#include "Sk2DPathEffect.h"
+#include "SkMemberInfo.h"
+#include "SkPaintParts.h"
+#include "SkPathEffect.h"
+#include "SkCornerPathEffect.h"
+
+#include "SkDashPathEffect.h"
+
+class SkDrawShapePathEffect : public SkDrawPathEffect {
+    DECLARE_PRIVATE_MEMBER_INFO(DrawShapePathEffect);
+    SkDrawShapePathEffect();
+    virtual ~SkDrawShapePathEffect();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* );
+    virtual SkPathEffect* getPathEffect();
+protected:
+    SkDrawable* addPath;
+    SkDrawable* addMatrix;
+    SkDrawPath* path;
+    SkPathEffect* fPathEffect;
+    friend class SkShape1DPathEffect;
+    friend class SkShape2DPathEffect;
+};
+
+class SkDrawShape1DPathEffect : public SkDrawShapePathEffect {
+    DECLARE_EXTRAS_MEMBER_INFO(SkDrawShape1DPathEffect);
+    SkDrawShape1DPathEffect(SkDisplayTypes );
+    virtual ~SkDrawShape1DPathEffect();
+    virtual void onEndElement(SkAnimateMaker& );
+private:
+    SkString phase;
+    SkString spacing;
+    friend class SkShape1DPathEffect;
+    typedef SkDrawShapePathEffect INHERITED;
+};
+
+class SkDrawShape2DPathEffect : public SkDrawShapePathEffect {
+    DECLARE_EXTRAS_MEMBER_INFO(SkDrawShape2DPathEffect);
+    SkDrawShape2DPathEffect(SkDisplayTypes );
+    virtual ~SkDrawShape2DPathEffect();
+    virtual void onEndElement(SkAnimateMaker& );
+private:
+    SkDrawMatrix* matrix;
+    friend class SkShape2DPathEffect;
+    typedef SkDrawShapePathEffect INHERITED;
+};
+
+class SkDrawComposePathEffect : public SkDrawPathEffect {
+    DECLARE_EXTRAS_MEMBER_INFO(SkDrawComposePathEffect);
+    SkDrawComposePathEffect(SkDisplayTypes );
+    virtual ~SkDrawComposePathEffect();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* );
+    virtual SkPathEffect* getPathEffect();
+    virtual bool isPaint() const; 
+private:
+    SkDrawPathEffect* effect1;
+    SkDrawPathEffect* effect2;
+};
+
+class SkDrawCornerPathEffect : public SkDrawPathEffect {
+    DECLARE_EXTRAS_MEMBER_INFO(SkDrawCornerPathEffect);
+    SkDrawCornerPathEffect(SkDisplayTypes );
+    virtual ~SkDrawCornerPathEffect();
+    virtual SkPathEffect* getPathEffect();
+private:
+    SkScalar radius;
+};
+
+//////////// SkShape1DPathEffect
+
+#include "SkAnimateMaker.h"
+#include "SkAnimatorScript.h"
+#include "SkDisplayApply.h"
+#include "SkDrawMatrix.h"
+#include "SkPaint.h"
+
+class SkShape1DPathEffect : public Sk1DPathEffect {
+public:
+    SkShape1DPathEffect(SkDrawShape1DPathEffect* draw, SkAnimateMaker* maker) :
+        fDraw(draw), fMaker(maker) {
+    }
+
+protected:
+    virtual SkScalar begin(SkScalar contourLength)
+    {
+        SkScriptValue value;
+        SkAnimatorScript engine(*fMaker, NULL, SkType_Float);
+        engine.propertyCallBack(GetContourLength, &contourLength);
+        value.fOperand.fScalar = 0;
+        engine.evaluate(fDraw->phase.c_str(), &value, SkType_Float);
+        return value.fOperand.fScalar;
+    }
+
+    virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure& )
+    {
+        fMaker->setExtraPropertyCallBack(fDraw->fType, GetDistance, &distance);
+        SkDrawPath* drawPath = NULL;
+        if (fDraw->addPath->isPath()) {
+            drawPath = (SkDrawPath*) fDraw->addPath;
+        } else {
+            SkApply* apply = (SkApply*) fDraw->addPath;
+            apply->refresh(*fMaker);
+            apply->activate(*fMaker);
+            apply->interpolate(*fMaker, SkScalarMulRound(distance, 1000));
+            drawPath = (SkDrawPath*) apply->getScope();
+        }
+        SkMatrix m;
+        m.reset();
+        if (fDraw->addMatrix) {
+            SkDrawMatrix* matrix;
+            if (fDraw->addMatrix->getType() == SkType_Matrix) 
+                matrix = (SkDrawMatrix*) fDraw->addMatrix;
+            else {
+                SkApply* apply = (SkApply*) fDraw->addMatrix;
+                apply->refresh(*fMaker);
+                apply->activate(*fMaker);
+                apply->interpolate(*fMaker, SkScalarMulRound(distance, 1000));
+                matrix = (SkDrawMatrix*) apply->getScope();
+            }
+        }
+        SkScalar result = 0;
+        SkAnimatorScript::EvaluateFloat(*fMaker, NULL, fDraw->spacing.c_str(), &result);
+        if (drawPath)
+            dst->addPath(drawPath->getPath(), m);
+        fMaker->clearExtraPropertyCallBack(fDraw->fType);
+        return result;
+    }
+
+private:
+    virtual void flatten(SkFlattenableWriteBuffer& ) {}
+    virtual Factory getFactory() { return NULL; }
+
+    static bool GetContourLength(const char* token, size_t len, void* clen, SkScriptValue* value) {
+        if (SK_LITERAL_STR_EQUAL("contourLength", token, len)) {
+            value->fOperand.fScalar = *(SkScalar*) clen;
+            value->fType = SkType_Float;
+            return true;
+        }
+        return false;
+    }
+
+    static bool GetDistance(const char* token, size_t len, void* dist, SkScriptValue* value) {
+        if (SK_LITERAL_STR_EQUAL("distance", token, len)) {
+            value->fOperand.fScalar = *(SkScalar*) dist;
+            value->fType = SkType_Float;
+            return true;
+        }
+        return false;
+    }
+
+    SkDrawShape1DPathEffect* fDraw;
+    SkAnimateMaker* fMaker;
+};
+
+//////////// SkDrawShapePathEffect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawShapePathEffect::fInfo[] = {
+    SK_MEMBER(addMatrix, Drawable), // either matrix or apply
+    SK_MEMBER(addPath, Drawable),   // either path or apply
+    SK_MEMBER(path, Path),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawShapePathEffect);
+
+SkDrawShapePathEffect::SkDrawShapePathEffect() : 
+    addPath(NULL), addMatrix(NULL), path(NULL), fPathEffect(NULL) {
+}
+
+SkDrawShapePathEffect::~SkDrawShapePathEffect() {
+    fPathEffect->safeUnref();
+}
+
+bool SkDrawShapePathEffect::add(SkAnimateMaker& , SkDisplayable* child) {
+    path = (SkDrawPath*) child;
+    return true;
+}
+
+SkPathEffect* SkDrawShapePathEffect::getPathEffect() {
+    fPathEffect->ref();
+    return fPathEffect;
+}
+
+//////////// SkDrawShape1DPathEffect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawShape1DPathEffect::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(phase, String),
+    SK_MEMBER(spacing, String),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawShape1DPathEffect);
+
+SkDrawShape1DPathEffect::SkDrawShape1DPathEffect(SkDisplayTypes type) : fType(type) {
+}
+
+SkDrawShape1DPathEffect::~SkDrawShape1DPathEffect() {
+}
+
+void SkDrawShape1DPathEffect::onEndElement(SkAnimateMaker& maker) {
+    if (addPath == NULL || (addPath->isPath() == false && addPath->isApply() == false))
+        maker.setErrorCode(SkDisplayXMLParserError::kUnknownError); // !!! add error
+    else
+        fPathEffect = new SkShape1DPathEffect(this, &maker);
+}
+
+////////// SkShape2DPathEffect
+
+class SkShape2DPathEffect : public Sk2DPathEffect {
+public:
+    SkShape2DPathEffect(SkDrawShape2DPathEffect* draw, SkAnimateMaker* maker, 
+        const SkMatrix& matrix) : Sk2DPathEffect(matrix), fDraw(draw), fMaker(maker) {
+    }
+
+protected:
+    virtual void begin(const SkIRect& uvBounds, SkPath* )
+    {
+        fUVBounds.set(SkIntToScalar(uvBounds.fLeft), SkIntToScalar(uvBounds.fTop),
+            SkIntToScalar(uvBounds.fRight), SkIntToScalar(uvBounds.fBottom));
+    }
+
+    virtual void next(const SkPoint& loc, int u, int v, SkPath* dst)
+    {
+        fLoc = loc;
+        fU = u;
+        fV = v;
+        SkDrawPath* drawPath;
+        fMaker->setExtraPropertyCallBack(fDraw->fType, Get2D, this);
+        if (fDraw->addPath->isPath()) {
+            drawPath = (SkDrawPath*) fDraw->addPath;
+        } else {
+            SkApply* apply = (SkApply*) fDraw->addPath;
+            apply->refresh(*fMaker);
+            apply->activate(*fMaker);
+            apply->interpolate(*fMaker, v);
+            drawPath = (SkDrawPath*) apply->getScope();
+        }
+        if (drawPath == NULL)
+            goto clearCallBack;
+        if (fDraw->matrix) {
+            SkDrawMatrix* matrix;
+            if (fDraw->matrix->getType() == SkType_Matrix) 
+                matrix = (SkDrawMatrix*) fDraw->matrix;
+            else {
+                SkApply* apply = (SkApply*) fDraw->matrix;
+                apply->activate(*fMaker);
+                apply->interpolate(*fMaker, v);
+                matrix = (SkDrawMatrix*) apply->getScope();
+            }
+            if (matrix) {
+                dst->addPath(drawPath->getPath(), matrix->getMatrix());
+                goto clearCallBack;
+            }
+        }
+        dst->addPath(drawPath->getPath());
+clearCallBack:
+        fMaker->clearExtraPropertyCallBack(fDraw->fType);
+    }
+
+private:
+
+    static bool Get2D(const char* token, size_t len, void* s2D, SkScriptValue* value) {
+        static const char match[] = "locX|locY|left|top|right|bottom|u|v" ;
+        SkShape2DPathEffect* shape2D = (SkShape2DPathEffect*) s2D;
+        int index;
+        if (SkAnimatorScript::MapEnums(match, token, len, &index) == false)
+            return false;
+        SkASSERT((sizeof(SkPoint) +     sizeof(SkRect)) / sizeof(SkScalar) == 6);
+        if (index < 6) {
+            value->fType = SkType_Float;
+            value->fOperand.fScalar = (&shape2D->fLoc.fX)[index];
+        } else {
+            value->fType = SkType_Int;
+            value->fOperand.fS32 = (&shape2D->fU)[index - 6];
+        }
+        return true;
+    }
+    
+    SkPoint fLoc;
+    SkRect fUVBounds;
+    int32_t fU;
+    int32_t fV;
+    SkDrawShape2DPathEffect* fDraw;
+    SkAnimateMaker* fMaker;
+
+    // illegal
+    SkShape2DPathEffect(const SkShape2DPathEffect&);
+    SkShape2DPathEffect& operator=(const SkShape2DPathEffect&);
+};
+
+////////// SkDrawShape2DPathEffect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawShape2DPathEffect::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(matrix, Matrix)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawShape2DPathEffect);
+
+SkDrawShape2DPathEffect::SkDrawShape2DPathEffect(SkDisplayTypes type) : fType(type) {
+}
+
+SkDrawShape2DPathEffect::~SkDrawShape2DPathEffect() {
+}
+
+void SkDrawShape2DPathEffect::onEndElement(SkAnimateMaker& maker) {
+    if (addPath == NULL || (addPath->isPath() == false && addPath->isApply() == false) ||
+            matrix == NULL)
+        maker.setErrorCode(SkDisplayXMLParserError::kUnknownError); // !!! add error
+    else
+        fPathEffect = new SkShape2DPathEffect(this, &maker, matrix->getMatrix());
+}
+
+////////// SkDrawComposePathEffect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawComposePathEffect::fInfo[] = {
+    SK_MEMBER(effect1, PathEffect),
+    SK_MEMBER(effect2, PathEffect)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawComposePathEffect);
+
+SkDrawComposePathEffect::SkDrawComposePathEffect(SkDisplayTypes type) : fType(type),
+    effect1(NULL), effect2(NULL) {
+}
+
+SkDrawComposePathEffect::~SkDrawComposePathEffect() {
+    delete effect1;
+    delete effect2;
+}
+
+bool SkDrawComposePathEffect::add(SkAnimateMaker& , SkDisplayable* child) {
+    if (effect1 == NULL)
+        effect1 = (SkDrawPathEffect*) child;
+    else
+        effect2 = (SkDrawPathEffect*) child;
+    return true;
+}
+
+SkPathEffect* SkDrawComposePathEffect::getPathEffect() {
+    SkPathEffect* e1 = effect1->getPathEffect();
+    SkPathEffect* e2 = effect2->getPathEffect();
+    SkPathEffect* composite = new SkComposePathEffect(e1, e2);
+    e1->unref();
+    e2->unref();
+    return composite;
+}
+
+bool SkDrawComposePathEffect::isPaint() const {
+    return true;
+}
+
+//////////// SkDrawCornerPathEffect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawCornerPathEffect::fInfo[] = {
+    SK_MEMBER(radius, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawCornerPathEffect);
+
+SkDrawCornerPathEffect::SkDrawCornerPathEffect(SkDisplayTypes type):
+    fType(type), radius(0) {
+}
+
+SkDrawCornerPathEffect::~SkDrawCornerPathEffect() {
+}
+
+SkPathEffect* SkDrawCornerPathEffect::getPathEffect() {
+    return new SkCornerPathEffect(radius);
+}
+
+/////////
+
+#include "SkExtras.h"
+
+const char kDrawShape1DPathEffectName[] = "pathEffect:shape1D";
+const char kDrawShape2DPathEffectName[] = "pathEffect:shape2D";
+const char kDrawComposePathEffectName[] = "pathEffect:compose";
+const char kDrawCornerPathEffectName[]  = "pathEffect:corner";
+
+class SkExtraPathEffects : public SkExtras {
+public:
+    SkExtraPathEffects(SkAnimator* animator) : 
+            skDrawShape1DPathEffectType(SkType_Unknown),
+            skDrawShape2DPathEffectType(SkType_Unknown),
+            skDrawComposePathEffectType(SkType_Unknown),
+            skDrawCornerPathEffectType(SkType_Unknown) {
+    }
+    
+    virtual SkDisplayable* createInstance(SkDisplayTypes type) {
+        SkDisplayable* result = NULL;
+        if (skDrawShape1DPathEffectType == type)
+            result = new SkDrawShape1DPathEffect(type);
+        else if (skDrawShape2DPathEffectType == type)
+            result = new SkDrawShape2DPathEffect(type);
+        else if (skDrawComposePathEffectType == type)
+            result = new SkDrawComposePathEffect(type);
+        else if (skDrawCornerPathEffectType == type)
+            result = new SkDrawCornerPathEffect(type);
+        return result;
+    }
+
+    virtual bool definesType(SkDisplayTypes type) {
+        return type == skDrawShape1DPathEffectType || 
+            type == skDrawShape2DPathEffectType || 
+            type == skDrawComposePathEffectType ||
+            type == skDrawCornerPathEffectType;
+    }
+
+#if SK_USE_CONDENSED_INFO == 0
+    virtual const SkMemberInfo* getMembers(SkDisplayTypes type, int* infoCountPtr) {
+        const SkMemberInfo* info = NULL;
+        int infoCount = 0;
+        if (skDrawShape1DPathEffectType == type) {
+            info = SkDrawShape1DPathEffect::fInfo;
+            infoCount = SkDrawShape1DPathEffect::fInfoCount;
+        } else if (skDrawShape2DPathEffectType == type) {
+            info = SkDrawShape2DPathEffect::fInfo;
+            infoCount = SkDrawShape2DPathEffect::fInfoCount;
+        } else if (skDrawComposePathEffectType == type) {
+            info = SkDrawComposePathEffect::fInfo;
+            infoCount = SkDrawShape1DPathEffect::fInfoCount;
+        } else if (skDrawCornerPathEffectType == type) {
+            info = SkDrawCornerPathEffect::fInfo;
+            infoCount = SkDrawCornerPathEffect::fInfoCount;
+        }
+        if (infoCountPtr)
+            *infoCountPtr = infoCount;
+        return info;
+    }
+#endif
+
+#ifdef SK_DEBUG
+    virtual const char* getName(SkDisplayTypes type) {
+        if (skDrawShape1DPathEffectType == type)
+            return kDrawShape1DPathEffectName;
+        else if (skDrawShape2DPathEffectType == type)
+            return kDrawShape2DPathEffectName;
+        else if (skDrawComposePathEffectType == type)
+            return kDrawComposePathEffectName;
+        else if (skDrawCornerPathEffectType == type)
+            return kDrawCornerPathEffectName;
+        return NULL;
+    }
+#endif
+
+    virtual SkDisplayTypes getType(const char name[], size_t len ) {
+        SkDisplayTypes* type = NULL;
+        if (SK_LITERAL_STR_EQUAL(kDrawShape1DPathEffectName, name, len))
+            type = &skDrawShape1DPathEffectType;
+        else if (SK_LITERAL_STR_EQUAL(kDrawShape2DPathEffectName, name, len))
+            type = &skDrawShape2DPathEffectType;
+        else if (SK_LITERAL_STR_EQUAL(kDrawComposePathEffectName, name, len))
+            type = &skDrawComposePathEffectType;
+        else if (SK_LITERAL_STR_EQUAL(kDrawCornerPathEffectName, name, len))
+            type = &skDrawCornerPathEffectType;
+        if (type) {
+            if (*type == SkType_Unknown)
+                *type = SkDisplayType::RegisterNewType();
+            return *type;
+        }
+        return SkType_Unknown;
+    }
+
+private:
+    SkDisplayTypes skDrawShape1DPathEffectType;
+    SkDisplayTypes skDrawShape2DPathEffectType;
+    SkDisplayTypes skDrawComposePathEffectType;
+    SkDisplayTypes skDrawCornerPathEffectType;
+};
+
+
+void InitializeSkExtraPathEffects(SkAnimator* animator) {
+    animator->addExtras(new SkExtraPathEffects(animator));
+}
+
+////////////////
+
+
+SkExtras::SkExtras() : fExtraCallBack(NULL), fExtraStorage(NULL) {
+}
diff --git a/src/animator/SkDrawFull.cpp b/src/animator/SkDrawFull.cpp
new file mode 100644
index 0000000..c6568db
--- /dev/null
+++ b/src/animator/SkDrawFull.cpp
@@ -0,0 +1,27 @@
+/* libs/graphics/animator/SkDrawFull.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 "SkDrawFull.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+
+bool SkFull::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawPaint(*maker.fPaint);
+    return false;
+}
+
diff --git a/src/animator/SkDrawFull.h b/src/animator/SkDrawFull.h
new file mode 100644
index 0000000..cfbb643
--- /dev/null
+++ b/src/animator/SkDrawFull.h
@@ -0,0 +1,30 @@
+/* libs/graphics/animator/SkDrawFull.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 SkDrawFull_DEFINED
+#define SkDrawFull_DEFINED
+
+#include "SkBoundable.h"
+
+class SkFull : public SkBoundable {
+    DECLARE_EMPTY_MEMBER_INFO(Full);
+    virtual bool draw(SkAnimateMaker& );
+private:
+    typedef SkBoundable INHERITED;
+};
+
+#endif // SkDrawFull_DEFINED
diff --git a/src/animator/SkDrawGradient.cpp b/src/animator/SkDrawGradient.cpp
new file mode 100644
index 0000000..e9061b5
--- /dev/null
+++ b/src/animator/SkDrawGradient.cpp
@@ -0,0 +1,235 @@
+/* libs/graphics/animator/SkDrawGradient.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 "SkDrawGradient.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimatorScript.h"
+#include "SkGradientShader.h"
+#include "SkUnitMapper.h"
+
+SkScalar SkUnitToScalar(U16CPU x) {
+#ifdef SK_SCALAR_IS_FLOAT
+    return x / 65535.0f;
+#else
+    return x + (x >> 8);
+#endif
+}
+
+U16CPU SkScalarToUnit(SkScalar x) {
+    SkScalar pin =  SkScalarPin(x, 0, SK_Scalar1);
+#ifdef SK_SCALAR_IS_FLOAT
+    return (int) (pin * 65535.0f);
+#else
+    return pin - (pin >= 32768);
+#endif
+}
+
+class SkGradientUnitMapper : public SkUnitMapper {
+public:
+    SkGradientUnitMapper(SkAnimateMaker* maker, const char* script) : fMaker(maker), fScript(script) {
+    }
+    
+    // overrides for SkFlattenable
+    virtual Factory getFactory() { return NULL; }
+    
+protected:
+    virtual uint16_t mapUnit16(uint16_t x) {
+        fUnit = SkUnitToScalar(x);
+        SkScriptValue value;
+        SkAnimatorScript engine(*fMaker, NULL, SkType_Float);
+        engine.propertyCallBack(GetUnitValue, &fUnit);
+        if (engine.evaluate(fScript, &value, SkType_Float)) 
+            x = SkScalarToUnit(value.fOperand.fScalar);
+        return x;
+    }
+
+    static bool GetUnitValue(const char* token, size_t len, void* unitPtr, SkScriptValue* value) {
+        if (SK_LITERAL_STR_EQUAL("unit", token, len)) {
+            value->fOperand.fScalar = *(SkScalar*) unitPtr;
+            value->fType = SkType_Float;
+            return true;
+        }
+        return false;
+    }
+
+    SkAnimateMaker* fMaker;
+    const char* fScript;
+    SkScalar fUnit;
+};
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkGradient::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER_ARRAY(offsets, Float),
+    SK_MEMBER(unitMapper, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkGradient);
+
+SkGradient::SkGradient() : fUnitMapper(NULL) {
+}
+
+SkGradient::~SkGradient() {
+    for (int index = 0; index < fDrawColors.count(); index++) 
+        delete fDrawColors[index];
+    delete fUnitMapper;
+}
+
+bool SkGradient::add(SkAnimateMaker& , SkDisplayable* child) {
+    SkASSERT(child);
+    if (child->isColor()) {
+        SkDrawColor* color = (SkDrawColor*) child;
+        *fDrawColors.append() = color;
+        return true;
+    }
+    return false;
+}
+
+int SkGradient::addPrelude() {
+    int count = fDrawColors.count();
+    fColors.setCount(count);
+    for (int index = 0; index < count; index++) 
+        fColors[index] = fDrawColors[index]->color;
+    return count;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkGradient::dumpRest(SkAnimateMaker* maker) {
+    dumpAttrs(maker);
+    //can a gradient have no colors?
+    bool closedYet = false;
+    SkDisplayList::fIndent += 4;
+    for (SkDrawColor** ptr = fDrawColors.begin(); ptr < fDrawColors.end(); ptr++) {
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        SkDrawColor* color = *ptr;
+        color->dump(maker);
+    }
+    SkDisplayList::fIndent -= 4;    
+    dumpChildren(maker, closedYet); //dumps the matrix if it has one
+}
+#endif
+
+void SkGradient::onEndElement(SkAnimateMaker& maker) {
+    if (offsets.count() != 0) {
+        if (offsets.count() != fDrawColors.count()) {
+            maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsDontMatchColors);
+            return;
+        }
+        if (offsets[0] != 0) {
+            maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustStartWithZero);
+            return;
+        }
+        if (offsets[offsets.count()-1] != SK_Scalar1) {
+            maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustEndWithOne);
+            return;
+        }
+        for (int i = 1; i < offsets.count(); i++) {
+            if (offsets[i] <= offsets[i-1]) {
+                maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustIncrease);
+                return;
+            }
+            if (offsets[i] > SK_Scalar1) {
+                maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustBeNoMoreThanOne);
+                return;
+            }
+        }
+    }
+    if (unitMapper.size() > 0) 
+        fUnitMapper = new SkGradientUnitMapper(&maker, unitMapper.c_str());
+    INHERITED::onEndElement(maker);
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkLinearGradient::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER_ARRAY(points, Float),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkLinearGradient);
+
+SkLinearGradient::SkLinearGradient() { 
+}
+
+void SkLinearGradient::onEndElement(SkAnimateMaker& maker)
+{
+    if (points.count() != 4)
+        maker.setErrorCode(SkDisplayXMLParserError::kGradientPointsLengthMustBeFour);
+    INHERITED::onEndElement(maker);
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkLinearGradient::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpRest(maker);
+    }
+#endif
+
+SkShader* SkLinearGradient::getShader() {
+    if (addPrelude() == 0 || points.count() != 4)
+        return NULL;
+    SkShader* shader = SkGradientShader::CreateLinear((SkPoint*)points.begin(),
+        fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, fUnitMapper);
+    SkAutoTDelete<SkShader> autoDel(shader);
+    addPostlude(shader);
+    (void)autoDel.detach();
+    return shader;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRadialGradient::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(center, Point),
+    SK_MEMBER(radius, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRadialGradient);
+
+SkRadialGradient::SkRadialGradient() : radius(0) { 
+    center.set(0, 0); 
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkRadialGradient::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpRest(maker);
+}
+#endif
+
+SkShader* SkRadialGradient::getShader() {
+    if (addPrelude() == 0)
+        return NULL;
+    SkShader* shader = SkGradientShader::CreateRadial(center,
+        radius, fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, fUnitMapper);
+    SkAutoTDelete<SkShader> autoDel(shader);
+    addPostlude(shader);
+    (void)autoDel.detach();
+    return shader;
+}
diff --git a/src/animator/SkDrawGradient.h b/src/animator/SkDrawGradient.h
new file mode 100644
index 0000000..30a86df
--- /dev/null
+++ b/src/animator/SkDrawGradient.h
@@ -0,0 +1,76 @@
+/* libs/graphics/animator/SkDrawGradient.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 SkDrawGradient_DEFINED
+#define SkDrawGradient_DEFINED
+
+#include "SkDrawColor.h"
+#include "SkDrawShader.h"
+#include "SkIntArray.h"
+
+class SkUnitMapper;
+
+class SkGradient : public SkDrawShader {
+    DECLARE_PRIVATE_MEMBER_INFO(Gradient);
+    SkGradient();
+    virtual ~SkGradient();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+#ifdef SK_DUMP_ENABLED
+    virtual void dumpRest(SkAnimateMaker*);
+#endif    
+    virtual void onEndElement(SkAnimateMaker& );
+protected:
+    SkTDScalarArray offsets;
+    SkString unitMapper;
+    SkTDColorArray fColors;
+    SkTDDrawColorArray fDrawColors;
+    SkUnitMapper* fUnitMapper;
+    int addPrelude();
+private:
+    typedef SkDrawShader INHERITED;
+};
+
+class SkLinearGradient : public SkGradient {
+    DECLARE_MEMBER_INFO(LinearGradient);
+    SkLinearGradient();
+    virtual void onEndElement(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker*);
+#endif
+    virtual SkShader* getShader();
+protected:
+    SkTDScalarArray points;
+private:
+    typedef SkGradient INHERITED;
+};
+
+class SkRadialGradient : public SkGradient {
+    DECLARE_MEMBER_INFO(RadialGradient);
+    SkRadialGradient();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker*);
+#endif    
+    virtual SkShader* getShader();
+protected:
+    SkPoint center;
+    SkScalar radius;
+private:
+    typedef SkGradient INHERITED;
+};
+
+#endif // SkDrawGradient_DEFINED
+
diff --git a/src/animator/SkDrawGroup.cpp b/src/animator/SkDrawGroup.cpp
new file mode 100644
index 0000000..aa53564
--- /dev/null
+++ b/src/animator/SkDrawGroup.cpp
@@ -0,0 +1,331 @@
+/* libs/graphics/animator/SkDrawGroup.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 "SkDrawGroup.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimatorScript.h"
+#include "SkCanvas.h"
+#include "SkDisplayApply.h"
+#include "SkPaint.h"
+#ifdef SK_DEBUG
+#include "SkDisplayList.h"
+#endif
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkGroup::fInfo[] = {
+    SK_MEMBER(condition, String),
+    SK_MEMBER(enableCondition, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkGroup);
+
+SkGroup::SkGroup() : fParentList(NULL), fOriginal(NULL) {
+}
+
+SkGroup::~SkGroup() {
+    if (fOriginal)  // has been copied
+        return;
+    int index = 0;
+    int max = fCopies.count() << 5;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        if (index >= max || markedForDelete(index))
+            delete *ptr;
+//      else {
+//          SkApply* apply = (SkApply*) *ptr;
+//          SkASSERT(apply->isApply());
+//          SkASSERT(apply->getScope());
+//          delete apply->getScope();
+//      }
+        index++;
+    }
+}
+
+bool SkGroup::add(SkAnimateMaker& , SkDisplayable* child) {
+    SkASSERT(child); 
+//  SkASSERT(child->isDrawable());
+    *fChildren.append() = (SkDrawable*) child;
+    if (child->isGroup()) {
+        SkGroup* groupie = (SkGroup*) child;
+        SkASSERT(groupie->fParentList == NULL);
+        groupie->fParentList = &fChildren;
+    }
+    return true;
+}
+
+bool SkGroup::contains(SkDisplayable* match) {
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable == match || drawable->contains(match))
+            return true;
+    }
+    return false;
+}
+
+SkGroup* SkGroup::copy() {
+    SkGroup* result = new SkGroup();
+    result->fOriginal = this;
+    result->fChildren = fChildren;
+    return result;
+}
+
+SkBool SkGroup::copySet(int index) {
+    return (fCopies[index >> 5] & 1 << (index & 0x1f)) != 0;
+}
+
+SkDisplayable* SkGroup::deepCopy(SkAnimateMaker* maker) {
+    SkDisplayable* copy = INHERITED::deepCopy(maker);
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDisplayable* displayable = (SkDisplayable*)*ptr;
+        SkDisplayable* deeperCopy = displayable->deepCopy(maker);
+        ((SkGroup*)copy)->add(*maker, deeperCopy);
+    }
+    return copy;
+}
+
+bool SkGroup::doEvent(SkDisplayEvent::Kind kind, SkEventState* state) {
+    bool handled = false;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable->isDrawable() == false)
+            continue;
+        handled |= drawable->doEvent(kind, state);
+    }
+    return handled;
+}
+
+bool SkGroup::draw(SkAnimateMaker& maker) {
+    bool conditionTrue = ifCondition(maker, this, condition);
+    bool result = false;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable->isDrawable() == false)
+            continue;
+        if (conditionTrue == false) {
+            if (drawable->isApply())
+                ((SkApply*) drawable)->disable();
+            continue;
+        }
+        maker.validate();
+        result |= drawable->draw(maker);
+        maker.validate();
+    }
+    return result;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkGroup::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    if (condition.size() > 0)
+        SkDebugf("condition=\"%s\" ", condition.c_str());
+    if (enableCondition.size() > 0)
+        SkDebugf("enableCondition=\"%s\" ", enableCondition.c_str());
+    dumpDrawables(maker);
+}
+
+void SkGroup::dumpDrawables(SkAnimateMaker* maker) {
+    SkDisplayList::fIndent += 4;
+    int save = SkDisplayList::fDumpIndex;
+    SkDisplayList::fDumpIndex = 0;
+    bool closedYet = false;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        if (closedYet == false) {
+            closedYet = true;
+            SkDebugf(">\n");
+        }
+        SkDrawable* drawable = *ptr;
+        drawable->dump(maker);
+        SkDisplayList::fDumpIndex++;
+    }
+    SkDisplayList::fIndent -= 4;
+    SkDisplayList::fDumpIndex = save;
+    if (closedYet) //we had children, now it's time to close the group
+        dumpEnd(maker);
+    else    //no children
+        SkDebugf("/>\n");
+}
+
+void SkGroup::dumpEvents() {
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        drawable->dumpEvents();
+    }
+}
+#endif
+
+bool SkGroup::enable(SkAnimateMaker& maker ) {
+    reset();
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (ifCondition(maker, drawable, enableCondition) == false)
+            continue;
+        drawable->enable(maker);
+    }
+    return true;    // skip add; already added so that scope is findable by children
+}
+
+int SkGroup::findGroup(SkDrawable* match,  SkTDDrawableArray** list,
+                 SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList) {
+    *list = &fChildren;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable->isGroup()) {
+            SkGroup* childGroup = (SkGroup*) drawable;
+            if (childGroup->fOriginal == match)
+                goto foundMatch;
+        }
+        if (drawable == match) {
+foundMatch:
+            *parent = this;
+            return (int) (ptr - fChildren.begin());
+        }
+    }
+    *grandList = &fChildren;
+    return SkDisplayList::SearchForMatch(match, list, parent, found, grandList);
+}
+
+bool SkGroup::hasEnable() const {
+    return true;
+}
+
+bool SkGroup::ifCondition(SkAnimateMaker& maker, SkDrawable* drawable,
+        SkString& conditionString) {
+    if (conditionString.size() == 0)
+        return true;
+    int32_t result;
+    bool success = SkAnimatorScript::EvaluateInt(maker, this, conditionString.c_str(), &result);
+#ifdef SK_DUMP_ENABLED
+    if (maker.fDumpGConditions) {
+        SkDebugf("group: ");
+        dumpBase(&maker);
+        SkDebugf("condition=%s ", conditionString.c_str());
+        if (success == false)
+            SkDebugf("(script failed)\n");
+        else
+            SkDebugf("success=%s\n", result != 0 ? "true" : "false");
+    }
+#endif
+    return success && result != 0;
+}
+
+void SkGroup::initialize() {
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable->isDrawable() == false)
+            continue;
+        drawable->initialize();
+    }
+}
+
+void SkGroup::markCopyClear(int index) {
+    if (index < 0)
+        index = fChildren.count();
+    fCopies[index >> 5] &= ~(1 << (index & 0x1f));
+}
+
+void SkGroup::markCopySet(int index) {
+    if (index < 0)
+        index = fChildren.count();
+    fCopies[index >> 5] |= 1 << (index & 0x1f);
+}
+
+void SkGroup::markCopySize(int index) {
+    if (index < 0)
+        index = fChildren.count() + 1;
+    int oldLongs = fCopies.count();
+    int newLongs = (index >> 5) + 1;
+    if (oldLongs < newLongs) {
+        fCopies.setCount(newLongs);
+        memset(&fCopies[oldLongs], 0, (newLongs - oldLongs) << 2);
+    }
+}
+
+void SkGroup::reset() {
+    if (fOriginal)  // has been copied
+        return;
+    int index = 0;
+    int max = fCopies.count() << 5;
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        if (index >= max || copySet(index) == false)
+            continue;
+        SkApply* apply = (SkApply*) *ptr;
+        SkASSERT(apply->isApply());
+        SkASSERT(apply->getScope());
+        *ptr = apply->getScope();
+        markCopyClear(index);
+        index++;
+    }
+}
+
+bool SkGroup::resolveIDs(SkAnimateMaker& maker, SkDisplayable* orig, SkApply* apply) {
+    SkGroup* original = (SkGroup*) orig;
+    SkTDDrawableArray& originalChildren = original->fChildren;
+    SkDrawable** originalPtr = originalChildren.begin();
+    SkDrawable** ptr = fChildren.begin();
+    SkDrawable** end = fChildren.end();
+    SkDrawable** origChild = ((SkGroup*) orig)->fChildren.begin();
+    while (ptr < end) {
+        SkDrawable* drawable = *ptr++;
+        maker.resolveID(drawable, *origChild++);
+        if (drawable->resolveIDs(maker, *originalPtr++, apply) == true)
+            return true; // failed
+    }
+    return false;
+}
+
+void SkGroup::setSteps(int steps) {
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        if (drawable->isDrawable() == false)
+            continue;
+        drawable->setSteps(steps);
+    }
+}
+
+#ifdef SK_DEBUG
+void SkGroup::validate() {
+    for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkDrawable* drawable = *ptr;
+        drawable->validate();
+    }
+}
+#endif
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkSave::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkSave);
+
+bool SkSave::draw(SkAnimateMaker& maker) {
+    maker.fCanvas->save();
+    SkPaint* save = maker.fPaint;
+    SkPaint local = SkPaint(*maker.fPaint);
+    maker.fPaint = &local;
+    bool result = INHERITED::draw(maker);
+    maker.fPaint = save;
+    maker.fCanvas->restore();
+    return result;
+}
+
+
diff --git a/src/animator/SkDrawGroup.h b/src/animator/SkDrawGroup.h
new file mode 100644
index 0000000..0009a4c
--- /dev/null
+++ b/src/animator/SkDrawGroup.h
@@ -0,0 +1,80 @@
+/* libs/graphics/animator/SkDrawGroup.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 SkDrawGroup_DEFINED
+#define SkDrawGroup_DEFINED
+
+#include "SkDrawable.h"
+#include "SkIntArray.h"
+#include "SkMemberInfo.h"
+
+class SkGroup : public SkDrawable { //interface for schema element <g>
+public:
+    DECLARE_MEMBER_INFO(Group);
+    SkGroup();
+    virtual ~SkGroup();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool contains(SkDisplayable* );
+    SkGroup* copy();
+    SkBool copySet(int index);
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual bool doEvent(SkDisplayEvent::Kind , SkEventState* state );
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+    virtual void dumpDrawables(SkAnimateMaker* );
+    virtual void dumpEvents();
+#endif
+    int findGroup(SkDrawable* drawable,  SkTDDrawableArray** list,
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList);
+    virtual bool enable(SkAnimateMaker& );
+    SkTDDrawableArray* getChildren() { return &fChildren; }
+    SkGroup* getOriginal() { return fOriginal; }
+    virtual bool hasEnable() const;
+    virtual void initialize();
+    SkBool isACopy() { return fOriginal != NULL; }
+    void markCopyClear(int index);
+    void markCopySet(int index);
+    void markCopySize(int index);
+    bool markedForDelete(int index) const { return (fCopies[index >> 5] & 1 << (index & 0x1f)) == 0; }
+    void reset();
+    bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* );
+    virtual void setSteps(int steps);
+#ifdef SK_DEBUG
+    virtual void validate();
+#endif
+protected:
+    bool ifCondition(SkAnimateMaker& maker, SkDrawable* drawable,
+        SkString& conditionString);
+    SkString condition;
+    SkString enableCondition;
+    SkTDDrawableArray fChildren;
+    SkTDDrawableArray* fParentList;
+    SkTDIntArray fCopies;
+    SkGroup* fOriginal;
+private:
+    typedef SkDrawable INHERITED;
+};
+
+class SkSave: public SkGroup {
+    DECLARE_MEMBER_INFO(Save);
+    virtual bool draw(SkAnimateMaker& );
+private:
+    typedef SkGroup INHERITED;
+};
+
+#endif // SkDrawGroup_DEFINED
diff --git a/src/animator/SkDrawLine.cpp b/src/animator/SkDrawLine.cpp
new file mode 100644
index 0000000..166cbbc
--- /dev/null
+++ b/src/animator/SkDrawLine.cpp
@@ -0,0 +1,43 @@
+/* libs/graphics/animator/SkDrawLine.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 "SkDrawLine.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkLine::fInfo[] = {
+    SK_MEMBER(x1, Float),
+    SK_MEMBER(x2, Float),
+    SK_MEMBER(y1, Float),
+    SK_MEMBER(y2, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkLine);
+
+SkLine::SkLine() : x1(0), x2(0), y1(0), y2(0) { 
+}
+
+bool SkLine::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawLine(x1, y1, x2, y2, *maker.fPaint);
+    return false;
+}
diff --git a/src/animator/SkDrawLine.h b/src/animator/SkDrawLine.h
new file mode 100644
index 0000000..8f771cf
--- /dev/null
+++ b/src/animator/SkDrawLine.h
@@ -0,0 +1,37 @@
+/* libs/graphics/animator/SkDrawLine.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 SkDrawLine_DEFINED
+#define SkDrawLine_DEFINED
+
+#include "SkBoundable.h"
+#include "SkMemberInfo.h"
+
+class SkLine : public SkBoundable {
+    DECLARE_MEMBER_INFO(Line);
+    SkLine();
+    virtual bool draw(SkAnimateMaker& );
+private:
+    SkScalar x1;
+    SkScalar x2;
+    SkScalar y1;
+    SkScalar y2;
+    typedef SkBoundable INHERITED;
+};
+
+#endif // SkDrawLine_DEFINED
+
diff --git a/src/animator/SkDrawMatrix.cpp b/src/animator/SkDrawMatrix.cpp
new file mode 100644
index 0000000..dbdbf19
--- /dev/null
+++ b/src/animator/SkDrawMatrix.cpp
@@ -0,0 +1,290 @@
+/* libs/graphics/animator/SkDrawMatrix.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 "SkDrawMatrix.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkParse.h"
+#include "SkMatrixParts.h"
+#include "SkScript.h"
+#include "SkTypedArray.h"
+
+enum SkDrawMatrix_Properties {
+    SK_PROPERTY(perspectX),
+    SK_PROPERTY(perspectY),
+    SK_PROPERTY(rotate),
+    SK_PROPERTY(scale),
+    SK_PROPERTY(scaleX),
+    SK_PROPERTY(scaleY),
+    SK_PROPERTY(skewX),
+    SK_PROPERTY(skewY),
+    SK_PROPERTY(translate),
+    SK_PROPERTY(translateX),
+    SK_PROPERTY(translateY)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawMatrix::fInfo[] = {
+    SK_MEMBER_ARRAY(matrix, Float),
+    SK_MEMBER_PROPERTY(perspectX, Float),
+    SK_MEMBER_PROPERTY(perspectY, Float),
+    SK_MEMBER_PROPERTY(rotate, Float),
+    SK_MEMBER_PROPERTY(scale, Float),
+    SK_MEMBER_PROPERTY(scaleX, Float),
+    SK_MEMBER_PROPERTY(scaleY, Float),
+    SK_MEMBER_PROPERTY(skewX, Float),
+    SK_MEMBER_PROPERTY(skewY, Float),
+    SK_MEMBER_PROPERTY(translate, Point),
+    SK_MEMBER_PROPERTY(translateX, Float),
+    SK_MEMBER_PROPERTY(translateY, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawMatrix);
+
+SkDrawMatrix::SkDrawMatrix() : fChildHasID(false), fDirty(false) { 
+    fConcat.reset();
+    fMatrix.reset(); 
+}
+
+SkDrawMatrix::~SkDrawMatrix() {
+    for (SkMatrixPart** part = fParts.begin(); part < fParts.end();  part++)
+        delete *part;
+}
+
+bool SkDrawMatrix::add(SkAnimateMaker& maker, SkDisplayable* child) {
+    SkASSERT(child && child->isMatrixPart());
+    SkMatrixPart* part = (SkMatrixPart*) child;
+    *fParts.append() = part;
+    if (part->add())
+        maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToMatrix); 
+    return true;
+}
+
+bool SkDrawMatrix::childrenNeedDisposing() const { 
+    return false;
+}
+
+SkDisplayable* SkDrawMatrix::deepCopy(SkAnimateMaker* maker) {
+    SkDrawMatrix* copy = (SkDrawMatrix*)
+        SkDisplayType::CreateInstance(maker, SkType_Matrix);
+    SkASSERT(fParts.count() == 0);
+    copy->fMatrix = fMatrix;
+    copy->fConcat = fConcat;
+    return copy;
+}
+
+void SkDrawMatrix::dirty() { 
+    fDirty = true; 
+}
+
+bool SkDrawMatrix::draw(SkAnimateMaker& maker) {
+    SkMatrix& concat = getMatrix();
+    maker.fCanvas->concat(concat);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawMatrix::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    if (fMatrix.isIdentity()) {
+        SkDebugf("matrix=\"identity\"/>\n");
+        return;
+    }
+    SkScalar result;
+    result = fMatrix[SkMatrix::kMScaleX];
+    if (result != SK_Scalar1)
+        SkDebugf("sx=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getScaleY();
+    if (result != SK_Scalar1)
+        SkDebugf("sy=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getSkewX();
+    if (result)
+        SkDebugf("skew-x=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getSkewY();
+    if (result)
+        SkDebugf("skew-y=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getTranslateX();
+    if (result)
+        SkDebugf("tx=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getTranslateY();
+    if (result)
+        SkDebugf("ty=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getPerspX();
+    if (result)
+        SkDebugf("perspect-x=\"%g\" ", SkScalarToFloat(result));
+    result = fMatrix.getPerspY();
+    if (result)
+        SkDebugf("perspect-y=\"%g\" ", SkScalarToFloat(result));
+    SkDebugf("/>\n");
+}
+#endif
+
+SkMatrix& SkDrawMatrix::getMatrix() {
+    if (fDirty == false)
+        return fConcat;
+    fMatrix.reset();
+    for (SkMatrixPart** part = fParts.begin(); part < fParts.end();  part++) {
+        (*part)->add();
+        fConcat = fMatrix;
+    }
+    fDirty = false;
+    return fConcat;
+}
+
+bool SkDrawMatrix::getProperty(int index, SkScriptValue* value) const {
+    value->fType = SkType_Float;
+    SkScalar result;
+    switch (index) {
+        case SK_PROPERTY(perspectX):
+            result = fMatrix.getPerspX();
+            break;
+        case SK_PROPERTY(perspectY):
+            result = fMatrix.getPerspY();
+            break;
+        case SK_PROPERTY(scaleX):
+            result = fMatrix.getScaleX();
+            break;
+        case SK_PROPERTY(scaleY):
+            result = fMatrix.getScaleY();
+            break;
+        case SK_PROPERTY(skewX):
+            result = fMatrix.getSkewX();
+            break;
+        case SK_PROPERTY(skewY):
+            result = fMatrix.getSkewY();
+            break;
+        case SK_PROPERTY(translateX):
+            result = fMatrix.getTranslateX();
+            break;
+        case SK_PROPERTY(translateY):
+            result = fMatrix.getTranslateY();
+            break;
+        default:
+//          SkASSERT(0);
+            return false;
+    }
+    value->fOperand.fScalar = result;
+    return true;
+}
+
+void SkDrawMatrix::initialize() {
+    fConcat = fMatrix;
+}
+
+void SkDrawMatrix::onEndElement(SkAnimateMaker& ) {
+    if (matrix.count() > 0) {
+        SkScalar* vals = matrix.begin();
+        fMatrix.setScaleX(vals[0]);
+        fMatrix.setSkewX(vals[1]);
+        fMatrix.setTranslateX(vals[2]);
+        fMatrix.setSkewY(vals[3]);
+        fMatrix.setScaleY(vals[4]);
+        fMatrix.setTranslateY(vals[5]);
+#ifdef SK_SCALAR_IS_FIXED
+        fMatrix.setPerspX(SkFixedToFract(vals[6]));
+        fMatrix.setPerspY(SkFixedToFract(vals[7]));
+#else
+        fMatrix.setPerspX(vals[6]);
+        fMatrix.setPerspY(vals[7]);
+#endif
+//      fMatrix.setPerspW(vals[8]);
+        goto setConcat;
+    }
+    if (fChildHasID == false) {
+        {
+            for (SkMatrixPart** part = fParts.begin(); part < fParts.end();  part++)
+                delete *part;
+        }
+        fParts.reset();
+setConcat:
+        fConcat = fMatrix;
+        fDirty = false;
+    }
+}
+
+void SkDrawMatrix::setChildHasID() { 
+    fChildHasID = true; 
+}
+
+bool SkDrawMatrix::setProperty(int index, SkScriptValue& scriptValue) {
+    SkScalar number = scriptValue.fOperand.fScalar;
+    switch (index) {
+        case SK_PROPERTY(translate):
+    //      SkScalar xy[2];
+            SkASSERT(scriptValue.fType == SkType_Array);
+            SkASSERT(scriptValue.fOperand.fArray->getType() == SkType_Float);
+            SkASSERT(scriptValue.fOperand.fArray->count() == 2);
+    //      SkParse::FindScalars(scriptValue.fOperand.fString->c_str(), xy, 2);
+            fMatrix.setTranslateX((*scriptValue.fOperand.fArray)[0].fScalar);
+            fMatrix.setTranslateY((*scriptValue.fOperand.fArray)[1].fScalar);
+            return true;
+        case SK_PROPERTY(perspectX):
+#ifdef SK_SCALAR_IS_FIXED
+            fMatrix.setPerspX(SkFixedToFract(number));
+#else
+            fMatrix.setPerspX(number);
+#endif  
+            break;
+        case SK_PROPERTY(perspectY):
+#ifdef SK_SCALAR_IS_FIXED
+            fMatrix.setPerspY(SkFixedToFract(number));
+#else
+            fMatrix.setPerspY(number);
+#endif  
+            break;
+        case SK_PROPERTY(rotate): {
+            SkMatrix temp;
+            temp.setRotate(number, 0, 0);
+            fMatrix.setScaleX(temp.getScaleX());
+            fMatrix.setScaleY(temp.getScaleY());
+            fMatrix.setSkewX(temp.getSkewX());
+            fMatrix.setSkewY(temp.getSkewY());
+            } break;
+        case SK_PROPERTY(scale):
+            fMatrix.setScaleX(number);
+            fMatrix.setScaleY(number);
+            break;
+        case SK_PROPERTY(scaleX):
+            fMatrix.setScaleX(number);
+            break;
+        case SK_PROPERTY(scaleY):
+            fMatrix.setScaleY(number);
+            break;
+        case SK_PROPERTY(skewX):
+            fMatrix.setSkewX(number);
+            break;
+        case SK_PROPERTY(skewY):
+            fMatrix.setSkewY(number);
+            break;
+        case SK_PROPERTY(translateX):
+            fMatrix.setTranslateX(number);
+            break;
+        case SK_PROPERTY(translateY):
+            fMatrix.setTranslateY(number);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    fConcat = fMatrix;
+    return true;
+}
+
diff --git a/src/animator/SkDrawMatrix.h b/src/animator/SkDrawMatrix.h
new file mode 100644
index 0000000..8339d8c
--- /dev/null
+++ b/src/animator/SkDrawMatrix.h
@@ -0,0 +1,82 @@
+/* libs/graphics/animator/SkDrawMatrix.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 SkDrawMatrix_DEFINED
+#define SkDrawMatrix_DEFINED
+
+#include "SkDrawable.h"
+#include "SkMatrix.h"
+#include "SkMemberInfo.h"
+#include "SkIntArray.h"
+
+class SkMatrixPart;
+
+class SkDrawMatrix : public SkDrawable {
+    DECLARE_DRAW_MEMBER_INFO(Matrix);
+    SkDrawMatrix();
+    virtual ~SkDrawMatrix();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool childrenNeedDisposing() const;
+    virtual void dirty();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    SkMatrix& getMatrix();
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual void initialize();
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual void setChildHasID();
+    virtual bool setProperty(int index, SkScriptValue& );
+
+    void concat(SkMatrix& inMatrix) {
+        fConcat.preConcat(inMatrix);
+    }
+
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+
+
+    void rotate(SkScalar degrees, SkPoint& center) {
+        fMatrix.preRotate(degrees, center.fX, center.fY);
+    }
+
+    void set(SkMatrix& src) {
+        fMatrix.preConcat(src);
+    }
+
+    void scale(SkScalar scaleX, SkScalar scaleY, SkPoint& center) {
+        fMatrix.preScale(scaleX, scaleY, center.fX, center.fY);
+    }
+
+    void skew(SkScalar skewX, SkScalar skewY, SkPoint& center) {
+        fMatrix.preSkew(skewX, skewY, center.fX, center.fY);
+    }
+
+    void translate(SkScalar x, SkScalar y) {
+        fMatrix.preTranslate(x, y);
+    }
+private:
+    SkTDScalarArray matrix;
+    SkMatrix fConcat;
+    SkMatrix fMatrix;
+    SkTDMatrixPartArray fParts;
+    SkBool8 fChildHasID;
+    SkBool8 fDirty;
+    typedef SkDrawable INHERITED;
+};
+
+#endif // SkDrawMatrix_DEFINED
diff --git a/src/animator/SkDrawOval.cpp b/src/animator/SkDrawOval.cpp
new file mode 100644
index 0000000..0e3a9ae
--- /dev/null
+++ b/src/animator/SkDrawOval.cpp
@@ -0,0 +1,37 @@
+/* libs/graphics/animator/SkDrawOval.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 "SkDrawOval.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkOval::fInfo[] = {
+    SK_MEMBER_INHERITED,
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkOval);
+
+bool SkOval::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawOval(fRect, *maker.fPaint);
+    return false;
+}
+
diff --git a/src/animator/SkDrawOval.h b/src/animator/SkDrawOval.h
new file mode 100644
index 0000000..0e7ae3f
--- /dev/null
+++ b/src/animator/SkDrawOval.h
@@ -0,0 +1,31 @@
+/* libs/graphics/animator/SkDrawOval.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 SkDrawOval_DEFINED
+#define SkDrawOval_DEFINED
+
+#include "SkDrawRectangle.h"
+
+class SkOval : public SkDrawRect {
+    DECLARE_MEMBER_INFO(Oval);
+    virtual bool draw(SkAnimateMaker& );
+private:
+    typedef SkDrawRect INHERITED;
+};
+
+#endif // SkDrawOval_DEFINED
+
diff --git a/src/animator/SkDrawPaint.cpp b/src/animator/SkDrawPaint.cpp
new file mode 100644
index 0000000..68caa5a
--- /dev/null
+++ b/src/animator/SkDrawPaint.cpp
@@ -0,0 +1,277 @@
+/* libs/graphics/animator/SkDrawPaint.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 "SkDrawPaint.h"
+#include "SkAnimateMaker.h"
+#include "SkDrawColor.h"
+#include "SkDrawShader.h"
+#include "SkMaskFilter.h"
+#include "SkPaintParts.h"
+#include "SkPathEffect.h"
+
+enum SkPaint_Functions {
+    SK_FUNCTION(measureText)
+};
+
+enum SkPaint_Properties {
+    SK_PROPERTY(ascent),
+    SK_PROPERTY(descent)
+};
+
+// !!! in the future, this could be compiled by build-condensed-info into an array of parameters
+// with a lookup table to find the first parameter -- for now, it is iteratively searched through
+const SkFunctionParamType SkDrawPaint::fFunctionParameters[] = {
+    (SkFunctionParamType) SkType_String,
+    (SkFunctionParamType) 0 // terminator for parameter list (there may be multiple parameter lists)
+};
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawPaint::fInfo[] = {
+    SK_MEMBER(antiAlias, Boolean),
+    SK_MEMBER_PROPERTY(ascent, Float),
+    SK_MEMBER(color, Color),
+    SK_MEMBER_PROPERTY(descent, Float),
+    SK_MEMBER(fakeBold, Boolean),
+    SK_MEMBER(filterBitmap, Boolean),
+    SK_MEMBER(linearText, Boolean),
+    SK_MEMBER(maskFilter, MaskFilter),
+    SK_MEMBER_FUNCTION(measureText, Float),
+    SK_MEMBER(pathEffect, PathEffect),
+    SK_MEMBER(shader, Shader),
+    SK_MEMBER(strikeThru, Boolean),
+    SK_MEMBER(stroke, Boolean),
+    SK_MEMBER(strokeCap, Cap),
+    SK_MEMBER(strokeJoin, Join),
+    SK_MEMBER(strokeMiter, Float),
+    SK_MEMBER(strokeWidth, Float),
+    SK_MEMBER(style, Style),
+    SK_MEMBER(textAlign, Align),
+    SK_MEMBER(textScaleX, Float),
+    SK_MEMBER(textSize, Float),
+    SK_MEMBER(textSkewX, Float),
+    SK_MEMBER(typeface, Typeface),
+    SK_MEMBER(underline, Boolean),
+    SK_MEMBER(xfermode, Xfermode)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawPaint);
+
+SkDrawPaint::SkDrawPaint() : antiAlias(-1), color(NULL), fakeBold(-1), filterBitmap(-1),
+    linearText(-1), maskFilter((SkDrawMaskFilter*) -1), pathEffect((SkDrawPathEffect*) -1),
+    shader((SkDrawShader*) -1), strikeThru(-1), stroke(-1),
+    strokeCap((SkPaint::Cap) -1), strokeJoin((SkPaint::Join) -1), strokeMiter(SK_ScalarNaN), 
+    strokeWidth(SK_ScalarNaN), style((SkPaint::Style) -1),
+    textAlign((SkPaint::Align) -1), textScaleX(SK_ScalarNaN), textSize(SK_ScalarNaN), 
+    textSkewX(SK_ScalarNaN), typeface((SkDrawTypeface*) -1),
+    underline(-1), xfermode((SkPorterDuff::Mode) -1), fOwnsColor(false), fOwnsMaskFilter(false), 
+    fOwnsPathEffect(false), fOwnsShader(false), fOwnsTypeface(false) {
+}
+
+SkDrawPaint::~SkDrawPaint() {
+    if (fOwnsColor)
+        delete color;
+    if (fOwnsMaskFilter)
+        delete maskFilter;
+    if (fOwnsPathEffect)
+        delete pathEffect;
+    if (fOwnsShader)
+        delete shader;
+    if (fOwnsTypeface)
+        delete typeface;
+}
+
+bool SkDrawPaint::add(SkAnimateMaker& maker, SkDisplayable* child) {
+    SkASSERT(child && child->isPaintPart());
+    SkPaintPart* part = (SkPaintPart*) child;
+    if (part->add())
+        maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToPaint); 
+    return true;
+}
+
+SkDisplayable* SkDrawPaint::deepCopy(SkAnimateMaker* maker) {
+    SkDrawColor* tempColor = color;
+    color = NULL;
+    SkDrawPaint* copy = (SkDrawPaint*) INHERITED::deepCopy(maker);
+    color = tempColor;
+    tempColor = (SkDrawColor*) color->deepCopy(maker);
+    tempColor->setParent(copy);
+    tempColor->add();
+    copy->fOwnsColor = true;
+    return copy;
+}
+
+bool SkDrawPaint::draw(SkAnimateMaker& maker) {
+    SkPaint* paint = maker.fPaint;
+    setupPaint(paint);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawPaint::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpAttrs(maker);
+    bool closedYet = false;
+    SkDisplayList::fIndent +=4;
+    //should i say if (maskFilter && ...?
+    if (maskFilter != (SkDrawMaskFilter*)-1) {
+        SkDebugf(">\n");
+        maskFilter->dump(maker);
+        closedYet = true;
+    }
+    if (pathEffect != (SkDrawPathEffect*) -1) {
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        pathEffect->dump(maker);
+    }
+    if (fOwnsTypeface) {
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        typeface->dump(maker);
+    }
+    SkDisplayList::fIndent -= 4;
+    dumpChildren(maker, closedYet);
+}
+#endif
+    
+void SkDrawPaint::executeFunction(SkDisplayable* target, int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* scriptValue) {
+        if (scriptValue == NULL)
+            return;
+    SkASSERT(target == this);
+    switch (index) {
+        case SK_FUNCTION(measureText): {
+            SkASSERT(parameters.count() == 1);
+            SkASSERT(type == SkType_Float);
+            SkPaint paint;
+            setupPaint(&paint);
+            scriptValue->fType = SkType_Float;
+            SkASSERT(parameters[0].fType == SkType_String);
+            scriptValue->fOperand.fScalar = paint.measureText(parameters[0].fOperand.fString->c_str(), 
+                parameters[0].fOperand.fString->size()); 
+//          SkDebugf("measureText: %s = %g\n", parameters[0].fOperand.fString->c_str(), 
+//              scriptValue->fOperand.fScalar / 65536.0f);
+            } break;
+        default:
+            SkASSERT(0);
+    }
+}
+
+const SkFunctionParamType* SkDrawPaint::getFunctionsParameters() {
+    return fFunctionParameters;
+}
+
+bool SkDrawPaint::getProperty(int index, SkScriptValue* value) const {
+    SkPaint::FontMetrics    metrics;
+    SkPaint paint;
+    setupPaint(&paint);
+    paint.getFontMetrics(&metrics);
+    switch (index) {
+        case SK_PROPERTY(ascent):
+            value->fOperand.fScalar = metrics.fAscent;
+            break;
+        case SK_PROPERTY(descent):
+            value->fOperand.fScalar = metrics.fDescent;
+            break;
+        // should consider returning fLeading as well (or roll it into ascent/descent somehow 
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    value->fType = SkType_Float;
+    return true;
+}
+
+bool SkDrawPaint::resolveIDs(SkAnimateMaker& maker, SkDisplayable* origDisp, SkApply* ) {
+    SkASSERT(origDisp->isPaint());
+    SkDrawPaint* original = (SkDrawPaint*) origDisp;
+    if (fOwnsColor && maker.resolveID(color, original->color) == false)
+        return true;
+    if (fOwnsMaskFilter && maker.resolveID(maskFilter, original->maskFilter) == false)
+        return true;
+    if (fOwnsPathEffect && maker.resolveID(pathEffect, original->pathEffect) == false)
+        return true;
+    if (fOwnsShader && maker.resolveID(shader, original->shader) == false)
+        return true;
+    if (fOwnsTypeface && maker.resolveID(typeface, original->typeface) == false)
+        return true;
+    return false; // succeeded 
+}
+
+void SkDrawPaint::setupPaint(SkPaint* paint) const {
+    if (antiAlias != -1)
+        paint->setAntiAlias(SkToBool(antiAlias));
+    if (color != NULL)
+        paint->setColor(color->getColor());
+    if (fakeBold != -1)
+        paint->setFakeBoldText(SkToBool(fakeBold));
+    if (filterBitmap != -1)
+        paint->setFilterBitmap(SkToBool(filterBitmap));
+    //  stroke is legacy; style setting if present overrides stroke
+    if (stroke != -1)
+        paint->setStyle(SkToBool(stroke) ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
+    if (style != (SkPaint::Style) -1)
+        paint->setStyle((SkPaint::Style) style);
+    if (linearText != -1)
+        paint->setLinearText(SkToBool(linearText));
+    if (maskFilter == NULL)
+        paint->setMaskFilter(NULL);
+    else if (maskFilter != (SkDrawMaskFilter*) -1)
+        paint->setMaskFilter(maskFilter->getMaskFilter())->safeUnref();
+    if (pathEffect == NULL)
+        paint->setPathEffect(NULL);
+    else if (pathEffect != (SkDrawPathEffect*) -1)
+        paint->setPathEffect(pathEffect->getPathEffect())->safeUnref();
+    if (shader == NULL)
+        paint->setShader(NULL);
+    else if (shader != (SkDrawShader*) -1)
+        paint->setShader(shader->getShader())->safeUnref();
+    if (strikeThru != -1)
+        paint->setStrikeThruText(SkToBool(strikeThru));
+    if (strokeCap != (SkPaint::Cap) -1)
+        paint->setStrokeCap((SkPaint::Cap) strokeCap);
+    if (strokeJoin != (SkPaint::Join) -1)
+        paint->setStrokeJoin((SkPaint::Join) strokeJoin);
+    if (SkScalarIsNaN(strokeMiter) == false)
+        paint->setStrokeMiter(strokeMiter);
+    if (SkScalarIsNaN(strokeWidth) == false)
+        paint->setStrokeWidth(strokeWidth);
+    if (textAlign != (SkPaint::Align) -1)
+        paint->setTextAlign((SkPaint::Align) textAlign);
+    if (SkScalarIsNaN(textScaleX) == false)
+        paint->setTextScaleX(textScaleX);
+    if (SkScalarIsNaN(textSize) == false)
+        paint->setTextSize(textSize);
+    if (SkScalarIsNaN(textSkewX) == false)
+        paint->setTextSkewX(textSkewX);
+    if (typeface == NULL)
+        paint->setTypeface(NULL);
+    else if (typeface != (SkDrawTypeface*) -1)
+        paint->setTypeface(typeface->getTypeface())->safeUnref();
+    if (underline != -1)
+        paint->setUnderlineText(SkToBool(underline));
+    if (xfermode != (SkPorterDuff::Mode) -1) 
+        paint->setPorterDuffXfermode((SkPorterDuff::Mode) xfermode);
+}
diff --git a/src/animator/SkDrawPaint.h b/src/animator/SkDrawPaint.h
new file mode 100644
index 0000000..ea77acd
--- /dev/null
+++ b/src/animator/SkDrawPaint.h
@@ -0,0 +1,88 @@
+/* libs/graphics/animator/SkDrawPaint.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 SkDrawPaint_DEFINED
+#define SkDrawPaint_DEFINED
+
+#include "SkDrawable.h"
+#include "SkIntArray.h"
+#include "SkMemberInfo.h"
+#include "SkPaint.h"
+#include "SkXfermode.h"
+
+class SkDrawMaskFilter;
+class SkDrawPathEffect;
+class SkDrawShader;
+class SkTransferMode;
+class SkDrawTypeface;
+
+class SkDrawPaint : public SkDrawable {
+    DECLARE_DRAW_MEMBER_INFO(Paint);
+    SkDrawPaint();
+    virtual ~SkDrawPaint();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual SkDisplayable* deepCopy(SkAnimateMaker* );
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void executeFunction(SkDisplayable* target, int index, 
+        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
+        SkScriptValue* );
+    virtual const SkFunctionParamType* getFunctionsParameters();
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply);
+protected:
+    static const SkFunctionParamType fFunctionParameters[];
+    void setupPaint(SkPaint* paint) const;
+public:
+    SkBool antiAlias;
+    SkDrawColor* color;
+    SkBool fakeBold;
+    SkBool filterBitmap;
+    SkBool linearText;
+    SkDrawMaskFilter* maskFilter;
+    SkDrawPathEffect* pathEffect;
+    SkDrawShader* shader;
+    SkBool strikeThru;
+    SkBool stroke;
+    int /*SkPaint::Cap*/ strokeCap;
+    int /*SkPaint::Join */ strokeJoin;
+    SkScalar strokeMiter;
+    SkScalar strokeWidth;
+    int /* SkPaint::Style */ style;
+    int /* SkPaint::Align */ textAlign;
+    SkScalar textScaleX;
+    SkScalar textSize;
+    SkScalar textSkewX;
+    SkDrawTypeface* typeface;
+    SkBool underline;
+    int /*SkXfermode::Modes*/ xfermode;
+    SkBool8 fOwnsColor;
+    SkBool8 fOwnsMaskFilter;
+    SkBool8 fOwnsPathEffect;
+    SkBool8 fOwnsShader;
+    SkBool8 fOwnsTransferMode;
+    SkBool8 fOwnsTypeface;
+private:
+    typedef SkDrawable INHERITED;
+    friend class SkTextToPath;
+    friend class SkSaveLayer;
+};
+
+#endif // SkDrawPaint_DEFINED
+
diff --git a/src/animator/SkDrawPath.cpp b/src/animator/SkDrawPath.cpp
new file mode 100644
index 0000000..895e597
--- /dev/null
+++ b/src/animator/SkDrawPath.cpp
@@ -0,0 +1,229 @@
+/* libs/graphics/animator/SkDrawPath.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 "SkDrawPath.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkMath.h"
+#include "SkMatrixParts.h"
+#include "SkPaint.h"
+#include "SkPathParts.h"
+
+enum SkPath_Properties {
+    SK_PROPERTY(fillType),
+    SK_PROPERTY(length)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawPath::fInfo[] = {
+    SK_MEMBER(d, String),
+    SK_MEMBER_PROPERTY(fillType, FillType),
+    SK_MEMBER_PROPERTY(length, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawPath);
+
+SkDrawPath::SkDrawPath()
+{
+    fParent = NULL;
+    fLength = SK_ScalarNaN;
+    fChildHasID = false;
+    fDirty = false;
+}
+
+SkDrawPath::~SkDrawPath() {
+    for (SkPathPart** part = fParts.begin(); part < fParts.end();  part++)
+        delete *part;
+}
+
+bool SkDrawPath::add(SkAnimateMaker& maker, SkDisplayable* child) {
+    SkASSERT(child && child->isPathPart());
+    SkPathPart* part = (SkPathPart*) child;
+    *fParts.append() = part;
+    if (part->add())
+        maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToPath); 
+    fDirty = false;
+    return true;
+}
+
+bool SkDrawPath::childrenNeedDisposing() const { 
+    return false; 
+}
+
+void SkDrawPath::dirty() { 
+    fDirty = true; 
+    fLength = SK_ScalarNaN;
+    if (fParent)
+        fParent->dirty();
+}
+
+bool SkDrawPath::draw(SkAnimateMaker& maker) {
+    SkPath& path = getPath();
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawPath(path, *maker.fPaint);
+    return false;
+}
+
+SkDisplayable* SkDrawPath::getParent() const {
+    return fParent;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawPath::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpAttrs(maker);
+    bool closedYet = false;
+    SkDisplayList::fIndent += 4;
+    for(SkPathPart** part = fParts.begin(); part < fParts.end(); part++) {
+        if (closedYet == false) {
+            SkDebugf(">\n");
+            closedYet = true;
+        }
+        (*part)->dump(maker);
+    }
+    SkDisplayList::fIndent -= 4;
+    if (closedYet)
+        dumpEnd(maker);
+    else
+        SkDebugf("/>\n");
+}
+#endif
+
+SkPath& SkDrawPath::getPath() {
+    if (fDirty == false)
+        return fPath;
+    if (d.size() > 0)
+    {
+        parseSVG();
+        d.reset();
+    }
+    else
+    {
+        fPath.reset();
+        for (SkPathPart** part = fParts.begin(); part < fParts.end();  part++)
+            (*part)->add();
+    }
+    fDirty = false;
+    return fPath;
+}
+    
+void SkDrawPath::onEndElement(SkAnimateMaker& ) {
+    if (d.size() > 0) {
+        parseSVG();
+        d.reset();
+        fDirty = false;
+        return;
+    }
+    if (fChildHasID == false) {
+        for (SkPathPart** part = fParts.begin(); part < fParts.end();  part++)
+            delete *part;
+        fParts.reset();
+        fDirty = false;
+    }
+}
+
+bool SkDrawPath::getProperty(int index, SkScriptValue* value) const {
+    switch (index) {
+        case SK_PROPERTY(length):
+            if (SkScalarIsNaN(fLength)) {
+                const SkPath& path = ((SkDrawPath*) this)->getPath();
+                SkPathMeasure pathMeasure(path, false);
+                fLength = pathMeasure.getLength();
+            }
+            value->fType = SkType_Float;
+            value->fOperand.fScalar = fLength;
+            break;
+        case SK_PROPERTY(fillType):
+            value->fType = SkType_FillType;
+            value->fOperand.fS32 = (int) fPath.getFillType();
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+void SkDrawPath::setChildHasID() { 
+    fChildHasID = true; 
+}
+
+bool SkDrawPath::setParent(SkDisplayable* parent) {
+    fParent = parent;
+    return false;
+}
+
+bool SkDrawPath::setProperty(int index, SkScriptValue& value)
+{
+    switch (index) {
+        case SK_PROPERTY(fillType):
+            SkASSERT(value.fType == SkType_FillType);
+            SkASSERT(value.fOperand.fS32 >= SkPath::kWinding_FillType &&
+                value.fOperand.fS32 <= SkPath::kEvenOdd_FillType);
+            fPath.setFillType((SkPath::FillType) value.fOperand.fS32);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true;
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkPolyline::fInfo[] = {
+    SK_MEMBER_ARRAY(points, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkPolyline);
+
+bool SkPolyline::add(SkAnimateMaker& , SkDisplayable*) const {
+    return false; 
+}
+
+void SkPolyline::onEndElement(SkAnimateMaker& maker) {
+    INHERITED::onEndElement(maker);
+    if (points.count() <= 0)
+        return;
+    fPath.reset();
+    fPath.moveTo(points[0], points[1]);
+    int count = points.count();
+    for (int index = 2; index < count; index += 2)
+        fPath.lineTo(points[index], points[index+1]);
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkPolygon::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkPolygon);
+
+void SkPolygon::onEndElement(SkAnimateMaker& maker) {
+    INHERITED::onEndElement(maker);
+    fPath.close();
+}
+
diff --git a/src/animator/SkDrawPath.h b/src/animator/SkDrawPath.h
new file mode 100644
index 0000000..673051a
--- /dev/null
+++ b/src/animator/SkDrawPath.h
@@ -0,0 +1,77 @@
+/* libs/graphics/animator/SkDrawPath.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 SkDrawPath_DEFINED
+#define SkDrawPath_DEFINED
+
+#include "SkBoundable.h"
+#include "SkIntArray.h"
+#include "SkMemberInfo.h"
+#include "SkPath.h"
+
+class SkDrawPath : public SkBoundable {
+    DECLARE_DRAW_MEMBER_INFO(Path);
+    SkDrawPath();
+    virtual ~SkDrawPath();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    bool childHasID() { return SkToBool(fChildHasID); }
+    virtual bool childrenNeedDisposing() const;
+    virtual void dirty();
+    virtual bool draw(SkAnimateMaker& );
+    virtual SkDisplayable* getParent() const;
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    SkPath& getPath();
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool setProperty(int index, SkScriptValue& value);
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual void setChildHasID();
+    virtual bool setParent(SkDisplayable* parent);
+    virtual bool isPath() const { return true; }
+public:
+    SkPath fPath;
+protected:
+    void parseSVG();
+    SkString d;
+    SkTDPathPartArray fParts;
+    mutable SkScalar fLength;
+    SkDisplayable* fParent; // SkPolyToPoly or SkFromPath, for instance
+    SkBool8 fChildHasID;
+    SkBool8 fDirty;
+private:
+    typedef SkBoundable INHERITED;
+};
+
+class SkPolyline : public SkDrawPath {
+    DECLARE_MEMBER_INFO(Polyline);
+    virtual bool add(SkAnimateMaker& , SkDisplayable*) const;
+    virtual void onEndElement(SkAnimateMaker& );
+protected:
+    SkTDScalarArray points;
+private:
+    typedef SkDrawPath INHERITED;
+};
+
+class SkPolygon : public SkPolyline {
+    DECLARE_MEMBER_INFO(Polygon);
+    virtual void onEndElement(SkAnimateMaker& );
+private:
+    typedef SkPolyline INHERITED;
+};
+
+#endif // SkDrawPath_DEFINED
diff --git a/src/animator/SkDrawPoint.cpp b/src/animator/SkDrawPoint.cpp
new file mode 100644
index 0000000..383a599
--- /dev/null
+++ b/src/animator/SkDrawPoint.cpp
@@ -0,0 +1,54 @@
+/* libs/graphics/animator/SkDrawPoint.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 "SkDrawPoint.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo Sk_Point::fInfo[] = {
+    SK_MEMBER_ALIAS(x, fPoint.fX, Float),
+    SK_MEMBER_ALIAS(y, fPoint.fY, Float)
+};
+
+#endif
+
+DEFINE_NO_VIRTUALS_GET_MEMBER(Sk_Point);
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawPoint::fInfo[] = {
+    SK_MEMBER_ALIAS(x, fPoint.fX, Float),
+    SK_MEMBER_ALIAS(y, fPoint.fY, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawPoint);
+
+SkDrawPoint::SkDrawPoint() { 
+    fPoint.set(0, 0);   
+}
+
+void SkDrawPoint::getBounds(SkRect* rect ) {
+    rect->fLeft = rect->fRight = fPoint.fX;
+    rect->fTop = rect->fBottom = fPoint.fY;
+}
+
+
diff --git a/src/animator/SkDrawPoint.h b/src/animator/SkDrawPoint.h
new file mode 100644
index 0000000..d06e5b7
--- /dev/null
+++ b/src/animator/SkDrawPoint.h
@@ -0,0 +1,41 @@
+/* libs/graphics/animator/SkDrawPoint.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 SkDrawPoint_DEFINED
+#define SkDrawPoint_DEFINED
+
+#include "SkBoundable.h"
+#include "SkMemberInfo.h"
+#include "SkPoint.h"
+
+struct Sk_Point {
+    DECLARE_NO_VIRTUALS_MEMBER_INFO(_Point);
+    Sk_Point();
+private:
+    SkPoint fPoint;
+};
+
+class SkDrawPoint : public SkDisplayable {
+    DECLARE_MEMBER_INFO(DrawPoint);
+    SkDrawPoint();
+    virtual void getBounds(SkRect*  );
+private:
+    SkPoint fPoint;
+    typedef SkDisplayable INHERITED;
+};
+
+#endif // SkDrawPoint_DEFINED
diff --git a/src/animator/SkDrawRectangle.cpp b/src/animator/SkDrawRectangle.cpp
new file mode 100644
index 0000000..f2054bd
--- /dev/null
+++ b/src/animator/SkDrawRectangle.cpp
@@ -0,0 +1,153 @@
+/* libs/graphics/animator/SkDrawRectangle.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 "SkDrawRectangle.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkMatrixParts.h"
+#include "SkPaint.h"
+#include "SkScript.h"
+
+enum SkRectangle_Properties {
+    SK_PROPERTY(height),
+    SK_PROPERTY(needsRedraw),
+    SK_PROPERTY(width)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawRect::fInfo[] = {
+    SK_MEMBER_ALIAS(bottom, fRect.fBottom, Float),
+    SK_MEMBER_PROPERTY(height, Float),
+    SK_MEMBER_ALIAS(left, fRect.fLeft, Float),
+    SK_MEMBER_PROPERTY(needsRedraw, Boolean),
+    SK_MEMBER_ALIAS(right, fRect.fRight, Float),
+    SK_MEMBER_ALIAS(top, fRect.fTop, Float),
+    SK_MEMBER_PROPERTY(width, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawRect);
+
+SkDrawRect::SkDrawRect() : fParent(NULL) { 
+    fRect.setEmpty(); 
+}
+
+void SkDrawRect::dirty() {
+    if (fParent)
+        fParent->dirty();
+}
+
+bool SkDrawRect::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawRect(fRect, *maker.fPaint);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawRect::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("left=\"%g\" top=\"%g\" right=\"%g\" bottom=\"%g\" />\n",
+        SkScalarToFloat(fRect.fLeft), SkScalarToFloat(fRect.fTop), SkScalarToFloat(fRect.fRight),
+        SkScalarToFloat(fRect.fBottom));
+}
+#endif
+
+SkDisplayable* SkDrawRect::getParent() const {
+    return fParent;
+}
+
+bool SkDrawRect::getProperty(int index, SkScriptValue* value) const {
+    SkScalar result;
+    switch (index) {
+        case SK_PROPERTY(height):
+            result = fRect.height();
+            break;
+        case SK_PROPERTY(needsRedraw):
+            value->fType = SkType_Boolean;
+            value->fOperand.fS32 = fBounds.isEmpty() == false;
+            return true;
+        case SK_PROPERTY(width):
+            result = fRect.width();
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    value->fType = SkType_Float;
+    value->fOperand.fScalar = result;
+    return true;
+}
+
+
+bool SkDrawRect::setParent(SkDisplayable* parent) {
+    fParent = parent;
+    return false;
+}
+
+bool SkDrawRect::setProperty(int index, SkScriptValue& value) {
+    SkScalar scalar = value.fOperand.fScalar;
+    switch (index) {
+        case SK_PROPERTY(height):
+            SkASSERT(value.fType == SkType_Float);
+            fRect.fBottom = scalar + fRect.fTop;
+            return true;
+        case SK_PROPERTY(needsRedraw):
+            return false;
+        case SK_PROPERTY(width):
+            SkASSERT(value.fType == SkType_Float);
+            fRect.fRight = scalar + fRect.fLeft;
+            return true;
+        default:
+            SkASSERT(0);
+    }
+    return false;
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRoundRect::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(rx, Float),
+    SK_MEMBER(ry, Float),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRoundRect);
+
+SkRoundRect::SkRoundRect() : rx(0), ry(0) {
+}
+
+bool SkRoundRect::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawRoundRect(fRect, rx, ry, *maker.fPaint);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkRoundRect::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("left=\"%g\" top=\"%g\" right=\"%g\" bottom=\"%g\" rx=\"%g\" ry=\"%g\" />\n",
+            SkScalarToFloat(fRect.fLeft), SkScalarToFloat(fRect.fTop), SkScalarToFloat(fRect.fRight),
+            SkScalarToFloat(fRect.fBottom), SkScalarToFloat(rx), SkScalarToFloat(ry));
+}
+#endif
+
+
+
diff --git a/src/animator/SkDrawRectangle.h b/src/animator/SkDrawRectangle.h
new file mode 100644
index 0000000..9f8bc0a
--- /dev/null
+++ b/src/animator/SkDrawRectangle.h
@@ -0,0 +1,64 @@
+/* libs/graphics/animator/SkDrawRectangle.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 SkDrawRectangle_DEFINED
+#define SkDrawRectangle_DEFINED
+
+#include "SkBoundable.h"
+#include "SkMemberInfo.h"
+#include "SkRect.h"
+
+class SkRectToRect;
+
+class SkDrawRect : public SkBoundable {
+    DECLARE_DRAW_MEMBER_INFO(Rect);
+    SkDrawRect();
+    virtual void dirty();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual SkDisplayable* getParent() const;
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool setParent(SkDisplayable* parent);
+    virtual bool setProperty(int index, SkScriptValue& );
+protected:
+    SkRect fRect;
+    SkDisplayable* fParent;
+private:
+    friend class SkDrawClip;
+    friend class SkRectToRect;
+    friend class SkSaveLayer;
+    typedef SkBoundable INHERITED;
+};
+
+class SkRoundRect : public SkDrawRect {
+    DECLARE_MEMBER_INFO(RoundRect);
+    SkRoundRect();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif    
+protected:
+    SkScalar rx;
+    SkScalar ry;
+private:
+    typedef SkDrawRect INHERITED;
+};
+
+#endif // SkDrawRectangle_DEFINED
+
diff --git a/src/animator/SkDrawSaveLayer.cpp b/src/animator/SkDrawSaveLayer.cpp
new file mode 100644
index 0000000..bc2288f
--- /dev/null
+++ b/src/animator/SkDrawSaveLayer.cpp
@@ -0,0 +1,86 @@
+/* libs/graphics/animator/SkDrawSaveLayer.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 "SkDrawSaveLayer.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkDrawPaint.h"
+#include "SkDrawRectangle.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkSaveLayer::fInfo[] = {
+    SK_MEMBER(bounds, Rect),
+    SK_MEMBER(paint, Paint)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkSaveLayer);
+
+SkSaveLayer::SkSaveLayer() : paint(NULL), bounds(NULL) {
+}
+
+SkSaveLayer::~SkSaveLayer(){
+}
+
+bool SkSaveLayer::draw(SkAnimateMaker& maker)
+{
+    if (!bounds) {
+        return false;
+    }
+    SkPaint* save = maker.fPaint;   
+    //paint is an SkDrawPaint
+    if (paint)
+    {
+        SkPaint realPaint;
+        paint->setupPaint(&realPaint);
+        maker.fCanvas->saveLayer(&bounds->fRect, &realPaint, SkCanvas::kHasAlphaLayer_SaveFlag);
+    }
+    else
+        maker.fCanvas->saveLayer(&bounds->fRect, save, SkCanvas::kHasAlphaLayer_SaveFlag);
+    SkPaint local = SkPaint(*maker.fPaint);
+    maker.fPaint = &local;
+    bool result = INHERITED::draw(maker);
+    maker.fPaint = save;
+    maker.fCanvas->restore();
+    return result;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkSaveLayer::dump(SkAnimateMaker* maker)
+{
+    dumpBase(maker);
+    //would dump enabled be defined but not debug?
+#ifdef SK_DEBUG
+    if (paint)
+        SkDebugf("paint=\"%s\" ", paint->id);
+    if (bounds)
+        SkDebugf("bounds=\"%s\" ", bounds->id);
+#endif
+    dumpDrawables(maker);
+}
+#endif
+
+void SkSaveLayer::onEndElement(SkAnimateMaker& maker)
+{
+    if (!bounds)
+        maker.setErrorCode(SkDisplayXMLParserError::kSaveLayerNeedsBounds);
+    INHERITED::onEndElement(maker);
+}
+
+
diff --git a/src/animator/SkDrawSaveLayer.h b/src/animator/SkDrawSaveLayer.h
new file mode 100644
index 0000000..52a36a4
--- /dev/null
+++ b/src/animator/SkDrawSaveLayer.h
@@ -0,0 +1,44 @@
+/* libs/graphics/animator/SkDrawSaveLayer.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 SkDrawSaveLayer_DEFINED
+#define SkDrawSaveLayer_DEFINED
+
+#include "SkDrawGroup.h"
+#include "SkMemberInfo.h"
+
+class SkDrawPaint;
+class SkDrawRect;
+
+class SkSaveLayer : public SkGroup {
+    DECLARE_MEMBER_INFO(SaveLayer);
+    SkSaveLayer();
+    virtual ~SkSaveLayer();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void onEndElement(SkAnimateMaker& );
+protected:
+    SkDrawPaint* paint;
+    SkDrawRect* bounds;
+private:
+    typedef SkGroup INHERITED;
+
+};
+
+#endif //SkDrawSaveLayer_DEFINED
diff --git a/src/animator/SkDrawShader.cpp b/src/animator/SkDrawShader.cpp
new file mode 100644
index 0000000..b38718e
--- /dev/null
+++ b/src/animator/SkDrawShader.cpp
@@ -0,0 +1,91 @@
+/* libs/graphics/animator/SkDrawShader.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 "SkDrawShader.h"
+#include "SkDrawBitmap.h"
+#include "SkDrawMatrix.h"
+#include "SkDrawPaint.h"
+#include "SkTemplates.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawShader::fInfo[] = {
+    SK_MEMBER(matrix, Matrix),
+    SK_MEMBER(tileMode, TileMode)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawShader);
+
+SkDrawShader::SkDrawShader() : matrix(NULL), 
+    tileMode(SkShader::kClamp_TileMode) {
+}
+
+bool SkDrawShader::add() {
+    if (fPaint->shader != (SkDrawShader*) -1)
+        return true;
+    fPaint->shader = this;
+    fPaint->fOwnsShader = true;
+    return false;
+}
+
+void SkDrawShader::addPostlude(SkShader* shader) {
+    if (matrix)
+        shader->setLocalMatrix(matrix->getMatrix());
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawBitmapShader::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(filterBitmap, Boolean),
+    SK_MEMBER(image, BaseBitmap)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawBitmapShader);
+
+SkDrawBitmapShader::SkDrawBitmapShader() : filterBitmap(-1), image(NULL) {}
+
+bool SkDrawBitmapShader::add() {
+    if (fPaint->shader != (SkDrawShader*) -1)
+        return true;
+    fPaint->shader = this;
+    fPaint->fOwnsShader = true;
+    return false;
+}
+
+SkShader* SkDrawBitmapShader::getShader() {
+    if (image == NULL)
+        return NULL;
+    
+    // note: bitmap shader now supports independent tile modes for X and Y
+    // we pass the same to both, but later we should extend this flexibility
+    // to the xml (e.g. tileModeX="repeat" tileModeY="clmap")
+    // 
+    // oops, bitmapshader no longer takes filterBitmap, but deduces it at
+    // draw-time from the paint 
+    SkShader* shader  = SkShader::CreateBitmapShader(image->fBitmap, 
+                                                    (SkShader::TileMode) tileMode,
+                                                    (SkShader::TileMode) tileMode);
+    SkAutoTDelete<SkShader> autoDel(shader);
+    addPostlude(shader);
+    (void)autoDel.detach();
+    return shader;
+}
diff --git a/src/animator/SkDrawShader.h b/src/animator/SkDrawShader.h
new file mode 100644
index 0000000..35ca419
--- /dev/null
+++ b/src/animator/SkDrawShader.h
@@ -0,0 +1,38 @@
+/* libs/graphics/animator/SkDrawShader.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 SkDrawShader_DEFINED
+#define SkDrawShader_DEFINED
+
+#include "SkPaintParts.h"
+#include "SkShader.h"
+
+class SkBaseBitmap;
+
+class SkDrawBitmapShader : public SkDrawShader {
+    DECLARE_DRAW_MEMBER_INFO(BitmapShader);
+    SkDrawBitmapShader();
+    virtual bool add();
+    virtual SkShader* getShader();
+protected:
+    SkBool filterBitmap;
+    SkBaseBitmap* image;
+private:
+    typedef SkDrawShader INHERITED;
+};
+
+#endif // SkDrawShader_DEFINED
diff --git a/src/animator/SkDrawText.cpp b/src/animator/SkDrawText.cpp
new file mode 100644
index 0000000..dcc3a75
--- /dev/null
+++ b/src/animator/SkDrawText.cpp
@@ -0,0 +1,64 @@
+/* libs/graphics/animator/SkDrawText.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 "SkDrawText.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+enum SkText_Properties {
+    SK_PROPERTY(length)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkText::fInfo[] = {
+    SK_MEMBER_PROPERTY(length, Int),
+    SK_MEMBER(text, String),
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkText);
+
+SkText::SkText() : x(0), y(0) {
+}
+
+SkText::~SkText() {
+}
+
+bool SkText::draw(SkAnimateMaker& maker) {
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawText(text.c_str(), text.size(), x, y, *maker.fPaint);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkText::dump(SkAnimateMaker* maker) {
+    INHERITED::dump(maker);
+}
+#endif
+
+bool SkText::getProperty(int index, SkScriptValue* value) const {
+    SkASSERT(index == SK_PROPERTY(length));
+    value->fType = SkType_Int;
+    value->fOperand.fS32 = (int32_t) text.size();
+    return true;
+}
+
diff --git a/src/animator/SkDrawText.h b/src/animator/SkDrawText.h
new file mode 100644
index 0000000..cb1d1b9
--- /dev/null
+++ b/src/animator/SkDrawText.h
@@ -0,0 +1,44 @@
+/* libs/graphics/animator/SkDrawText.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 SkDrawText_DEFINED
+#define SkDrawText_DEFINED
+
+#include "SkBoundable.h"
+#include "SkMemberInfo.h"
+
+class SkText : public SkBoundable {
+    DECLARE_MEMBER_INFO(Text);
+    SkText();
+    virtual ~SkText();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool getProperty(int index, SkScriptValue* value) const ; 
+    const char* getText() { return text.c_str(); }
+    size_t getSize() { return text.size(); }
+protected:
+    SkString text;
+    SkScalar x;
+    SkScalar y;
+private:
+    friend class SkTextToPath;
+    typedef SkBoundable INHERITED;
+};
+
+#endif // SkDrawText_DEFINED
diff --git a/src/animator/SkDrawTextBox.cpp b/src/animator/SkDrawTextBox.cpp
new file mode 100644
index 0000000..4c4ac76
--- /dev/null
+++ b/src/animator/SkDrawTextBox.cpp
@@ -0,0 +1,90 @@
+/* libs/graphics/animator/SkDrawTextBox.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 "SkDrawTextBox.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+enum SkDrawTextBox_Properties {
+    foo = 100,
+    SK_PROPERTY(spacingAlign),
+    SK_PROPERTY(mode)
+};
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawTextBox::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(mode, TextBoxMode),
+    SK_MEMBER_ALIAS(spacingAdd, fSpacingAdd, Float),
+    SK_MEMBER(spacingAlign, TextBoxAlign),
+    SK_MEMBER_ALIAS(spacingMul, fSpacingMul, Float),
+    SK_MEMBER_ALIAS(text, fText, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawTextBox);
+
+SkDrawTextBox::SkDrawTextBox()
+{
+    fSpacingMul     = SK_Scalar1;
+    fSpacingAdd     = 0;
+    spacingAlign    = SkTextBox::kStart_SpacingAlign;
+    mode            = SkTextBox::kLineBreak_Mode;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawTextBox::dump(SkAnimateMaker* maker)
+{
+    dumpBase(maker);
+    dumpAttrs(maker);
+    if (mode == 0) 
+        SkDebugf("mode=\"oneLine\" ");
+    if (spacingAlign == 1)
+        SkDebugf("spacingAlign=\"center\" ");
+    else if (spacingAlign == 2)
+        SkDebugf("spacingAlign=\"end\" ");
+    SkDebugf("/>\n");
+}
+#endif
+
+bool SkDrawTextBox::getProperty(int index, SkScriptValue* value) const
+{
+    return this->INHERITED::getProperty(index, value);
+}
+
+bool SkDrawTextBox::setProperty(int index, SkScriptValue& scriptValue)
+{
+    return this->INHERITED::setProperty(index, scriptValue);
+}
+
+bool SkDrawTextBox::draw(SkAnimateMaker& maker)
+{
+    SkTextBox   box;
+    box.setMode((SkTextBox::Mode) mode);
+    box.setSpacingAlign((SkTextBox::SpacingAlign) spacingAlign);
+    box.setBox(fRect);
+    box.setSpacing(fSpacingMul, fSpacingAdd);
+    SkBoundableAuto boundable(this, maker);
+    box.draw(maker.fCanvas, fText.c_str(), fText.size(), *maker.fPaint);
+    return false;
+}
+
+
diff --git a/src/animator/SkDrawTextBox.h b/src/animator/SkDrawTextBox.h
new file mode 100644
index 0000000..ee97c22
--- /dev/null
+++ b/src/animator/SkDrawTextBox.h
@@ -0,0 +1,47 @@
+/* libs/graphics/animator/SkDrawTextBox.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 SkDrawTextBox_DEFINED
+#define SkDrawTextBox_DEFINED
+
+#include "SkDrawRectangle.h"
+#include "SkTextBox.h"
+
+class SkDrawTextBox : public SkDrawRect {
+    DECLARE_DRAW_MEMBER_INFO(TextBox);
+    SkDrawTextBox();
+
+    // overrides
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual bool getProperty(int index, SkScriptValue* value) const;
+    virtual bool setProperty(int index, SkScriptValue& );
+
+private:
+    SkString fText;
+    SkScalar fSpacingMul;
+    SkScalar fSpacingAdd;
+    int /*SkTextBox::Mode*/  mode;
+    int /*SkTextBox::SpacingAlign*/ spacingAlign;
+
+    typedef SkDrawRect INHERITED;
+};
+
+#endif // SkDrawTextBox_DEFINED
+
diff --git a/src/animator/SkDrawTo.cpp b/src/animator/SkDrawTo.cpp
new file mode 100644
index 0000000..342f7d4
--- /dev/null
+++ b/src/animator/SkDrawTo.cpp
@@ -0,0 +1,64 @@
+/* libs/graphics/animator/SkDrawTo.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 "SkDrawTo.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkDrawBitmap.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawTo::fInfo[] = {
+    SK_MEMBER(drawOnce, Boolean),
+    SK_MEMBER(use, Bitmap)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawTo);
+
+SkDrawTo::SkDrawTo() : drawOnce(false), use(NULL), fDrawnOnce(false) {
+}
+
+#if 0
+SkDrawTo::~SkDrawTo() {
+    SkASSERT(0);
+}
+#endif
+
+bool SkDrawTo::draw(SkAnimateMaker& maker) {
+    if (fDrawnOnce)
+        return false;
+    SkCanvas canvas(use->fBitmap);
+    SkCanvas* save = maker.fCanvas;
+    maker.fCanvas = &canvas;
+    INHERITED::draw(maker);
+    maker.fCanvas = save;
+    fDrawnOnce = drawOnce;
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawTo::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpAttrs(maker);
+    if (use)
+        SkDebugf("use=\"%s\" ", use->id);
+    dumpDrawables(maker);
+}
+#endif
+
diff --git a/src/animator/SkDrawTo.h b/src/animator/SkDrawTo.h
new file mode 100644
index 0000000..0ad78e8
--- /dev/null
+++ b/src/animator/SkDrawTo.h
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkDrawTo.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 SkDrawTo_DEFINED
+#define SkDrawTo_DEFINED
+
+#include "SkDrawGroup.h"
+#include "SkMemberInfo.h"
+
+class SkDrawBitmap;
+
+class SkDrawTo : public SkGroup {
+    DECLARE_MEMBER_INFO(DrawTo);
+    SkDrawTo();
+//  virtual ~SkDrawTo();
+    virtual bool draw(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+protected:
+    SkBool drawOnce;
+    SkDrawBitmap* use;
+private:
+    typedef SkGroup INHERITED;
+    SkBool fDrawnOnce;
+};
+
+#endif // SkDrawTo_DEFINED
diff --git a/src/animator/SkDrawTransparentShader.cpp b/src/animator/SkDrawTransparentShader.cpp
new file mode 100644
index 0000000..c1da687
--- /dev/null
+++ b/src/animator/SkDrawTransparentShader.cpp
@@ -0,0 +1,24 @@
+/* libs/graphics/animator/SkDrawTransparentShader.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 "SkDrawTransparentShader.h"
+#include "SkTransparentShader.h"
+
+SkShader* SkDrawTransparentShader::getShader() {
+    return new SkTransparentShader();
+}
+
diff --git a/src/animator/SkDrawTransparentShader.h b/src/animator/SkDrawTransparentShader.h
new file mode 100644
index 0000000..e831883
--- /dev/null
+++ b/src/animator/SkDrawTransparentShader.h
@@ -0,0 +1,29 @@
+/* libs/graphics/animator/SkDrawTransparentShader.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 SkDrawTransparentShader_DEFINED
+#define SkDrawTransparentShader_DEFINED
+
+#include "SkPaintParts.h"
+
+class SkDrawTransparentShader : public SkDrawShader {
+    DECLARE_EMPTY_MEMBER_INFO(TransparentShader);
+    virtual SkShader* getShader();
+};
+
+#endif // SkDrawTransparentShader_DEFINED
+
diff --git a/src/animator/SkDrawable.cpp b/src/animator/SkDrawable.cpp
new file mode 100644
index 0000000..6968fe1
--- /dev/null
+++ b/src/animator/SkDrawable.cpp
@@ -0,0 +1,33 @@
+/* libs/graphics/animator/SkDrawable.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 "SkDrawable.h"
+
+bool SkDrawable::doEvent(SkDisplayEvent::Kind , SkEventState* ) {
+    return false;
+}
+
+bool SkDrawable::isDrawable() const { 
+    return true; 
+}
+
+void SkDrawable::initialize() {
+}
+
+void SkDrawable::setSteps(int steps) {
+}
+
diff --git a/src/animator/SkDrawable.h b/src/animator/SkDrawable.h
new file mode 100644
index 0000000..7284a25
--- /dev/null
+++ b/src/animator/SkDrawable.h
@@ -0,0 +1,36 @@
+/* libs/graphics/animator/SkDrawable.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 SkDrawable_DEFINED
+#define SkDrawable_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkDisplayEvent.h"
+#include "SkMath.h"
+
+struct SkEventState;
+
+class SkDrawable :  public SkDisplayable {
+public:
+    virtual bool doEvent(SkDisplayEvent::Kind , SkEventState* state );
+    virtual bool draw(SkAnimateMaker& ) = 0; 
+    virtual void initialize();
+    virtual bool isDrawable() const;
+    virtual void setSteps(int steps);
+};
+
+#endif // SkDrawable_DEFINED
diff --git a/src/animator/SkDump.cpp b/src/animator/SkDump.cpp
new file mode 100644
index 0000000..eac956c
--- /dev/null
+++ b/src/animator/SkDump.cpp
@@ -0,0 +1,158 @@
+/* libs/graphics/animator/SkDump.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 "SkDump.h"
+
+#ifdef SK_DUMP_ENABLED
+
+#include "SkAnimateMaker.h"
+#include "SkAnimatorScript.h"
+#include "SkDisplayEvents.h"
+#include "SkDisplayList.h"
+#include "SkString.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDump::fInfo[] = {
+    SK_MEMBER(displayList, Boolean),
+    SK_MEMBER(eventList, Boolean),
+    SK_MEMBER(events, Boolean),
+    SK_MEMBER(groups, Boolean),
+    SK_MEMBER(name, String),
+    SK_MEMBER(posts, Boolean),
+    SK_MEMBER(script, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDump);
+
+SkDump::SkDump() : displayList(-1), eventList(-1), events(-1), groups(-1), posts(-1) {
+}
+
+bool SkDump::enable(SkAnimateMaker& maker ) {
+    if (script.size() > 0)
+        return evaluate(maker);
+    bool hasAttr = false;
+    if (events > 0)
+        hasAttr |= maker.fDumpEvents = true;
+    if (posts > 0)
+        hasAttr |= maker.fDumpPosts = true;
+    if (groups > 0)
+        hasAttr |= maker.fDumpGConditions = true;
+    if ((hasAttr |= (eventList > 0)) == true)
+        maker.fEvents.dump(maker);
+    if ((hasAttr |= (name.size() > 0)) == true)
+        maker.dump(name.c_str());
+    if (displayList > 0 || displayList != 0 && hasAttr == false)
+        maker.fDisplayList.dump(&maker);
+    return true;
+}
+
+bool SkDump::evaluate(SkAnimateMaker &maker) {
+    SkAnimatorScript scriptEngine(maker, NULL, SkType_Int);
+    SkScriptValue value;
+    const char* cScript = script.c_str();
+    bool success = scriptEngine.evaluateScript(&cScript, &value);
+    SkDebugf("%*s<dump script=\"%s\" answer=\" ", SkDisplayList::fIndent, "", script.c_str());
+    if (success == false) {
+        SkDebugf("INVALID\" />\n");
+        return false;
+    }
+    switch (value.fType) {
+        case SkType_Float:
+            SkDebugf("%g\" />\n", SkScalarToFloat(value.fOperand.fScalar));
+            break;
+        case SkType_Int:
+            SkDebugf("%d\" />\n", value.fOperand.fS32);
+            break;
+        case SkType_String:
+            SkDebugf("%s\" />\n", value.fOperand.fString->c_str());
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+
+bool SkDump::hasEnable() const {
+    return true;
+}
+
+void SkDump::GetEnumString(SkDisplayTypes type, int index, SkString* result) {
+    int badEnum = index;
+    const SkDisplayEnumMap& map = SkAnimatorScript::GetEnumValues(type);
+    const char* str  = map.fValues;
+    while (--index >= 0) {
+        str = strchr(str, '|');
+        if (str == NULL) {
+            result->reset();
+            result->appendS32(badEnum);
+            return;
+        }
+        str += 1;
+    }
+    const char* end = strchr(str, '|');
+    if (end == NULL)
+        end = str + strlen(str);
+    result->set(str, end - str);
+}
+
+#else
+
+// in the release version, <dump> is allowed, and its attributes are defined, but
+// are not stored and have no effect
+
+#if SK_USE_CONDENSED_INFO == 0
+
+enum SkDump_Properties {
+    SK_PROPERTY(displayList),
+    SK_PROPERTY(eventList),
+    SK_PROPERTY(events),
+    SK_PROPERTY(groups),
+    SK_PROPERTY(name),
+    SK_PROPERTY(posts),
+    SK_PROPERTY(script)
+};
+
+const SkMemberInfo SkDump::fInfo[] = {
+    SK_MEMBER_PROPERTY(displayList, Boolean),
+    SK_MEMBER_PROPERTY(eventList, Boolean),
+    SK_MEMBER_PROPERTY(events, Boolean),
+    SK_MEMBER_PROPERTY(groups, Boolean),
+    SK_MEMBER_PROPERTY(name, String),
+    SK_MEMBER_PROPERTY(posts, Boolean),
+    SK_MEMBER_PROPERTY(script, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDump);
+
+bool SkDump::enable(SkAnimateMaker& maker ) {
+    return true;
+}
+
+bool SkDump::hasEnable() const {
+    return true;
+}
+
+bool SkDump::setProperty(int index, SkScriptValue& ) {
+    return index <= SK_PROPERTY(posts); 
+}
+
+#endif
diff --git a/src/animator/SkDump.h b/src/animator/SkDump.h
new file mode 100644
index 0000000..73a6957
--- /dev/null
+++ b/src/animator/SkDump.h
@@ -0,0 +1,51 @@
+/* libs/graphics/animator/SkDump.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 SkDump_DEFINED
+#define SkDump_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+
+class SkAnimateMaker;
+class SkString;
+
+class SkDump : public SkDisplayable {
+    DECLARE_MEMBER_INFO(Dump);
+#ifdef SK_DUMP_ENABLED
+    SkDump();
+    virtual bool enable(SkAnimateMaker & );
+    bool evaluate(SkAnimateMaker &);
+    virtual bool hasEnable() const;
+    static void GetEnumString(SkDisplayTypes , int index, SkString* result);
+    SkBool displayList;
+    SkBool eventList;
+    SkBool events;
+    SkString name;
+    SkBool groups;
+    SkBool posts;
+    SkString script;
+#else
+    virtual bool enable(SkAnimateMaker & );
+    virtual bool hasEnable() const;
+    virtual bool setProperty(int index, SkScriptValue& );
+#endif
+};
+
+
+#endif // SkDump_DEFINED
+
diff --git a/src/animator/SkExtraPathEffects.xsd b/src/animator/SkExtraPathEffects.xsd
new file mode 100644
index 0000000..9592443
--- /dev/null
+++ b/src/animator/SkExtraPathEffects.xsd
@@ -0,0 +1,33 @@
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

+xmlns:Sk="urn:screenplay"

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

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

+		schemaLocation="SkAnimateSchema.xsd" />

+		

+	<xs:element name="composePathEffect" >

+		<xs:complexType>

+			<xs:choice maxOccurs="1">

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

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

+			</xs:choice>

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

+		</xs:complexType>

+	</xs:element>

+

+	<xs:element name="shape1DPathEffect" >

+		<xs:complexType>

+			<xs:choice maxOccurs="1">

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

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

+			</xs:choice>

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

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

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

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

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

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

+		</xs:complexType>

+	</xs:element>

+		

+</xs:schema>

+
diff --git a/src/animator/SkExtras.h b/src/animator/SkExtras.h
new file mode 100644
index 0000000..88e9b2d
--- /dev/null
+++ b/src/animator/SkExtras.h
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkExtras.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 SkExtras_DEFINED
+#define SkExtras_DEFINED
+
+#include "SkScript.h"
+
+class SkExtras {
+public:
+            SkExtras();
+    virtual ~SkExtras() {}
+
+    virtual SkDisplayable* createInstance(SkDisplayTypes type) = 0;
+    virtual bool definesType(SkDisplayTypes type) = 0;
+#if SK_USE_CONDENSED_INFO == 0
+    virtual const SkMemberInfo* getMembers(SkDisplayTypes type, int* infoCountPtr) = 0;
+#endif
+#ifdef SK_DEBUG
+    virtual const char* getName(SkDisplayTypes type) = 0;
+#endif
+    virtual SkDisplayTypes getType(const char match[], size_t len ) = 0;
+
+    SkScriptEngine::_propertyCallBack fExtraCallBack;
+    void* fExtraStorage;
+};
+
+#endif // SkExtras_DEFINED
diff --git a/src/animator/SkGetCondensedInfo.cpp b/src/animator/SkGetCondensedInfo.cpp
new file mode 100644
index 0000000..ee91caa
--- /dev/null
+++ b/src/animator/SkGetCondensedInfo.cpp
@@ -0,0 +1,130 @@
+/* libs/graphics/animator/SkGetCondensedInfo.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 "SkMemberInfo.h"
+
+#if SK_USE_CONDENSED_INFO == 1
+
+// SkCondensed.cpp is auto-generated
+// To generate it, execute SkDisplayType::BuildCondensedInfo()
+#ifdef SK_DEBUG
+#include "SkCondensedDebug.cpp"
+#else
+#include "SkCondensedRelease.cpp"
+#endif
+
+static int _searchByName(const unsigned char* lengths, int count, const char* strings, const char target[]) {
+    int lo = 0;
+    int hi = count - 1;
+    while (lo < hi) {
+        int mid = (hi + lo) >> 1;
+        if (strcmp(&strings[lengths[mid << 2]], target) < 0)
+            lo = mid + 1;
+        else 
+            hi = mid;
+    }
+    if (strcmp(&strings[lengths[hi << 2]], target) != 0)
+        return -1;
+    return hi;
+}
+
+static int _searchByType(SkDisplayTypes type) {
+    unsigned char match = (unsigned char) type;
+    int lo = 0;
+    int hi = kTypeIDs - 1;
+    while (lo < hi) {
+        int mid = (hi + lo) >> 1;
+        if (gTypeIDs[mid] < match)
+            lo = mid + 1;
+        else
+            hi = mid;
+    }
+    if (gTypeIDs[hi] != type)
+        return -1;
+    return hi;
+}
+
+const SkMemberInfo* SkDisplayType::GetMembers(SkAnimateMaker* , SkDisplayTypes type, int* infoCountPtr) {
+    int lookup = _searchByType(type);
+    if (lookup < 0)
+        return NULL;
+    if (infoCountPtr)
+        *infoCountPtr = gInfoCounts[lookup];
+    return gInfoTables[lookup];
+}
+
+// !!! replace with inline
+const SkMemberInfo* SkDisplayType::GetMember(SkAnimateMaker* , SkDisplayTypes type, const char** matchPtr ) {
+    const SkMemberInfo* info = SkMemberInfo::Find(type, matchPtr);
+    SkASSERT(info);
+    return info;
+}
+
+static const SkMemberInfo* _lookup(int lookup, const char** matchPtr) {
+    int count = gInfoCounts[lookup];
+    const SkMemberInfo* info = gInfoTables[lookup];
+    if (info->fType == SkType_BaseClassInfo) {
+        int baseTypeLookup = info->fOffset;
+        const SkMemberInfo* result = _lookup(baseTypeLookup, matchPtr);
+        if (result != NULL)
+            return result;
+        if (--count == 0)
+            return NULL;
+        info++;
+    }
+    SkASSERT(info->fType != SkType_BaseClassInfo);
+    const char* match = *matchPtr;
+    const char* strings = gInfoNames[lookup];
+    int index = _searchByName(&info->fName, count, strings, match);
+    if (index < 0)
+        return NULL;
+    return &info[index];
+}
+
+const SkMemberInfo* SkMemberInfo::Find(SkDisplayTypes type, int* index) {
+    int count = gInfoCounts[lookup];
+    const SkMemberInfo* info = gInfoTables[lookup];
+    if (info->fType == SkType_BaseClassInfo) {
+        int baseTypeLookup = info->fOffset;
+        const SkMemberInfo* result = Find(baseTypeLookup, index);
+        if (result != NULL)
+            return result;
+        if (--count == 0)
+            return NULL;
+        info++;
+    }
+    SkASSERT(info->fType != SkType_BaseClassInfo);
+    if (*index >= count) {
+        *index -= count;
+        return NULL;
+    }
+    return &info[index];
+}
+
+const SkMemberInfo* SkMemberInfo::Find(SkDisplayTypes type, const char** matchPtr) {
+    int lookup = _searchByType(type);
+    SkASSERT(lookup >= 0);
+    return _lookup(lookup, matchPtr);
+}
+
+const SkMemberInfo* SkMemberInfo::getInherited() const {
+    int baseTypeLookup = fOffset;
+    return gInfoTables[baseTypeLookup];
+}
+
+#endif
+
diff --git a/src/animator/SkHitClear.cpp b/src/animator/SkHitClear.cpp
new file mode 100644
index 0000000..e7e301d
--- /dev/null
+++ b/src/animator/SkHitClear.cpp
@@ -0,0 +1,41 @@
+/* libs/graphics/animator/SkHitClear.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 "SkHitClear.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkHitClear::fInfo[] = {
+    SK_MEMBER_ARRAY(targets, Displayable)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkHitClear);
+
+bool SkHitClear::enable(SkAnimateMaker& maker) {
+    for (int tIndex = 0; tIndex < targets.count(); tIndex++) {
+        SkDisplayable* target = targets[tIndex];
+        target->clearBounder();
+    }
+    return true;
+}
+
+bool SkHitClear::hasEnable() const {
+    return true;
+}
+
diff --git a/src/animator/SkHitClear.h b/src/animator/SkHitClear.h
new file mode 100644
index 0000000..f2b50c0
--- /dev/null
+++ b/src/animator/SkHitClear.h
@@ -0,0 +1,33 @@
+/* libs/graphics/animator/SkHitClear.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 SkHitClear_DEFINED
+#define SkHitClear_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkTypedArray.h"
+
+class SkHitClear : public SkDisplayable {
+    DECLARE_MEMBER_INFO(HitClear);
+    virtual bool enable(SkAnimateMaker& );
+    virtual bool hasEnable() const;
+private:
+    SkTDDisplayableArray targets;
+};
+
+#endif // SkHitClear_DEFINED
diff --git a/src/animator/SkHitTest.cpp b/src/animator/SkHitTest.cpp
new file mode 100644
index 0000000..585249a
--- /dev/null
+++ b/src/animator/SkHitTest.cpp
@@ -0,0 +1,83 @@
+/* libs/graphics/animator/SkHitTest.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 "SkHitTest.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkHitTest::fInfo[] = {
+    SK_MEMBER_ARRAY(bullets, Displayable),
+    SK_MEMBER_ARRAY(hits, Int),
+    SK_MEMBER_ARRAY(targets, Displayable),
+    SK_MEMBER(value, Boolean)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkHitTest);
+
+SkHitTest::SkHitTest() : value(false) {
+}
+
+bool SkHitTest::draw(SkAnimateMaker& maker) {
+    hits.setCount(bullets.count());
+    value = false;
+    int bulletCount = bullets.count();
+    int targetCount = targets.count();
+    for (int bIndex = 0; bIndex < bulletCount; bIndex++) {
+        SkDisplayable* bullet = bullets[bIndex];
+        SkRect bBounds;
+        bullet->getBounds(&bBounds);
+        hits[bIndex] = -1;
+        if (bBounds.fLeft == (int16_t)0x8000U)
+            continue;
+        for (int tIndex = 0; tIndex < targetCount; tIndex++) {
+            SkDisplayable* target = targets[tIndex];
+            SkRect tBounds;
+            target->getBounds(&tBounds);
+            if (bBounds.intersect(tBounds)) {
+                hits[bIndex] = tIndex;
+                value = true;
+                break;
+            }
+        }
+    }
+    return false;
+}
+
+bool SkHitTest::enable(SkAnimateMaker& maker) {
+    for (int bIndex = 0; bIndex < bullets.count(); bIndex++) {
+        SkDisplayable* bullet = bullets[bIndex];
+        bullet->enableBounder();
+    }
+    for (int tIndex = 0; tIndex < targets.count(); tIndex++) {
+        SkDisplayable* target = targets[tIndex];
+        target->enableBounder();
+    }
+    return false;
+}
+
+bool SkHitTest::hasEnable() const {
+    return true;
+}
+
+const SkMemberInfo* SkHitTest::preferredChild(SkDisplayTypes type) {
+    if (bullets.count() == 0)
+        return getMember("bullets");
+    return getMember("targets"); // !!! cwap! need to refer to member through enum like kScope instead
+}
+
diff --git a/src/animator/SkHitTest.h b/src/animator/SkHitTest.h
new file mode 100644
index 0000000..b3a9d85
--- /dev/null
+++ b/src/animator/SkHitTest.h
@@ -0,0 +1,38 @@
+/* libs/graphics/animator/SkHitTest.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 SkHitTest_DEFINED
+#define SkHitTest_DEFINED
+
+#include "SkDrawable.h"
+#include "SkTypedArray.h"
+
+class SkHitTest : public SkDrawable {
+    DECLARE_MEMBER_INFO(HitTest);
+    SkHitTest();
+    virtual bool draw(SkAnimateMaker& );
+    virtual bool enable(SkAnimateMaker& );
+    virtual bool hasEnable() const;
+    virtual const SkMemberInfo* preferredChild(SkDisplayTypes type);
+private:
+    SkTDDisplayableArray bullets;
+    SkTDIntArray hits;
+    SkTDDisplayableArray targets;
+    SkBool value;
+};
+
+#endif // SkHitTest_DEFINED
diff --git a/src/animator/SkIntArray.h b/src/animator/SkIntArray.h
new file mode 100644
index 0000000..b2a49c1
--- /dev/null
+++ b/src/animator/SkIntArray.h
@@ -0,0 +1,66 @@
+/* libs/graphics/animator/SkIntArray.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 SkIntArray_DEFINED
+#define SkIntArray_DEFINED
+
+#include "SkColor.h"
+#include "SkDisplayType.h"
+#include "SkMath.h"
+#include "SkTDArray_Experimental.h"
+
+class SkActive;
+class SkAnimateBase;
+class SkData;
+class SkDisplayable;
+class SkDisplayEvent;
+class SkDrawable;
+class SkDrawColor;
+class SkMatrixPart;
+struct SkMemberInfo;
+class SkPathPart;
+class SkPaintPart;
+class SkTypedArray;
+class SkString;
+union SkOperand;
+
+typedef SkIntArray(int) SkTDIntArray;
+typedef SkIntArray(SkColor) SkTDColorArray;
+typedef SkIntArray(SkDisplayTypes) SkTDDisplayTypesArray;
+typedef SkIntArray(SkMSec) SkTDMSecArray;
+typedef SkIntArray(SkScalar) SkTDScalarArray;
+
+typedef SkLongArray(SkActive*) SkTDActiveArray; 
+typedef SkLongArray(SkAnimateBase*) SkTDAnimateArray; 
+typedef SkLongArray(SkData*) SkTDDataArray; 
+typedef SkLongArray(SkDisplayable*) SkTDDisplayableArray; 
+typedef SkLongArray(SkDisplayEvent*) SkTDDisplayEventArray; 
+typedef SkLongArray(SkDrawable*) SkTDDrawableArray; 
+typedef SkLongArray(SkDrawColor*) SkTDDrawColorArray; 
+typedef SkLongArray(SkMatrixPart*) SkTDMatrixPartArray; 
+typedef SkLongArray(const SkMemberInfo*) SkTDMemberInfoArray;
+typedef SkLongArray(SkPaintPart*) SkTDPaintPartArray; 
+typedef SkLongArray(SkPathPart*) SkTDPathPartArray; 
+typedef SkLongArray(SkTypedArray*) SkTDTypedArrayArray; 
+typedef SkLongArray(SkString*) SkTDStringArray; 
+typedef SkLongArray(SkOperand) SkTDOperandArray; 
+typedef SkLongArray(SkOperand*) SkTDOperandPtrArray; 
+
+#endif // SkIntArray_DEFINED
+
+
+
diff --git a/src/animator/SkMatrixParts.cpp b/src/animator/SkMatrixParts.cpp
new file mode 100644
index 0000000..56bb2d0
--- /dev/null
+++ b/src/animator/SkMatrixParts.cpp
@@ -0,0 +1,302 @@
+/* libs/graphics/animator/SkMatrixParts.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 "SkMatrixParts.h"
+#include "SkAnimateMaker.h"
+#include "SkDrawMatrix.h"
+#include "SkDrawRectangle.h"
+#include "SkDrawPath.h"
+
+SkMatrixPart::SkMatrixPart() : fMatrix(NULL) {
+}
+
+void SkMatrixPart::dirty() { 
+    fMatrix->dirty(); 
+}
+
+SkDisplayable* SkMatrixPart::getParent() const {
+    return fMatrix;
+}
+
+bool SkMatrixPart::setParent(SkDisplayable* parent) {
+    SkASSERT(parent != NULL);
+    if (parent->isMatrix() == false)
+        return true;
+    fMatrix = (SkDrawMatrix*) parent;
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRotate::fInfo[] = {
+    SK_MEMBER(center, Point),
+    SK_MEMBER(degrees, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRotate);
+
+SkRotate::SkRotate() : degrees(0) { 
+    center.fX = center.fY = 0; 
+}
+
+bool SkRotate::add() {
+    fMatrix->rotate(degrees, center);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkScale::fInfo[] = {
+    SK_MEMBER(center, Point),
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkScale);
+
+SkScale::SkScale() : x(SK_Scalar1), y(SK_Scalar1) { 
+    center.fX = center.fY = 0; 
+}
+
+bool SkScale::add() {
+    fMatrix->scale(x, y, center);   
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkSkew::fInfo[] = {
+    SK_MEMBER(center, Point),
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkSkew);
+
+SkSkew::SkSkew() : x(0), y(0) { 
+    center.fX = center.fY = 0; 
+}
+
+bool SkSkew::add() {
+    fMatrix->skew(x, y, center);    
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkTranslate::fInfo[] = {
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkTranslate);
+
+SkTranslate::SkTranslate() : x(0), y(0) {
+}
+
+bool SkTranslate::add() {
+    fMatrix->translate(x, y);   
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkFromPath::fInfo[] = {
+    SK_MEMBER(mode, FromPathMode),
+    SK_MEMBER(offset, Float),
+    SK_MEMBER(path, Path)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkFromPath);
+
+SkFromPath::SkFromPath() : 
+    mode(0), offset(0), path(NULL) {
+}
+
+SkFromPath::~SkFromPath() {
+}
+
+bool SkFromPath::add() {
+    if (path == NULL)
+        return true;
+    static const uint8_t gFlags[] = {
+        SkPathMeasure::kGetPosAndTan_MatrixFlag,    // normal
+        SkPathMeasure::kGetTangent_MatrixFlag,      // angle
+        SkPathMeasure::kGetPosition_MatrixFlag      // position
+    };
+    if ((unsigned)mode >= SK_ARRAY_COUNT(gFlags))
+        return true;
+    SkMatrix result;
+    fPathMeasure.setPath(&path->getPath(), false);
+    if (fPathMeasure.getMatrix(offset, &result, (SkPathMeasure::MatrixFlags)gFlags[mode]))
+        fMatrix->set(result);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRectToRect::fInfo[] = {
+    SK_MEMBER(destination, Rect),
+    SK_MEMBER(source, Rect)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRectToRect);
+
+SkRectToRect::SkRectToRect() : 
+    source(NULL), destination(NULL) {
+}
+
+SkRectToRect::~SkRectToRect() {
+}
+
+bool SkRectToRect::add() {
+    if (source == NULL || destination == NULL)
+        return true;
+    SkMatrix temp;
+    temp.setRectToRect(source->fRect, destination->fRect,
+                       SkMatrix::kFill_ScaleToFit);
+    fMatrix->set(temp);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkRectToRect::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("/>\n");
+    SkDisplayList::fIndent += 4;
+    if (source) {
+        SkDebugf("%*s<source>\n", SkDisplayList::fIndent, "");
+        SkDisplayList::fIndent += 4;
+        source->dump(maker);
+        SkDisplayList::fIndent -= 4;
+        SkDebugf("%*s</source>\n", SkDisplayList::fIndent, "");
+    }
+    if (destination) {
+        SkDebugf("%*s<destination>\n", SkDisplayList::fIndent, "");
+        SkDisplayList::fIndent += 4;
+        destination->dump(maker);
+        SkDisplayList::fIndent -= 4;
+        SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, "");        
+    }
+    SkDisplayList::fIndent -= 4;
+    dumpEnd(maker);
+}
+#endif
+
+const SkMemberInfo* SkRectToRect::preferredChild(SkDisplayTypes ) {
+    if (source == NULL)
+        return getMember("source"); // !!! cwap! need to refer to member through enum like kScope instead
+    else {
+        SkASSERT(destination == NULL);
+        return getMember("destination");
+    }
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkPolyToPoly::fInfo[] = {
+    SK_MEMBER(destination, Polygon),
+    SK_MEMBER(source, Polygon)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkPolyToPoly);
+
+SkPolyToPoly::SkPolyToPoly() : source(NULL), destination(NULL) {
+}
+
+SkPolyToPoly::~SkPolyToPoly() {
+}
+
+bool SkPolyToPoly::add() {
+    SkASSERT(source);
+    SkASSERT(destination);
+    SkPoint src[4];
+    SkPoint dst[4];
+    SkPath& sourcePath = source->getPath();
+    int srcPts = sourcePath.getPoints(src, 4);
+    SkPath& destPath = destination->getPath();
+    int dstPts = destPath.getPoints(dst, 4);
+    if (srcPts != dstPts)
+        return true;
+    SkMatrix temp;
+    temp.setPolyToPoly(src, dst, srcPts);
+    fMatrix->set(temp);
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkPolyToPoly::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    SkDebugf("/>\n");
+    SkDisplayList::fIndent += 4;
+    if (source) {
+        SkDebugf("%*s<source>\n", SkDisplayList::fIndent, "");
+        SkDisplayList::fIndent += 4;
+        source->dump(maker);
+        SkDisplayList::fIndent -= 4;
+        SkDebugf("%*s</source>\n", SkDisplayList::fIndent, "");
+    }
+    if (destination) {
+        SkDebugf("%*s<destination>\n", SkDisplayList::fIndent, "");
+        SkDisplayList::fIndent += 4;
+        destination->dump(maker);
+        SkDisplayList::fIndent -= 4;
+        SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, "");        
+    }
+    SkDisplayList::fIndent -= 4;
+    dumpEnd(maker);
+}
+#endif
+
+void SkPolyToPoly::onEndElement(SkAnimateMaker& ) {
+    SkASSERT(source);
+    SkASSERT(destination);
+    if (source->childHasID() || destination->childHasID())
+        fMatrix->setChildHasID();
+}
+
+const SkMemberInfo* SkPolyToPoly::preferredChild(SkDisplayTypes ) {
+    if (source == NULL)
+        return getMember("source"); // !!! cwap! need to refer to member through enum like kScope instead
+    else {
+        SkASSERT(destination == NULL);
+        return getMember("destination");
+    }
+}
+
+
diff --git a/src/animator/SkMatrixParts.h b/src/animator/SkMatrixParts.h
new file mode 100644
index 0000000..d97d9fb
--- /dev/null
+++ b/src/animator/SkMatrixParts.h
@@ -0,0 +1,127 @@
+/* libs/graphics/animator/SkMatrixParts.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 SkMatrixParts_DEFINED
+#define SkMatrixParts_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkPathMeasure.h"
+
+class SkDrawPath;
+class SkDrawRect;
+class SkPolygon;
+
+class SkDrawMatrix;
+// class SkMatrix;
+
+class SkMatrixPart : public SkDisplayable {
+public:
+    SkMatrixPart();
+    virtual bool add() = 0;
+    virtual void dirty();
+    virtual SkDisplayable* getParent() const;
+    virtual bool setParent(SkDisplayable* parent);
+#ifdef SK_DEBUG
+    virtual bool isMatrixPart() const { return true; }
+#endif
+protected:
+    SkDrawMatrix* fMatrix;
+};
+
+class SkRotate : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(Rotate);
+    SkRotate();
+protected:
+    virtual bool add();
+    SkScalar degrees;
+    SkPoint center;
+};
+
+class SkScale : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(Scale);
+    SkScale();
+protected:
+    virtual bool add();
+    SkScalar x;
+    SkScalar y;
+    SkPoint center;
+};
+
+class SkSkew : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(Skew);
+    SkSkew();
+protected:
+    virtual bool add();
+    SkScalar x;
+    SkScalar y;
+    SkPoint center;
+};
+
+class SkTranslate : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(Translate);
+    SkTranslate();
+protected:
+    virtual bool add();
+    SkScalar x;
+    SkScalar y;
+};
+
+class SkFromPath : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(FromPath);
+    SkFromPath();
+    virtual ~SkFromPath();
+protected:
+    virtual bool add();
+    int32_t mode;
+    SkScalar offset;
+    SkDrawPath* path;
+    SkPathMeasure fPathMeasure;
+};
+
+class SkRectToRect : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(RectToRect);
+    SkRectToRect();
+    virtual ~SkRectToRect();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual const SkMemberInfo* preferredChild(SkDisplayTypes type);
+protected:
+    virtual bool add();
+    SkDrawRect* source;
+    SkDrawRect* destination;
+};
+
+class SkPolyToPoly : public SkMatrixPart {
+    DECLARE_MEMBER_INFO(PolyToPoly);
+    SkPolyToPoly();
+    virtual ~SkPolyToPoly();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker* );
+#endif
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual const SkMemberInfo* preferredChild(SkDisplayTypes type);
+protected:
+    virtual bool add();
+    SkPolygon* source;
+    SkPolygon* destination;
+};
+
+// !!! add concat matrix ? 
+
+#endif // SkMatrixParts_DEFINED
diff --git a/src/animator/SkMemberInfo.cpp b/src/animator/SkMemberInfo.cpp
new file mode 100644
index 0000000..50536c2
--- /dev/null
+++ b/src/animator/SkMemberInfo.cpp
@@ -0,0 +1,568 @@
+/* libs/graphics/animator/SkMemberInfo.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 "SkMemberInfo.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimatorScript.h"
+#include "SkBase64.h"
+#include "SkCamera.h"
+#include "SkDisplayable.h"
+#include "SkDisplayTypes.h"
+#include "SkDraw3D.h"
+#include "SkDrawColor.h"
+#include "SkParse.h"
+#include "SkScript.h"
+#include "SkTSearch.h"
+#include "SkTypedArray.h"
+
+size_t SkMemberInfo::GetSize(SkDisplayTypes type) { // size of simple types only
+    size_t byteSize;
+    switch (type) {
+        case SkType_ARGB:
+            byteSize = sizeof(SkColor);
+            break;
+        case SkType_AddMode:
+        case SkType_Align:
+        case SkType_ApplyMode:
+        case SkType_ApplyTransition:
+        case SkType_BitmapEncoding:
+        case SkType_Boolean:
+        case SkType_Cap:
+        case SkType_EventCode:
+        case SkType_EventKind:
+        case SkType_EventMode:
+        case SkType_FilterType:
+        case SkType_FontStyle:
+        case SkType_FromPathMode:
+        case SkType_Join:
+        case SkType_MaskFilterBlurStyle:
+        case SkType_PathDirection:
+        case SkType_Style:
+        case SkType_TileMode:
+        case SkType_Xfermode:
+            byteSize = sizeof(int);
+            break;
+        case SkType_Base64: // assume base64 data is always const, copied by ref
+        case SkType_Displayable:
+        case SkType_Drawable:
+        case SkType_Matrix:
+            byteSize = sizeof(void*); 
+            break;
+        case SkType_MSec:
+            byteSize = sizeof(SkMSec);
+            break;
+        case SkType_Point:
+            byteSize = sizeof(SkPoint);
+            break;
+        case SkType_3D_Point:
+            byteSize = sizeof(Sk3D_Point);
+            break;
+        case SkType_Int:
+            byteSize = sizeof(int32_t);
+            break;
+        case SkType_Float:
+            byteSize = sizeof(SkScalar);
+            break;
+        case SkType_DynamicString:
+        case SkType_String:
+            byteSize = sizeof(SkString);    // assume we'll copy by reference, not value
+            break;
+        default:
+//          SkASSERT(0);
+            byteSize = 0;
+    }
+    return byteSize;
+}
+
+bool SkMemberInfo::getArrayValue(const SkDisplayable* displayable, int index, SkOperand* value) const {
+    SkASSERT(fType != SkType_String && fType != SkType_MemberProperty);
+    char* valuePtr = (char*) *(SkOperand**) memberData(displayable);
+    SkDisplayTypes type = (SkDisplayTypes) 0;
+    if (displayable->getType() == SkType_Array) {
+        SkDisplayArray* dispArray = (SkDisplayArray*) displayable;
+        if (dispArray->values.count() <= index)
+            return false;
+        type = dispArray->values.getType();
+    } else
+        SkASSERT(0); // incomplete
+    size_t byteSize = GetSize(type);
+    memcpy(value, valuePtr + index * byteSize, byteSize);
+    return true;
+}
+
+size_t SkMemberInfo::getSize(const SkDisplayable* displayable) const {
+    size_t byteSize;
+    switch (fType) {
+        case SkType_MemberProperty:
+            byteSize = GetSize(propertyType());
+            break;
+        case SkType_Array: {
+            SkDisplayTypes type;
+            if (displayable == NULL)
+                return sizeof(int);
+            if (displayable->getType() == SkType_Array) {
+                SkDisplayArray* dispArray = (SkDisplayArray*) displayable;
+                type = dispArray->values.getType();
+            } else
+                type = propertyType();
+            SkTDOperandArray* array = (SkTDOperandArray*) memberData(displayable);
+            byteSize = GetSize(type) * array->count();
+            } break;
+        default:
+            byteSize = GetSize((SkDisplayTypes) fType);
+    }
+    return byteSize;
+}
+
+void SkMemberInfo::getString(const SkDisplayable* displayable, SkString** string) const {
+    if (fType == SkType_MemberProperty) {
+        SkScriptValue value;
+        displayable->getProperty(propertyIndex(), &value);
+        SkASSERT(value.fType == SkType_String);
+        *string = value.fOperand.fString;
+        return;
+    }
+    SkASSERT(fCount == sizeof(SkString) / sizeof(SkScalar));
+    SkASSERT(fType == SkType_String || fType == SkType_DynamicString);
+    void* valuePtr = memberData(displayable);
+    *string = (SkString*) valuePtr;
+}
+
+void SkMemberInfo::getValue(const SkDisplayable* displayable, SkOperand value[], int count) const {
+    SkASSERT(fType != SkType_String && fType != SkType_MemberProperty);
+    SkASSERT(count == fCount);  
+    void* valuePtr = memberData(displayable);
+    size_t byteSize = getSize(displayable);
+    SkASSERT(sizeof(value[0].fScalar) == sizeof(value[0])); // no support for 64 bit pointers, yet
+    memcpy(value, valuePtr, byteSize);
+}
+
+void SkMemberInfo::setString(SkDisplayable* displayable, SkString* value) const {
+    SkString* string = (SkString*) memberData(displayable);
+    string->set(*value);
+    displayable->dirty();
+}
+
+void SkMemberInfo::setValue(SkDisplayable* displayable, const SkOperand values[], 
+                            int count) const {
+    SkASSERT(sizeof(values[0].fScalar) == sizeof(values[0]));   // no support for 64 bit pointers, yet
+    char* dst = (char*) memberData(displayable);
+    if (fType == SkType_Array) {
+        SkTDScalarArray* array = (SkTDScalarArray* ) dst;
+        array->setCount(count);
+        dst = (char*) array->begin();
+    }
+    memcpy(dst, values, count * sizeof(SkOperand));
+    displayable->dirty();
+}
+
+                            
+static inline bool is_between(int c, int min, int max)
+{
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_hex(int c)
+{
+    if (is_between(c, '0', '9'))
+        return true;
+    c |= 0x20;  // make us lower-case
+    if (is_between(c, 'a', 'f'))
+        return true;
+    return false;
+}
+
+
+bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage, 
+    int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType,
+    const char rawValue[], size_t rawValueLen) const 
+{
+    SkString valueStr(rawValue, rawValueLen);
+    SkScriptValue scriptValue;
+    scriptValue.fType = SkType_Unknown;
+    scriptValue.fOperand.fS32 = 0;
+    SkDisplayTypes type = getType();
+    SkAnimatorScript engine(maker, displayable, type);
+    if (arrayStorage)
+        displayable = NULL;
+    bool success = true;
+    void* untypedStorage = NULL;
+    if (displayable && fType != SkType_MemberProperty && fType != SkType_MemberFunction)
+        untypedStorage = (SkTDOperandArray*) memberData(displayable);
+
+    if (type == SkType_ARGB) {
+        // for both SpiderMonkey and SkiaScript, substitute any #xyz or #xxyyzz first
+            // it's enough to expand the colors into 0xFFxxyyzz
+        const char* poundPos;
+        while ((poundPos = strchr(valueStr.c_str(), '#')) != NULL) {
+            size_t offset = poundPos - valueStr.c_str();
+            if (valueStr.size() - offset < 4)
+                break;
+            char r = poundPos[1];
+            char g = poundPos[2];
+            char b = poundPos[3];
+            if (is_hex(r) == false || is_hex(g) == false || is_hex(b) == false)
+                break;
+            char hex = poundPos[4];
+            if (is_hex(hex) == false) {
+                valueStr.insertUnichar(offset + 1, r);
+                valueStr.insertUnichar(offset + 3, g);
+                valueStr.insertUnichar(offset + 5, b);
+            }
+            *(char*) poundPos = '0'; // overwrite '#'
+            valueStr.insert(offset + 1, "xFF");
+        } 
+    }
+    if (SkDisplayType::IsDisplayable(&maker, type) || SkDisplayType::IsEnum(&maker, type) || type == SkType_ARGB)
+        goto scriptCommon;
+    switch (type) {
+        case SkType_String:
+#if 0
+            if (displayable && displayable->isAnimate()) {
+                
+                goto noScriptString;
+            } 
+            if (strncmp(rawValue, "#string:", sizeof("#string:") - 1) == 0) {
+                SkASSERT(sizeof("string") == sizeof("script"));
+                char* stringHeader = valueStr.writable_str();
+                memcpy(&stringHeader[1], "script", sizeof("script") - 1);
+                rawValue = valueStr.c_str();
+                goto noScriptString;
+            } else 
+#endif
+            if (strncmp(rawValue, "#script:", sizeof("#script:") - 1) != 0)
+                goto noScriptString;
+            valueStr.remove(0, 8);
+        case SkType_Unknown:
+        case SkType_Int: 
+        case SkType_MSec:  // for the purposes of script, MSec is treated as a Scalar
+        case SkType_Point:
+        case SkType_3D_Point:
+        case SkType_Float:
+        case SkType_Array:
+scriptCommon: {
+                const char* script = valueStr.c_str();
+                success = engine.evaluateScript(&script, &scriptValue);
+                if (success == false) {
+                    maker.setScriptError(engine);
+                    return false;
+                }
+            }
+            SkASSERT(success);
+            if (scriptValue.fType == SkType_Displayable) {
+                if (type == SkType_String) {
+                    const char* charPtr;
+                    maker.findKey(scriptValue.fOperand.fDisplayable, &charPtr);
+                    scriptValue.fOperand.fString = new SkString(charPtr);
+                    scriptValue.fType = SkType_String;
+                    engine.SkScriptEngine::track(scriptValue.fOperand.fString);
+                    break;
+                }
+                SkASSERT(SkDisplayType::IsDisplayable(&maker, type));
+                if (displayable)
+                    displayable->setReference(this, scriptValue.fOperand.fDisplayable);
+                else
+                    arrayStorage->begin()[0].fDisplayable = scriptValue.fOperand.fDisplayable;
+                return true;
+            }
+            if (type != scriptValue.fType) {
+                if (scriptValue.fType == SkType_Array) {
+                    engine.forget(scriptValue.getArray());
+                    goto writeStruct; // real structs have already been written by script
+                }
+                switch (type) {
+                    case SkType_String:
+                        success = engine.convertTo(SkType_String, &scriptValue);
+                        break;
+                    case SkType_MSec:
+                    case SkType_Float:
+                        success = engine.convertTo(SkType_Float, &scriptValue);
+                        break;
+                    case SkType_Int:
+                        success = engine.convertTo(SkType_Int, &scriptValue);
+                        break;
+                    case SkType_Array:
+                        success = engine.convertTo(arrayType(), &scriptValue);
+                        // !!! incomplete; create array of appropriate type and add scriptValue to it
+                        SkASSERT(0);
+                        break;
+                    case SkType_Displayable:
+                    case SkType_Drawable:
+                        return false;   // no way to convert other types to this
+                    default:    // to avoid warnings
+                        break;
+                }
+                if (success == false)
+                    return false;
+            }
+            if (type == SkType_MSec)
+                scriptValue.fOperand.fMSec = SkScalarMulRound(scriptValue.fOperand.fScalar, 1000);
+            scriptValue.fType = type;
+        break;
+        noScriptString:
+        case SkType_DynamicString:
+            if (fType == SkType_MemberProperty && displayable) {
+                SkString string(rawValue, rawValueLen);
+                SkScriptValue scriptValue;
+                scriptValue.fOperand.fString = &string;
+                scriptValue.fType = SkType_String;
+                displayable->setProperty(propertyIndex(), scriptValue);
+            } else if (displayable) {
+                SkString* string = (SkString*) memberData(displayable);
+                string->set(rawValue, rawValueLen);
+            } else {
+                SkASSERT(arrayStorage->count() == 1);
+                arrayStorage->begin()->fString->set(rawValue, rawValueLen);
+            }
+            goto dirty;
+        case SkType_Base64: {
+            SkBase64 base64;
+            base64.decode(rawValue, rawValueLen);
+            *(SkBase64* ) untypedStorage = base64;
+            } goto dirty;
+        default:
+            SkASSERT(0);
+            break;
+    }
+//  if (SkDisplayType::IsStruct(type) == false) 
+    {
+writeStruct:
+        if (writeValue(displayable, arrayStorage, storageOffset, maxStorage, 
+                untypedStorage, outType, scriptValue)) {
+                    maker.setErrorCode(SkDisplayXMLParserError::kUnexpectedType);
+            return false;
+        }
+    }
+dirty:
+    if (displayable)
+        displayable->dirty();
+    return true;
+}
+
+bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage, 
+        int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType,
+        SkString& raw) const {
+    return setValue(maker, arrayStorage, storageOffset, maxStorage, displayable, outType, raw.c_str(),
+        raw.size());
+}
+
+bool SkMemberInfo::writeValue(SkDisplayable* displayable, SkTDOperandArray* arrayStorage, 
+    int storageOffset, int maxStorage, void* untypedStorage, SkDisplayTypes outType, 
+    SkScriptValue& scriptValue) const
+{
+    SkOperand* storage = untypedStorage ? (SkOperand*) untypedStorage : arrayStorage ? 
+        arrayStorage->begin() : NULL;
+    if (storage)
+        storage += storageOffset;
+    SkDisplayTypes type = getType();
+    if (fType == SkType_MemberProperty) {
+        if(displayable)
+            displayable->setProperty(propertyIndex(), scriptValue);
+        else {
+            SkASSERT(storageOffset < arrayStorage->count());
+            switch (scriptValue.fType) {
+                case SkType_Boolean:
+                case SkType_Float:
+                case SkType_Int:
+                    memcpy(&storage->fScalar, &scriptValue.fOperand.fScalar, sizeof(SkScalar));
+                    break;
+                case SkType_Array:
+                    memcpy(&storage->fScalar, scriptValue.fOperand.fArray->begin(), scriptValue.fOperand.fArray->count() * sizeof(SkScalar));
+                    break;
+                case SkType_String:
+                    storage->fString->set(*scriptValue.fOperand.fString);
+                    break;
+                default:
+                    SkASSERT(0);    // type isn't handled yet
+            }
+        }
+    } else if (fType == SkType_MemberFunction) {
+        SkASSERT(scriptValue.fType == SkType_Array);
+        if (displayable)
+            displayable->executeFunction(displayable, this, scriptValue.fOperand.fArray, NULL);
+        else {
+            int count = scriptValue.fOperand.fArray->count();
+    //      SkASSERT(maxStorage == 0 || count == maxStorage);
+            if (arrayStorage->count() == 2)
+                arrayStorage->setCount(2 * count);
+            else {
+                storageOffset *= count;
+                SkASSERT(count + storageOffset <= arrayStorage->count());
+            }
+            memcpy(&(*arrayStorage)[storageOffset], scriptValue.fOperand.fArray->begin(), count * sizeof(SkOperand));
+        }
+
+    } else if (fType == SkType_Array) {
+        SkTypedArray* destArray = (SkTypedArray*) (untypedStorage ? untypedStorage : arrayStorage);
+        SkASSERT(destArray);
+    //  destArray->setCount(0);
+        if (scriptValue.fType != SkType_Array) {
+            SkASSERT(type == scriptValue.fType);
+    //      SkASSERT(storageOffset + 1 <= maxStorage);
+            destArray->setCount(storageOffset + 1);
+            (*destArray)[storageOffset] = scriptValue.fOperand;
+        } else {
+            if (type == SkType_Unknown) {
+                type = scriptValue.fOperand.fArray->getType();
+                destArray->setType(type);
+            }
+            SkASSERT(type == scriptValue.fOperand.fArray->getType());
+            int count = scriptValue.fOperand.fArray->count();
+    //      SkASSERT(storageOffset + count <= maxStorage);
+            destArray->setCount(storageOffset + count);
+            memcpy(destArray->begin() + storageOffset, scriptValue.fOperand.fArray->begin(), sizeof(SkOperand) * count);
+        }
+    } else if (type == SkType_String) {
+        SkString* string = untypedStorage ? (SkString*) untypedStorage : (*arrayStorage)[storageOffset].fString;
+        string->set(*scriptValue.fOperand.fString);
+    } else if (type == SkType_ARGB && outType == SkType_Float) {
+        SkTypedArray* array = scriptValue.fOperand.fArray;
+        SkASSERT(scriptValue.fType == SkType_Int || scriptValue.fType == SkType_ARGB || 
+            scriptValue.fType == SkType_Array);
+        SkASSERT(scriptValue.fType != SkType_Array || (array != NULL &&
+            array->getType() == SkType_Int));
+        int numberOfColors = scriptValue.fType == SkType_Array ? array->count() : 1;
+        int numberOfComponents = numberOfColors * 4;
+    //  SkASSERT(maxStorage == 0 || maxStorage == numberOfComponents);
+        if (maxStorage == 0)
+            arrayStorage->setCount(numberOfComponents);
+        for (int index = 0; index < numberOfColors; index++) {
+            SkColor color = scriptValue.fType == SkType_Array ? 
+                (SkColor) array->begin()[index].fS32 : (SkColor) scriptValue.fOperand.fS32;
+            storage[0].fScalar = SkIntToScalar(SkColorGetA(color));
+            storage[1].fScalar = SkIntToScalar(SkColorGetR(color));
+            storage[2].fScalar = SkIntToScalar(SkColorGetG(color));
+            storage[3].fScalar = SkIntToScalar(SkColorGetB(color));
+            storage += 4;
+        }
+    } else if (SkDisplayType::IsStruct(NULL /* !!! maker*/, type)) {
+        if (scriptValue.fType != SkType_Array)
+            return true;    // error
+        SkASSERT(sizeof(SkScalar) == sizeof(SkOperand)); // !!! no 64 bit pointer support yet
+        int count = scriptValue.fOperand.fArray->count();
+        if (count > 0) {
+            SkASSERT(fCount == count);
+            memcpy(storage, scriptValue.fOperand.fArray->begin(), count * sizeof(SkOperand));
+        }
+    } else if (scriptValue.fType == SkType_Array) {
+        SkASSERT(scriptValue.fOperand.fArray->getType() == type);
+        SkASSERT(scriptValue.fOperand.fArray->count() == getCount());
+        memcpy(storage, scriptValue.fOperand.fArray->begin(), getCount() * sizeof(SkOperand));
+    } else {
+        memcpy(storage, &scriptValue.fOperand, sizeof(SkOperand));
+    }
+    return false;
+}
+
+
+//void SkMemberInfo::setValue(SkDisplayable* displayable, const char value[], const char name[]) const {
+//  void* valuePtr = (void*) ((char*) displayable + fOffset);
+//  switch (fType) {
+//      case SkType_Point3D: {
+//          static const char xyz[] = "x|y|z";
+//          int index = find_one(xyz, name);
+//          SkASSERT(index >= 0);
+//          valuePtr = (void*) ((char*) valuePtr + index * sizeof(SkScalar));
+//          } break;
+//      default:
+//          SkASSERT(0);
+//  }
+//  SkParse::FindScalar(value, (SkScalar*) valuePtr);
+//  displayable->dirty();
+//}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+// Find Nth memberInfo
+const SkMemberInfo* SkMemberInfo::Find(const SkMemberInfo info[], int count, int* index) {
+    SkASSERT(*index >= 0);
+    if (info->fType == SkType_BaseClassInfo) {
+        const SkMemberInfo* inherited = (SkMemberInfo*) info->fName;
+        const SkMemberInfo* result = SkMemberInfo::Find(inherited, info->fCount, index);
+        if (result != NULL)
+            return result;
+        if (--count == 0)
+            return NULL;
+        info++;
+    }
+    SkASSERT(info->fName);
+    SkASSERT(info->fType != SkType_BaseClassInfo);
+    if (*index >= count) {
+        *index -= count;
+        return NULL;
+    }
+    return &info[*index];
+}
+
+// Find named memberinfo
+const SkMemberInfo* SkMemberInfo::Find(const SkMemberInfo info[], int count, const char** matchPtr) {
+    const char* match = *matchPtr;
+    if (info->fType == SkType_BaseClassInfo) {
+        const SkMemberInfo* inherited = (SkMemberInfo*) info->fName;
+        const SkMemberInfo* result = SkMemberInfo::Find(inherited, info->fCount, matchPtr);
+        if (result != NULL)
+            return result;
+        if (--count == 0)
+            return NULL;
+        info++;
+    }
+    SkASSERT(info->fName);
+    SkASSERT(info->fType != SkType_BaseClassInfo);
+    int index = SkStrSearch(&info->fName, count, match, sizeof(*info));
+    if (index < 0 || index >= count)
+        return NULL;
+    return &info[index];
+}
+
+const SkMemberInfo* SkMemberInfo::getInherited() const {
+    return (SkMemberInfo*) fName;
+}
+
+#endif // SK_USE_CONDENSED_INFO == 0
+
+#if 0
+bool SkMemberInfo::SetValue(void* valuePtr, const char value[], SkDisplayTypes type, 
+                            int count) {
+    switch (type) {
+        case SkType_Animate:
+        case SkType_BaseBitmap: 
+        case SkType_Bitmap:
+        case SkType_Dash:
+        case SkType_Displayable:
+        case SkType_Drawable:
+        case SkType_Matrix:
+        case SkType_Path:
+        case SkType_Text:
+        case SkType_3D_Patch:
+            return false; // ref to object; caller must resolve
+        case SkType_MSec: {
+            SkParse::FindMSec(value, (SkMSec*) valuePtr);
+            } break;
+        case SkType_3D_Point:
+        case SkType_Point:
+    //  case SkType_PointArray:
+        case SkType_ScalarArray: 
+            SkParse::FindScalars(value, (SkScalar*) valuePtr, count);
+            break;
+        default:
+            SkASSERT(0);
+    }
+    return true;
+}
+#endif
+
+
diff --git a/src/animator/SkMemberInfo.h b/src/animator/SkMemberInfo.h
new file mode 100644
index 0000000..e45994e
--- /dev/null
+++ b/src/animator/SkMemberInfo.h
@@ -0,0 +1,283 @@
+/* libs/graphics/animator/SkMemberInfo.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 SkMemberInfo_DEFINED
+#define SkMemberInfo_DEFINED
+
+#if defined SK_BUILD_CONDENSED
+    #define SK_USE_CONDENSED_INFO 0
+#elif defined  SK_BUILD_FOR_BREW
+    #define SK_USE_CONDENSED_INFO 1 /* required by BREW to handle its lack of writable globals */
+#else
+    #define SK_USE_CONDENSED_INFO 0 /* optional, but usually 1 unless Cary is testing something */
+#endif
+
+#include "SkDisplayType.h"
+#include "SkScript.h"
+#include "SkString.h"
+#include "SkIntArray.h"
+
+class SkAnimateMaker;
+class SkDisplayable;
+class SkScriptEngine;
+
+// temporary hacks until name change is more complete
+#define SkFloat SkScalar
+#define SkInt SkS32
+
+struct SkMemberInfo {
+    //!!! alternative:
+    // if fCount == 0, record is member property
+    // then fType can be type, so caller doesn't have to check
+#if SK_USE_CONDENSED_INFO == 0
+    const char* fName;  // may be NULL for anonymous functions
+    size_t fOffset; // if negative, is index into member pointer table (for properties and functions)
+    SkDisplayTypes fType;
+    int fCount;         // for properties, actual type (count is always assumed to be 1)
+#else
+    unsigned char fName;
+    signed char fOffset;
+    unsigned char fType;
+    signed char fCount;
+#endif
+    SkDisplayTypes arrayType() const {
+        SkASSERT(fType == SkType_Array);
+        return (SkDisplayTypes) fCount;  // hack, but worth it?
+    }
+    int functionIndex() const {
+        SkASSERT(fType == SkType_MemberFunction);
+        return (signed) fOffset > 0 ? -1 + (int) fOffset : -1 - (int) fOffset;
+    }
+    bool getArrayValue(const SkDisplayable* displayable, int index, SkOperand* value) const;
+    int getCount() const {
+        return fType == SkType_MemberProperty || fType == SkType_Array ||
+            fType == SkType_MemberFunction ? 1 : fCount;
+    }
+    const SkMemberInfo* getInherited() const;
+    size_t getSize(const SkDisplayable* ) const;
+    void getString(const SkDisplayable* , SkString** string) const;
+    SkDisplayTypes getType() const {
+        return fType == SkType_MemberProperty || fType == SkType_Array ||
+            fType == SkType_MemberFunction ? (SkDisplayTypes) fCount : (SkDisplayTypes) fType;
+    }
+    void getValue(const SkDisplayable* , SkOperand values[], int count) const;
+    bool isEnum() const;
+    const char* mapEnums(const char* match, int* value) const;
+    void* memberData(const SkDisplayable* displayable) const {
+        SkASSERT(fType != SkType_MemberProperty &&  fType != SkType_MemberFunction);
+        return (void*) ((const char*) displayable + fOffset);
+    }
+    int propertyIndex() const {
+        SkASSERT(fType == SkType_MemberProperty);
+        return (signed) fOffset > 0 ? -1 + (int) fOffset : -1 - (int) fOffset;
+    }
+    SkDisplayTypes propertyType() const {
+        SkASSERT(fType == SkType_MemberProperty || fType == SkType_Array);
+        return (SkDisplayTypes) fCount;  // hack, but worth it?
+    }
+    void setMemberData(SkDisplayable* displayable, const void* child, size_t size) const {
+        SkASSERT(fType != SkType_MemberProperty &&  fType != SkType_MemberFunction);
+        memcpy((char*) displayable + fOffset, child, size);
+    }
+    void setString(SkDisplayable* , SkString* ) const;
+    void setValue(SkDisplayable* , const SkOperand values[], int count) const;
+    bool setValue(SkAnimateMaker& , SkTDOperandArray* storage, 
+        int storageOffset, int maxStorage, SkDisplayable* , 
+        SkDisplayTypes outType, const char value[], size_t len) const;
+    bool setValue(SkAnimateMaker& , SkTDOperandArray* storage, 
+        int storageOffset, int maxStorage, SkDisplayable* , 
+        SkDisplayTypes outType, SkString& str) const;
+//  void setValue(SkDisplayable* , const char value[], const char name[]) const;
+    bool writeValue(SkDisplayable* displayable, SkTDOperandArray* arrayStorage, 
+        int storageOffset, int maxStorage, void* untypedStorage, SkDisplayTypes outType, 
+        SkScriptValue& scriptValue) const;
+#if SK_USE_CONDENSED_INFO == 0
+    static const SkMemberInfo* Find(const SkMemberInfo [], int count, int* index);
+    static const SkMemberInfo* Find(const SkMemberInfo [], int count, const char** name);
+#else
+    static const SkMemberInfo* Find(SkDisplayTypes type, int* index);
+    static const SkMemberInfo* Find(SkDisplayTypes type, const char** name);
+#endif
+    static size_t GetSize(SkDisplayTypes type); // size of simple types only
+//  static bool SetValue(void* value, const char* name, SkDisplayTypes , int count);
+};
+
+#define SK_MEMBER(_member, _type) \
+    { #_member, SK_OFFSETOF(BASE_CLASS, _member), SkType_##_type, \
+    sizeof(((BASE_CLASS*) 1)->_member) / sizeof(SkScalar) }
+
+#define SK_MEMBER_ALIAS(_member, _alias, _type) \
+    { #_member, SK_OFFSETOF(BASE_CLASS, _alias), SkType_##_type, \
+    sizeof(((BASE_CLASS*) 1)->_alias) / sizeof(SkScalar) }
+
+#define SK_MEMBER_ARRAY(_member, _type) \
+    { #_member, SK_OFFSETOF(BASE_CLASS, _member), SkType_Array, \
+    (int) SkType_##_type }
+
+#define SK_MEMBER_INHERITED \
+    { (const char*) INHERITED::fInfo, 0, SkType_BaseClassInfo, INHERITED::fInfoCount }
+
+// #define SK_MEMBER_KEY_TYPE(_member, _type) 
+//  {#_member, (size_t) -1, SkType_##_type, 0}
+
+#define SK_FUNCTION(_member) \
+    k_##_member##Function
+
+#define SK_PROPERTY(_member) \
+    k_##_member##Property
+
+#define SK_MEMBER_DYNAMIC_FUNCTION(_member, _type) \
+    {#_member, (size_t) (+1 + SK_FUNCTION(_member)), SkType_MemberFunction, \
+    (int) SkType_##_type }
+
+#define SK_MEMBER_DYNAMIC_PROPERTY(_member, _type) \
+    {#_member, (size_t) (1 + SK_PROPERTY(_member)), SkType_MemberProperty, \
+    (int) SkType_##_type }
+
+#define SK_MEMBER_FUNCTION(_member, _type) \
+    {#_member, (size_t) (-1 - SK_FUNCTION(_member)), SkType_MemberFunction, \
+    (int) SkType_##_type }
+
+#define SK_MEMBER_PROPERTY(_member, _type) \
+    {#_member, (size_t) (-1 - SK_PROPERTY(_member)), SkType_MemberProperty, \
+    (int) SkType_##_type }
+
+#if SK_USE_CONDENSED_INFO == 0
+
+#define DECLARE_PRIVATE_MEMBER_INFO(_type) \
+public: \
+    static const SkMemberInfo fInfo[]; \
+    static const int fInfoCount; \
+    virtual const SkMemberInfo* getMember(int index); \
+    virtual const SkMemberInfo* getMember(const char name[]); \
+    typedef Sk##_type BASE_CLASS
+
+#define DECLARE_MEMBER_INFO(_type) \
+public: \
+    static const SkMemberInfo fInfo[]; \
+    static const int fInfoCount; \
+    virtual const SkMemberInfo* getMember(int index); \
+    virtual const SkMemberInfo* getMember(const char name[]); \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef Sk##_type BASE_CLASS
+
+#define DECLARE_DRAW_MEMBER_INFO(_type) \
+public: \
+    static const SkMemberInfo fInfo[]; \
+    static const int fInfoCount; \
+    virtual const SkMemberInfo* getMember(int index); \
+    virtual const SkMemberInfo* getMember(const char name[]); \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef SkDraw##_type BASE_CLASS
+
+#define DECLARE_DISPLAY_MEMBER_INFO(_type) \
+public: \
+    static const SkMemberInfo fInfo[]; \
+    static const int fInfoCount; \
+    virtual const SkMemberInfo* getMember(int index); \
+    virtual const SkMemberInfo* getMember(const char name[]); \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef SkDisplay##_type BASE_CLASS
+
+#define DECLARE_EMPTY_MEMBER_INFO(_type) \
+public: \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; }
+    
+#define DECLARE_EXTRAS_MEMBER_INFO(_type) \
+public: \
+    static const SkMemberInfo fInfo[]; \
+    static const int fInfoCount; \
+    virtual const SkMemberInfo* getMember(int index); \
+    virtual const SkMemberInfo* getMember(const char name[]); \
+    SkDisplayTypes fType; \
+    virtual SkDisplayTypes getType() const { return fType; } \
+    typedef _type BASE_CLASS
+
+#define DECLARE_NO_VIRTUALS_MEMBER_INFO(_type) \
+public: \
+    static const SkMemberInfo fInfo[]; \
+    static const int fInfoCount; \
+    typedef Sk##_type BASE_CLASS
+
+#define DEFINE_GET_MEMBER(_class) \
+    const SkMemberInfo* _class::getMember(int index) { \
+        const SkMemberInfo* result = SkMemberInfo::Find(fInfo, SK_ARRAY_COUNT(fInfo), &index); \
+        return result; \
+    } \
+    const SkMemberInfo* _class::getMember(const char name[]) { \
+        const SkMemberInfo* result = SkMemberInfo::Find(fInfo, SK_ARRAY_COUNT(fInfo), &name); \
+        return result; \
+    } \
+    const int _class::fInfoCount = SK_ARRAY_COUNT(fInfo)
+
+#define DEFINE_NO_VIRTUALS_GET_MEMBER(_class) \
+    const int _class::fInfoCount = SK_ARRAY_COUNT(fInfo)
+
+#else
+
+#define DECLARE_PRIVATE_MEMBER_INFO(_type) \
+public: \
+    typedef Sk##_type BASE_CLASS
+
+#define DECLARE_MEMBER_INFO(_type) \
+public: \
+    virtual const SkMemberInfo* getMember(int index) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \
+    virtual const SkMemberInfo* getMember(const char name[]) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &name); } \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef Sk##_type BASE_CLASS
+
+#define DECLARE_DRAW_MEMBER_INFO(_type) \
+public: \
+    virtual const SkMemberInfo* getMember(int index) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \
+    virtual const SkMemberInfo* getMember(const char name[]) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &name); } \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef SkDraw##_type BASE_CLASS
+
+#define DECLARE_DISPLAY_MEMBER_INFO(_type) \
+public: \
+    virtual const SkMemberInfo* getMember(int index) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \
+    virtual const SkMemberInfo* getMember(const char name[]) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &name); } \
+    virtual SkDisplayTypes getType() const { return SkType_##_type; } \
+    typedef SkDisplay##_type BASE_CLASS
+
+#define DECLARE_EXTRAS_MEMBER_INFO(_type) \
+public: \
+    virtual const SkMemberInfo* getMember(int index) { \
+        return SkDisplayType::GetMember(NULL, SkType_##_type, &index); } \
+    virtual const SkMemberInfo* getMember(const char name[]) { \
+        return SkDisplayType::GetMember(NULL, fType, &name); } \
+    SkDisplayTypes fType; \
+    virtual SkDisplayTypes getType() const { return fType; } \
+    typedef _type BASE_CLASS
+
+#define DECLARE_NO_VIRTUALS_MEMBER_INFO(_type) \
+public: \
+    typedef Sk##_type BASE_CLASS
+
+#define DEFINE_GET_MEMBER(_class)
+#define DEFINE_NO_VIRTUALS_GET_MEMBER(_class)
+
+#endif
+
+#endif // SkMemberInfo_DEFINED
+
diff --git a/src/animator/SkOpArray.cpp b/src/animator/SkOpArray.cpp
new file mode 100644
index 0000000..eb3fcec
--- /dev/null
+++ b/src/animator/SkOpArray.cpp
@@ -0,0 +1,16 @@
+#include "SkOpArray.h"
+
+SkOpArray::SkOpArray() : fType(SkOperand2::kNoType) {
+}
+
+SkOpArray::SkOpArray(SkOperand2::OpType type) : fType(type) {
+}
+
+bool SkOpArray::getIndex(int index, SkOperand2* operand) {
+	if (index >= count()) {
+		SkASSERT(0);
+		return false;
+	}
+	*operand = begin()[index];
+	return true;
+}
diff --git a/src/animator/SkOpArray.h b/src/animator/SkOpArray.h
new file mode 100644
index 0000000..5c511c0
--- /dev/null
+++ b/src/animator/SkOpArray.h
@@ -0,0 +1,22 @@
+#ifndef SkOpArray_DEFINED
+#define SkOpArray_DEFINED
+
+#include "SkOperand2.h"
+#include "SkTDArray_Experimental.h"
+
+typedef SkLongArray(SkOperand2) SkTDOperand2Array; 
+
+class SkOpArray : public SkTDOperand2Array {
+public:
+	SkOpArray();
+	SkOpArray(SkOperand2::OpType type);
+	bool getIndex(int index, SkOperand2* operand);
+	SkOperand2::OpType getType() { return fType; }
+	void setType(SkOperand2::OpType type) { 
+		fType = type;
+	}
+protected:
+	SkOperand2::OpType fType;
+};
+
+#endif // SkOpArray_DEFINED
diff --git a/src/animator/SkOperand.h b/src/animator/SkOperand.h
new file mode 100644
index 0000000..2c7e93b
--- /dev/null
+++ b/src/animator/SkOperand.h
@@ -0,0 +1,54 @@
+/* libs/graphics/animator/SkOperand.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 SkOperand_DEFINED
+#define SkOperand_DEFINED
+
+#include "SkDisplayType.h"
+
+class SkTypedArray;
+class SkDisplayable;
+class SkDrawable;
+class SkString;
+
+union SkOperand {
+//  SkOperand() {}
+//  SkOperand(SkScalar scalar) : fScalar(scalar) {}
+    SkTypedArray* fArray;
+    SkDisplayable* fDisplayable;
+    SkDrawable* fDrawable;
+    void* fObject;
+    int32_t fS32;
+    SkMSec fMSec;
+    SkScalar fScalar;
+    SkString* fString;
+};
+
+struct SkScriptValue {
+    SkOperand fOperand;
+    SkDisplayTypes fType;
+    SkTypedArray* getArray() { SkASSERT(fType == SkType_Array); return fOperand.fArray; }
+    SkDisplayable* getDisplayable() { SkASSERT(fType == SkType_Displayable); return fOperand.fDisplayable; }
+    SkDrawable* getDrawable() { SkASSERT(fType == SkType_Drawable); return fOperand.fDrawable; }
+    int32_t getS32(SkAnimateMaker* maker) { SkASSERT(fType == SkType_Int || fType == SkType_Boolean ||
+        SkDisplayType::IsEnum(maker, fType)); return fOperand.fS32; }
+    SkMSec getMSec() { SkASSERT(fType == SkType_MSec); return fOperand.fMSec; }
+    SkScalar getScalar() { SkASSERT(fType == SkType_Float); return fOperand.fScalar; }
+    SkString* getString() { SkASSERT(fType == SkType_String); return fOperand.fString; }
+};
+
+#endif // SkOperand_DEFINED
diff --git a/src/animator/SkOperand2.h b/src/animator/SkOperand2.h
new file mode 100644
index 0000000..f482e66
--- /dev/null
+++ b/src/animator/SkOperand2.h
@@ -0,0 +1,47 @@
+#ifndef SkOperand2_DEFINED
+#define SkOperand2_DEFINED
+
+#include "SkScalar.h"
+
+class SkOpArray;
+class SkString;
+
+union SkOperand2 {
+	enum OpType {
+		kNoType,
+		kS32 = 1,
+		kScalar = 2,
+		kString = 4,
+		kArray = 8,
+		kObject = 16
+	};
+	SkOpArray* fArray;
+	void* fObject;
+	size_t fReference;
+	int32_t fS32;
+	SkScalar fScalar;
+	SkString* fString;
+};
+
+struct SkScriptValue2 {
+	enum IsConstant {
+		kConstant,
+		kVariable
+	};
+	enum IsWritten {
+		kUnwritten,
+		kWritten
+	};
+	SkOperand2 fOperand;
+	SkOperand2::OpType fType : 8;
+	IsConstant fIsConstant : 8;
+	IsWritten fIsWritten : 8;
+	SkOpArray* getArray() { SkASSERT(fType == SkOperand2::kArray); return fOperand.fArray; }
+	void* getObject() { SkASSERT(fType == SkOperand2::kObject); return fOperand.fObject; }
+	int32_t getS32() { SkASSERT(fType == SkOperand2::kS32); return fOperand.fS32; }
+	SkScalar getScalar() { SkASSERT(fType == SkOperand2::kScalar); return fOperand.fScalar; }
+	SkString* getString() { SkASSERT(fType == SkOperand2::kString); return fOperand.fString; }
+        bool isConstant() const { return fIsConstant == kConstant; }
+};
+
+#endif // SkOperand2_DEFINED
diff --git a/src/animator/SkOperandInterpolator.h b/src/animator/SkOperandInterpolator.h
new file mode 100644
index 0000000..f155276
--- /dev/null
+++ b/src/animator/SkOperandInterpolator.h
@@ -0,0 +1,56 @@
+/* libs/graphics/animator/SkOperandInterpolator.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 SkOperandInterpolator_DEFINED
+#define SkOperandInterpolator_DEFINED
+
+#include "SkDisplayType.h"
+#include "SkInterpolator.h"
+#include "SkOperand.h"
+
+class SkOperandInterpolator : public SkInterpolatorBase {
+public:
+    SkOperandInterpolator();
+    SkOperandInterpolator(int elemCount, int frameCount, SkDisplayTypes type);
+    SkOperand* getValues() { return fValues; }
+    int getValuesCount() { return fFrameCount * fElemCount; }
+    void    reset(int elemCount, int frameCount, SkDisplayTypes type);
+
+    /** Add or replace a key frame, copying the values[] data into the interpolator.
+        @param index    The index of this frame (frames must be ordered by time)
+        @param time The millisecond time for this frame
+        @param values   The array of values [elemCount] for this frame. The data is copied
+                        into the interpolator.
+        @param blend    A positive scalar specifying how to blend between this and the next key frame.
+                        [0...1) is a cubic lag/log/lag blend (slow to change at the beginning and end)
+                        1 is a linear blend (default)
+                        (1...inf) is a cubic log/lag/log blend (fast to change at the beginning and end)
+    */
+    bool    setKeyFrame(int index, SkMSec time, const SkOperand values[], SkScalar blend = SK_Scalar1);
+    Result timeToValues(SkMSec time, SkOperand values[]) const;
+    SkDEBUGCODE(static void UnitTest();)
+private:
+    SkDisplayTypes fType;
+    SkOperand* fValues;     // pointer into fStorage
+#ifdef SK_DEBUG
+    SkOperand(* fValuesArray)[10];
+#endif
+    typedef SkInterpolatorBase INHERITED;
+};
+
+#endif // SkOperandInterpolator_DEFINED
+
diff --git a/src/animator/SkOperandIterpolator.cpp b/src/animator/SkOperandIterpolator.cpp
new file mode 100644
index 0000000..2bddd34
--- /dev/null
+++ b/src/animator/SkOperandIterpolator.cpp
@@ -0,0 +1,159 @@
+/* libs/graphics/animator/SkOperandIterpolator.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 "SkOperandInterpolator.h"
+#include "SkScript.h"
+
+SkOperandInterpolator::SkOperandInterpolator() {
+    INHERITED::reset(0, 0);
+    fType = SkType_Unknown;
+}
+
+SkOperandInterpolator::SkOperandInterpolator(int elemCount, int frameCount, 
+                                             SkDisplayTypes type)
+{
+    this->reset(elemCount, frameCount, type);
+}
+
+void SkOperandInterpolator::reset(int elemCount, int frameCount, SkDisplayTypes type)
+{
+//  SkASSERT(type == SkType_String || type == SkType_Float || type == SkType_Int ||
+//      type == SkType_Displayable || type == SkType_Drawable);
+    INHERITED::reset(elemCount, frameCount);
+    fType = type;
+    fStorage = sk_malloc_throw((sizeof(SkOperand) * elemCount + sizeof(SkTimeCode)) * frameCount);
+    fTimes = (SkTimeCode*) fStorage;
+    fValues = (SkOperand*) ((char*) fStorage + sizeof(SkTimeCode) * frameCount);
+#ifdef SK_DEBUG
+    fTimesArray = (SkTimeCode(*)[10]) fTimes;
+    fValuesArray = (SkOperand(*)[10]) fValues;
+#endif
+}
+
+bool SkOperandInterpolator::setKeyFrame(int index, SkMSec time, const SkOperand values[], SkScalar blend)
+{
+    SkASSERT(values != NULL);
+    blend = SkScalarPin(blend, 0, SK_Scalar1);
+
+    bool success = ~index == SkTSearch<SkMSec>(&fTimes->fTime, index, time, sizeof(SkTimeCode));
+    SkASSERT(success);
+    if (success) {
+        SkTimeCode* timeCode = &fTimes[index];
+        timeCode->fTime = time;
+        timeCode->fBlend[0] = SK_Scalar1 - blend;
+        timeCode->fBlend[1] = 0;
+        timeCode->fBlend[2] = 0;
+        timeCode->fBlend[3] = SK_Scalar1 - blend;
+        SkOperand* dst = &fValues[fElemCount * index];
+        memcpy(dst, values, fElemCount * sizeof(SkOperand));
+    }
+    return success;
+}
+
+SkInterpolatorBase::Result SkOperandInterpolator::timeToValues(SkMSec time, SkOperand values[]) const
+{
+    SkScalar T;
+    int index;
+    SkBool exact;
+    Result result = timeToT(time, &T, &index, &exact);
+    if (values)
+    {
+        const SkOperand* nextSrc = &fValues[index * fElemCount];
+
+        if (exact)
+            memcpy(values, nextSrc, fElemCount * sizeof(SkScalar));
+        else
+        {
+            SkASSERT(index > 0);
+
+            const SkOperand* prevSrc = nextSrc - fElemCount;
+
+            if (fType == SkType_Float || fType == SkType_3D_Point) {
+                for (int i = fElemCount - 1; i >= 0; --i)
+                    values[i].fScalar = SkScalarInterp(prevSrc[i].fScalar, nextSrc[i].fScalar, T);
+            } else if (fType == SkType_Int || fType == SkType_MSec) {
+                for (int i = fElemCount - 1; i >= 0; --i) {
+                    int32_t a = prevSrc[i].fS32;
+                    int32_t b = nextSrc[i].fS32;
+                    values[i].fS32 = a + SkScalarRound((b - a) * T);
+                }
+            } else
+                memcpy(values, prevSrc, sizeof(SkOperand) * fElemCount);
+        }
+    }
+    return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#ifdef SK_SUPPORT_UNITTEST
+    static SkOperand* iset(SkOperand array[3], int a, int b, int c)
+    {
+        array[0].fScalar = SkIntToScalar(a);
+        array[1].fScalar = SkIntToScalar(b);
+        array[2].fScalar = SkIntToScalar(c);
+        return array;
+    }
+#endif
+
+void SkOperandInterpolator::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    SkOperandInterpolator   inter(3, 2, SkType_Float);
+    SkOperand       v1[3], v2[3], v[3], vv[3];
+    Result          result;
+
+    inter.setKeyFrame(0, 100, iset(v1, 10, 20, 30), 0);
+    inter.setKeyFrame(1, 200, iset(v2, 110, 220, 330));
+
+    result = inter.timeToValues(0, v);
+    SkASSERT(result == kFreezeStart_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(99, v);
+    SkASSERT(result == kFreezeStart_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(100, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(200, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, v2, sizeof(v)) == 0);
+
+    result = inter.timeToValues(201, v);
+    SkASSERT(result == kFreezeEnd_Result);
+    SkASSERT(memcmp(v, v2, sizeof(v)) == 0);
+
+    result = inter.timeToValues(150, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, iset(vv, 60, 120, 180), sizeof(v)) == 0);
+
+    result = inter.timeToValues(125, v);
+    SkASSERT(result == kNormal_Result);
+    result = inter.timeToValues(175, v);
+    SkASSERT(result == kNormal_Result);
+#endif
+}
+
+#endif
+
+
diff --git a/src/animator/SkPaintParts.cpp b/src/animator/SkPaintParts.cpp
new file mode 100644
index 0000000..48799c6
--- /dev/null
+++ b/src/animator/SkPaintParts.cpp
@@ -0,0 +1,111 @@
+/* libs/graphics/animator/SkPaintParts.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 "SkPaintParts.h"
+#include "SkDrawPaint.h"
+#ifdef SK_DUMP_ENABLED
+#include "SkDisplayList.h"
+#include "SkDump.h"
+#endif
+
+SkPaintPart::SkPaintPart() : fPaint(NULL) {
+}
+
+SkDisplayable* SkPaintPart::getParent() const {
+    return fPaint;
+}
+
+bool SkPaintPart::setParent(SkDisplayable* parent) {
+    SkASSERT(parent != NULL);
+    if (parent->isPaint() == false)
+        return true;
+    fPaint = (SkDrawPaint*) parent;
+    return false;
+}
+
+
+// SkDrawMaskFilter
+bool SkDrawMaskFilter::add() {
+    if (fPaint->maskFilter != (SkDrawMaskFilter*) -1)
+        return true;
+    fPaint->maskFilter = this;
+    fPaint->fOwnsMaskFilter = true;
+    return false;
+}
+
+SkMaskFilter* SkDrawMaskFilter::getMaskFilter() {
+    return NULL;
+}
+
+
+// SkDrawPathEffect
+bool SkDrawPathEffect::add() {
+    if (fPaint->isPaint()) {
+        if (fPaint->pathEffect != (SkDrawPathEffect*) -1)
+            return true;
+        fPaint->pathEffect = this;
+        fPaint->fOwnsPathEffect = true;
+        return false;
+    }
+    fPaint->add(*(SkAnimateMaker*) NULL, this);
+    return false;
+}
+
+SkPathEffect* SkDrawPathEffect::getPathEffect() {
+    return NULL;
+}
+
+
+// SkDrawShader
+SkShader* SkDrawShader::getShader() {
+    return NULL;
+}
+
+
+// Typeface
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawTypeface::fInfo[] = {
+    SK_MEMBER(fontName, String),
+    SK_MEMBER(style, FontStyle)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawTypeface);
+
+SkDrawTypeface::SkDrawTypeface() : style (SkTypeface::kNormal){
+}
+
+bool SkDrawTypeface::add() {
+    if (fPaint->typeface != (SkDrawTypeface*) -1)
+        return true;
+    fPaint->typeface = this;
+    fPaint->fOwnsTypeface = true;
+    return false;
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawTypeface::dump(SkAnimateMaker* maker) {
+    SkDebugf("%*s<typeface fontName=\"%s\" ", SkDisplayList::fIndent, "", fontName.c_str());
+    SkString string;
+    SkDump::GetEnumString(SkType_FontStyle, style, &string);
+    SkDebugf("style=\"%s\" />\n", string.c_str());
+}
+#endif
+
+
diff --git a/src/animator/SkPaintParts.h b/src/animator/SkPaintParts.h
new file mode 100644
index 0000000..a8bb8bd
--- /dev/null
+++ b/src/animator/SkPaintParts.h
@@ -0,0 +1,83 @@
+/* libs/graphics/animator/SkPaintParts.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 SkPaintParts_DEFINED
+#define SkPaintParts_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+class SkDrawPaint;
+class SkDrawMatrix;
+
+class SkPaintPart : public SkDisplayable {
+public:
+    SkPaintPart();
+    virtual bool add() = 0;
+    virtual SkDisplayable* getParent() const;
+    virtual bool setParent(SkDisplayable* parent);
+#ifdef SK_DEBUG
+    virtual bool isPaintPart() const { return true; }
+#endif
+protected:
+    SkDrawPaint* fPaint;
+};
+
+class SkDrawMaskFilter : public SkPaintPart {
+    DECLARE_EMPTY_MEMBER_INFO(MaskFilter);
+    virtual SkMaskFilter* getMaskFilter();
+protected:
+    virtual bool add();
+};
+
+class SkDrawPathEffect : public SkPaintPart {
+    DECLARE_EMPTY_MEMBER_INFO(PathEffect);
+    virtual SkPathEffect* getPathEffect();
+protected:
+    virtual bool add();
+};
+
+class SkDrawShader : public SkPaintPart {
+    DECLARE_DRAW_MEMBER_INFO(Shader);
+    SkDrawShader();
+    virtual SkShader* getShader();
+protected:
+    virtual bool add();
+    void addPostlude(SkShader* shader);
+    SkDrawMatrix* matrix;
+    int /*SkShader::TileMode*/ tileMode;
+};
+
+class SkDrawTypeface  : public SkPaintPart {
+    DECLARE_DRAW_MEMBER_INFO(Typeface);
+    SkDrawTypeface();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker *);
+#endif
+    SkTypeface* getTypeface() {
+        return SkTypeface::Create(fontName.c_str(), style); }
+protected:
+    virtual bool add();
+    SkString fontName;
+    SkTypeface::Style style;
+};
+
+#endif // SkPaintParts_DEFINED
diff --git a/src/animator/SkPathParts.cpp b/src/animator/SkPathParts.cpp
new file mode 100644
index 0000000..46d2232
--- /dev/null
+++ b/src/animator/SkPathParts.cpp
@@ -0,0 +1,328 @@
+/* libs/graphics/animator/SkPathParts.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 "SkPathParts.h"
+#include "SkAnimateMaker.h"
+#include "SkDrawMatrix.h"
+#include "SkDrawRectangle.h"
+#include "SkDrawPath.h"
+
+SkPathPart::SkPathPart() : fPath(NULL) {
+}
+
+void SkPathPart::dirty() { 
+    fPath->dirty(); 
+}
+
+SkDisplayable* SkPathPart::getParent() const {
+    return fPath;
+}
+
+bool SkPathPart::setParent(SkDisplayable* parent) {
+    SkASSERT(parent != NULL);
+    if (parent->isPath() == false)
+        return true;
+    fPath = (SkDrawPath*) parent;
+    return false;
+}
+
+// MoveTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkMoveTo::fInfo[] = {
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkMoveTo);
+
+SkMoveTo::SkMoveTo() : x(0), y(0) {
+}
+
+bool SkMoveTo::add() {
+    fPath->fPath.moveTo(x, y);
+    return false;
+}
+
+
+// RMoveTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRMoveTo::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRMoveTo);
+
+bool SkRMoveTo::add() {
+    fPath->fPath.rMoveTo(x, y);
+    return false;
+}
+
+
+// LineTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkLineTo::fInfo[] = {
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkLineTo);
+
+SkLineTo::SkLineTo() : x(0), y(0) {
+}
+
+bool SkLineTo::add() {
+    fPath->fPath.lineTo(x, y);  
+    return false;
+}
+
+
+// RLineTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRLineTo::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRLineTo);
+
+bool SkRLineTo::add() {
+    fPath->fPath.rLineTo(x, y); 
+    return false;
+}
+
+
+// QuadTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkQuadTo::fInfo[] = {
+    SK_MEMBER(x1, Float),
+    SK_MEMBER(x2, Float),
+    SK_MEMBER(y1, Float),
+    SK_MEMBER(y2, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkQuadTo);
+
+SkQuadTo::SkQuadTo() : x1(0), y1(0), x2(0), y2(0) {
+}
+
+bool SkQuadTo::add() {
+    fPath->fPath.quadTo(x1, y1, x2, y2);
+    return false;
+}
+
+
+// RQuadTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRQuadTo::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRQuadTo);
+
+bool SkRQuadTo::add() {
+    fPath->fPath.rQuadTo(x1, y1, x2, y2);
+    return false;
+}
+
+
+// CubicTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkCubicTo::fInfo[] = {
+    SK_MEMBER(x1, Float),
+    SK_MEMBER(x2, Float),
+    SK_MEMBER(x3, Float),
+    SK_MEMBER(y1, Float),
+    SK_MEMBER(y2, Float),
+    SK_MEMBER(y3, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkCubicTo);
+
+SkCubicTo::SkCubicTo() : x1(0), y1(0), x2(0), y2(0), x3(0), y3(0) {
+}
+
+bool SkCubicTo::add() {
+    fPath->fPath.cubicTo(x1, y1, x2, y2, x3, y3);
+    return false;
+}
+
+
+// RCubicTo
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkRCubicTo::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkRCubicTo);
+
+bool SkRCubicTo::add() {
+    fPath->fPath.rCubicTo(x1, y1, x2, y2, x3, y3);
+    return false;
+}
+
+
+// SkClose
+bool SkClose::add() {
+    fPath->fPath.close();
+    return false;
+}
+
+
+// SkAddGeom
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddGeom::fInfo[] = {
+    SK_MEMBER(direction, PathDirection)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddGeom);
+
+SkAddGeom::SkAddGeom() : direction(SkPath::kCCW_Direction) {
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddRect::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER_ALIAS(bottom, fRect.fBottom, Float),
+    SK_MEMBER_ALIAS(left, fRect.fLeft, Float),
+    SK_MEMBER_ALIAS(right, fRect.fRight, Float),
+    SK_MEMBER_ALIAS(top, fRect.fTop, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddRect);
+
+SkAddRect::SkAddRect() { 
+    fRect.setEmpty(); 
+}
+
+bool SkAddRect::add() {
+    fPath->fPath.addRect(fRect, (SkPath::Direction) direction);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddOval::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddOval);
+
+bool SkAddOval::add() {
+    fPath->fPath.addOval(fRect,  (SkPath::Direction) direction);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddCircle::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(radius, Float),
+    SK_MEMBER(x, Float),
+    SK_MEMBER(y, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddCircle);
+
+SkAddCircle::SkAddCircle() : radius(0), x(0), y(0) {
+}
+
+bool SkAddCircle::add() {
+    fPath->fPath.addCircle(x, y, radius,  (SkPath::Direction) direction);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddRoundRect::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(rx, Float),
+    SK_MEMBER(ry, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddRoundRect);
+
+SkAddRoundRect::SkAddRoundRect() : rx(0), ry(0) {
+}
+
+bool SkAddRoundRect::add() {
+    fPath->fPath.addRoundRect(fRect, rx, ry,  (SkPath::Direction) direction);
+    return false;
+}
+
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkAddPath::fInfo[] = {
+    SK_MEMBER(matrix, Matrix),
+    SK_MEMBER(path, Path)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkAddPath);
+
+SkAddPath::SkAddPath() : matrix(NULL), path(NULL) {
+}
+
+bool SkAddPath::add() {
+    SkASSERT (path != NULL); 
+    if (matrix)
+        fPath->fPath.addPath(path->fPath, matrix->getMatrix());
+    else
+        fPath->fPath.addPath(path->fPath);
+    return false;
+}
+
+
diff --git a/src/animator/SkPathParts.h b/src/animator/SkPathParts.h
new file mode 100644
index 0000000..72e4185
--- /dev/null
+++ b/src/animator/SkPathParts.h
@@ -0,0 +1,173 @@
+/* libs/graphics/animator/SkPathParts.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 SkPathParts_DEFINED
+#define SkPathParts_DEFINED
+
+#include "SkDisplayable.h"
+#include "SkMemberInfo.h"
+#include "SkPath.h"
+
+class SkDrawPath;
+class SkDrawMatrix;
+
+class SkPathPart : public SkDisplayable {
+public:
+    SkPathPart();
+    virtual bool add() = 0;
+    virtual void dirty();
+    virtual SkDisplayable* getParent() const;
+    virtual bool setParent(SkDisplayable* parent);
+#ifdef SK_DEBUG
+    virtual bool isPathPart() const { return true; }
+#endif
+protected:
+    SkDrawPath* fPath;
+};
+
+class SkMoveTo : public SkPathPart {
+    DECLARE_MEMBER_INFO(MoveTo);
+    SkMoveTo();
+    virtual bool add();
+protected:
+    SkScalar x;
+    SkScalar y;
+};
+
+class SkRMoveTo : public SkMoveTo {
+    DECLARE_MEMBER_INFO(RMoveTo);
+    virtual bool add();
+private:
+    typedef SkMoveTo INHERITED;
+};
+
+class SkLineTo : public SkPathPart {
+    DECLARE_MEMBER_INFO(LineTo);
+    SkLineTo();
+    virtual bool add();
+protected:
+    SkScalar x;
+    SkScalar y;
+};
+
+class SkRLineTo : public SkLineTo {
+    DECLARE_MEMBER_INFO(RLineTo);
+    virtual bool add();
+private:
+    typedef SkLineTo INHERITED;
+};
+
+class SkQuadTo : public SkPathPart {
+    DECLARE_MEMBER_INFO(QuadTo);
+    SkQuadTo();
+    virtual bool add();
+protected:
+    SkScalar x1;
+    SkScalar y1;
+    SkScalar x2;
+    SkScalar y2;
+};
+
+class SkRQuadTo : public SkQuadTo {
+    DECLARE_MEMBER_INFO(RQuadTo);
+    virtual bool add();
+private:
+    typedef SkQuadTo INHERITED;
+};
+
+class SkCubicTo : public SkPathPart {
+    DECLARE_MEMBER_INFO(CubicTo);
+    SkCubicTo();
+    virtual bool add();
+protected:
+    SkScalar x1;
+    SkScalar y1;
+    SkScalar x2;
+    SkScalar y2;
+    SkScalar x3;
+    SkScalar y3;
+};
+
+class SkRCubicTo : public SkCubicTo {
+    DECLARE_MEMBER_INFO(RCubicTo);
+    virtual bool add();
+private:
+    typedef SkCubicTo INHERITED;
+};
+
+class SkClose : public SkPathPart {
+    DECLARE_EMPTY_MEMBER_INFO(Close);
+    virtual bool add();
+};
+
+class SkAddGeom : public SkPathPart {
+    DECLARE_PRIVATE_MEMBER_INFO(AddGeom);
+    SkAddGeom();
+protected:
+    int /*SkPath::Direction*/ direction;
+};
+
+class SkAddRect : public SkAddGeom {
+    DECLARE_MEMBER_INFO(AddRect);
+    SkAddRect();
+    virtual bool add();
+protected:
+    SkRect fRect;
+private:
+    typedef SkAddGeom INHERITED;
+};
+
+class SkAddOval : public SkAddRect {
+    DECLARE_MEMBER_INFO(AddOval);
+    virtual bool add();
+private:
+    typedef SkAddRect INHERITED;
+};
+
+class SkAddCircle : public SkAddGeom {
+    DECLARE_MEMBER_INFO(AddCircle);
+    SkAddCircle();
+    virtual bool add();
+private:
+    SkScalar radius;
+    SkScalar x;
+    SkScalar y;
+    typedef SkAddGeom INHERITED;
+};
+
+class SkAddRoundRect : public SkAddRect {
+    DECLARE_MEMBER_INFO(AddRoundRect);
+    SkAddRoundRect();
+    virtual bool add();
+private:
+    SkScalar rx;
+    SkScalar ry;
+    typedef SkAddRect INHERITED;
+};
+
+class SkAddPath : public SkPathPart {
+    DECLARE_MEMBER_INFO(AddPath);
+    SkAddPath();
+    virtual bool add();
+private:
+    typedef SkPathPart INHERITED;
+    SkDrawMatrix* matrix;
+    SkDrawPath* path;
+};
+
+#endif // SkPathParts_DEFINED
+
diff --git a/src/animator/SkPostParts.cpp b/src/animator/SkPostParts.cpp
new file mode 100644
index 0000000..254b17d
--- /dev/null
+++ b/src/animator/SkPostParts.cpp
@@ -0,0 +1,65 @@
+/* libs/graphics/animator/SkPostParts.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 "SkPostParts.h"
+#include "SkDisplayPost.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkData::fInfo[] = {
+    SK_MEMBER_INHERITED
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkData);
+
+SkData::SkData() : fParent(NULL) {}
+
+bool SkData::add() {
+    SkASSERT(name.size() > 0);
+    const char* dataName = name.c_str();
+    if (fInt != (int) SK_NaN32)
+        fParent->fEvent.setS32(dataName, fInt);
+    else if (SkScalarIsNaN(fFloat) == false)
+        fParent->fEvent.setScalar(dataName, fFloat);
+    else if (string.size() > 0) 
+        fParent->fEvent.setString(dataName, string);
+//  else
+//      SkASSERT(0);
+    return false;
+}
+
+void SkData::dirty() {
+    fParent->dirty();
+}
+
+SkDisplayable* SkData::getParent() const {
+    return fParent;
+}
+
+bool SkData::setParent(SkDisplayable* displayable) {
+    if (displayable->isPost() == false)
+        return true;
+    fParent = (SkPost*) displayable;
+    return false;
+}
+
+void SkData::onEndElement(SkAnimateMaker&) {
+    add();
+}
+
diff --git a/src/animator/SkPostParts.h b/src/animator/SkPostParts.h
new file mode 100644
index 0000000..ef41fe5
--- /dev/null
+++ b/src/animator/SkPostParts.h
@@ -0,0 +1,39 @@
+/* libs/graphics/animator/SkPostParts.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 SkPostParts_DEFINED
+#define SkPostParts_DEFINED
+
+#include "SkDisplayInput.h"
+
+class SkPost;
+
+class SkData: public SkInput {
+    DECLARE_MEMBER_INFO(Data);
+    SkData();
+    bool add();
+    virtual void dirty();
+    virtual SkDisplayable* getParent() const;
+    virtual void onEndElement(SkAnimateMaker& );
+    virtual bool setParent(SkDisplayable* );
+protected:
+    SkPost* fParent;
+    typedef SkInput INHERITED;
+    friend class SkPost;
+};
+
+#endif // SkPostParts_DEFINED
diff --git a/src/animator/SkSVGPath.cpp b/src/animator/SkSVGPath.cpp
new file mode 100644
index 0000000..86eeee8
--- /dev/null
+++ b/src/animator/SkSVGPath.cpp
@@ -0,0 +1,243 @@
+/* libs/graphics/animator/SkSVGPath.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 <ctype.h>
+#include "SkDrawPath.h"
+#include "SkParse.h"
+#include "SkPoint.h"
+#include "SkUtils.h"
+#define QUADRATIC_APPROXIMATION 1
+
+#if QUADRATIC_APPROXIMATION
+////////////////////////////////////////////////////////////////////////////////////
+//functions to approximate a cubic using two quadratics
+
+//      midPt sets the first argument to be the midpoint of the other two
+//      it is used by quadApprox
+static inline void midPt(SkPoint& dest,const SkPoint& a,const SkPoint& b)
+{
+    dest.set(SkScalarAve(a.fX, b.fX),SkScalarAve(a.fY, b.fY));
+}
+//      quadApprox - makes an approximation, which we hope is faster
+static void quadApprox(SkPath &fPath, const SkPoint &p0, const SkPoint &p1, const SkPoint &p2)
+{
+    //divide the cubic up into two cubics, then convert them into quadratics
+    //define our points
+    SkPoint c,j,k,l,m,n,o,p,q, mid;
+    fPath.getLastPt(&c);
+    midPt(j, p0, c);
+    midPt(k, p0, p1);
+    midPt(l, p1, p2);
+    midPt(o, j, k);
+    midPt(p, k, l);
+    midPt(q, o, p);
+    //compute the first half
+    m.set(SkScalarHalf(3*j.fX - c.fX), SkScalarHalf(3*j.fY - c.fY));
+    n.set(SkScalarHalf(3*o.fX -q.fX), SkScalarHalf(3*o.fY - q.fY));
+    midPt(mid,m,n);
+    fPath.quadTo(mid,q);
+    c = q;
+    //compute the second half
+    m.set(SkScalarHalf(3*p.fX - c.fX), SkScalarHalf(3*p.fY - c.fY));
+    n.set(SkScalarHalf(3*l.fX -p2.fX),SkScalarHalf(3*l.fY -p2.fY));
+    midPt(mid,m,n);
+    fPath.quadTo(mid,p2);
+}
+#endif
+
+
+static inline bool is_between(int c, int min, int max)
+{
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_ws(int c)
+{
+    return is_between(c, 1, 32);
+}
+
+static inline bool is_digit(int c)
+{
+    return is_between(c, '0', '9');
+}
+
+static inline bool is_sep(int c)
+{
+    return is_ws(c) || c == ',';
+}
+
+static const char* skip_ws(const char str[])
+{
+    SkASSERT(str);
+    while (is_ws(*str))
+        str++;
+    return str;
+}
+
+static const char* skip_sep(const char str[])
+{
+    SkASSERT(str);
+    while (is_sep(*str))
+        str++;
+    return str;
+}
+
+static const char* find_points(const char str[], SkPoint value[], int count,
+     bool isRelative, SkPoint* relative)
+{
+    str = SkParse::FindScalars(str, &value[0].fX, count * 2);
+    if (isRelative) {
+        for (int index = 0; index < count; index++) {
+            value[index].fX += relative->fX;
+            value[index].fY += relative->fY;
+        }
+    }
+    return str;
+}
+
+static const char* find_scalar(const char str[], SkScalar* value, 
+    bool isRelative, SkScalar relative)
+{
+    str = SkParse::FindScalar(str, value);
+    if (isRelative)
+        *value += relative;
+    return str;
+}
+
+void SkDrawPath::parseSVG() {
+    fPath.reset();
+    const char* data = d.c_str();
+    SkPoint f = {0, 0};
+    SkPoint c = {0, 0};
+    SkPoint lastc = {0, 0};
+    SkPoint points[3];
+    char op = '\0';
+    char previousOp = '\0';
+    bool relative = false;
+    do {
+        data = skip_ws(data);
+        if (data[0] == '\0')
+            break;
+        char ch = data[0];
+        if (is_digit(ch) || ch == '-' || ch == '+') {
+            if (op == '\0')
+                return;
+        }
+        else {
+            op = ch;
+            relative = false;
+            if (islower(op)) {
+                op = (char) toupper(op);
+                relative = true;
+            }
+            data++;
+            data = skip_sep(data);
+        }
+        switch (op) {
+            case 'M':
+                data = find_points(data, points, 1, relative, &c);
+                fPath.moveTo(points[0]);
+                op = 'L';
+                c = points[0];
+                break;
+            case 'L': 
+                data = find_points(data, points, 1, relative, &c);
+                fPath.lineTo(points[0]);
+                c = points[0];
+                break;
+            case 'H': {
+                SkScalar x;
+                data = find_scalar(data, &x, relative, c.fX);
+                fPath.lineTo(x, c.fY);
+                c.fX = x;
+            }
+                break;
+            case 'V': {
+                SkScalar y;
+                data = find_scalar(data, &y, relative, c.fY);
+                fPath.lineTo(c.fX, y);
+                c.fY = y;
+            }
+                break;
+            case 'C': 
+                data = find_points(data, points, 3, relative, &c);
+                goto cubicCommon;
+            case 'S': 
+                data = find_points(data, &points[1], 2, relative, &c);
+                points[0] = c;
+                if (previousOp == 'C' || previousOp == 'S') {
+                    points[0].fX -= lastc.fX - c.fX;
+                    points[0].fY -= lastc.fY - c.fY;
+                }
+            cubicCommon:
+    //          if (data[0] == '\0')
+    //              return;
+#if QUADRATIC_APPROXIMATION
+                    quadApprox(fPath, points[0], points[1], points[2]);
+#else   //this way just does a boring, slow old cubic
+                    fPath.cubicTo(points[0], points[1], points[2]);
+#endif
+        //if we are using the quadApprox, lastc is what it would have been if we had used
+        //cubicTo
+                    lastc = points[1];
+                    c = points[2];
+                break;
+            case 'Q':  // Quadratic Bezier Curve
+                data = find_points(data, points, 2, relative, &c);
+                goto quadraticCommon;
+            case 'T':
+                data = find_points(data, &points[1], 1, relative, &c);
+                points[0] = points[1];
+                if (previousOp == 'Q' || previousOp == 'T') {
+                    points[0].fX = c.fX * 2 - lastc.fX;
+                    points[0].fY = c.fY * 2 - lastc.fY;
+                }
+            quadraticCommon:
+                fPath.quadTo(points[0], points[1]);
+                lastc = points[0];
+                c = points[1];
+                break;
+            case 'Z':
+                fPath.close();
+#if 0   // !!! still a bug?
+                if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) {
+                    c.fX -= SkScalar.Epsilon;   // !!! enough?
+                    fPath.moveTo(c);
+                    fPath.lineTo(f);
+                    fPath.close();
+                }
+#endif
+                c = f;
+                op = '\0';
+                break;
+            case '~': {
+                SkPoint args[2];
+                data = find_points(data, args, 2, false, NULL);
+                fPath.moveTo(args[0].fX, args[0].fY);
+                fPath.lineTo(args[1].fX, args[1].fY);
+            }
+                break;
+            default:
+                SkASSERT(0);
+                return;
+        }
+        if (previousOp == 0)
+            f = c;
+        previousOp = op;
+    } while (data[0] > 0);
+}
+
diff --git a/src/animator/SkScript.cpp b/src/animator/SkScript.cpp
new file mode 100644
index 0000000..3b67d7b
--- /dev/null
+++ b/src/animator/SkScript.cpp
@@ -0,0 +1,1918 @@
+/* libs/graphics/animator/SkScript.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 "SkScript.h"
+#include "SkMath.h"
+#include "SkParse.h"
+#include "SkString.h"
+#include "SkTypedArray.h"
+
+/* things to do
+    ? re-enable support for struct literals (e.g., for initializing points or rects)
+        {x:1, y:2}
+    ? use standard XML / script notation like document.getElementById("canvas");  
+    finish support for typed arrays
+        ? allow indexing arrays by string
+            this could map to the 'name' attribute of a given child of an array
+        ? allow multiple types in the array
+    remove SkDisplayType.h  // from SkOperand.h
+    merge type and operand arrays into scriptvalue array
+*/
+
+#ifdef SK_DEBUG
+static const char* errorStrings[] = {
+        "array index of out bounds", // kArrayIndexOutOfBounds
+        "could not find reference id", // kCouldNotFindReferencedID
+        "dot operator expects object", // kDotOperatorExpectsObject
+        "error in array index", // kErrorInArrrayIndex
+        "error in function parameters", // kErrorInFunctionParameters
+        "expected array", // kExpectedArray
+        "expected boolean expression", // kExpectedBooleanExpression
+        "expected field name", // kExpectedFieldName
+        "expected hex", // kExpectedHex
+        "expected int for condition operator", // kExpectedIntForConditionOperator
+        "expected number", // kExpectedNumber
+        "expected number for array index", // kExpectedNumberForArrayIndex
+        "expected operator", // kExpectedOperator
+        "expected token", // kExpectedToken
+        "expected token before dot operator", // kExpectedTokenBeforeDotOperator
+        "expected value", // kExpectedValue
+        "handle member failed", // kHandleMemberFailed
+        "handle member function failed", // kHandleMemberFunctionFailed
+        "handle unbox failed", // kHandleUnboxFailed
+        "index out of range", // kIndexOutOfRange
+        "mismatched array brace", // kMismatchedArrayBrace
+        "mismatched brackets", // kMismatchedBrackets
+        "no function handler found", // kNoFunctionHandlerFound
+        "premature end", // kPrematureEnd
+        "too many parameters", // kTooManyParameters
+        "type conversion failed", // kTypeConversionFailed
+        "unterminated string" // kUnterminatedString
+};
+#endif
+
+const SkScriptEngine::SkOperatorAttributes SkScriptEngine::gOpAttributes[] = {
+    { kNoType, kNoType, kNoBias }, //   kUnassigned,
+    { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsString }, // kAdd
+    // kAddInt = kAdd,
+    { kNoType, kNoType, kNoBias },  // kAddScalar,
+    { kNoType, kNoType, kNoBias },  // kAddString,
+    { kNoType, kNoType, kNoBias },  // kArrayOp,
+    { kInt, kInt, kNoBias }, // kBitAnd
+    { kNoType, kInt, kNoBias }, // kBitNot
+    { kInt, kInt, kNoBias }, // kBitOr
+    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kDivide
+    // kDivideInt = kDivide
+    { kNoType, kNoType, kNoBias },  // kDivideScalar
+    { kNoType, kNoType, kNoBias },  // kElse
+    { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kEqual
+    // kEqualInt = kEqual
+    { kNoType, kNoType, kNoBias },  // kEqualScalar
+    { kNoType, kNoType, kNoBias },  // kEqualString
+    { kInt, kNoType, kNoBias },     // kFlipOps
+    { SkOpType(kInt | kScalar | kString), SkOpType(kInt | kScalar | kString), kTowardsNumber }, // kGreaterEqual
+    // kGreaterEqualInt = kGreaterEqual
+    { kNoType, kNoType, kNoBias },  // kGreaterEqualScalar
+    { kNoType, kNoType, kNoBias },  // kGreaterEqualString
+    { kNoType, kNoType, kNoBias },  // kIf
+    { kNoType, kInt, kNoBias }, // kLogicalAnd  (really, ToBool)
+    { kNoType, kInt, kNoBias }, // kLogicalNot
+    { kInt, kInt, kNoBias }, // kLogicalOr
+    { kNoType, SkOpType(kInt | kScalar), kNoBias }, // kMinus
+    // kMinusInt = kMinus
+    { kNoType, kNoType, kNoBias },  // kMinusScalar
+    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kModulo
+    // kModuloInt = kModulo
+    { kNoType, kNoType, kNoBias },  // kModuloScalar
+    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kMultiply
+    // kMultiplyInt = kMultiply
+    { kNoType, kNoType, kNoBias },  // kMultiplyScalar
+    { kNoType, kNoType, kNoBias },  // kParen
+    { kInt, kInt, kNoBias }, // kShiftLeft
+    { kInt, kInt, kNoBias }, // kShiftRight
+    { SkOpType(kInt | kScalar), SkOpType(kInt | kScalar), kNoBias }, // kSubtract
+    // kSubtractInt = kSubtract
+    { kNoType, kNoType, kNoBias },  // kSubtractScalar
+    { kInt, kInt, kNoBias } // kXor
+};
+
+// Note that the real precedence for () [] is '2'
+// but here, precedence means 'while an equal or smaller precedence than the current operator
+// is on the stack, process it. This allows 3+5*2 to defer the add until after the multiply
+// is preformed, since the add precedence is not smaller than multiply.
+// But, (3*4 does not process the '(', since brackets are greater than all other precedences
+#define kBracketPrecedence 16
+#define kIfElsePrecedence 15
+
+const signed char SkScriptEngine::gPrecedence[] = {
+        -1, //  kUnassigned,
+        6, // kAdd,
+        // kAddInt = kAdd,
+        6, // kAddScalar,
+        6, // kAddString,   // string concat
+        kBracketPrecedence, // kArrayOp,
+        10, // kBitAnd,
+        4, // kBitNot,
+        12, // kBitOr,
+        5, // kDivide,
+        // kDivideInt = kDivide,
+        5, // kDivideScalar,
+        kIfElsePrecedence, // kElse,
+        9, // kEqual,
+        // kEqualInt = kEqual,
+        9, // kEqualScalar,
+        9, // kEqualString,
+        -1, // kFlipOps,
+        8, // kGreaterEqual,
+        // kGreaterEqualInt = kGreaterEqual,
+        8, // kGreaterEqualScalar,
+        8, // kGreaterEqualString,
+        kIfElsePrecedence, // kIf,
+        13, // kLogicalAnd,
+        4, // kLogicalNot,
+        14, // kLogicalOr,
+        4, // kMinus,
+        // kMinusInt = kMinus,
+        4, // kMinusScalar,
+        5, // kModulo,
+        // kModuloInt = kModulo,
+        5, // kModuloScalar,
+        5, // kMultiply,
+        // kMultiplyInt = kMultiply,
+        5, // kMultiplyScalar,
+        kBracketPrecedence, // kParen,
+        7, // kShiftLeft,
+        7, // kShiftRight,  // signed
+        6, // kSubtract,
+        // kSubtractInt = kSubtract,
+        6, // kSubtractScalar,
+        11, // kXor
+};
+
+static inline bool is_between(int c, int min, int max)
+{
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_ws(int c)
+{
+    return is_between(c, 1, 32);
+}
+
+static int token_length(const char* start) {
+    char ch = start[0];
+    if (! is_between(ch, 'a' , 'z') &&  ! is_between(ch, 'A', 'Z') && ch != '_' && ch != '$')
+        return -1;
+    int length = 0;
+    do
+        ch = start[++length];
+    while (is_between(ch, 'a' , 'z') || is_between(ch, 'A', 'Z') || is_between(ch, '0', '9') ||
+        ch == '_' || ch == '$');
+    return length;
+}
+
+SkScriptEngine::SkScriptEngine(SkOpType returnType) :
+    fTokenLength(0), fReturnType(returnType), fError(kNoError)
+{
+    SkSuppress noInitialSuppress;
+    noInitialSuppress.fOperator = kUnassigned;
+    noInitialSuppress.fOpStackDepth = 0;
+    noInitialSuppress.fSuppress = false;
+    fSuppressStack.push(noInitialSuppress);
+    *fOpStack.push() = kParen;
+    fTrackArray.appendClear();
+    fTrackString.appendClear();
+}
+
+SkScriptEngine::~SkScriptEngine() {
+    for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
+        delete *stringPtr;
+    for (SkTypedArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
+        delete *arrayPtr;
+}
+
+int SkScriptEngine::arithmeticOp(char ch, char nextChar, bool lastPush) {
+    SkOp op = kUnassigned;
+    bool reverseOperands = false;
+    bool negateResult = false;
+    int advance = 1;
+    switch (ch) {
+        case '+':
+            // !!! ignoring unary plus as implemented here has the side effect of
+            // suppressing errors like +"hi"
+            if (lastPush == false)  // unary plus, don't push an operator
+                goto returnAdv;
+            op = kAdd;
+            break;
+        case '-':
+            op = lastPush ? kSubtract : kMinus;
+            break;
+        case '*':
+            op = kMultiply;
+            break;
+        case '/':
+            op = kDivide;
+            break;
+        case '>':
+            if (nextChar == '>') {
+                op = kShiftRight;
+                goto twoChar;
+            } 
+            op = kGreaterEqual;
+            if (nextChar == '=')
+                goto twoChar;
+            reverseOperands = negateResult = true;
+            break;
+        case '<':
+            if (nextChar == '<') {
+                op = kShiftLeft;
+                goto twoChar;
+            }
+            op = kGreaterEqual;
+            reverseOperands = nextChar == '=';
+            negateResult = ! reverseOperands;
+            advance += reverseOperands;
+            break;
+        case '=':
+            if (nextChar == '=') {
+                op = kEqual;
+                goto twoChar;
+            }
+            break;
+        case '!':
+            if (nextChar == '=') {
+                op = kEqual;
+                negateResult = true;
+twoChar:
+                advance++;
+                break;
+            } 
+            op = kLogicalNot;
+            break;
+        case '?':
+            op = kIf;
+            break;
+        case ':':
+            op = kElse;
+            break;
+        case '^':
+            op = kXor;
+            break;
+        case '(':
+            *fOpStack.push() = kParen;  // push even if eval is suppressed
+            goto returnAdv;
+        case '&':
+            SkASSERT(nextChar != '&');
+            op = kBitAnd;
+            break;
+        case '|':
+            SkASSERT(nextChar != '|');
+            op = kBitOr;
+            break;
+        case '%':
+            op = kModulo;
+            break;
+        case '~':
+            op = kBitNot;
+            break;
+    }
+    if (op == kUnassigned)
+        return 0;
+    if (fSuppressStack.top().fSuppress == false) {
+        signed char precedence = gPrecedence[op];
+        do {
+            int idx = 0;
+            SkOp compare;
+            do {
+                compare = fOpStack.index(idx);
+                if ((compare & kArtificialOp) == 0)
+                    break;
+                idx++;
+            } while (true);
+            signed char topPrecedence = gPrecedence[compare];
+            SkASSERT(topPrecedence != -1);
+            if (topPrecedence > precedence || topPrecedence == precedence && 
+                    gOpAttributes[op].fLeftType == kNoType) {
+                break;
+            }
+            if (processOp() == false)
+                return 0;   // error
+        } while (true);
+        if (negateResult)
+            *fOpStack.push() = (SkOp) (kLogicalNot | kArtificialOp);
+        fOpStack.push(op);
+        if (reverseOperands)
+            *fOpStack.push() = (SkOp) (kFlipOps | kArtificialOp);
+    }
+returnAdv:
+    return advance;
+}
+
+void SkScriptEngine::boxCallBack(_boxCallBack func, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fBoxCallBack = func;
+    commonCallBack(kBox, callBack, userStorage);
+}
+
+void SkScriptEngine::commonCallBack(CallBackType type, UserCallBack& callBack, void* userStorage) {
+    callBack.fCallBackType = type;
+    callBack.fUserStorage = userStorage;
+    *fUserCallBacks.prepend() = callBack;
+}
+
+bool SkScriptEngine::convertParams(SkTDArray<SkScriptValue>& params, 
+        const SkFunctionParamType* paramTypes, int paramCount) {
+    if (params.count() > paramCount) {
+        fError = kTooManyParameters;
+        return false;   // too many parameters passed
+    }
+    for (int index = 0; index < params.count(); index++) {
+        if (convertTo((SkDisplayTypes) paramTypes[index], &params[index]) == false)
+            return false;
+    }
+    return true;
+}
+
+bool SkScriptEngine::convertTo(SkDisplayTypes toType, SkScriptValue* value ) {
+    SkDisplayTypes type = value->fType;
+    if (type == toType)
+        return true;
+    if (ToOpType(type) == kObject) {
+#if 0   // !!! I want object->string to get string from displaystringtype, not id
+        if (ToOpType(toType) == kString) {
+            bool success = handleObjectToString(value->fOperand.fObject);
+            if (success == false)
+                return false;
+            SkOpType type;
+            fTypeStack.pop(&type);
+            value->fType = ToDisplayType(type);
+            fOperandStack.pop(&value->fOperand);
+            return true;
+        }
+#endif
+        if (handleUnbox(value) == false) {
+            fError = kHandleUnboxFailed;
+            return false;
+        }
+        return convertTo(toType, value);
+    }
+    return ConvertTo(this, toType, value);
+}
+
+bool SkScriptEngine::evaluateDot(const char*& script, bool suppressed) { 
+    size_t fieldLength = token_length(++script);        // skip dot
+    if (fieldLength == 0) {
+        fError = kExpectedFieldName;
+        return false;
+    }
+    const char* field = script;
+    script += fieldLength;
+    bool success = handleProperty(suppressed);
+    if (success == false) {
+        fError = kCouldNotFindReferencedID; // note: never generated by standard animator plugins
+        return false;
+    }
+    return evaluateDotParam(script, suppressed, field, fieldLength);
+}
+
+bool SkScriptEngine::evaluateDotParam(const char*& script, bool suppressed, 
+        const char* field, size_t fieldLength) { 
+    void* object;
+    if (suppressed)
+        object = NULL;
+    else {
+        if (fTypeStack.top() != kObject) {
+            fError = kDotOperatorExpectsObject;
+            return false;
+        }
+        object = fOperandStack.top().fObject;
+        fTypeStack.pop();
+        fOperandStack.pop();
+    }
+    char ch; // see if it is a simple member or a function
+    while (is_ws(ch = script[0])) 
+        script++;
+    bool success = true;
+    if (ch != '(') {
+            if (suppressed == false) {
+                if ((success = handleMember(field, fieldLength, object)) == false)
+                    fError = kHandleMemberFailed;
+            }
+    } else {
+        SkTDArray<SkScriptValue> params;
+        *fBraceStack.push() = kFunctionBrace;
+        success = functionParams(&script, params);
+        if (success && suppressed == false &&
+                (success = handleMemberFunction(field, fieldLength, object, params)) == false) 
+            fError = kHandleMemberFunctionFailed;       
+    }
+    return success; 
+}
+
+bool SkScriptEngine::evaluateScript(const char** scriptPtr, SkScriptValue* value) {
+#ifdef SK_DEBUG
+    const char** original = scriptPtr;
+#endif
+    bool success;
+    const char* inner;
+    if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) {
+        *scriptPtr += sizeof("#script:") - 1;
+        if (fReturnType == kNoType || fReturnType == kString) {
+            success = innerScript(scriptPtr, value);
+            if (success == false)
+                goto end;
+            inner = value->fOperand.fString->c_str();
+            scriptPtr = &inner;
+        }
+    }
+    {
+        success = innerScript(scriptPtr, value);
+        if (success == false)
+            goto end;
+        const char* script = *scriptPtr;
+        char ch;
+        while (is_ws(ch = script[0]))
+            script++;
+        if (ch != '\0') {
+            // error may trigger on scripts like "50,0" that were intended to be written as "[50, 0]"
+            fError = kPrematureEnd;
+            success = false;
+        }
+    }
+end:
+#ifdef SK_DEBUG
+    if (success == false) {
+        SkDebugf("script failed: %s", *original);
+        if (fError)
+            SkDebugf(" %s", errorStrings[fError - 1]);
+        SkDebugf("\n");
+    }
+#endif
+    return success;
+}
+
+void SkScriptEngine::forget(SkTypedArray* array) {
+    if (array->getType() == SkType_String) {
+        for (int index = 0; index < array->count(); index++) {
+            SkString* string = (*array)[index].fString;
+            int found = fTrackString.find(string);
+            if (found >= 0)
+                fTrackString.remove(found);
+        }
+        return;
+    }
+    if (array->getType() == SkType_Array) {
+        for (int index = 0; index < array->count(); index++) {
+            SkTypedArray* child = (*array)[index].fArray;
+            forget(child);  // forgets children of child
+            int found = fTrackArray.find(child);
+            if (found >= 0)
+                fTrackArray.remove(found);
+        }
+    }
+}
+
+void SkScriptEngine::functionCallBack(_functionCallBack func, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fFunctionCallBack = func;
+    commonCallBack(kFunction, callBack, userStorage);
+}
+
+bool SkScriptEngine::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue>& params) {
+    (*scriptPtr)++; // skip open paren
+    *fOpStack.push() = kParen;
+    *fBraceStack.push() = kFunctionBrace;
+    SkBool suppressed = fSuppressStack.top().fSuppress;
+    do {
+        SkScriptValue value;
+        bool success = innerScript(scriptPtr, suppressed ? NULL : &value);
+        if (success == false) {
+            fError = kErrorInFunctionParameters;
+            return false;
+        }
+        if (suppressed)
+            continue;
+        *params.append() = value;
+    } while ((*scriptPtr)[-1] == ',');
+    fBraceStack.pop();
+    fOpStack.pop(); // pop paren
+    (*scriptPtr)++; // advance beyond close paren
+    return true;
+}
+
+#ifdef SK_DEBUG
+bool SkScriptEngine::getErrorString(SkString* str) const {
+    if (fError)
+        str->set(errorStrings[fError - 1]);
+    return fError != 0;
+}
+#endif
+
+bool SkScriptEngine::innerScript(const char** scriptPtr, SkScriptValue* value) {
+    const char* script = *scriptPtr;
+    char ch;
+    bool lastPush = false;
+    bool success = true;
+    int opBalance = fOpStack.count();
+    int baseBrace = fBraceStack.count();
+    int suppressBalance = fSuppressStack.count();
+    while ((ch = script[0]) != '\0') {
+        if (is_ws(ch)) {
+            script++;
+            continue;
+        }
+        SkBool suppressed = fSuppressStack.top().fSuppress;
+        SkOperand operand;
+        const char* dotCheck;
+        if (fBraceStack.count() > baseBrace) {
+#if 0   // disable support for struct brace
+            if (ch == ':') {
+                SkASSERT(fTokenLength > 0);
+                SkASSERT(fBraceStack.top() == kStructBrace);
+                ++script;
+                SkASSERT(fDisplayable);
+                SkString token(fToken, fTokenLength);
+                fTokenLength = 0;
+                const char* tokenName = token.c_str();
+                const SkMemberInfo* tokenInfo SK_INIT_TO_AVOID_WARNING;
+                if (suppressed == false) {
+                    SkDisplayTypes type = fInfo->getType();
+                    tokenInfo = SkDisplayType::GetMember(type, &tokenName);
+                    SkASSERT(tokenInfo);
+                }
+                SkScriptValue tokenValue;
+                success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
+                SkASSERT(success);
+                if (suppressed == false) {
+                    if (tokenValue.fType == SkType_Displayable) {
+                        SkASSERT(SkDisplayType::IsDisplayable(tokenInfo->getType()));
+                        fDisplayable->setReference(tokenInfo, tokenValue.fOperand.fDisplayable);
+                    } else {
+                        if (tokenValue.fType != tokenInfo->getType()) {
+                            if (convertTo(tokenInfo->getType(), &tokenValue) == false)
+                                return false;
+                        }
+                        tokenInfo->writeValue(fDisplayable, NULL, 0, 0, 
+                            (void*) ((char*) fInfo->memberData(fDisplayable) + tokenInfo->fOffset + fArrayOffset),
+                            tokenInfo->getType(), tokenValue);
+                    }
+                }
+                lastPush = false;
+                continue;
+            } else 
+#endif              
+            if (fBraceStack.top() == kArrayBrace) {
+                SkScriptValue tokenValue;
+                success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
+                if (success == false) {
+                    fError = kErrorInArrrayIndex;
+                    return false;
+                }
+                if (suppressed == false) {
+#if 0 // no support for structures for now
+                    if (tokenValue.fType == SkType_Structure) {
+                        fArrayOffset += (int) fInfo->getSize(fDisplayable);
+                    } else 
+#endif
+                    {
+                        SkDisplayTypes type = ToDisplayType(fReturnType);
+                        if (fReturnType == kNoType) {
+                            // !!! short sighted; in the future, allow each returned array component to carry 
+                            // its own type, and let caller do any needed conversions
+                            if (value->fOperand.fArray->count() == 0)
+                                value->fOperand.fArray->setType(type = tokenValue.fType);
+                            else
+                                type = value->fOperand.fArray->getType();
+                        }
+                        if (tokenValue.fType != type) {
+                            if (convertTo(type, &tokenValue) == false)
+                                return false;
+                        }
+                        *value->fOperand.fArray->append() = tokenValue.fOperand;
+                    }
+                }
+                lastPush = false;
+                continue;
+            } else {
+                if (token_length(script) == 0) {
+                    fError = kExpectedToken;
+                    return false;
+                }
+            }
+        }
+        if (lastPush != false && fTokenLength > 0) {
+            if (ch == '(') {
+                *fBraceStack.push() = kFunctionBrace;
+                if (handleFunction(&script, SkToBool(suppressed)) == false)
+                    return false;
+                lastPush = true;
+                continue;
+            } else if (ch == '[') {
+                if (handleProperty(SkToBool(suppressed)) == false)
+                    return false;   // note: never triggered by standard animator plugins
+                if (handleArrayIndexer(&script, SkToBool(suppressed)) == false)
+                    return false;
+                lastPush = true;
+                continue;
+            } else if (ch != '.') {
+                if (handleProperty(SkToBool(suppressed)) == false)
+                    return false;   // note: never triggered by standard animator plugins
+                lastPush = true;
+                continue;
+            }
+        }
+        if (ch == '0' && (script[1] & ~0x20) == 'X') {
+            if (lastPush != false) {
+                fError = kExpectedOperator;
+                return false;
+            }
+            script += 2;
+            script = SkParse::FindHex(script, (uint32_t*)&operand.fS32);
+            if (script == NULL) {
+                fError = kExpectedHex;
+                return false;
+            }
+            goto intCommon;
+        }
+        if (lastPush == false && ch == '.')
+            goto scalarCommon;
+        if (ch >= '0' && ch <= '9') {
+            if (lastPush != false) {
+                fError = kExpectedOperator;
+                return false;
+            }
+            dotCheck = SkParse::FindS32(script, &operand.fS32);
+            if (dotCheck[0] != '.') {
+                script = dotCheck;
+intCommon:
+                if (suppressed == false)
+                    *fTypeStack.push() = kInt;
+            } else {
+scalarCommon:
+                script = SkParse::FindScalar(script, &operand.fScalar);
+                if (suppressed == false)
+                    *fTypeStack.push() = kScalar;
+            }
+            if (suppressed == false)
+                fOperandStack.push(operand);
+            lastPush = true;
+            continue;
+        }
+        int length = token_length(script);
+        if (length > 0) {
+            if (lastPush != false) {
+                fError = kExpectedOperator;
+                return false;
+            }
+            fToken = script;
+            fTokenLength = length;
+            script += length;
+            lastPush = true;
+            continue;
+        }
+        char startQuote = ch;
+        if (startQuote == '\'' || startQuote == '\"') {
+            if (lastPush != false) {
+                fError = kExpectedOperator;
+                return false;
+            }
+            operand.fString = new SkString();
+            track(operand.fString);
+            ++script;
+
+            // <mrr> this is a lot of calls to append() one char at at time
+            // how hard to preflight script so we know how much to grow fString by?
+            do {
+                if (script[0] == '\\')
+                    ++script;
+                operand.fString->append(script, 1);
+                ++script;
+                if (script[0] == '\0') {
+                    fError = kUnterminatedString;
+                    return false;
+                }
+            } while (script[0] != startQuote);
+            ++script;
+            if (suppressed == false) {
+                *fTypeStack.push() = kString;
+                fOperandStack.push(operand);
+            }
+            lastPush = true;
+            continue;
+        }
+        ;
+        if (ch ==  '.') {
+            if (fTokenLength == 0) {
+                SkScriptValue scriptValue;
+                SkDEBUGCODE(scriptValue.fOperand.fObject = NULL);
+                int tokenLength = token_length(++script);
+                const char* token = script;
+                script += tokenLength;
+                if (suppressed == false) {
+                    if (fTypeStack.count() == 0) {
+                        fError = kExpectedTokenBeforeDotOperator;
+                        return false;
+                    }
+                    SkOpType topType;
+                    fTypeStack.pop(&topType);
+                    fOperandStack.pop(&scriptValue.fOperand);
+                    scriptValue.fType = ToDisplayType(topType);
+                    handleBox(&scriptValue);
+                }
+                success = evaluateDotParam(script, SkToBool(suppressed), token, tokenLength);
+                if (success == false)
+                    return false;
+                lastPush = true;
+                continue; 
+            }
+            // get next token, and evaluate immediately
+            success = evaluateDot(script, SkToBool(suppressed));
+            if (success == false)               
+                return false;
+            lastPush = true;
+            continue;
+        }
+        if (ch == '[') {
+            if (lastPush == false) {
+                script++;
+                *fBraceStack.push() = kArrayBrace;
+                if (suppressed)
+                    continue;
+                operand.fArray = value->fOperand.fArray = new SkTypedArray(ToDisplayType(fReturnType));
+                track(value->fOperand.fArray);
+                *fTypeStack.push() = (SkOpType) kArray;
+                fOperandStack.push(operand);
+                continue;
+            }
+            if (handleArrayIndexer(&script, SkToBool(suppressed)) == false)
+                return false;
+            lastPush = true;
+            continue;
+        }
+#if 0 // structs not supported for now
+        if (ch == '{') {
+            if (lastPush == false) {
+                script++;
+                *fBraceStack.push() = kStructBrace;
+                if (suppressed)
+                    continue;
+                operand.fS32 = 0;
+                *fTypeStack.push() = (SkOpType) kStruct;
+                fOperandStack.push(operand);
+                continue;
+            }
+            SkASSERT(0); // braces in other contexts aren't supported yet
+        }
+#endif
+        if (ch == ')' && fBraceStack.count() > 0) {
+            SkBraceStyle braceStyle = fBraceStack.top(); 
+            if (braceStyle == kFunctionBrace) {
+                fBraceStack.pop();
+                break;
+            }
+        }
+        if (ch == ',' || ch == ']') {
+            if (ch != ',') {
+                SkBraceStyle match;
+                fBraceStack.pop(&match);
+                if (match != kArrayBrace) {
+                    fError = kMismatchedArrayBrace;
+                    return false;
+                }
+            }
+            script++;
+            // !!! see if brace or bracket is correct closer
+            break;
+        }
+        char nextChar = script[1];
+        int advance = logicalOp(ch, nextChar);
+        if (advance < 0)     // error
+            return false;
+        if (advance == 0) 
+            advance = arithmeticOp(ch, nextChar, lastPush);
+        if (advance == 0) // unknown token
+            return false;
+        if (advance > 0)
+            script += advance;
+        lastPush = ch == ']' || ch == ')';
+    }
+    bool suppressed = SkToBool(fSuppressStack.top().fSuppress);
+    if (fTokenLength > 0) {
+        success = handleProperty(suppressed);
+        if (success == false)
+            return false;   // note: never triggered by standard animator plugins
+    }
+    while (fOpStack.count() > opBalance) {   // leave open paren
+        if ((fError = opError()) != kNoError)
+            return false;
+        if (processOp() == false)
+            return false;
+    }   
+    SkOpType topType = fTypeStack.count() > 0 ? fTypeStack.top() : kNoType;
+    if (suppressed == false && topType != fReturnType &&
+            topType == kString && fReturnType != kNoType) { // if result is a string, give handle property a chance to convert it to the property value
+        SkString* string = fOperandStack.top().fString;
+        fToken = string->c_str();
+        fTokenLength = string->size();
+        fOperandStack.pop();
+        fTypeStack.pop();
+        success = handleProperty(SkToBool(fSuppressStack.top().fSuppress));
+        if (success == false) { // if it couldn't convert, return string (error?)
+            SkOperand operand;
+            operand.fS32 = 0;
+            *fTypeStack.push() = kString;
+            operand.fString = string;
+            fOperandStack.push(operand);
+        }
+    }
+    if (value) {
+        if (fOperandStack.count() == 0)
+            return false;
+        SkASSERT(fOperandStack.count() >= 1);
+        SkASSERT(fTypeStack.count() >= 1);
+        fOperandStack.pop(&value->fOperand);
+        SkOpType type;
+        fTypeStack.pop(&type);
+        value->fType = ToDisplayType(type);
+//      SkASSERT(value->fType != SkType_Unknown);
+        if (topType != fReturnType && topType == kObject && fReturnType != kNoType) {
+            if (convertTo(ToDisplayType(fReturnType), value) == false)
+                return false;
+        }
+    }
+    while (fSuppressStack.count() > suppressBalance)
+        fSuppressStack.pop();
+    *scriptPtr = script;
+    return true; // no error
+}
+
+void SkScriptEngine::memberCallBack(_memberCallBack member , void* userStorage) {
+    UserCallBack callBack;
+    callBack.fMemberCallBack = member;
+    commonCallBack(kMember, callBack, userStorage);
+}
+
+void SkScriptEngine::memberFunctionCallBack(_memberFunctionCallBack func, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fMemberFunctionCallBack = func;
+    commonCallBack(kMemberFunction, callBack, userStorage);
+}
+
+#if 0
+void SkScriptEngine::objectToStringCallBack(_objectToStringCallBack func, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fObjectToStringCallBack = func;
+    commonCallBack(kObjectToString, callBack, userStorage);
+}
+#endif
+
+bool SkScriptEngine::handleArrayIndexer(const char** scriptPtr, bool suppressed) {
+    SkScriptValue scriptValue;
+    (*scriptPtr)++;
+    *fOpStack.push() = kParen;
+    *fBraceStack.push() = kArrayBrace;
+    SkOpType saveType = fReturnType;
+    fReturnType = kInt;
+    bool success = innerScript(scriptPtr, suppressed == false ? &scriptValue : NULL);
+    if (success == false)
+        return false;
+    fReturnType = saveType;
+    if (suppressed == false) {
+        if (convertTo(SkType_Int, &scriptValue) == false)
+            return false;
+        int index = scriptValue.fOperand.fS32;
+        SkScriptValue scriptValue;
+        SkOpType type;
+        fTypeStack.pop(&type);
+        fOperandStack.pop(&scriptValue.fOperand);
+        scriptValue.fType = ToDisplayType(type);
+        if (type == kObject) {
+            success = handleUnbox(&scriptValue);
+            if (success == false)
+                return false;
+            if (ToOpType(scriptValue.fType) != kArray) {
+                fError = kExpectedArray;
+                return false;
+            }
+        }
+        *fTypeStack.push() = scriptValue.fOperand.fArray->getOpType();
+//      SkASSERT(index >= 0);
+        if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) {
+            fError = kArrayIndexOutOfBounds;
+            return false;
+        }
+        scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index];
+        fOperandStack.push(scriptValue.fOperand);
+    }
+    fOpStack.pop(); // pop paren
+    return success;
+}
+
+bool SkScriptEngine::handleBox(SkScriptValue* scriptValue) {
+    bool success = true;
+    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+        if (callBack->fCallBackType != kBox)
+            continue;
+        success = (*callBack->fBoxCallBack)(callBack->fUserStorage, scriptValue);
+        if (success) {
+            fOperandStack.push(scriptValue->fOperand);
+            *fTypeStack.push() = ToOpType(scriptValue->fType);
+            goto done;
+        }
+    }
+done:
+    return success;
+}
+
+bool SkScriptEngine::handleFunction(const char** scriptPtr, bool suppressed) {
+    SkScriptValue callbackResult;
+    SkTDArray<SkScriptValue> params;
+    SkString functionName(fToken, fTokenLength);
+    fTokenLength = 0;
+    bool success = functionParams(scriptPtr, params);
+    if (success == false)
+        goto done;
+    if (suppressed == true)
+        return true;
+    {
+        for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+            if (callBack->fCallBackType != kFunction)
+                continue;
+            success = (*callBack->fFunctionCallBack)(functionName.c_str(), functionName.size(), params, 
+                callBack->fUserStorage, &callbackResult);
+            if (success) {
+                fOperandStack.push(callbackResult.fOperand);
+                *fTypeStack.push() = ToOpType(callbackResult.fType);
+                goto done;
+            }
+        }
+    }
+    fError = kNoFunctionHandlerFound;
+    return false;
+done:
+    return success;
+}
+
+bool SkScriptEngine::handleMember(const char* field, size_t len, void* object) {
+    SkScriptValue callbackResult;
+    bool success = true;
+    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+        if (callBack->fCallBackType != kMember)
+            continue;
+        success = (*callBack->fMemberCallBack)(field, len, object, callBack->fUserStorage, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkType_String)
+                track(callbackResult.fOperand.fString);
+            fOperandStack.push(callbackResult.fOperand);
+            *fTypeStack.push() = ToOpType(callbackResult.fType);
+            goto done;
+        }
+    }
+    return false;
+done:
+    return success;
+}
+
+bool SkScriptEngine::handleMemberFunction(const char* field, size_t len, void* object, SkTDArray<SkScriptValue>& params) {
+    SkScriptValue callbackResult;
+    bool success = true;
+    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+        if (callBack->fCallBackType != kMemberFunction)
+            continue;
+        success = (*callBack->fMemberFunctionCallBack)(field, len, object, params, 
+            callBack->fUserStorage, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkType_String)
+                track(callbackResult.fOperand.fString);
+            fOperandStack.push(callbackResult.fOperand);
+            *fTypeStack.push() = ToOpType(callbackResult.fType);
+            goto done;
+        }
+    }
+    return false;
+done:
+    return success;
+}
+
+#if 0
+bool SkScriptEngine::handleObjectToString(void* object) {
+    SkScriptValue callbackResult;
+    bool success = true;
+    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+        if (callBack->fCallBackType != kObjectToString)
+            continue;
+        success = (*callBack->fObjectToStringCallBack)(object, 
+            callBack->fUserStorage, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkType_String)
+                track(callbackResult.fOperand.fString);
+            fOperandStack.push(callbackResult.fOperand);
+            *fTypeStack.push() = ToOpType(callbackResult.fType);
+            goto done;
+        }
+    }
+    return false;
+done:
+    return success;
+}
+#endif
+
+bool SkScriptEngine::handleProperty(bool suppressed) {
+    SkScriptValue callbackResult;
+    bool success = true;
+    if (suppressed) 
+        goto done;
+    success = false; // note that with standard animator-script plugins, callback never returns false
+    {
+        for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+            if (callBack->fCallBackType != kProperty)
+                continue;
+            success = (*callBack->fPropertyCallBack)(fToken, fTokenLength, 
+                callBack->fUserStorage, &callbackResult);
+            if (success) {
+                if (callbackResult.fType == SkType_String && callbackResult.fOperand.fString == NULL) {
+                    callbackResult.fOperand.fString = new SkString(fToken, fTokenLength);
+                    track(callbackResult.fOperand.fString);
+                }
+                fOperandStack.push(callbackResult.fOperand);
+                *fTypeStack.push() = ToOpType(callbackResult.fType);
+                goto done;
+            }
+        }
+    }
+done:
+    fTokenLength = 0;
+    return success;
+}
+
+bool SkScriptEngine::handleUnbox(SkScriptValue* scriptValue) {
+    bool success = true;
+    for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
+        if (callBack->fCallBackType != kUnbox)
+            continue;
+        success = (*callBack->fUnboxCallBack)(callBack->fUserStorage, scriptValue);
+        if (success) {
+            if (scriptValue->fType == SkType_String)
+                track(scriptValue->fOperand.fString);
+            goto done;
+        }
+    }
+    return false;
+done:
+    return success;
+}
+
+// note that entire expression is treated as if it were enclosed in parens
+// an open paren is always the first thing in the op stack
+
+int SkScriptEngine::logicalOp(char ch, char nextChar) {
+    int advance = 1;
+    SkOp match;
+    signed char precedence;
+    switch (ch) {
+        case ')':
+            match = kParen;
+            break;
+        case ']':
+            match = kArrayOp;
+            break;
+        case '?':
+            match = kIf;
+            break;
+        case ':':
+            match = kElse;
+            break;
+        case '&':
+            if (nextChar != '&')
+                goto noMatch;
+            match = kLogicalAnd;
+            advance = 2;
+            break;
+        case '|':
+            if (nextChar != '|')
+                goto noMatch;
+            match = kLogicalOr;
+            advance = 2;
+            break;
+        default:
+noMatch:
+            return 0;
+    }
+    SkSuppress suppress;
+    precedence = gPrecedence[match];
+    if (fSuppressStack.top().fSuppress) {
+        if (fSuppressStack.top().fOpStackDepth < fOpStack.count()) {
+            SkOp topOp = fOpStack.top();
+            if (gPrecedence[topOp] <= precedence)
+                fOpStack.pop();
+            goto goHome;
+        }
+        bool changedPrecedence = gPrecedence[fSuppressStack.top().fOperator] < precedence;
+        if (changedPrecedence)
+            fSuppressStack.pop();
+        if (precedence == kIfElsePrecedence) {
+            if (match == kIf) {
+                if (changedPrecedence)
+                    fOpStack.pop();
+                else
+                    *fOpStack.push() = kIf;
+            } else {
+                if (fSuppressStack.top().fOpStackDepth == fOpStack.count()) {
+                    goto flipSuppress;
+                }
+                fOpStack.pop();
+            }
+        }
+        if (changedPrecedence == false)
+            goto goHome;
+    }
+    while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence) {
+        if (processOp() == false)
+            return false;
+    }
+    if (fSuppressStack.top().fOpStackDepth > fOpStack.count())
+        fSuppressStack.pop();
+    switch (match) {
+        case kParen:
+        case kArrayOp:
+            if (fOpStack.count() <= 1 || fOpStack.top() != match) {
+                fError = kMismatchedBrackets;
+                return -1;
+            }
+            if (match == kParen) 
+                fOpStack.pop();
+            else {
+                SkOpType indexType;
+                fTypeStack.pop(&indexType);
+                if (indexType != kInt && indexType != kScalar) {
+                    fError = kExpectedNumberForArrayIndex; // (although, could permit strings eventually)
+                    return -1;
+                }
+                SkOperand indexOperand;
+                fOperandStack.pop(&indexOperand);
+                int index = indexType == kScalar ? SkScalarFloor(indexOperand.fScalar) : 
+                    indexOperand.fS32;
+                SkOpType arrayType;
+                fTypeStack.pop(&arrayType);
+                if ((unsigned)arrayType != (unsigned)kArray) {
+                    fError = kExpectedArray;
+                    return -1;
+                }
+                SkOperand arrayOperand;
+                fOperandStack.pop(&arrayOperand);
+                SkTypedArray* array = arrayOperand.fArray;
+                SkOperand operand;
+                if (array->getIndex(index, &operand) == false) {
+                    fError = kIndexOutOfRange;
+                    return -1;
+                }
+                SkOpType resultType = array->getOpType();
+                fTypeStack.push(resultType);
+                fOperandStack.push(operand);
+            }
+            break;
+        case kIf: {
+            SkScriptValue ifValue;
+            SkOpType ifType;
+            fTypeStack.pop(&ifType);
+            ifValue.fType = ToDisplayType(ifType);
+            fOperandStack.pop(&ifValue.fOperand);
+            if (convertTo(SkType_Int, &ifValue) == false)
+                return -1;
+            if (ifValue.fType != SkType_Int) {
+                fError = kExpectedIntForConditionOperator;
+                return -1;
+            }
+            suppress.fSuppress = ifValue.fOperand.fS32 == 0;
+            suppress.fOperator = kIf;
+            suppress.fOpStackDepth = fOpStack.count(); 
+            suppress.fElse = false;
+            fSuppressStack.push(suppress);
+            // if left is true, do only up to colon
+            // if left is false, do only after colon
+            } break;
+        case kElse:
+flipSuppress:
+            if (fSuppressStack.top().fElse == true)
+                fSuppressStack.pop();
+            fSuppressStack.top().fElse = true;
+            fSuppressStack.top().fSuppress ^= true;
+            // flip last do / don't do consideration from last '?'
+            break;
+        case kLogicalAnd:
+        case kLogicalOr: {
+            if (fTypeStack.top() != kInt) {
+                fError = kExpectedBooleanExpression;
+                return -1;
+            }
+            int32_t topInt = fOperandStack.top().fS32;
+            if (fOpStack.top() != kLogicalAnd)
+                *fOpStack.push() = kLogicalAnd; // really means 'to bool', and is appropriate for 'or'
+            if (match == kLogicalOr ? topInt != 0 : topInt == 0) {
+                suppress.fSuppress = true;
+                suppress.fOperator = match;
+                suppress.fOpStackDepth = fOpStack.count(); 
+                fSuppressStack.push(suppress);
+            } else {
+                fTypeStack.pop();
+                fOperandStack.pop();
+            }
+        }   break;
+        default:
+            SkASSERT(0);
+    }
+goHome:
+    return advance;
+}
+
+SkScriptEngine::Error SkScriptEngine::opError() {
+    int opCount = fOpStack.count();
+    int operandCount = fOperandStack.count();
+    if (opCount == 0) {
+        if (operandCount != 1)
+            return kExpectedOperator;
+        return kNoError;
+    }
+    SkOp op = (SkOp) (fOpStack.top() & ~kArtificialOp);
+    const SkOperatorAttributes* attributes = &gOpAttributes[op];
+    if (attributes->fLeftType != kNoType && operandCount < 2)
+        return kExpectedValue;
+    if (attributes->fLeftType == kNoType && operandCount < 1)
+        return kExpectedValue;
+    return kNoError;
+}
+
+bool SkScriptEngine::processOp() {
+    SkOp op;
+    fOpStack.pop(&op);
+    op = (SkOp) (op & ~kArtificialOp);
+    const SkOperatorAttributes* attributes = &gOpAttributes[op];
+    SkOpType type2;
+    fTypeStack.pop(&type2);
+    SkOpType type1 = type2;
+    SkOperand operand2;
+    fOperandStack.pop(&operand2);
+    SkOperand operand1 = operand2; // !!! not really needed, suppresses warning
+    if (attributes->fLeftType != kNoType) {
+        fTypeStack.pop(&type1);
+        fOperandStack.pop(&operand1);
+        if (op == kFlipOps) {
+            SkTSwap(type1, type2);
+            SkTSwap(operand1, operand2);
+            fOpStack.pop(&op);
+            op = (SkOp) (op & ~kArtificialOp);
+            attributes = &gOpAttributes[op];
+        }
+        if (type1 == kObject && (type1 & attributes->fLeftType) == 0) {
+            SkScriptValue val;
+            val.fType = ToDisplayType(type1);
+            val.fOperand = operand1;
+            bool success = handleUnbox(&val);
+            if (success == false)
+                return false;
+            type1 = ToOpType(val.fType);
+            operand1 = val.fOperand;
+        }
+    }
+    if (type2 == kObject && (type2 & attributes->fLeftType) == 0) {
+        SkScriptValue val;
+        val.fType = ToDisplayType(type2);
+        val.fOperand = operand2;
+        bool success = handleUnbox(&val);
+        if (success == false)
+            return false;
+        type2 = ToOpType(val.fType);
+        operand2 = val.fOperand;
+    }
+    if (attributes->fLeftType != kNoType) {
+        if (type1 != type2) {
+            if ((attributes->fLeftType & kString) && attributes->fBias & kTowardsString && ((type1 | type2) & kString)) {
+                if (type1 == kInt || type1 == kScalar) {
+                    convertToString(operand1, type1 == kInt ? SkType_Int : SkType_Float);
+                    type1 = kString;
+                }
+                if (type2 == kInt || type2 == kScalar) {
+                    convertToString(operand2, type2 == kInt ? SkType_Int : SkType_Float);
+                    type2 = kString;
+                }
+            } else if (attributes->fLeftType & kScalar && ((type1 | type2) & kScalar)) {
+                if (type1 == kInt) {
+                    operand1.fScalar = IntToScalar(operand1.fS32);
+                    type1 = kScalar;
+                }
+                if (type2 == kInt) {
+                    operand2.fScalar = IntToScalar(operand2.fS32);
+                     type2 = kScalar;
+                }
+            }
+        }
+        if ((type1 & attributes->fLeftType) == 0 || type1 != type2) {
+            if (type1 == kString) {
+                const char* result = SkParse::FindScalar(operand1.fString->c_str(), &operand1.fScalar);
+                if (result == NULL) {
+                    fError = kExpectedNumber;
+                    return false;
+                }
+                type1 = kScalar;
+            }
+            if (type1 == kScalar && (attributes->fLeftType == kInt || type2 == kInt)) {
+                operand1.fS32 = SkScalarFloor(operand1.fScalar);
+                type1 = kInt;
+            }
+        }
+    }
+    if ((type2 & attributes->fRightType) == 0 || type1 != type2) {
+        if (type2 == kString) {
+            const char* result = SkParse::FindScalar(operand2.fString->c_str(), &operand2.fScalar);
+            if (result == NULL) {
+                fError = kExpectedNumber;
+                return false;
+            }
+            type2 = kScalar;
+        }
+        if (type2 == kScalar && (attributes->fRightType == kInt || type1 == kInt)) {
+            operand2.fS32 = SkScalarFloor(operand2.fScalar);
+            type2 = kInt;
+        }
+    }
+    if (type2 == kScalar)
+        op = (SkOp) (op + 1);
+    else if (type2 == kString)
+        op = (SkOp) (op + 2);
+    switch(op) {
+        case kAddInt:
+            operand2.fS32 += operand1.fS32;
+            break;
+        case kAddScalar:
+            operand2.fScalar += operand1.fScalar;
+            break;
+        case kAddString:
+            if (fTrackString.find(operand1.fString) < 0) {
+                operand1.fString = SkNEW_ARGS(SkString, (*operand1.fString));
+                track(operand1.fString);
+            }
+            operand1.fString->append(*operand2.fString);
+            operand2 = operand1;
+            break;
+        case kBitAnd:
+            operand2.fS32 &= operand1.fS32;
+            break;
+        case kBitNot:
+            operand2.fS32 = ~operand2.fS32;
+            break;
+        case kBitOr:
+            operand2.fS32 |= operand1.fS32;
+            break;
+        case kDivideInt:
+            if (operand2.fS32 == 0) {
+                operand2.fS32 = operand1.fS32 == 0 ? SK_NaN32 : operand1.fS32 > 0 ? SK_MaxS32 : -SK_MaxS32;
+                break;
+            } else {
+                int32_t original = operand2.fS32;
+                operand2.fS32 = operand1.fS32 / operand2.fS32;
+                if (original * operand2.fS32 == operand1.fS32)
+                    break;    // integer divide was good enough
+                operand2.fS32 = original;
+                type2 = kScalar;
+            }
+        case kDivideScalar:
+            if (operand2.fScalar == 0)
+                operand2.fScalar = operand1.fScalar == 0 ? SK_ScalarNaN : operand1.fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax;
+            else
+                operand2.fScalar = SkScalarDiv(operand1.fScalar, operand2.fScalar);
+            break;
+        case kEqualInt:
+            operand2.fS32 = operand1.fS32 == operand2.fS32;
+            break;
+        case kEqualScalar:
+            operand2.fS32 = operand1.fScalar == operand2.fScalar;
+            type2 = kInt;
+            break;
+        case kEqualString:
+            operand2.fS32 = *operand1.fString == *operand2.fString;
+            type2 = kInt;
+            break;
+        case kGreaterEqualInt:
+            operand2.fS32 = operand1.fS32 >= operand2.fS32;
+            break;
+        case kGreaterEqualScalar:
+            operand2.fS32 = operand1.fScalar >= operand2.fScalar;
+            type2 = kInt;
+            break;
+        case kGreaterEqualString:
+            operand2.fS32 = strcmp(operand1.fString->c_str(), operand2.fString->c_str()) >= 0;
+            type2 = kInt;
+            break;
+        case kLogicalAnd:
+            operand2.fS32 = !! operand2.fS32;   // really, ToBool
+            break;
+        case kLogicalNot:
+            operand2.fS32 = ! operand2.fS32;
+            break;
+        case kLogicalOr:
+            SkASSERT(0);    // should have already been processed
+            break;
+        case kMinusInt:
+            operand2.fS32 = -operand2.fS32;
+            break;
+        case kMinusScalar:
+            operand2.fScalar = -operand2.fScalar;
+            break;
+        case kModuloInt:
+            operand2.fS32 = operand1.fS32 % operand2.fS32;
+            break;
+        case kModuloScalar:
+            operand2.fScalar = SkScalarMod(operand1.fScalar, operand2.fScalar);
+            break;
+        case kMultiplyInt:
+            operand2.fS32 *= operand1.fS32;
+            break;
+        case kMultiplyScalar:
+            operand2.fScalar = SkScalarMul(operand1.fScalar, operand2.fScalar);
+            break;
+        case kShiftLeft:
+            operand2.fS32 = operand1.fS32 << operand2.fS32;
+            break;
+        case kShiftRight:
+            operand2.fS32 = operand1.fS32 >> operand2.fS32;
+            break;
+        case kSubtractInt:
+            operand2.fS32 = operand1.fS32 - operand2.fS32;
+            break;
+        case kSubtractScalar:
+            operand2.fScalar = operand1.fScalar - operand2.fScalar;
+            break;
+        case kXor:
+            operand2.fS32 ^= operand1.fS32;
+            break;
+        default:
+            SkASSERT(0);
+    }
+    fTypeStack.push(type2);
+    fOperandStack.push(operand2);
+    return true;
+}
+
+void SkScriptEngine::propertyCallBack(_propertyCallBack prop, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fPropertyCallBack = prop;
+    commonCallBack(kProperty, callBack, userStorage);
+}
+
+void SkScriptEngine::track(SkTypedArray* array) { 
+    SkASSERT(fTrackArray.find(array) < 0);  
+    *(fTrackArray.end() - 1) = array; 
+    fTrackArray.appendClear(); 
+}
+
+void SkScriptEngine::track(SkString* string) { 
+    SkASSERT(fTrackString.find(string) < 0);  
+    *(fTrackString.end() - 1) = string; 
+    fTrackString.appendClear(); 
+}
+
+void SkScriptEngine::unboxCallBack(_unboxCallBack func, void* userStorage) {
+    UserCallBack callBack;
+    callBack.fUnboxCallBack = func;
+    commonCallBack(kUnbox, callBack, userStorage);
+}
+
+bool SkScriptEngine::ConvertTo(SkScriptEngine* engine, SkDisplayTypes toType, SkScriptValue* value ) {
+    SkASSERT(value);
+    if (SkDisplayType::IsEnum(NULL /* fMaker */, toType))
+        toType = SkType_Int;
+    if (toType == SkType_Point || toType == SkType_3D_Point)
+        toType = SkType_Float;
+    if (toType == SkType_Drawable)
+        toType = SkType_Displayable;
+    SkDisplayTypes type = value->fType;
+    if (type == toType) 
+        return true;
+    SkOperand& operand = value->fOperand;
+    bool success = true;
+    switch (toType) {
+        case SkType_Int:
+            if (type == SkType_Boolean)
+                break;
+            if (type == SkType_Float)
+                operand.fS32 = SkScalarFloor(operand.fScalar);
+            else {
+                if (type != SkType_String) {
+                    success = false;
+                    break; // error
+                }
+                success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL;
+            }
+            break;
+        case SkType_Float:
+            if (type == SkType_Int) {
+                if ((uint32_t)operand.fS32 == SK_NaN32)
+                    operand.fScalar = SK_ScalarNaN;
+                else if (SkAbs32(operand.fS32) == SK_MaxS32)
+                    operand.fScalar = SkSign32(operand.fS32) * SK_ScalarMax;
+                else
+                    operand.fScalar = SkIntToScalar(operand.fS32);
+            } else {
+                if (type != SkType_String) {
+                    success = false;
+                    break; // error
+                }
+                success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL;
+            }
+            break;
+        case SkType_String: {
+            SkString* strPtr = new SkString();
+            SkASSERT(engine);
+            engine->track(strPtr);
+            if (type == SkType_Int)
+                strPtr->appendS32(operand.fS32);
+            else if (type == SkType_Displayable) 
+                SkASSERT(0); // must call through instance version instead of static version
+            else {
+                if (type != SkType_Float) {
+                    success = false;
+                    break;
+                }
+                strPtr->appendScalar(operand.fScalar);
+            }
+            operand.fString = strPtr;
+            } break;
+        case SkType_Array: {
+            SkTypedArray* array = new SkTypedArray(type);
+            *array->append() = operand;
+            engine->track(array);
+            operand.fArray = array;
+            } break;
+        default:
+            SkASSERT(0);
+    }
+    value->fType = toType;
+    if (success == false)
+        engine->fError = kTypeConversionFailed;
+    return success;
+}
+
+SkScalar SkScriptEngine::IntToScalar(int32_t s32) {
+    SkScalar scalar;
+    if ((uint32_t)s32 == SK_NaN32)
+        scalar = SK_ScalarNaN;
+    else if (SkAbs32(s32) == SK_MaxS32)
+        scalar = SkSign32(s32) * SK_ScalarMax;
+    else
+        scalar = SkIntToScalar(s32);
+    return scalar;
+}
+
+SkDisplayTypes SkScriptEngine::ToDisplayType(SkOpType type) {
+    int val = type;
+    switch (val) {
+        case kNoType:
+            return SkType_Unknown;
+        case kInt:
+            return SkType_Int;
+        case kScalar:
+            return SkType_Float;
+        case kString:
+            return SkType_String;
+        case kArray:
+            return SkType_Array;
+        case kObject:
+            return SkType_Displayable;
+//      case kStruct:
+//          return SkType_Structure;
+        default:
+            SkASSERT(0);
+            return SkType_Unknown;
+    }
+}
+
+SkScriptEngine::SkOpType SkScriptEngine::ToOpType(SkDisplayTypes type) {
+    if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type))
+        return (SkOpType) kObject;
+    if (SkDisplayType::IsEnum(NULL /* fMaker */, type))
+        return kInt;
+    switch (type) {
+        case SkType_ARGB:
+        case SkType_MSec:
+        case SkType_Int:
+            return kInt;
+        case SkType_Float:
+        case SkType_Point:
+        case SkType_3D_Point:
+            return kScalar;
+        case SkType_Base64:
+        case SkType_DynamicString:
+        case SkType_String:
+            return kString;
+        case SkType_Array:
+            return (SkOpType) kArray;
+        case SkType_Unknown:
+            return kNoType;
+        default:
+            SkASSERT(0);
+            return kNoType;
+    }
+}
+
+bool SkScriptEngine::ValueToString(SkScriptValue value, SkString* string) {
+    switch (value.fType) {
+        case kInt:
+            string->reset();
+            string->appendS32(value.fOperand.fS32);
+            break;
+        case kScalar:
+            string->reset();
+            string->appendScalar(value.fOperand.fScalar);
+            break;
+        case kString:
+            string->set(*value.fOperand.fString);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true; // no error
+}
+
+#ifdef SK_SUPPORT_UNITTEST
+
+#ifdef SK_CAN_USE_FLOAT
+    #include "SkFloatingPoint.h"
+#endif
+
+#define DEF_SCALAR_ANSWER   0
+#define DEF_STRING_ANSWER   NULL
+
+#define testInt(expression) { #expression, SkType_Int, expression, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
+#ifdef SK_SCALAR_IS_FLOAT
+    #define testScalar(expression) { #expression, SkType_Float, 0, (float) expression, DEF_STRING_ANSWER }
+    #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, sk_float_mod(exp1, exp2), DEF_STRING_ANSWER }
+#else
+    #ifdef SK_CAN_USE_FLOAT
+        #define testScalar(expression) { #expression, SkType_Float, 0, (int) ((expression) * 65536.0f), DEF_STRING_ANSWER }
+        #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, (int) (sk_float_mod(exp1, exp2)  * 65536.0f), DEF_STRING_ANSWER }
+    #endif
+#endif
+#define testTrue(expression) { #expression, SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
+#define testFalse(expression) { #expression, SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
+
+#if !defined(SK_BUILD_FOR_BREW)
+static const SkScriptNAnswer scriptTests[]  = {
+    testInt(1>1/2),
+    testInt((6+7)*8),
+    testInt(0&&1?2:3),
+    testInt(3*(4+5)),
+#ifdef SK_CAN_USE_FLOAT
+    testScalar(1.0+2.0), 
+    testScalar(1.0+5), 
+    testScalar(3.0-1.0), 
+    testScalar(6-1.0), 
+    testScalar(- -5.5- -1.5), 
+    testScalar(2.5*6.), 
+    testScalar(0.5*4), 
+    testScalar(4.5/.5), 
+    testScalar(9.5/19), 
+    testRemainder(9.5, 0.5), 
+    testRemainder(9.,2), 
+    testRemainder(9,2.5),
+    testRemainder(-9,2.5),
+    testTrue(-9==-9.0),
+    testTrue(-9.==-4.0-5),
+    testTrue(-9.*1==-4-5),
+    testFalse(-9!=-9.0),
+    testFalse(-9.!=-4.0-5),
+    testFalse(-9.*1!=-4-5),
+#endif
+    testInt(0x123),
+    testInt(0XABC),
+    testInt(0xdeadBEEF),
+    {   "'123'+\"456\"", SkType_String, 0, 0, "123456" },
+    {   "123+\"456\"", SkType_String, 0, 0, "123456" },
+    {   "'123'+456", SkType_String, 0, 0, "123456" },
+    {   "'123'|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    {   "123|\"456\"", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    {   "'123'|456", SkType_Int, 123|456, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    {   "'2'<11", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    {   "2<'11'", SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    {   "'2'<'11'", SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER },
+    testInt(123),
+    testInt(-345),
+    testInt(+678),
+    testInt(1+2+3),
+    testInt(3*4+5),
+    testInt(6+7*8),
+    testInt(-1-2-8/4),
+    testInt(-9%4),
+    testInt(9%-4),
+    testInt(-9%-4),
+    testInt(123|978),
+    testInt(123&978),
+    testInt(123^978),
+    testInt(2<<4),
+    testInt(99>>3),
+    testInt(~55),
+    testInt(~~55),
+    testInt(!55),
+    testInt(!!55),
+    // both int
+    testInt(2<2),
+    testInt(2<11),
+    testInt(20<11),
+    testInt(2<=2),
+    testInt(2<=11),
+    testInt(20<=11),
+    testInt(2>2),
+    testInt(2>11),
+    testInt(20>11),
+    testInt(2>=2),
+    testInt(2>=11),
+    testInt(20>=11),
+    testInt(2==2),
+    testInt(2==11),
+    testInt(20==11),
+    testInt(2!=2),
+    testInt(2!=11),
+    testInt(20!=11),
+#ifdef SK_CAN_USE_FLOAT
+    // left int, right scalar
+    testInt(2<2.),
+    testInt(2<11.),
+    testInt(20<11.),
+    testInt(2<=2.),
+    testInt(2<=11.),
+    testInt(20<=11.),
+    testInt(2>2.),
+    testInt(2>11.),
+    testInt(20>11.),
+    testInt(2>=2.),
+    testInt(2>=11.),
+    testInt(20>=11.),
+    testInt(2==2.),
+    testInt(2==11.),
+    testInt(20==11.),
+    testInt(2!=2.),
+    testInt(2!=11.),
+    testInt(20!=11.),
+    // left scalar, right int
+        testInt(2.<2),
+    testInt(2.<11),
+    testInt(20.<11),
+    testInt(2.<=2),
+    testInt(2.<=11),
+    testInt(20.<=11),
+    testInt(2.>2),
+    testInt(2.>11),
+    testInt(20.>11),
+    testInt(2.>=2),
+    testInt(2.>=11),
+    testInt(20.>=11),
+    testInt(2.==2),
+    testInt(2.==11),
+    testInt(20.==11),
+    testInt(2.!=2),
+    testInt(2.!=11),
+    testInt(20.!=11),
+    // both scalar
+    testInt(2.<11.),
+    testInt(20.<11.),
+    testInt(2.<=2.),
+    testInt(2.<=11.),
+    testInt(20.<=11.),
+    testInt(2.>2.),
+    testInt(2.>11.),
+    testInt(20.>11.),
+    testInt(2.>=2.),
+    testInt(2.>=11.),
+    testInt(20.>=11.),
+    testInt(2.==2.),
+    testInt(2.==11.),
+    testInt(20.==11.),
+    testInt(2.!=2.),
+    testInt(2.!=11.),
+    testInt(20.!=11.),
+#endif
+    // int, string (string is int)
+    testFalse(2<'2'),
+    testTrue(2<'11'),
+    testFalse(20<'11'),
+    testTrue(2<='2'),
+    testTrue(2<='11'),
+    testFalse(20<='11'),
+    testFalse(2>'2'),
+    testFalse(2>'11'),
+    testTrue(20>'11'),
+    testTrue(2>='2'),
+    testFalse(2>='11'),
+    testTrue(20>='11'),
+    testTrue(2=='2'),
+    testFalse(2=='11'),
+    testFalse(2!='2'),
+    testTrue(2!='11'),
+    // int, string (string is scalar)
+    testFalse(2<'2.'),
+    testTrue(2<'11.'),
+    testFalse(20<'11.'),
+    testTrue(2=='2.'),
+    testFalse(2=='11.'),
+#ifdef SK_CAN_USE_FLOAT
+    // scalar, string
+    testFalse(2.<'2.'),
+    testTrue(2.<'11.'),
+    testFalse(20.<'11.'),
+    testTrue(2.=='2.'),
+    testFalse(2.=='11.'),
+    // string, int
+    testFalse('2'<2),
+    testTrue('2'<11),
+    testFalse('20'<11),
+    testTrue('2'==2),
+    testFalse('2'==11),
+    // string, scalar
+    testFalse('2'<2.),
+    testTrue('2'<11.),
+    testFalse('20'<11.),
+    testTrue('2'==2.),
+    testFalse('2'==11.),
+#endif
+    // string, string
+    testFalse('2'<'2'),
+    testFalse('2'<'11'),
+    testFalse('20'<'11'),
+    testTrue('2'=='2'),
+    testFalse('2'=='11'),
+    // logic
+    testInt(1?2:3),
+    testInt(0?2:3),
+    testInt(1&&2||3),
+    testInt(1&&0||3),
+    testInt(1&&0||0),
+    testInt(1||0&&3),
+    testInt(0||0&&3),
+    testInt(0||1&&3),
+    testInt(1?(2?3:4):5),
+    testInt(0?(2?3:4):5),
+    testInt(1?(0?3:4):5),
+    testInt(0?(0?3:4):5),
+    testInt(1?2?3:4:5),
+    testInt(0?2?3:4:5),
+    testInt(1?0?3:4:5),
+    testInt(0?0?3:4:5),
+    
+    testInt(1?2:(3?4:5)),
+    testInt(0?2:(3?4:5)),
+    testInt(1?0:(3?4:5)),
+    testInt(0?0:(3?4:5)),
+    testInt(1?2:3?4:5),
+    testInt(0?2:3?4:5),
+    testInt(1?0:3?4:5),
+    testInt(0?0:3?4:5)
+#ifdef SK_CAN_USE_FLOAT
+    , { "123.5", SkType_Float, 0, SkIntToScalar(123) + SK_Scalar1/2, DEF_STRING_ANSWER }
+#endif
+};
+#endif // build for brew
+
+#define SkScriptNAnswer_testCount   SK_ARRAY_COUNT(scriptTests)
+
+void SkScriptEngine::UnitTest() {
+#if !defined(SK_BUILD_FOR_BREW)
+    for (unsigned index = 0; index < SkScriptNAnswer_testCount; index++) {
+        SkScriptEngine engine(SkScriptEngine::ToOpType(scriptTests[index].fType));
+        SkScriptValue value;
+        const char* script = scriptTests[index].fScript;
+        SkASSERT(engine.evaluateScript(&script, &value) == true);
+        SkASSERT(value.fType == scriptTests[index].fType);
+        SkScalar error;
+        switch (value.fType) {
+            case SkType_Int:
+                SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
+                break;
+            case SkType_Float:
+                error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
+                SkASSERT(error < SK_Scalar1 / 10000);
+                break;
+            case SkType_String:
+                SkASSERT(strcmp(value.fOperand.fString->c_str(), scriptTests[index].fStringAnswer) == 0);
+                break;
+            default:
+                SkASSERT(0);
+        }
+    }
+#endif
+}
+#endif
+
diff --git a/src/animator/SkScript.h b/src/animator/SkScript.h
new file mode 100644
index 0000000..bb7d978
--- /dev/null
+++ b/src/animator/SkScript.h
@@ -0,0 +1,274 @@
+/* libs/graphics/animator/SkScript.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 SkScript_DEFINED
+#define SkScript_DEFINED
+
+#include "SkOperand.h"
+#include "SkIntArray.h"
+#include "SkTDict.h"
+#include "SkTDStack.h"
+
+class SkAnimateMaker;
+
+class SkScriptEngine {
+public:
+    enum Error {
+        kNoError,
+        kArrayIndexOutOfBounds,
+        kCouldNotFindReferencedID,
+        kDotOperatorExpectsObject,
+        kErrorInArrrayIndex,
+        kErrorInFunctionParameters,
+        kExpectedArray,
+        kExpectedBooleanExpression,
+        kExpectedFieldName,
+        kExpectedHex,
+        kExpectedIntForConditionOperator,
+        kExpectedNumber,
+        kExpectedNumberForArrayIndex,
+        kExpectedOperator,
+        kExpectedToken,
+        kExpectedTokenBeforeDotOperator,
+        kExpectedValue,
+        kHandleMemberFailed,
+        kHandleMemberFunctionFailed,
+        kHandleUnboxFailed,
+        kIndexOutOfRange,
+        kMismatchedArrayBrace,
+        kMismatchedBrackets,
+        kNoFunctionHandlerFound,
+        kPrematureEnd,
+        kTooManyParameters,
+        kTypeConversionFailed,
+        kUnterminatedString
+    };
+
+    enum SkOpType {
+        kNoType,
+        kInt = 1,
+        kScalar = 2,
+        kString = 4,
+        kArray = 8,
+        kObject = 16
+//      kStruct = 32
+    };
+
+    typedef bool (*_boxCallBack)(void* userStorage, SkScriptValue* result);
+    typedef bool (*_functionCallBack)(const char* func, size_t len, SkTDArray<SkScriptValue>& params,
+        void* userStorage, SkScriptValue* result);
+    typedef bool (*_memberCallBack)(const char* member, size_t len, void* object, 
+        void* userStorage, SkScriptValue* result);
+    typedef bool (*_memberFunctionCallBack)(const char* member, size_t len, void* object, 
+        SkTDArray<SkScriptValue>& params, void* userStorage, SkScriptValue* result);
+//  typedef bool (*_objectToStringCallBack)(void* object, void* userStorage, SkScriptValue* result);
+    typedef bool (*_propertyCallBack)(const char* prop, size_t len, void* userStorage, SkScriptValue* result);
+    typedef bool (*_unboxCallBack)(void* userStorage, SkScriptValue* result);
+    SkScriptEngine(SkOpType returnType);
+    ~SkScriptEngine();
+    void boxCallBack(_boxCallBack func, void* userStorage);
+    bool convertTo(SkDisplayTypes , SkScriptValue* );
+    bool evaluateScript(const char** script, SkScriptValue* value);
+    void forget(SkTypedArray* array);
+    void functionCallBack(_functionCallBack func, void* userStorage);
+    Error getError() const { return fError; }
+#ifdef SK_DEBUG
+    bool getErrorString(SkString* err) const;
+#endif
+    void memberCallBack(_memberCallBack , void* userStorage);
+    void memberFunctionCallBack(_memberFunctionCallBack , void* userStorage);
+//  void objectToStringCallBack(_objectToStringCallBack , void* userStorage);
+    void propertyCallBack(_propertyCallBack prop, void* userStorage);
+    void track(SkTypedArray* array);
+    void track(SkString* string);
+    void unboxCallBack(_unboxCallBack func, void* userStorage);
+    static bool ConvertTo(SkScriptEngine* , SkDisplayTypes toType, SkScriptValue* value);
+    static SkScalar IntToScalar(int32_t );
+    static SkDisplayTypes ToDisplayType(SkOpType type);
+    static SkOpType ToOpType(SkDisplayTypes type);
+    static bool ValueToString(SkScriptValue value, SkString* string);
+
+    enum CallBackType {
+        kBox,
+        kFunction,
+        kMember,
+        kMemberFunction,
+    //  kObjectToString,
+        kProperty,
+        kUnbox
+    };
+
+    struct UserCallBack {
+        CallBackType fCallBackType;
+        void* fUserStorage;
+        union {
+            _boxCallBack fBoxCallBack;
+            _functionCallBack fFunctionCallBack;
+            _memberCallBack fMemberCallBack;
+            _memberFunctionCallBack fMemberFunctionCallBack;
+    //      _objectToStringCallBack fObjectToStringCallBack;
+            _propertyCallBack fPropertyCallBack;
+            _unboxCallBack fUnboxCallBack;
+        };
+    };
+
+    enum SkOp {
+        kUnassigned,
+        kAdd,
+        kAddInt = kAdd,
+        kAddScalar,
+        kAddString, // string concat
+        kArrayOp,
+        kBitAnd,
+        kBitNot,
+        kBitOr,
+        kDivide,
+        kDivideInt = kDivide,
+        kDivideScalar,
+        kElse,
+        kEqual,
+        kEqualInt = kEqual,
+        kEqualScalar,
+        kEqualString,
+        kFlipOps,
+        kGreaterEqual,
+        kGreaterEqualInt = kGreaterEqual,
+        kGreaterEqualScalar,
+        kGreaterEqualString,
+        kIf,
+        kLogicalAnd,
+        kLogicalNot,
+        kLogicalOr,
+        kMinus,
+        kMinusInt = kMinus,
+        kMinusScalar,
+        kModulo,
+        kModuloInt = kModulo,
+        kModuloScalar,
+        kMultiply,
+        kMultiplyInt = kMultiply,
+        kMultiplyScalar,
+        kParen,
+        kShiftLeft,
+        kShiftRight,    // signed
+        kSubtract,
+        kSubtractInt = kSubtract,
+        kSubtractScalar,
+        kXor,
+        kArtificialOp = 0x40
+    };
+
+    enum SkOpBias {
+        kNoBias,
+        kTowardsNumber = 0,
+        kTowardsString
+    };
+    
+protected:
+
+    struct SkOperatorAttributes {
+        unsigned int fLeftType : 3; // SkOpType, but only lower values
+        unsigned int fRightType : 3;     // SkOpType, but only lower values
+        SkOpBias fBias : 1;
+    };
+
+    struct SkSuppress { // !!! could be compressed to a long
+        SkOp fOperator; // operand which enabled suppression
+        int fOpStackDepth; // depth when suppression operator was found
+        SkBool8 fSuppress; // set if suppression happens now, as opposed to later
+        SkBool8 fElse; // set on the : half of ? :
+    };
+
+    static const SkOperatorAttributes gOpAttributes[];
+    static const signed char gPrecedence[];
+    int arithmeticOp(char ch, char nextChar, bool lastPush);
+    void commonCallBack(CallBackType type, UserCallBack& callBack, void* userStorage);
+    bool convertParams(SkTDArray<SkScriptValue>&, const SkFunctionParamType* ,
+                                    int paramTypeCount);
+    void convertToString(SkOperand& operand, SkDisplayTypes type) {
+        SkScriptValue scriptValue;
+        scriptValue.fOperand = operand;
+        scriptValue.fType = type;
+        convertTo(SkType_String, &scriptValue);
+        operand = scriptValue.fOperand;
+    }
+    bool evaluateDot(const char*& script, bool suppressed);
+    bool evaluateDotParam(const char*& script, bool suppressed, const char* field, size_t fieldLength);
+    bool functionParams(const char** scriptPtr, SkTDArray<SkScriptValue>& params);
+    bool handleArrayIndexer(const char** scriptPtr, bool suppressed);
+    bool handleBox(SkScriptValue* value);
+    bool handleFunction(const char** scriptPtr, bool suppressed);
+    bool handleMember(const char* field, size_t len, void* object);
+    bool handleMemberFunction(const char* field, size_t len, void* object, SkTDArray<SkScriptValue>& params);
+//  bool handleObjectToString(void* object);
+    bool handleProperty(bool suppressed);
+    bool handleUnbox(SkScriptValue* scriptValue);
+    bool innerScript(const char** scriptPtr, SkScriptValue* value);
+    int logicalOp(char ch, char nextChar);
+    Error opError();
+    bool processOp();
+    void setAnimateMaker(SkAnimateMaker* maker) { fMaker = maker; }
+    bool setError(Error , const char* pos);
+    enum SkBraceStyle {
+    //  kStructBrace,
+        kArrayBrace,
+        kFunctionBrace
+    };
+
+#if 0
+    SkIntArray(SkBraceStyle) fBraceStack;       // curly, square, function paren
+    SkIntArray(SkOp) fOpStack;
+    SkIntArray(SkOpType) fTypeStack;
+    SkTDOperandArray fOperandStack;
+    SkTDArray<SkSuppress> fSuppressStack;
+#else
+    SkTDStack<SkBraceStyle> fBraceStack;        // curly, square, function paren
+    SkTDStack<SkOp> fOpStack;
+    SkTDStack<SkOpType> fTypeStack;
+    SkTDStack<SkOperand> fOperandStack;
+    SkTDStack<SkSuppress> fSuppressStack;
+#endif
+    SkAnimateMaker* fMaker;
+    SkTDTypedArrayArray fTrackArray;
+    SkTDStringArray fTrackString;
+    const char* fToken; // one-deep stack
+    size_t fTokenLength;
+    SkTDArray<UserCallBack> fUserCallBacks;
+    SkOpType fReturnType;
+    Error fError;
+    int fErrorPosition;
+private:
+    friend class SkTypedArray;
+#ifdef SK_SUPPORT_UNITTEST
+public:
+    static void UnitTest();
+#endif
+};
+
+#ifdef SK_SUPPORT_UNITTEST
+
+struct SkScriptNAnswer {
+    const char* fScript;
+    SkDisplayTypes fType;
+    int32_t fIntAnswer;
+    SkScalar fScalarAnswer;
+    const char* fStringAnswer;
+};
+
+#endif
+
+#endif // SkScript_DEFINED
diff --git a/src/animator/SkScript2.h b/src/animator/SkScript2.h
new file mode 100644
index 0000000..4d8bd8c
--- /dev/null
+++ b/src/animator/SkScript2.h
@@ -0,0 +1,285 @@
+#ifndef SkScript2_DEFINED
+#define SkScript2_DEFINED
+
+#include "SkOperand2.h"
+#include "SkStream.h"
+#include "SkTDArray.h"
+#include "SkTDArray_Experimental.h"
+#include "SkTDict.h"
+#include "SkTDStack.h"
+
+typedef SkLongArray(SkString*) SkTDStringArray; 
+
+class SkAnimateMaker;
+class SkScriptCallBack;
+
+class SkScriptEngine2 {
+public:
+	enum Error {
+		kNoError,
+		kArrayIndexOutOfBounds,
+		kCouldNotFindReferencedID,
+		kFunctionCallFailed,
+		kMemberOpFailed,
+		kPropertyOpFailed
+	};
+
+	enum Attrs {
+		kConstant,
+		kVariable
+	};
+
+	SkScriptEngine2(SkOperand2::OpType returnType);
+	~SkScriptEngine2();
+	bool convertTo(SkOperand2::OpType , SkScriptValue2* );
+	bool evaluateScript(const char** script, SkScriptValue2* value);
+	void forget(SkOpArray* array);
+	Error getError() { return fError; }
+	SkOperand2::OpType getReturnType() { return fReturnType; }
+	void track(SkOpArray* array) { 
+		SkASSERT(fTrackArray.find(array) < 0);  
+		*fTrackArray.append() = array; }
+	void track(SkString* string) { 
+		SkASSERT(fTrackString.find(string) < 0);  
+		*fTrackString.append() = string; 
+	}
+	static bool ConvertTo(SkScriptEngine2* , SkOperand2::OpType toType, SkScriptValue2* value);
+	static SkScalar IntToScalar(int32_t );
+	static bool ValueToString(const SkScriptValue2& value, SkString* string);
+
+	enum Op {		// used by tokenizer attribute table
+		kUnassigned,
+		kAdd,
+		kBitAnd,
+		kBitNot,
+		kBitOr,
+		kDivide,
+		kEqual,
+		kFlipOps,
+		kGreaterEqual,
+		kLogicalAnd,
+		kLogicalNot,
+		kLogicalOr,
+		kMinus,
+		kModulo,
+		kMultiply,
+		kShiftLeft,
+		kShiftRight,	// signed
+		kSubtract,
+		kXor,
+// following not in attribute table
+		kArrayOp,
+		kElse,
+		kIf,
+		kParen,
+		kLastLogicalOp,
+		kArtificialOp = 0x20
+	};
+
+	enum TypeOp {	// generated by tokenizer
+		kNop, // should never get generated
+		kAccumulatorPop,
+		kAccumulatorPush,
+		kAddInt,
+		kAddScalar,
+		kAddString,	// string concat
+		kArrayIndex,
+		kArrayParam,
+		kArrayToken,
+		kBitAndInt,
+		kBitNotInt,
+		kBitOrInt,
+		kBoxToken,
+		kCallback,
+		kDivideInt,
+		kDivideScalar,
+		kDotOperator,
+		kElseOp,
+		kEnd,
+		kEqualInt,
+		kEqualScalar,
+		kEqualString,
+		kFunctionCall,
+		kFlipOpsOp,
+		kFunctionToken,
+		kGreaterEqualInt,
+		kGreaterEqualScalar,
+		kGreaterEqualString,
+		kIfOp,
+		kIntToScalar,
+		kIntToScalar2,
+		kIntToString,
+		kIntToString2,
+		kIntegerAccumulator,
+		kIntegerOperand,
+		kLogicalAndInt,
+		kLogicalNotInt,
+		kLogicalOrInt,
+		kMemberOp,
+		kMinusInt,
+		kMinusScalar,
+		kModuloInt,
+		kModuloScalar,
+		kMultiplyInt,
+		kMultiplyScalar,
+		kPropertyOp,
+		kScalarAccumulator,
+		kScalarOperand,
+		kScalarToInt,
+		kScalarToInt2,
+		kScalarToString,
+		kScalarToString2,
+		kShiftLeftInt,
+		kShiftRightInt,	// signed
+		kStringAccumulator,
+		kStringOperand,
+		kStringToInt,
+		kStringToScalar,
+		kStringToScalar2,
+		kStringTrack,
+		kSubtractInt,
+		kSubtractScalar,
+		kToBool,
+		kUnboxToken,
+		kUnboxToken2,
+		kXorInt,
+		kLastTypeOp
+	};
+
+	enum OpBias {
+		kNoBias,
+		kTowardsNumber = 0,
+		kTowardsString
+	};
+
+protected:
+
+	enum BraceStyle {
+	//	kStructBrace,
+		kArrayBrace,
+		kFunctionBrace
+	};
+
+	enum AddTokenRegister {
+		kAccumulator,
+		kOperand
+	};
+	
+	enum ResultIsBoolean {
+		kResultIsNotBoolean,
+		kResultIsBoolean
+	};
+
+	struct OperatorAttributes {
+		unsigned int fLeftType : 3;	// SkOpType union, but only lower values
+		unsigned int fRightType : 3;	 // SkOpType union, but only lower values
+		OpBias fBias : 1;
+		ResultIsBoolean fResultIsBoolean : 1;
+	};
+	
+	struct Branch {
+		Branch() {
+		}
+		
+		Branch(Op op, int depth, unsigned offset) : fOffset(offset), fOpStackDepth(depth), fOperator(op),
+			fPrimed(kIsNotPrimed), fDone(kIsNotDone) {
+		}
+
+		enum Primed {
+			kIsNotPrimed,
+			kIsPrimed
+		};
+
+		enum Done {
+			kIsNotDone,
+			kIsDone,
+		};
+
+		unsigned fOffset : 16; // offset in generated stream where branch needs to go
+		int fOpStackDepth : 7; // depth when operator was found
+		Op fOperator : 6; // operand which generated branch
+		mutable Primed fPrimed : 1;	// mark when next instruction generates branch
+		Done fDone : 1;	// mark when branch is complete
+		void prime() { fPrimed = kIsPrimed; }
+		void resolve(SkDynamicMemoryWStream* , size_t offset);
+	};
+
+	static const OperatorAttributes gOpAttributes[];
+	static const signed char gPrecedence[];
+	static const TypeOp gTokens[];
+	void addToken(TypeOp );
+	void addTokenConst(SkScriptValue2* , AddTokenRegister , SkOperand2::OpType , TypeOp );
+	void addTokenInt(int );
+	void addTokenScalar(SkScalar );
+	void addTokenString(const SkString& );
+	void addTokenValue(const SkScriptValue2& , AddTokenRegister );
+	int arithmeticOp(char ch, char nextChar, bool lastPush);
+	bool convertParams(SkTDArray<SkScriptValue2>* ,
+		const SkOperand2::OpType* paramTypes, int paramTypeCount);
+	void convertToString(SkOperand2* operand, SkOperand2::OpType type) {
+		SkScriptValue2 scriptValue;
+		scriptValue.fOperand = *operand;
+		scriptValue.fType = type;
+		convertTo(SkOperand2::kString, &scriptValue);
+		*operand = scriptValue.fOperand;
+	}
+	bool evaluateDot(const char*& script);
+	bool evaluateDotParam(const char*& script, const char* field, size_t fieldLength);
+	bool functionParams(const char** scriptPtr, SkTDArray<SkScriptValue2>* params);
+	size_t getTokenOffset();
+	SkOperand2::OpType getUnboxType(SkOperand2 scriptValue);
+	bool handleArrayIndexer(const char** scriptPtr);
+	bool handleFunction(const char** scriptPtr);
+	bool handleMember(const char* field, size_t len, void* object);
+	bool handleMemberFunction(const char* field, size_t len, void* object, 
+		SkTDArray<SkScriptValue2>* params);
+	bool handleProperty();
+	bool handleUnbox(SkScriptValue2* scriptValue);
+	bool innerScript(const char** scriptPtr, SkScriptValue2* value);
+	int logicalOp(char ch, char nextChar);
+	void processLogicalOp(Op op);
+	bool processOp();
+	void resolveBranch(Branch& );
+//	void setAnimateMaker(SkAnimateMaker* maker) { fMaker = maker; }
+	SkDynamicMemoryWStream fStream;
+	SkDynamicMemoryWStream* fActiveStream;
+	SkTDStack<BraceStyle> fBraceStack;		// curly, square, function paren
+	SkTDStack<Branch> fBranchStack;  // logical operators, slot to store forward branch
+	SkLongArray(SkScriptCallBack*) fCallBackArray;
+	SkTDStack<Op> fOpStack;
+	SkTDStack<SkScriptValue2> fValueStack;
+//	SkAnimateMaker* fMaker;
+	SkLongArray(SkOpArray*) fTrackArray;
+	SkTDStringArray fTrackString;
+	const char* fToken; // one-deep stack
+	size_t fTokenLength;
+	SkOperand2::OpType fReturnType;
+	Error fError;
+	SkOperand2::OpType fAccumulatorType;	// tracking for code generation
+	SkBool fBranchPopAllowed;
+	SkBool fConstExpression;
+	SkBool fOperandInUse;
+private:
+#ifdef SK_DEBUG
+public:
+	void decompile(const unsigned char* , size_t );
+	static void UnitTest();
+	static void ValidateDecompileTable();
+#endif
+};
+
+#ifdef SK_DEBUG
+
+struct SkScriptNAnswer2 {
+	const char* fScript;
+	SkOperand2::OpType fType;
+	int32_t fIntAnswer;
+	SkScalar fScalarAnswer;
+	const char* fStringAnswer;
+};
+
+#endif
+
+
+#endif // SkScript2_DEFINED
+
diff --git a/src/animator/SkScriptCallBack.h b/src/animator/SkScriptCallBack.h
new file mode 100644
index 0000000..725e493
--- /dev/null
+++ b/src/animator/SkScriptCallBack.h
@@ -0,0 +1,58 @@
+#ifndef SkScriptCallBack_DEFINED
+#define SkScriptCallBack_DEFINED
+
+#include "SkOperand2.h"
+#include "SkTDArray_Experimental.h"
+
+class SkScriptCallBack {
+public:
+	enum Type {
+		kBox,
+		kFunction,
+		kMember,
+		kMemberFunction,
+		kProperty,
+		kUnbox
+	};
+
+	virtual bool getReference(const char* , size_t len, SkScriptValue2* result) {  return false; }
+	virtual SkOperand2::OpType getReturnType(size_t ref, SkOperand2*) { 
+		return SkOperand2::kS32; }
+	virtual Type getType() const = 0;
+};
+
+class SkScriptCallBackConvert : public SkScriptCallBack {
+public:
+	virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) = 0;
+};
+
+class SkScriptCallBackFunction : public SkScriptCallBack {
+public:
+	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) = 0;
+	virtual Type getType() const { return kFunction; }
+	virtual bool invoke(size_t ref, SkOpArray* params, SkOperand2* value) = 0;
+};
+
+class SkScriptCallBackMember: public SkScriptCallBack {
+public:
+	bool getMemberReference(const char* , size_t len, void* object, SkScriptValue2* ref);
+	virtual Type getType() const { return kMember; }
+	virtual bool invoke(size_t ref, void* object, SkOperand2* value) = 0;
+};
+
+class SkScriptCallBackMemberFunction : public SkScriptCallBack {
+public:
+	bool getMemberReference(const char* , size_t len, void* object, SkScriptValue2* ref);
+	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) = 0;
+	virtual Type getType() const { return kMemberFunction; }
+	virtual bool invoke(size_t ref, void* object, SkOpArray* params, SkOperand2* value) = 0;
+};
+
+class SkScriptCallBackProperty : public SkScriptCallBack {
+public:
+	virtual bool getConstValue(const char* name, size_t len, SkOperand2* value) { return false; }
+	virtual bool getResult(size_t ref, SkOperand2* answer) { return false; }
+	virtual Type getType() const { return kProperty; }
+};
+
+#endif // SkScriptCallBack_DEFINED
diff --git a/src/animator/SkScriptDecompile.cpp b/src/animator/SkScriptDecompile.cpp
new file mode 100644
index 0000000..d582d33
--- /dev/null
+++ b/src/animator/SkScriptDecompile.cpp
@@ -0,0 +1,221 @@
+/* libs/graphics/animator/SkScriptDecompile.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 "SkScript2.h"
+
+#ifdef SK_DEBUG
+
+#define TypeOpName(op) {SkScriptEngine2::op, #op }
+
+static const struct OpName {
+    SkScriptEngine2::TypeOp fOp;
+    const char* fName;
+} gOpNames[] = {
+    TypeOpName(kNop), // should never get generated
+    TypeOpName(kAccumulatorPop),
+    TypeOpName(kAccumulatorPush),
+    TypeOpName(kAddInt),
+    TypeOpName(kAddScalar),
+    TypeOpName(kAddString), // string concat
+    TypeOpName(kArrayIndex),
+    TypeOpName(kArrayParam),
+    TypeOpName(kArrayToken),
+    TypeOpName(kBitAndInt),
+    TypeOpName(kBitNotInt),
+    TypeOpName(kBitOrInt),
+    TypeOpName(kBoxToken),
+    TypeOpName(kCallback),
+    TypeOpName(kDivideInt),
+    TypeOpName(kDivideScalar),
+    TypeOpName(kDotOperator),
+    TypeOpName(kElseOp),
+    TypeOpName(kEnd),
+    TypeOpName(kEqualInt),
+    TypeOpName(kEqualScalar),
+    TypeOpName(kEqualString),
+    TypeOpName(kFunctionCall),
+    TypeOpName(kFlipOpsOp),
+    TypeOpName(kFunctionToken),
+    TypeOpName(kGreaterEqualInt),
+    TypeOpName(kGreaterEqualScalar),
+    TypeOpName(kGreaterEqualString),
+    TypeOpName(kIfOp),
+    TypeOpName(kIntToScalar),
+    TypeOpName(kIntToScalar2),
+    TypeOpName(kIntToString),
+    TypeOpName(kIntToString2),
+    TypeOpName(kIntegerAccumulator),
+    TypeOpName(kIntegerOperand),
+    TypeOpName(kLogicalAndInt),
+    TypeOpName(kLogicalNotInt),
+    TypeOpName(kLogicalOrInt),
+    TypeOpName(kMemberOp),
+    TypeOpName(kMinusInt),
+    TypeOpName(kMinusScalar),
+    TypeOpName(kModuloInt),
+    TypeOpName(kModuloScalar),
+    TypeOpName(kMultiplyInt),
+    TypeOpName(kMultiplyScalar),
+    TypeOpName(kPropertyOp),
+    TypeOpName(kScalarAccumulator),
+    TypeOpName(kScalarOperand),
+    TypeOpName(kScalarToInt),
+    TypeOpName(kScalarToInt2),
+    TypeOpName(kScalarToString),
+    TypeOpName(kScalarToString2),
+    TypeOpName(kShiftLeftInt),
+    TypeOpName(kShiftRightInt), // signed
+    TypeOpName(kStringAccumulator),
+    TypeOpName(kStringOperand),
+    TypeOpName(kStringToInt),
+    TypeOpName(kStringToScalar),
+    TypeOpName(kStringToScalar2),
+    TypeOpName(kStringTrack),
+    TypeOpName(kSubtractInt),
+    TypeOpName(kSubtractScalar),
+    TypeOpName(kToBool),
+    TypeOpName(kUnboxToken),
+    TypeOpName(kUnboxToken2),
+    TypeOpName(kXorInt)
+};
+
+static size_t gOpNamesSize = sizeof(gOpNames) / sizeof(gOpNames[0]);
+
+#define OperandName(op) {SkOperand2::op, #op }
+
+static const struct OperName {
+    SkOperand2::OpType fType;
+    const char* fName;
+} gOperandNames[] = {
+    OperandName(kNoType),
+    OperandName(kS32),
+    OperandName(kScalar),
+    OperandName(kString),
+    OperandName(kArray),
+    OperandName(kObject)
+};  
+
+static size_t gOperandNamesSize = sizeof(gOperandNames) / sizeof(gOperandNames[0]);
+
+// check to see that there are no missing or duplicate entries
+void SkScriptEngine2::ValidateDecompileTable() {
+    SkScriptEngine2::TypeOp op = SkScriptEngine2::kNop;
+    int index;
+    for (index = 0; index < gOpNamesSize; index++) {
+        SkASSERT(gOpNames[index].fOp == op);
+        op = (SkScriptEngine2::TypeOp) (op + 1);
+    }
+    index = 0;
+    SkOperand2::OpType type = SkOperand2::kNoType;
+    SkASSERT(gOperandNames[index].fType == type);
+    for (; index < gOperandNamesSize - 1; ) {
+        type = (SkOperand2::OpType) (1 << index);
+        SkASSERT(gOperandNames[++index].fType == type);
+    }
+}
+
+void SkScriptEngine2::decompile(const unsigned char* start, size_t length) {
+    SkASSERT(length > 0);
+    const unsigned char* opCode = start;
+    do {
+        SkASSERT(opCode - start < length);
+        SkScriptEngine2::TypeOp op = (SkScriptEngine2::TypeOp) *opCode++;
+        SkASSERT(op < gOpNamesSize);
+        SkDebugf("%d: %s", opCode - start - 1, gOpNames[op].fName);
+        switch (op) {
+        case SkScriptEngine2::kCallback: {
+            int index;
+            memcpy(&index, opCode, sizeof(index));
+            opCode += sizeof(index);
+            SkDebugf(" index: %d", index);
+            } break;
+        case SkScriptEngine2::kFunctionCall: 
+        case SkScriptEngine2::kMemberOp:
+        case SkScriptEngine2::kPropertyOp: {
+            size_t ref;
+            memcpy(&ref, opCode, sizeof(ref));
+            opCode += sizeof(ref);
+            SkDebugf(" ref: %d", ref);
+            } break;
+        case SkScriptEngine2::kIntegerAccumulator:
+        case SkScriptEngine2::kIntegerOperand: {
+            int32_t integer;
+            memcpy(&integer, opCode, sizeof(integer));
+            opCode += sizeof(int32_t);
+            SkDebugf(" integer: %d", integer);
+            } break;
+        case SkScriptEngine2::kScalarAccumulator:
+        case SkScriptEngine2::kScalarOperand: {
+            SkScalar scalar;
+            memcpy(&scalar, opCode, sizeof(scalar));
+            opCode += sizeof(SkScalar);
+#ifdef SK_CAN_USE_FLOAT
+            SkDebugf(" scalar: %g", SkScalarToFloat(scalar));
+#else
+            SkDebugf(" scalar: %x", scalar);
+#endif
+            } break;
+        case SkScriptEngine2::kStringAccumulator:
+        case SkScriptEngine2::kStringOperand: {
+            int size;
+            SkString* strPtr = new SkString();
+            memcpy(&size, opCode, sizeof(size));
+            opCode += sizeof(size);
+            strPtr->set((char*) opCode, size);
+            opCode += size;
+            SkDebugf(" string: %s", strPtr->c_str());
+            delete strPtr;
+            } break;
+        case SkScriptEngine2::kBoxToken: {
+            SkOperand2::OpType type;
+            memcpy(&type, opCode, sizeof(type));
+            opCode += sizeof(type);
+            int index = 0;
+            if (type == 0)
+                SkDebugf(" type: %s", gOperandNames[index].fName);
+            else {
+                while (type != 0) {
+                    SkASSERT(index + 1 < gOperandNamesSize);
+                    if (type & (1 << index)) {
+                        type = (SkOperand2::OpType) (type & ~(1 << index));
+                        SkDebugf(" type: %s", gOperandNames[index + 1].fName);
+                    }
+                    index++;
+                }
+            }
+            } break;
+        case SkScriptEngine2::kIfOp:
+        case SkScriptEngine2::kLogicalAndInt:
+        case SkScriptEngine2::kElseOp:
+        case SkScriptEngine2::kLogicalOrInt: {
+            int size;
+            memcpy(&size, opCode, sizeof(size));
+            opCode += sizeof(size);
+            SkDebugf(" offset (address): %d (%d)", size, opCode - start + size);
+            } break;
+        case SkScriptEngine2::kEnd:
+            goto done;
+        case SkScriptEngine2::kNop:
+                SkASSERT(0);
+    }
+    SkDebugf("\n");
+    } while (true);
+done:
+    SkDebugf("\n");
+}
+
+#endif
diff --git a/src/animator/SkScriptRuntime.cpp b/src/animator/SkScriptRuntime.cpp
new file mode 100644
index 0000000..6d8c208
--- /dev/null
+++ b/src/animator/SkScriptRuntime.cpp
@@ -0,0 +1,342 @@
+#include "SkScriptRuntime.h"
+#include "SkScript2.h"
+#include "SkParse.h"
+#include "SkScriptCallBack.h"
+#include "SkString.h"
+#include "SkOpArray.h"
+
+// script tokenizer
+
+// turn text into token string
+// turn number literals into inline UTF8-style values
+// process operators to turn standard notation into stack notation
+
+// defer processing until the tokens can all be resolved
+// then, turn token strings into indices into the appropriate tables / dictionaries
+
+// consider: const evaluation?
+
+// replace script string with script tokens preceeded by special value
+
+// need second version of script plugins that return private index of found value?
+	// then would need in script index of plugin, private index
+
+// encode brace stack push/pop as opcodes
+
+// should token script enocde type where possible?
+
+// current flow:
+	// strip whitespace
+	// if in array brace [ recurse, continue
+	// if token, handle function, or array, or property (continue)
+	// parse number, continue
+	// parse token, continue
+	// parse string literal, continue
+	// if dot operator, handle dot, continue
+	// if [ , handle array literal or accessor, continue
+	// if ), pop (if function, break)
+	// if ], pop ; if ',' break
+	// handle logical ops
+	// or, handle arithmetic ops
+	// loop
+
+// !!! things to do
+	// add separate processing loop to advance while suppressed
+	// or, include jump offset to skip suppressed code?
+
+SkScriptRuntime::~SkScriptRuntime() {
+	for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
+		delete *stringPtr;
+	for (SkOpArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
+		delete *arrayPtr;
+}
+
+bool SkScriptRuntime::executeTokens(unsigned char* opCode) {
+	SkOperand2 operand[2];	// 1=accumulator and 2=operand
+	SkScriptEngine2::TypeOp op;
+	size_t ref;
+	int index, size;
+	int registerLoad;
+	SkScriptCallBack* callBack SK_INIT_TO_AVOID_WARNING;
+	do {
+	switch ((op = (SkScriptEngine2::TypeOp) *opCode++)) {
+		case SkScriptEngine2::kArrayToken:	// create an array
+			operand[0].fArray = new SkOpArray(SkOperand2::kNoType /*fReturnType*/);
+			break;
+		case SkScriptEngine2::kArrayIndex:	// array accessor
+			index = operand[1].fS32;
+			if (index >= operand[0].fArray->count()) {
+				fError = kArrayIndexOutOfBounds;
+				return false;
+			}
+			operand[0] = operand[0].fArray->begin()[index];
+			break;
+		case SkScriptEngine2::kArrayParam:	// array initializer, or function param
+			*operand[0].fArray->append() = operand[1];
+			break;
+		case SkScriptEngine2::kCallback:
+			memcpy(&index, opCode, sizeof(index));
+			opCode += sizeof(index);
+			callBack = fCallBackArray[index];
+			break;
+		case SkScriptEngine2::kFunctionCall: {
+			memcpy(&ref, opCode, sizeof(ref));
+			opCode += sizeof(ref);
+			SkScriptCallBackFunction* callBackFunction = (SkScriptCallBackFunction*) callBack;
+			if (callBackFunction->invoke(ref, operand[0].fArray, /* params */
+					&operand[0] /* result */) == false) {
+				fError = kFunctionCallFailed;
+				return false;
+			}
+			} break;
+		case SkScriptEngine2::kMemberOp: {
+			memcpy(&ref, opCode, sizeof(ref));
+			opCode += sizeof(ref);
+			SkScriptCallBackMember* callBackMember = (SkScriptCallBackMember*) callBack;
+			if (callBackMember->invoke(ref, operand[0].fObject, &operand[0]) == false) {
+				fError = kMemberOpFailed;
+				return false;
+			}
+			} break;
+		case SkScriptEngine2::kPropertyOp: {
+			memcpy(&ref, opCode, sizeof(ref));
+			opCode += sizeof(ref);
+			SkScriptCallBackProperty* callBackProperty = (SkScriptCallBackProperty*) callBack;
+			if (callBackProperty->getResult(ref, &operand[0])== false) {
+				fError = kPropertyOpFailed;
+				return false;
+			}
+			} break;
+		case SkScriptEngine2::kAccumulatorPop:
+			fRunStack.pop(&operand[0]);
+			break;
+		case SkScriptEngine2::kAccumulatorPush:
+			*fRunStack.push() = operand[0];
+			break;
+		case SkScriptEngine2::kIntegerAccumulator:
+		case SkScriptEngine2::kIntegerOperand:
+			registerLoad = op - SkScriptEngine2::kIntegerAccumulator;
+			memcpy(&operand[registerLoad].fS32, opCode, sizeof(int32_t));
+			opCode += sizeof(int32_t);
+			break;
+		case SkScriptEngine2::kScalarAccumulator:
+		case SkScriptEngine2::kScalarOperand:
+			registerLoad = op - SkScriptEngine2::kScalarAccumulator;
+			memcpy(&operand[registerLoad].fScalar, opCode, sizeof(SkScalar));
+			opCode += sizeof(SkScalar);
+			break;
+		case SkScriptEngine2::kStringAccumulator:
+		case SkScriptEngine2::kStringOperand: {
+			SkString* strPtr = new SkString();
+			track(strPtr);
+			registerLoad = op - SkScriptEngine2::kStringAccumulator;
+			memcpy(&size, opCode, sizeof(size));
+			opCode += sizeof(size);
+			strPtr->set((char*) opCode, size);
+			opCode += size;
+			operand[registerLoad].fString = strPtr;
+			} break;
+		case SkScriptEngine2::kStringTrack: // call after kObjectToValue
+			track(operand[0].fString);
+			break;
+		case SkScriptEngine2::kBoxToken: {
+			SkOperand2::OpType type;
+			memcpy(&type, opCode, sizeof(type));
+			opCode += sizeof(type);
+			SkScriptCallBackConvert* callBackBox = (SkScriptCallBackConvert*) callBack;
+			if (callBackBox->convert(type, &operand[0]) == false)
+				return false;
+			} break;
+		case SkScriptEngine2::kUnboxToken:
+		case SkScriptEngine2::kUnboxToken2: {
+			SkScriptCallBackConvert* callBackUnbox = (SkScriptCallBackConvert*) callBack;
+			if (callBackUnbox->convert(SkOperand2::kObject, &operand[0]) == false)
+				return false;
+			} break;
+		case SkScriptEngine2::kIfOp:
+		case SkScriptEngine2::kLogicalAndInt:
+			memcpy(&size, opCode, sizeof(size));
+			opCode += sizeof(size);
+			if (operand[0].fS32 == 0)
+				opCode += size; // skip to else (or end of if predicate)
+			break;
+		case SkScriptEngine2::kElseOp:
+			memcpy(&size, opCode, sizeof(size));
+			opCode += sizeof(size);
+			opCode += size; // if true: after predicate, always skip to end of else
+			break;
+		case SkScriptEngine2::kLogicalOrInt:
+			memcpy(&size, opCode, sizeof(size));
+			opCode += sizeof(size);
+			if (operand[0].fS32 != 0)
+				opCode += size; // skip to kToBool opcode after || predicate
+			break;
+		// arithmetic conversion ops
+		case SkScriptEngine2::kFlipOpsOp:
+			SkTSwap(operand[0], operand[1]);
+			break;
+		case SkScriptEngine2::kIntToString: 
+		case SkScriptEngine2::kIntToString2: 
+		case SkScriptEngine2::kScalarToString:
+		case SkScriptEngine2::kScalarToString2:{
+			SkString* strPtr = new SkString();
+			track(strPtr);
+			if (op == SkScriptEngine2::kIntToString || op == SkScriptEngine2::kIntToString2)
+				strPtr->appendS32(operand[op - SkScriptEngine2::kIntToString].fS32);
+			else
+				strPtr->appendScalar(operand[op - SkScriptEngine2::kScalarToString].fScalar);
+			operand[0].fString = strPtr;
+			} break;
+		case SkScriptEngine2::kIntToScalar:
+		case SkScriptEngine2::kIntToScalar2:
+			operand[0].fScalar = SkScriptEngine2::IntToScalar(operand[op - SkScriptEngine2::kIntToScalar].fS32);
+			break;
+		case SkScriptEngine2::kStringToInt:
+			if (SkParse::FindS32(operand[0].fString->c_str(), &operand[0].fS32) == false)
+				return false; 
+			break;
+		case SkScriptEngine2::kStringToScalar:
+		case SkScriptEngine2::kStringToScalar2:
+			if (SkParse::FindScalar(operand[0].fString->c_str(), 
+					&operand[op - SkScriptEngine2::kStringToScalar].fScalar) == false) 
+				return false; 
+			break;
+		case SkScriptEngine2::kScalarToInt:
+			operand[0].fS32 = SkScalarFloor(operand[0].fScalar);
+			break;
+		// arithmetic ops
+		case SkScriptEngine2::kAddInt:
+			operand[0].fS32 += operand[1].fS32;
+			break;
+		case SkScriptEngine2::kAddScalar:
+			operand[0].fScalar += operand[1].fScalar;
+			break;
+		case SkScriptEngine2::kAddString:
+//			if (fTrackString.find(operand[1].fString) < 0) {
+//				operand[1].fString = SkNEW_ARGS(SkString, (*operand[1].fString));
+//				track(operand[1].fString);
+//			}
+			operand[0].fString->append(*operand[1].fString);
+			break;
+		case SkScriptEngine2::kBitAndInt:
+			operand[0].fS32 &= operand[1].fS32;
+			break;
+		case SkScriptEngine2::kBitNotInt:
+			operand[0].fS32 = ~operand[0].fS32;
+			break;
+		case SkScriptEngine2::kBitOrInt:
+			operand[0].fS32 |= operand[1].fS32;
+			break;
+		case SkScriptEngine2::kDivideInt:
+			SkASSERT(operand[1].fS32 != 0);
+			if (operand[1].fS32 == 0)
+				operand[0].fS32 = operand[0].fS32 == 0 ? SK_NaN32 : 
+					operand[0].fS32 > 0 ? SK_MaxS32 : -SK_MaxS32;
+			else
+			if (operand[1].fS32 != 0) // throw error on divide by zero?
+				operand[0].fS32 /= operand[1].fS32;
+			break;
+		case SkScriptEngine2::kDivideScalar:
+			if (operand[1].fScalar == 0)
+				operand[0].fScalar = operand[0].fScalar == 0 ? SK_ScalarNaN : 
+					operand[0].fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax;
+			else
+				operand[0].fScalar = SkScalarDiv(operand[0].fScalar, operand[1].fScalar);
+			break;
+		case SkScriptEngine2::kEqualInt:
+			operand[0].fS32 = operand[0].fS32 == operand[1].fS32;
+			break;
+		case SkScriptEngine2::kEqualScalar:
+			operand[0].fS32 = operand[0].fScalar == operand[1].fScalar;
+			break;
+		case SkScriptEngine2::kEqualString:
+			operand[0].fS32 = *operand[0].fString == *operand[1].fString;
+			break;
+		case SkScriptEngine2::kGreaterEqualInt:
+			operand[0].fS32 = operand[0].fS32 >= operand[1].fS32;
+			break;
+		case SkScriptEngine2::kGreaterEqualScalar:
+			operand[0].fS32 = operand[0].fScalar >= operand[1].fScalar;
+			break;
+		case SkScriptEngine2::kGreaterEqualString:
+			operand[0].fS32 = strcmp(operand[0].fString->c_str(), operand[1].fString->c_str()) >= 0;
+			break;
+		case SkScriptEngine2::kToBool:
+			operand[0].fS32 = !! operand[0].fS32;
+			break;
+		case SkScriptEngine2::kLogicalNotInt:
+			operand[0].fS32 = ! operand[0].fS32;
+			break;
+		case SkScriptEngine2::kMinusInt:
+			operand[0].fS32 = -operand[0].fS32;
+			break;
+		case SkScriptEngine2::kMinusScalar:
+			operand[0].fScalar = -operand[0].fScalar;
+			break;
+		case SkScriptEngine2::kModuloInt:
+			operand[0].fS32 %= operand[1].fS32;
+			break;
+		case SkScriptEngine2::kModuloScalar:
+			operand[0].fScalar = SkScalarMod(operand[0].fScalar, operand[1].fScalar);
+			break;
+		case SkScriptEngine2::kMultiplyInt:
+			operand[0].fS32 *= operand[1].fS32;
+			break;
+		case SkScriptEngine2::kMultiplyScalar:
+			operand[0].fScalar = SkScalarMul(operand[0].fScalar, operand[1].fScalar);
+			break;
+		case SkScriptEngine2::kShiftLeftInt:
+			operand[0].fS32 <<= operand[1].fS32;
+			break;
+		case SkScriptEngine2::kShiftRightInt:
+			operand[0].fS32 >>= operand[1].fS32;
+			break;
+		case SkScriptEngine2::kSubtractInt:
+			operand[0].fS32 -= operand[1].fS32;
+			break;
+		case SkScriptEngine2::kSubtractScalar:
+			operand[0].fScalar -= operand[1].fScalar;
+			break;
+		case SkScriptEngine2::kXorInt:
+			operand[0].fS32 ^= operand[1].fS32;
+			break;
+		case SkScriptEngine2::kEnd:
+			goto done;
+		case SkScriptEngine2::kNop:
+				SkASSERT(0);
+	}
+	} while (true);
+done:
+	fRunStack.push(operand[0]);
+	return true;
+}
+
+bool SkScriptRuntime::getResult(SkOperand2* result) {
+	if (fRunStack.count() == 0)
+		return false;
+	fRunStack.pop(result);
+	return true;
+}
+
+void SkScriptRuntime::track(SkOpArray* array) { 
+	SkASSERT(fTrackArray.find(array) < 0);  
+	*fTrackArray.append() = array; 
+}
+
+void SkScriptRuntime::track(SkString* string) { 
+	SkASSERT(fTrackString.find(string) < 0);  
+	*fTrackString.append() = string; 
+}
+
+void SkScriptRuntime::untrack(SkOpArray* array) {
+	int index = fTrackArray.find(array);
+	SkASSERT(index >= 0);
+	fTrackArray.begin()[index] = NULL;
+}
+
+void SkScriptRuntime::untrack(SkString* string) {
+	int index = fTrackString.find(string);
+	SkASSERT(index >= 0);
+	fTrackString.begin()[index] = NULL;
+}
+
diff --git a/src/animator/SkScriptRuntime.h b/src/animator/SkScriptRuntime.h
new file mode 100644
index 0000000..c864fe4
--- /dev/null
+++ b/src/animator/SkScriptRuntime.h
@@ -0,0 +1,43 @@
+#ifndef SkScriptRuntime_DEFINED
+#define SkScriptRuntime_DEFINED
+
+#include "SkOperand2.h"
+#include "SkTDArray_Experimental.h"
+#include "SkTDStack.h"
+
+class SkScriptCallBack;
+
+typedef SkLongArray(SkString*) SkTDStringArray; 
+typedef SkLongArray(SkScriptCallBack*) SkTDScriptCallBackArray; 
+
+class SkScriptRuntime {
+public:
+	enum SkError {
+		kNoError,
+		kArrayIndexOutOfBounds,
+		kCouldNotFindReferencedID,
+		kFunctionCallFailed,
+		kMemberOpFailed,
+		kPropertyOpFailed
+	};
+
+	SkScriptRuntime(SkTDScriptCallBackArray& callBackArray) : fCallBackArray(callBackArray)
+		{  }
+	~SkScriptRuntime();
+	bool executeTokens(unsigned char* opCode);
+	bool getResult(SkOperand2* result);
+	void untrack(SkOpArray* array);
+	void untrack(SkString* string);
+private:
+	void track(SkOpArray* array);
+	void track(SkString* string);
+	SkTDScriptCallBackArray& fCallBackArray;
+	SkError fError;
+	SkTDStack<SkOperand2> fRunStack;
+	SkLongArray(SkOpArray*) fTrackArray;
+	SkTDStringArray fTrackString;
+	// illegal
+	SkScriptRuntime& operator=(const SkScriptRuntime&);
+};
+
+#endif // SkScriptRuntime_DEFINED
\ No newline at end of file
diff --git a/src/animator/SkScriptTokenizer.cpp b/src/animator/SkScriptTokenizer.cpp
new file mode 100644
index 0000000..d75e68e
--- /dev/null
+++ b/src/animator/SkScriptTokenizer.cpp
@@ -0,0 +1,1514 @@
+#include "SkScript2.h"
+#include "SkFloatingPoint.h"
+#include "SkMath.h"
+#include "SkParse.h"
+#include "SkScriptCallBack.h"
+#include "SkScriptRuntime.h"
+#include "SkString.h"
+#include "SkOpArray.h"
+
+const SkScriptEngine2::OperatorAttributes SkScriptEngine2::gOpAttributes[] = {
+{ SkOperand2::kNoType },
+{ SkOperand2::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::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kDivide
+{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), 
+    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar |SkOperand2:: kString), kTowardsNumber, 
+    kResultIsBoolean }, // kEqual
+{ SkOperand2::kS32 },     // 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::OpType(SkOperand2::kS32 | SkOperand2::kScalar), 
+    SkOperand2::OpType(SkOperand2::kS32 |SkOperand2:: kScalar), kNoBias }, // 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), 
+    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kSubtract
+{ SkOperand2::kS32, SkOperand2::kS32, kNoBias } // kXor
+};
+
+#define kBracketPrecedence 16
+#define kIfElsePrecedence 15
+
+const signed char SkScriptEngine2::gPrecedence[] = {
+    17, // kUnassigned,
+    6, // kAdd,
+    10, // kBitAnd,
+    4, // kBitNot,
+    12, // kBitOr,
+    5, // kDivide,
+    9, // kEqual,
+    -1, // kFlipOps,
+    8, // kGreaterEqual,
+    13, // kLogicalAnd,
+    4, // kLogicalNot,
+    14, // kLogicalOr,
+    4, // kMinus,
+    5, // kModulo,
+    5, // kMultiply,
+    7, // kShiftLeft,
+    7, // kShiftRight,    // signed
+    6, // kSubtract,
+    11, // kXor
+    kBracketPrecedence, // kArrayOp
+    kIfElsePrecedence, // kElse
+    kIfElsePrecedence, // kIf
+    kBracketPrecedence, // kParen
+};
+
+const SkScriptEngine2::TypeOp SkScriptEngine2::gTokens[] = {
+    kNop, // unassigned
+    kAddInt, // kAdd,
+    kBitAndInt, // kBitAnd,
+    kBitNotInt, // kBitNot,
+    kBitOrInt, // kBitOr,
+    kDivideInt, // kDivide,
+    kEqualInt, // kEqual,
+    kFlipOpsOp, // kFlipOps,
+    kGreaterEqualInt, // kGreaterEqual,
+    kLogicalAndInt, // kLogicalAnd,
+    kLogicalNotInt, // kLogicalNot,
+    kLogicalOrInt, // kLogicalOr,
+    kMinusInt, // kMinus,
+    kModuloInt, // kModulo,
+    kMultiplyInt, // kMultiply,
+    kShiftLeftInt, // kShiftLeft,
+    kShiftRightInt, // kShiftRight,    // signed
+    kSubtractInt, // kSubtract,
+    kXorInt // kXor
+};
+
+static inline bool is_between(int c, int min, int max)
+{
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_ws(int c)
+{
+    return is_between(c, 1, 32);
+}
+
+static int token_length(const char* start) {
+    char ch = start[0];
+    if (! is_between(ch, 'a' , 'z') &&  ! is_between(ch, 'A', 'Z') && ch != '_' && ch != '$')
+        return -1;
+    int length = 0;
+    do
+        ch = start[++length];
+    while (is_between(ch, 'a' , 'z') || is_between(ch, 'A', 'Z') || is_between(ch, '0', '9') ||
+           ch == '_' || ch == '$');
+    return length;
+}
+
+SkScriptEngine2::SkScriptEngine2(SkOperand2::OpType returnType) : fActiveStream(&fStream),
+fTokenLength(0), fReturnType(returnType), fError(kNoError), 
+fAccumulatorType(SkOperand2::kNoType),
+fBranchPopAllowed(true), fConstExpression(true), fOperandInUse(false)
+{
+    Branch branch(kUnassigned, 0, 0);
+    fBranchStack.push(branch);
+    *fOpStack.push() = (Op) kParen;
+}
+
+SkScriptEngine2::~SkScriptEngine2() {
+    for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
+        delete *stringPtr;
+    for (SkOpArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
+        delete *arrayPtr;
+}
+
+void SkScriptEngine2::addToken(SkScriptEngine2::TypeOp op) {
+    int limit = fBranchStack.count() - 1;
+    for (int index = 0; index < limit; index++) {
+        Branch& branch = fBranchStack.index(index);
+        if (branch.fPrimed == Branch::kIsPrimed)
+            resolveBranch(branch);
+    }
+    if (fBranchPopAllowed) {
+        while (fBranchStack.top().fDone == Branch::kIsDone)
+            fBranchStack.pop();
+    }
+    unsigned char charOp = (unsigned char) op;
+    fActiveStream->write(&charOp, sizeof(charOp));
+}
+
+void SkScriptEngine2::addTokenConst(SkScriptValue2* value, AddTokenRegister reg, 
+                                    SkOperand2::OpType toType, SkScriptEngine2::TypeOp op) {
+    if (value->fIsConstant == SkScriptValue2::kConstant && convertTo(toType, value))
+        return;
+    addTokenValue(*value, reg);
+    addToken(op);
+    value->fIsWritten = SkScriptValue2::kWritten;
+    value->fType = toType;
+}
+
+void SkScriptEngine2::addTokenInt(int integer) {
+    fActiveStream->write(&integer, sizeof(integer));
+}
+
+void SkScriptEngine2::addTokenScalar(SkScalar scalar) {
+    fActiveStream->write(&scalar, sizeof(scalar));
+}
+
+void SkScriptEngine2::addTokenString(const SkString& string) {
+    int size = string.size();
+    addTokenInt(size);
+    fActiveStream->write(string.c_str(), size);
+}
+
+void SkScriptEngine2::addTokenValue(const SkScriptValue2& value, AddTokenRegister reg) {
+    if (value.isConstant() == false) {
+        if (reg == kAccumulator) {
+            if (fAccumulatorType == SkOperand2::kNoType)
+                addToken(kAccumulatorPop);
+        } else {
+            ; // !!! incomplete?
+        }
+        return;
+    }
+    if (reg == kAccumulator && fAccumulatorType != SkOperand2::kNoType)
+        addToken(kAccumulatorPush);
+    switch (value.fType) {
+        case SkOperand2::kS32:
+            addToken(reg == kAccumulator ? kIntegerAccumulator : kIntegerOperand);
+            addTokenInt(value.fOperand.fS32);
+            if (reg == kAccumulator)
+                fAccumulatorType = SkOperand2::kS32;
+            else
+                fOperandInUse = true;
+            break;
+        case SkOperand2::kScalar:
+            addToken(reg == kAccumulator ? kScalarAccumulator : kScalarOperand);
+            addTokenScalar(value.fOperand.fScalar);
+            if (reg == kAccumulator)
+                fAccumulatorType = SkOperand2::kScalar;
+            else
+                fOperandInUse = true;
+            break;
+        case SkOperand2::kString:
+            addToken(reg == kAccumulator ? kStringAccumulator : kStringOperand);
+            addTokenString(*value.fOperand.fString);
+            if (reg == kAccumulator)
+                fAccumulatorType = SkOperand2::kString;
+            else
+                fOperandInUse = true;
+            break;
+        default:
+            SkASSERT(0); //!!! not implemented yet
+    }
+}
+
+int SkScriptEngine2::arithmeticOp(char ch, char nextChar, bool lastPush) {
+    Op op = kUnassigned;
+    bool reverseOperands = false;
+    bool negateResult = false;
+    int advance = 1;
+    switch (ch) {
+        case '+':
+            // !!! ignoring unary plus as implemented here has the side effect of
+            // suppressing errors like +"hi"
+            if (lastPush == false)    // unary plus, don't push an operator
+                goto returnAdv;
+            op = kAdd;
+            break;
+        case '-':
+            op = lastPush ? kSubtract : kMinus;
+            break;
+        case '*':
+            op = kMultiply;
+            break;
+        case '/':
+            op = kDivide;
+            break;
+        case '>':
+            if (nextChar == '>') {
+                op = kShiftRight;
+                goto twoChar;
+            } 
+            op = kGreaterEqual;
+            if (nextChar == '=')
+                goto twoChar;
+                reverseOperands = negateResult = true;
+            break;
+        case '<':
+            if (nextChar == '<') {
+                op = kShiftLeft;
+                goto twoChar;
+            }
+            op = kGreaterEqual;
+            reverseOperands = nextChar == '=';
+            negateResult = ! reverseOperands;
+            advance += reverseOperands;
+            break;
+        case '=':
+            if (nextChar == '=') {
+                op = kEqual;
+                goto twoChar;
+            }
+            break;
+        case '!':
+            if (nextChar == '=') {
+                op = kEqual;
+                negateResult = true;
+twoChar:
+                    advance++;
+                break;
+            } 
+            op = kLogicalNot;
+            break;
+        case '?':
+            op =(Op)  kIf;
+            break;
+        case ':':
+            op = (Op) kElse;
+            break;
+        case '^':
+            op = kXor;
+            break;
+        case '(':
+            *fOpStack.push() = (Op) kParen;
+            goto returnAdv;
+        case '&':
+            SkASSERT(nextChar != '&');
+            op = kBitAnd;
+            break;
+        case '|':
+            SkASSERT(nextChar != '|');
+            op = kBitOr;
+            break;
+        case '%':
+            op = kModulo;
+            break;
+        case '~':
+            op = kBitNot;
+            break;
+    }
+    if (op == kUnassigned)
+        return 0;
+    signed char precedence = gPrecedence[op];
+    do {
+        int idx = 0;
+        Op compare;
+        do {
+            compare = fOpStack.index(idx);
+            if ((compare & kArtificialOp) == 0)
+                break;
+            idx++;
+        } while (true);
+        signed char topPrecedence = gPrecedence[compare];
+        SkASSERT(topPrecedence != -1);
+        if (topPrecedence > precedence || topPrecedence == precedence && 
+            gOpAttributes[op].fLeftType == SkOperand2::kNoType) {
+            break;
+        }
+        processOp();
+    } while (true);
+    if (negateResult)
+        *fOpStack.push() = (Op) (kLogicalNot | kArtificialOp);
+    fOpStack.push(op);
+    if (reverseOperands)
+        *fOpStack.push() = (Op) (kFlipOps | kArtificialOp);
+returnAdv:
+        return advance;
+}
+
+bool SkScriptEngine2::convertParams(SkTDArray<SkScriptValue2>* params, 
+                                    const SkOperand2::OpType* paramTypes, int paramCount) {
+    int count = params->count();
+    if (count > paramCount) {
+        SkASSERT(0);
+        return false;    // too many parameters passed
+    }
+    for (int index = 0; index < count; index++) 
+        convertTo(paramTypes[index], &(*params)[index]);
+    return true;
+}
+
+bool SkScriptEngine2::convertTo(SkOperand2::OpType toType, SkScriptValue2* value ) {
+    SkOperand2::OpType type = value->fType;
+    if (type == toType)
+        return true;
+    if (type == SkOperand2::kObject) {
+        if (handleUnbox(value) == false)
+            return false;
+        return convertTo(toType, value);
+    }
+    return ConvertTo(this, toType, value);
+}
+
+bool SkScriptEngine2::evaluateDot(const char*& script) { 
+    size_t fieldLength = token_length(++script);        // skip dot
+    SkASSERT(fieldLength > 0); // !!! add error handling
+    const char* field = script;
+    script += fieldLength;
+    bool success = handleProperty();
+    if (success == false) {
+        fError = kCouldNotFindReferencedID;
+        goto error;
+    }
+    return evaluateDotParam(script, field, fieldLength);
+error:
+        return false;
+}
+
+bool SkScriptEngine2::evaluateDotParam(const char*& script, const char* field, size_t fieldLength) { 
+    SkScriptValue2& top = fValueStack.top();
+    if (top.fType != SkOperand2::kObject)
+        return false;
+    void* object = top.fOperand.fObject;
+    fValueStack.pop();
+    char ch; // see if it is a simple member or a function
+    while (is_ws(ch = script[0])) 
+        script++;
+    bool success = true;
+    if (ch != '(')
+        success = handleMember(field, fieldLength, object);
+    else {
+        SkTDArray<SkScriptValue2> params;
+        *fBraceStack.push() = kFunctionBrace;
+        success = functionParams(&script, &params);
+        if (success)
+            success = handleMemberFunction(field, fieldLength, object, &params);
+    }
+    return success; 
+}
+
+bool SkScriptEngine2::evaluateScript(const char** scriptPtr, SkScriptValue2* value) {
+    //    fArrayOffset = 0;        // no support for structures for now
+    bool success;
+    const char* inner;
+    if (strncmp(*scriptPtr, "#script:", sizeof("#script:") - 1) == 0) {
+        *scriptPtr += sizeof("#script:") - 1;
+        if (fReturnType == SkOperand2::kNoType || fReturnType == SkOperand2::kString) {
+            success = innerScript(scriptPtr, value);
+            SkASSERT(success);
+            inner = value->fOperand.fString->c_str();
+            scriptPtr = &inner;
+        }
+    }
+    success = innerScript(scriptPtr, value);
+    const char* script = *scriptPtr;
+    char ch;
+    while (is_ws(ch = script[0]))
+        script++;
+    if (ch != '\0') {
+        // error may trigger on scripts like "50,0" that were intended to be written as "[50, 0]"
+        return false;
+    }
+    return success;
+}
+
+void SkScriptEngine2::forget(SkOpArray* array) {
+    if (array->getType() == SkOperand2::kString) {
+        for (int index = 0; index < array->count(); index++) {
+            SkString* string = (*array)[index].fString;
+            int found = fTrackString.find(string);
+            if (found >= 0)
+                fTrackString.remove(found);
+        }
+        return;
+    }
+    if (array->getType() == SkOperand2::kArray) {
+        for (int index = 0; index < array->count(); index++) {
+            SkOpArray* child = (*array)[index].fArray;
+            forget(child);    // forgets children of child
+            int found = fTrackArray.find(child);
+            if (found >= 0)
+                fTrackArray.remove(found);
+        }
+    }
+}
+
+bool SkScriptEngine2::functionParams(const char** scriptPtr, SkTDArray<SkScriptValue2>* params) {
+    (*scriptPtr)++; // skip open paren
+    *fOpStack.push() = (Op) kParen;
+    *fBraceStack.push() = kFunctionBrace;
+    do {
+        SkScriptValue2 value;
+        bool success = innerScript(scriptPtr, &value);
+        SkASSERT(success);
+        if (success == false)
+            return false;
+        *params->append() = value;
+    } while ((*scriptPtr)[-1] == ',');
+    fBraceStack.pop();
+    fOpStack.pop(); // pop paren
+    (*scriptPtr)++; // advance beyond close paren
+    return true;
+}
+
+size_t SkScriptEngine2::getTokenOffset() {
+    return fActiveStream->getOffset();
+}
+
+SkOperand2::OpType SkScriptEngine2::getUnboxType(SkOperand2 scriptValue) {
+    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
+        if ((*callBack)->getType() != SkScriptCallBack::kUnbox)
+            continue;
+        return (*callBack)->getReturnType(0, &scriptValue);
+    }
+    return SkOperand2::kObject;
+}
+
+bool SkScriptEngine2::innerScript(const char** scriptPtr, SkScriptValue2* value) {
+    const char* script = *scriptPtr;
+    char ch;
+    bool lastPush = false;
+    bool success = true;
+    int opBalance = fOpStack.count();
+    int baseBrace = fBraceStack.count();
+    int branchBalance = fBranchStack.count();
+    while ((ch = script[0]) != '\0') {
+        if (is_ws(ch)) {
+            script++;
+            continue;
+        }
+        SkScriptValue2 operand;
+        const char* dotCheck;
+        if (fBraceStack.count() > baseBrace) {
+            if (fBraceStack.top() == kArrayBrace) {
+                SkScriptValue2 tokenValue;
+                success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
+                SkASSERT(success);
+                {
+                    SkOperand2::OpType type = fReturnType;
+                    if (fReturnType == SkOperand2::kNoType) {
+                        // !!! short sighted; in the future, allow each returned array component to carry 
+                        // its own type, and let caller do any needed conversions
+                        if (value->fOperand.fArray->count() == 0)
+                            value->fOperand.fArray->setType(type = tokenValue.fType);
+                        else
+                            type = value->fOperand.fArray->getType();
+                    }
+                    if (tokenValue.fType != type)
+                        convertTo(type, &tokenValue);
+                    *value->fOperand.fArray->append() = tokenValue.fOperand;
+                }
+                lastPush = false;
+                continue;
+            } else
+                SkASSERT(token_length(script) > 0);
+        }
+        if (lastPush != false && fTokenLength > 0) {
+            if (ch == '(') {
+                *fBraceStack.push() = kFunctionBrace;
+                SkString functionName(fToken, fTokenLength);
+                
+                if (handleFunction(&script) == false)
+                    return false;
+                lastPush = true;
+                continue;
+            } else if (ch == '[') {
+                if (handleProperty() == false) {
+                    SkASSERT(0);
+                    return false;
+                }
+                if (handleArrayIndexer(&script) == false)
+                    return false;
+                lastPush = true;
+                continue;
+            } else if (ch != '.') {
+                if (handleProperty() == false) {
+                    SkASSERT(0);
+                    return false;
+                }
+                lastPush = true;
+                continue;
+            }
+        }
+        if (ch == '0' && (script[1] & ~0x20) == 'X') {
+            SkASSERT(lastPush == false);
+            script += 2;
+            script = SkParse::FindHex(script, (uint32_t*) &operand.fOperand.fS32);
+            SkASSERT(script);
+            goto intCommon;
+        }
+        if (lastPush == false && ch == '.')
+            goto scalarCommon;
+        if (ch >= '0' && ch <= '9') {
+            SkASSERT(lastPush == false);
+            dotCheck = SkParse::FindS32(script, &operand.fOperand.fS32);
+            if (dotCheck[0] != '.') {
+                script = dotCheck;
+intCommon:
+                operand.fType = SkOperand2::kS32;
+            } else {
+scalarCommon:
+                script = SkParse::FindScalar(script, &operand.fOperand.fScalar);
+                operand.fType = SkOperand2::kScalar;
+            }
+            operand.fIsConstant = SkScriptValue2::kConstant;
+            fValueStack.push(operand);
+            lastPush = true;
+            continue;
+        }
+        int length = token_length(script);
+        if (length > 0) {
+            SkASSERT(lastPush == false);
+            fToken = script;
+            fTokenLength = length;
+            script += length;
+            lastPush = true;
+            continue;
+        }
+        char startQuote = ch;
+        if (startQuote == '\'' || startQuote == '\"') {
+            SkASSERT(lastPush == false);
+            operand.fOperand.fString = new SkString();
+            ++script;
+            const char* stringStart = script;
+            do {    // measure string
+                if (script[0] == '\\')
+                    ++script;
+                ++script;
+                SkASSERT(script[0]); // !!! throw an error
+            } while (script[0] != startQuote);
+            operand.fOperand.fString->set(stringStart, script - stringStart);
+            script = stringStart;
+            char* stringWrite = operand.fOperand.fString->writable_str();
+            do {    // copy string
+                if (script[0] == '\\')
+                    ++script;
+                *stringWrite++ = script[0];
+                ++script;
+                SkASSERT(script[0]); // !!! throw an error
+            } while (script[0] != startQuote);
+            ++script;
+            track(operand.fOperand.fString);
+            operand.fType = SkOperand2::kString;
+            operand.fIsConstant = SkScriptValue2::kConstant;
+            fValueStack.push(operand);
+            lastPush = true;
+            continue;
+        }
+        if (ch ==  '.') {
+            if (fTokenLength == 0) {
+                SkScriptValue2 scriptValue;
+                SkDEBUGCODE(scriptValue.fOperand.fObject = NULL);
+                int tokenLength = token_length(++script);
+                const char* token = script;
+                script += tokenLength;
+                SkASSERT(fValueStack.count() > 0); // !!! add error handling
+                SkScriptValue2 top;
+                fValueStack.pop(&top);
+                
+                addTokenInt(top.fType);
+                addToken(kBoxToken);
+                top.fType = SkOperand2::kObject;
+                top.fIsConstant = SkScriptValue2::kVariable;
+                fConstExpression = false;
+                fValueStack.push(top);
+                success = evaluateDotParam(script, token, tokenLength);
+                SkASSERT(success);
+                lastPush = true;
+                continue; 
+            }
+            // get next token, and evaluate immediately
+            success = evaluateDot(script);
+            if (success == false) {
+                //                SkASSERT(0);
+                return false;
+            }
+            lastPush = true;
+            continue;
+        }
+        if (ch == '[') {
+            if (lastPush == false) {
+                script++;
+                *fBraceStack.push() = kArrayBrace;
+                operand.fOperand.fArray = value->fOperand.fArray = new SkOpArray(fReturnType);
+                track(value->fOperand.fArray);
+                
+                operand.fType = SkOperand2::kArray;
+                operand.fIsConstant = SkScriptValue2::kVariable;
+                fValueStack.push(operand);
+                continue;
+            }
+            if (handleArrayIndexer(&script) == false)
+                return false;
+            lastPush = true;
+            continue;
+        }
+#if 0 // structs not supported for now
+        if (ch == '{') {
+            if (lastPush == false) {
+                script++;
+                *fBraceStack.push() = kStructBrace;
+                operand.fS32 = 0;
+                *fTypeStack.push() = (SkOpType) kStruct;
+                fOperandStack.push(operand);
+                continue;
+            }
+            SkASSERT(0); // braces in other contexts aren't supported yet
+        }
+#endif
+        if (ch == ')' && fBraceStack.count() > 0) {
+            BraceStyle braceStyle = fBraceStack.top(); 
+            if (braceStyle == kFunctionBrace) {
+                fBraceStack.pop();
+                break;
+            }
+        }
+        if (ch == ',' || ch == ']') {
+            if (ch != ',') {
+                BraceStyle match;
+                fBraceStack.pop(&match);
+                SkASSERT(match == kArrayBrace);
+            }
+            script++;
+            // !!! see if brace or bracket is correct closer
+            break;
+        }
+        char nextChar = script[1];
+        int advance = logicalOp(ch, nextChar);
+        if (advance == 0) 
+            advance = arithmeticOp(ch, nextChar, lastPush);
+        if (advance == 0) // unknown token
+            return false;
+        if (advance > 0)
+            script += advance;
+        lastPush = ch == ']' || ch == ')';
+    }
+    if (fTokenLength > 0) {
+        success = handleProperty();
+        SkASSERT(success);
+    }
+    int branchIndex = 0;
+    branchBalance = fBranchStack.count() - branchBalance;
+    fBranchPopAllowed = false;
+    while (branchIndex < branchBalance) {
+        Branch& branch = fBranchStack.index(branchIndex++);
+        if (branch.fPrimed == Branch::kIsPrimed)
+            break;
+        Op branchOp = branch.fOperator;
+        SkOperand2::OpType lastType = fValueStack.top().fType;
+        addTokenValue(fValueStack.top(), kAccumulator);
+        fValueStack.pop();
+        if (branchOp == kLogicalAnd || branchOp == kLogicalOr) {
+            if (branch.fOperator == kLogicalAnd)
+                branch.prime();
+            addToken(kToBool);
+        } else {
+            resolveBranch(branch);
+            SkScriptValue2 operand;
+            operand.fType = lastType;
+            // !!! note that many branching expressions could be constant
+            // today, we always evaluate branches as returning variables
+            operand.fIsConstant = SkScriptValue2::kVariable;
+            fValueStack.push(operand);
+        }
+        if (branch.fDone == Branch::kIsNotDone)
+            branch.prime();
+    }
+    fBranchPopAllowed = true;
+    while (fBranchStack.top().fDone == Branch::kIsDone)
+        fBranchStack.pop();
+    while (fOpStack.count() > opBalance) {     // leave open paren
+        if (processOp() == false)
+            return false;
+    }
+    SkOperand2::OpType topType = fValueStack.count() > 0 ? fValueStack.top().fType : SkOperand2::kNoType;
+    if (topType != fReturnType &&
+        topType == SkOperand2::kString && fReturnType != SkOperand2::kNoType) { // if result is a string, give handle property a chance to convert it to the property value
+        SkString* string = fValueStack.top().fOperand.fString;
+        fToken = string->c_str();
+        fTokenLength = string->size();
+        fValueStack.pop();
+        success = handleProperty();
+        if (success == false) {    // if it couldn't convert, return string (error?)
+            SkScriptValue2 operand;
+            operand.fType = SkOperand2::kString;
+            operand.fOperand.fString = string;
+            operand.fIsConstant = SkScriptValue2::kVariable;     // !!! ?
+            fValueStack.push(operand);
+        }
+    }
+    if (fStream.getOffset() > 0) {
+        addToken(kEnd);
+#ifdef SK_DEBUG
+        decompile((const unsigned char*)fStream.getStream(), fStream.getOffset());
+#endif
+        SkScriptRuntime runtime(fCallBackArray);
+        runtime.executeTokens((unsigned char*) fStream.getStream());
+        SkScriptValue2 value1;
+        runtime.getResult(&value1.fOperand);
+        value1.fType = fReturnType;
+        fValueStack.push(value1);
+    }
+    if (value) {
+        if (fValueStack.count() == 0)
+            return false;
+        fValueStack.pop(value);
+        if (value->fType != fReturnType && value->fType == SkOperand2::kObject && 
+            fReturnType != SkOperand2::kNoType)
+            convertTo(fReturnType, value);
+    }
+    //    if (fBranchStack.top().fOpStackDepth > fOpStack.count())
+    //        resolveBranch();
+    *scriptPtr = script;
+    return true; // no error
+}
+
+bool SkScriptEngine2::handleArrayIndexer(const char** scriptPtr) {
+    SkScriptValue2 scriptValue;
+    (*scriptPtr)++;
+    *fOpStack.push() = (Op) kParen;
+    *fBraceStack.push() = kArrayBrace;
+    SkOperand2::OpType saveType = fReturnType;
+    fReturnType = SkOperand2::kS32;
+    bool success = innerScript(scriptPtr, &scriptValue);
+    fReturnType = saveType;
+    SkASSERT(success);
+    success = convertTo(SkOperand2::kS32, &scriptValue);
+    SkASSERT(success);
+    int index = scriptValue.fOperand.fS32;
+    fValueStack.pop(&scriptValue);
+    if (scriptValue.fType == SkOperand2::kObject) {
+        success = handleUnbox(&scriptValue);
+        SkASSERT(success);
+        SkASSERT(scriptValue.fType == SkOperand2::kArray);
+    }
+    scriptValue.fType = scriptValue.fOperand.fArray->getType();
+    //    SkASSERT(index >= 0);
+    if ((unsigned) index >= (unsigned) scriptValue.fOperand.fArray->count()) {
+        fError = kArrayIndexOutOfBounds;
+        return false;
+    }
+    scriptValue.fOperand = scriptValue.fOperand.fArray->begin()[index];
+    scriptValue.fIsConstant = SkScriptValue2::kVariable;
+    fValueStack.push(scriptValue);
+    fOpStack.pop(); // pop paren
+    return success;
+}
+
+bool SkScriptEngine2::handleFunction(const char** scriptPtr) {
+    const char* functionName = fToken;
+    size_t functionNameLen = fTokenLength;
+    fTokenLength = 0;
+    SkTDArray<SkScriptValue2> params;
+    bool success = functionParams(scriptPtr, &params);
+    if (success == false)
+        goto done;
+    {
+        for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
+            if ((*callBack)->getType() != SkScriptCallBack::kFunction)
+                continue;
+            SkScriptValue2 callbackResult;
+            success = (*callBack)->getReference(functionName, functionNameLen, &callbackResult);
+            if (success) {
+                callbackResult.fType = (*callBack)->getReturnType(callbackResult.fOperand.fReference, NULL);
+                callbackResult.fIsConstant = SkScriptValue2::kVariable;
+                fValueStack.push(callbackResult);
+                goto done;
+            }
+        }
+    }
+    return false;
+done:
+        fOpStack.pop();
+    return success;
+}
+
+bool SkScriptEngine2::handleMember(const char* field, size_t len, void* object) {
+    bool success = true;
+    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
+        if ((*callBack)->getType() != SkScriptCallBack::kMember)
+            continue;
+        SkScriptValue2 callbackResult;
+        success = (*callBack)->getReference(field, len, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkOperand2::kString)
+                track(callbackResult.fOperand.fString);
+            callbackResult.fIsConstant = SkScriptValue2::kVariable;
+            fValueStack.push(callbackResult);
+            goto done;
+        }
+    }
+    return false;
+done:
+        return success;
+}
+
+bool SkScriptEngine2::handleMemberFunction(const char* field, size_t len, void* object, 
+                                           SkTDArray<SkScriptValue2>* params) {
+    bool success = true;
+    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
+        if ((*callBack)->getType() != SkScriptCallBack::kMemberFunction)
+            continue;
+        SkScriptValue2 callbackResult;
+        success = (*callBack)->getReference(field, len, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkOperand2::kString)
+                track(callbackResult.fOperand.fString);
+            callbackResult.fIsConstant = SkScriptValue2::kVariable;
+            fValueStack.push(callbackResult);
+            goto done;
+        }
+    }
+    return false;
+done:
+        return success;
+}
+
+bool SkScriptEngine2::handleProperty() {
+    bool success = true;
+    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
+        if ((*callBack)->getType() != SkScriptCallBack::kProperty)
+            continue;
+        SkScriptValue2 callbackResult;
+        success = (*callBack)->getReference(fToken, fTokenLength, &callbackResult);
+        if (success) {
+            if (callbackResult.fType == SkOperand2::kString && callbackResult.fOperand.fString == NULL) {
+                callbackResult.fOperand.fString = new SkString(fToken, fTokenLength);
+                track(callbackResult.fOperand.fString);
+            }
+            callbackResult.fIsConstant = SkScriptValue2::kVariable;
+            fValueStack.push(callbackResult);
+            goto done;
+        }
+    }
+done:
+        fTokenLength = 0;
+    return success;
+}
+
+bool SkScriptEngine2::handleUnbox(SkScriptValue2* scriptValue) {
+    bool success = true;
+    for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
+        if ((*callBack)->getType() != SkScriptCallBack::kUnbox)
+            continue;
+        SkScriptCallBackConvert* callBackConvert = (SkScriptCallBackConvert*) *callBack;
+        success = callBackConvert->convert(scriptValue->fType, &scriptValue->fOperand);
+        if (success) {
+            if (scriptValue->fType == SkOperand2::kString)
+                track(scriptValue->fOperand.fString);
+            goto done;
+        }
+    }
+    return false;
+done:
+        return success;
+}
+
+// note that entire expression is treated as if it were enclosed in parens
+// an open paren is always the first thing in the op stack
+
+int SkScriptEngine2::logicalOp(char ch, char nextChar) {
+    int advance = 1;
+    Op op;
+    signed char precedence;
+    switch (ch) {
+        case ')':
+            op = (Op) kParen;
+            break;
+        case ']':
+            op = (Op) kArrayOp;
+            break;
+        case '?':
+            op = (Op) kIf;
+            break;
+        case ':':
+            op = (Op) kElse;
+            break;
+        case '&':
+            if (nextChar != '&')
+                goto noMatch;
+            op = kLogicalAnd;
+            advance = 2;
+            break;
+        case '|':
+            if (nextChar != '|')
+                goto noMatch;
+            op = kLogicalOr;
+            advance = 2;
+            break;
+        default:
+            noMatch:
+            return 0;
+    }
+    precedence = gPrecedence[op];
+    int branchIndex = 0;
+    fBranchPopAllowed = false;
+    do {
+        while (gPrecedence[fOpStack.top() & ~kArtificialOp] < precedence)
+            processOp();
+        Branch& branch = fBranchStack.index(branchIndex++);
+        Op branchOp = branch.fOperator;
+        if (gPrecedence[branchOp] >= precedence)
+            break;
+        addTokenValue(fValueStack.top(), kAccumulator);
+        fValueStack.pop();
+        if (branchOp == kLogicalAnd || branchOp == kLogicalOr) {
+            if (branch.fOperator == kLogicalAnd)
+                branch.prime();
+            addToken(kToBool);
+        } else
+            resolveBranch(branch);
+        if (branch.fDone == Branch::kIsNotDone)
+            branch.prime();
+    } while (true);
+    fBranchPopAllowed = true;
+    while (fBranchStack.top().fDone == Branch::kIsDone)
+        fBranchStack.pop();
+    processLogicalOp(op);
+    return advance;
+}
+
+void SkScriptEngine2::processLogicalOp(Op op) {
+    switch (op) {
+        case kParen:
+        case kArrayOp:
+            SkASSERT(fOpStack.count() > 1 && fOpStack.top() == op);    // !!! add error handling
+            if (op == kParen) 
+                fOpStack.pop();
+            else {
+                SkScriptValue2 value;
+                fValueStack.pop(&value);
+                SkASSERT(value.fType == SkOperand2::kS32 || value.fType == SkOperand2::kScalar); // !!! add error handling (although, could permit strings eventually)
+                int index = value.fType == SkOperand2::kScalar ? SkScalarFloor(value.fOperand.fScalar) : 
+                    value.fOperand.fS32;
+                SkScriptValue2 arrayValue;
+                fValueStack.pop(&arrayValue);
+                SkASSERT(arrayValue.fType == SkOperand2::kArray);  // !!! add error handling
+                SkOpArray* array = arrayValue.fOperand.fArray;
+                SkOperand2 operand;
+                bool success = array->getIndex(index, &operand);
+                SkASSERT(success); // !!! add error handling
+                SkScriptValue2 resultValue;
+                resultValue.fType = array->getType();
+                resultValue.fOperand = operand;
+                resultValue.fIsConstant = SkScriptValue2::kVariable;
+                fValueStack.push(resultValue);
+            }
+                break;
+        case kIf: {
+            if (fAccumulatorType == SkOperand2::kNoType) {
+                addTokenValue(fValueStack.top(), kAccumulator);
+                fValueStack.pop();
+            }
+            SkASSERT(fAccumulatorType != SkOperand2::kString); // !!! add error handling
+            addToken(kIfOp);
+            Branch branch(op, fOpStack.count(), getTokenOffset());
+            *fBranchStack.push() = branch;
+            addTokenInt(0); // placeholder for future branch
+            fAccumulatorType = SkOperand2::kNoType;
+        } break;
+        case kElse: {
+            addTokenValue(fValueStack.top(), kAccumulator);
+            fValueStack.pop();
+            addToken(kElseOp);
+            size_t newOffset = getTokenOffset();
+            addTokenInt(0); // placeholder for future branch
+            Branch& branch = fBranchStack.top();
+            resolveBranch(branch);
+            branch.fOperator = op;
+            branch.fDone = Branch::kIsNotDone;
+            SkASSERT(branch.fOpStackDepth == fOpStack.count());
+            branch.fOffset = newOffset;
+            fAccumulatorType = SkOperand2::kNoType;
+        } break;
+        case kLogicalAnd:
+        case kLogicalOr: {
+            Branch& oldTop = fBranchStack.top();
+            Branch::Primed wasPrime = oldTop.fPrimed;
+            Branch::Done wasDone = oldTop.fDone;
+            oldTop.fPrimed = Branch::kIsNotPrimed;
+            oldTop.fDone = Branch::kIsNotDone;
+            if (fAccumulatorType == SkOperand2::kNoType) {
+                SkASSERT(fValueStack.top().fType == SkOperand2::kS32); // !!! add error handling, and conversion to int?
+                addTokenValue(fValueStack.top(), kAccumulator);
+                fValueStack.pop();
+            } else
+                SkASSERT(fAccumulatorType == SkOperand2::kS32);
+            // if 'and', write beq goto opcode after end of predicate (after to bool)
+            // if 'or', write bne goto to bool
+            addToken(op == kLogicalAnd ? kLogicalAndInt : kLogicalOrInt);
+            Branch branch(op, fOpStack.count(), getTokenOffset());
+            addTokenInt(0); // placeholder for future branch            
+            oldTop.fPrimed = wasPrime;
+            oldTop.fDone = wasDone;
+            *fBranchStack.push() = branch;
+            fAccumulatorType = SkOperand2::kNoType;
+        }    break;
+        default:
+            SkASSERT(0);
+    }
+}
+
+bool SkScriptEngine2::processOp() {
+    Op op;
+    fOpStack.pop(&op);
+    op = (Op) (op & ~kArtificialOp);
+    const OperatorAttributes* attributes = &gOpAttributes[op];
+    SkScriptValue2 value1 = { 0 };
+    SkScriptValue2 value2;
+    fValueStack.pop(&value2);
+    value2.fIsWritten = SkScriptValue2::kUnwritten;
+    //    SkScriptEngine2::SkTypeOp convert1[3];
+    //    SkScriptEngine2::SkTypeOp convert2[3];
+    //    SkScriptEngine2::SkTypeOp* convert2Ptr = convert2;
+    bool constantOperands = value2.fIsConstant == SkScriptValue2::kConstant;
+    if (attributes->fLeftType != SkOperand2::kNoType) {
+        fValueStack.pop(&value1);
+        constantOperands &= value1.fIsConstant == SkScriptValue2::kConstant; 
+        value1.fIsWritten = SkScriptValue2::kUnwritten;
+        if (op == kFlipOps) {
+            SkTSwap(value1, value2);
+            fOpStack.pop(&op);
+            op = (Op) (op & ~kArtificialOp);
+            attributes = &gOpAttributes[op];
+            if (constantOperands == false)
+                addToken(kFlipOpsOp);
+        }
+        if (value1.fType == SkOperand2::kObject && (value1.fType & attributes->fLeftType) == 0) {
+            value1.fType = getUnboxType(value1.fOperand);
+            addToken(kUnboxToken);
+        }
+    }
+    if (value2.fType == SkOperand2::kObject && (value2.fType & attributes->fLeftType) == 0) {
+        value1.fType = getUnboxType(value2.fOperand);
+        addToken(kUnboxToken2);
+    }
+    if (attributes->fLeftType != SkOperand2::kNoType) {
+        if (value1.fType != value2.fType) {
+            if ((attributes->fLeftType & SkOperand2::kString) && attributes->fBias & kTowardsString && 
+                ((value1.fType | value2.fType) & SkOperand2::kString)) {
+                if (value1.fType == SkOperand2::kS32 || value1.fType == SkOperand2::kScalar) {
+                    addTokenConst(&value1, kAccumulator, SkOperand2::kString, 
+                                  value1.fType == SkOperand2::kS32 ? kIntToString : kScalarToString);
+                }
+                if (value2.fType == SkOperand2::kS32 || value2.fType == SkOperand2::kScalar) {
+                    addTokenConst(&value2, kOperand, SkOperand2::kString, 
+                                  value2.fType == SkOperand2::kS32 ? kIntToString2 : kScalarToString2);
+                }
+            } else if (attributes->fLeftType & SkOperand2::kScalar && ((value1.fType | value2.fType) & 
+                                                                       SkOperand2::kScalar)) {
+                if (value1.fType == SkOperand2::kS32)
+                    addTokenConst(&value1, kAccumulator, SkOperand2::kScalar, kIntToScalar);
+                if (value2.fType == SkOperand2::kS32)
+                    addTokenConst(&value2, kOperand, SkOperand2::kScalar, kIntToScalar2);
+            }
+        }
+        if ((value1.fType & attributes->fLeftType) == 0 || value1.fType != value2.fType) {
+            if (value1.fType == SkOperand2::kString)
+                addTokenConst(&value1, kAccumulator, SkOperand2::kScalar, kStringToScalar);
+            if (value1.fType == SkOperand2::kScalar && (attributes->fLeftType == SkOperand2::kS32 || 
+                                                        value2.fType == SkOperand2::kS32))
+                addTokenConst(&value1, kAccumulator, SkOperand2::kS32, kScalarToInt);
+        }
+    }
+    AddTokenRegister rhRegister = attributes->fLeftType != SkOperand2::kNoType ?
+        kOperand : kAccumulator;
+    if ((value2.fType & attributes->fRightType) == 0 || value1.fType != value2.fType) {
+        if (value2.fType == SkOperand2::kString)
+            addTokenConst(&value2, rhRegister, SkOperand2::kScalar, kStringToScalar2);
+        if (value2.fType == SkOperand2::kScalar && (attributes->fRightType == SkOperand2::kS32 || 
+                                                    value1.fType == SkOperand2::kS32))
+            addTokenConst(&value2, rhRegister, SkOperand2::kS32, kScalarToInt2);
+    }
+    TypeOp typeOp = gTokens[op];
+    if (value2.fType == SkOperand2::kScalar)
+        typeOp = (TypeOp) (typeOp + 1);
+    else if (value2.fType == SkOperand2::kString)
+        typeOp = (TypeOp) (typeOp + 2);
+    SkDynamicMemoryWStream stream;
+    SkOperand2::OpType saveType;
+    SkBool saveOperand;
+    if (constantOperands) {
+        fActiveStream = &stream;
+        saveType = fAccumulatorType;
+        saveOperand = fOperandInUse;
+        fAccumulatorType = SkOperand2::kNoType;
+        fOperandInUse = false;
+    }
+    if (attributes->fLeftType != SkOperand2::kNoType) {    // two operands
+        if (value1.fIsWritten == SkScriptValue2::kUnwritten)
+            addTokenValue(value1, kAccumulator);
+    }
+    if (value2.fIsWritten == SkScriptValue2::kUnwritten)
+        addTokenValue(value2, rhRegister);
+    addToken(typeOp);
+    if (constantOperands) {
+        addToken(kEnd);
+#ifdef SK_DEBUG        
+        decompile((const unsigned char*) stream.getStream(), stream.getOffset());
+#endif
+        SkScriptRuntime runtime(fCallBackArray);
+        runtime.executeTokens((unsigned char*) stream.getStream());
+        runtime.getResult(&value1.fOperand);
+        if (attributes->fResultIsBoolean == kResultIsBoolean)
+            value1.fType = SkOperand2::kS32;
+        else if (attributes->fLeftType == SkOperand2::kNoType) // unary operand
+            value1.fType = value2.fType;
+        fValueStack.push(value1);
+        if (value1.fType == SkOperand2::kString)
+            runtime.untrack(value1.fOperand.fString);
+        else if (value1.fType == SkOperand2::kArray)
+            runtime.untrack(value1.fOperand.fArray);
+        fActiveStream = &fStream;
+        fAccumulatorType = saveType;
+        fOperandInUse = saveOperand;
+        return true;
+    }
+    value2.fIsConstant = SkScriptValue2::kVariable;
+    fValueStack.push(value2);
+    return true;
+}
+
+void SkScriptEngine2::Branch::resolve(SkDynamicMemoryWStream* stream, size_t off) {
+    SkASSERT(fDone == kIsNotDone);
+    fPrimed = kIsNotPrimed;
+    fDone = kIsDone;
+    SkASSERT(off > fOffset + sizeof(size_t));
+    size_t offset = off - fOffset - sizeof(offset);
+    stream->write(&offset, fOffset, sizeof(offset));
+}
+
+void SkScriptEngine2::resolveBranch(SkScriptEngine2::Branch& branch) {
+    branch.resolve(fActiveStream, getTokenOffset());
+}
+
+bool SkScriptEngine2::ConvertTo(SkScriptEngine2* engine, SkOperand2::OpType toType, SkScriptValue2* value ) {
+    SkASSERT(value);
+    SkOperand2::OpType type = value->fType;
+    if (type == toType) 
+        return true;
+    SkOperand2& operand = value->fOperand;
+    bool success = true;
+    switch (toType) {
+        case SkOperand2::kS32:
+            if (type == SkOperand2::kScalar)
+                operand.fS32 = SkScalarFloor(operand.fScalar);
+            else {
+                SkASSERT(type == SkOperand2::kString);
+                success = SkParse::FindS32(operand.fString->c_str(), &operand.fS32) != NULL;
+            }
+                break;
+        case SkOperand2::kScalar:
+            if (type == SkOperand2::kS32)
+                operand.fScalar = IntToScalar(operand.fS32);
+            else {
+                SkASSERT(type == SkOperand2::kString);
+                success = SkParse::FindScalar(operand.fString->c_str(), &operand.fScalar) != NULL;
+            }
+                break;
+        case SkOperand2::kString: {
+            SkString* strPtr = new SkString();
+            SkASSERT(engine);
+            engine->track(strPtr);
+            if (type == SkOperand2::kS32)
+                strPtr->appendS32(operand.fS32);
+            else {
+                SkASSERT(type == SkOperand2::kScalar);
+                strPtr->appendScalar(operand.fScalar);
+            }
+            operand.fString = strPtr;
+        } break;
+        case SkOperand2::kArray: {
+            SkOpArray* array = new SkOpArray(type);
+            *array->append() = operand;
+            engine->track(array);
+            operand.fArray = array;
+        } break;
+        default:
+            SkASSERT(0);
+    }
+    value->fType = toType;
+    return success;
+}
+
+SkScalar SkScriptEngine2::IntToScalar(int32_t s32) {
+    SkScalar scalar;
+    if (s32 == SK_NaN32)
+        scalar = SK_ScalarNaN;
+    else if (SkAbs32(s32) == SK_MaxS32)
+        scalar = SkSign32(s32) * SK_ScalarMax;
+    else
+        scalar = SkIntToScalar(s32);
+    return scalar;
+}
+
+bool SkScriptEngine2::ValueToString(const SkScriptValue2& value, SkString* string) {
+    switch (value.fType) {
+        case SkOperand2::kS32:
+            string->reset();
+            string->appendS32(value.fOperand.fS32);
+            break;
+        case SkOperand2::kScalar:
+            string->reset();
+            string->appendScalar(value.fOperand.fScalar);
+            break;
+        case SkOperand2::kString:
+            string->set(*value.fOperand.fString);
+            break;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+    return true; // no error
+}
+
+#ifdef SK_DEBUG
+
+#define testInt(expression) { #expression, SkOperand2::kS32, expression }
+#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) }
+#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) }
+#endif
+#endif
+#define testTrue(expression) { #expression, SkOperand2::kS32, 1 }
+#define testFalse(expression) { #expression, SkOperand2::kS32, 0 }
+
+#if !defined(SK_BUILD_FOR_BREW)
+static const SkScriptNAnswer2 scriptTests[]  = {
+    testInt(1||0&&3),
+#ifdef SK_CAN_USE_FLOAT
+    testScalar(- -5.5- -1.5),
+    testScalar(1.0+5), 
+#endif
+    testInt((6+7)*8),
+    testInt(3*(4+5)),
+#ifdef SK_CAN_USE_FLOAT
+    testScalar(1.0+2.0),
+    testScalar(3.0-1.0), 
+    testScalar(6-1.0), 
+    testScalar(2.5*6.), 
+    testScalar(0.5*4), 
+    testScalar(4.5/.5), 
+    testScalar(9.5/19), 
+    testRemainder(9.5, 0.5), 
+    testRemainder(9.,2), 
+    testRemainder(9,2.5),
+    testRemainder(-9,2.5),
+    testTrue(-9==-9.0),
+    testTrue(-9.==-4.0-5),
+    testTrue(-9.*1==-4-5),
+    testFalse(-9!=-9.0),
+    testFalse(-9.!=-4.0-5),
+    testFalse(-9.*1!=-4-5),
+#endif
+    testInt(0x123),
+    testInt(0XABC),
+    testInt(0xdeadBEEF),
+    {    "'123'+\"456\"", SkOperand2::kString, 0, 0, "123456" },
+    {    "123+\"456\"", SkOperand2::kString, 0, 0, "123456" },
+    {    "'123'+456", SkOperand2::kString, 0, 0, "123456" },
+    {    "'123'|\"456\"", SkOperand2::kS32, 123|456 },
+    {    "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 },
+    testInt(123),
+    testInt(-345),
+    testInt(+678),
+    testInt(1+2+3),
+    testInt(3*4+5),
+    testInt(6+7*8),
+    testInt(-1-2-8/4),
+    testInt(-9%4),
+    testInt(9%-4),
+    testInt(-9%-4),
+    testInt(123|978),
+    testInt(123&978),
+    testInt(123^978),
+    testInt(2<<4),
+    testInt(99>>3),
+    testInt(~55),
+    testInt(~~55),
+    testInt(!55),
+    testInt(!!55),
+    // both int
+    testInt(2<2),
+    testInt(2<11),
+    testInt(20<11),
+    testInt(2<=2),
+    testInt(2<=11),
+    testInt(20<=11),
+    testInt(2>2),
+    testInt(2>11),
+    testInt(20>11),
+    testInt(2>=2),
+    testInt(2>=11),
+    testInt(20>=11),
+    testInt(2==2),
+    testInt(2==11),
+    testInt(20==11),
+    testInt(2!=2),
+    testInt(2!=11),
+    testInt(20!=11),
+#ifdef SK_CAN_USE_FLOAT
+    // left int, right scalar
+    testInt(2<2.),
+    testInt(2<11.),
+    testInt(20<11.),
+    testInt(2<=2.),
+    testInt(2<=11.),
+    testInt(20<=11.),
+    testInt(2>2.),
+    testInt(2>11.),
+    testInt(20>11.),
+    testInt(2>=2.),
+    testInt(2>=11.),
+    testInt(20>=11.),
+    testInt(2==2.),
+    testInt(2==11.),
+    testInt(20==11.),
+    testInt(2!=2.),
+    testInt(2!=11.),
+    testInt(20!=11.),
+    // left scalar, right int
+    testInt(2.<2),
+    testInt(2.<11),
+    testInt(20.<11),
+    testInt(2.<=2),
+    testInt(2.<=11),
+    testInt(20.<=11),
+    testInt(2.>2),
+    testInt(2.>11),
+    testInt(20.>11),
+    testInt(2.>=2),
+    testInt(2.>=11),
+    testInt(20.>=11),
+    testInt(2.==2),
+    testInt(2.==11),
+    testInt(20.==11),
+    testInt(2.!=2),
+    testInt(2.!=11),
+    testInt(20.!=11),
+    // both scalar
+    testInt(2.<11.),
+    testInt(20.<11.),
+    testInt(2.<=2.),
+    testInt(2.<=11.),
+    testInt(20.<=11.),
+    testInt(2.>2.),
+    testInt(2.>11.),
+    testInt(20.>11.),
+    testInt(2.>=2.),
+    testInt(2.>=11.),
+    testInt(20.>=11.),
+    testInt(2.==2.),
+    testInt(2.==11.),
+    testInt(20.==11.),
+    testInt(2.!=2.),
+    testInt(2.!=11.),
+    testInt(20.!=11.),
+#endif
+    // int, string (string is int)
+    testFalse(2<'2'),
+    testTrue(2<'11'),
+    testFalse(20<'11'),
+    testTrue(2<='2'),
+    testTrue(2<='11'),
+    testFalse(20<='11'),
+    testFalse(2>'2'),
+    testFalse(2>'11'),
+    testTrue(20>'11'),
+    testTrue(2>='2'),
+    testFalse(2>='11'),
+    testTrue(20>='11'),
+    testTrue(2=='2'),
+    testFalse(2=='11'),
+    testFalse(2!='2'),
+    testTrue(2!='11'),
+    // int, string (string is scalar)
+    testFalse(2<'2.'),
+    testTrue(2<'11.'),
+    testFalse(20<'11.'),
+    testTrue(2=='2.'),
+    testFalse(2=='11.'),
+#ifdef SK_CAN_USE_FLOAT
+    // scalar, string
+    testFalse(2.<'2.'),
+    testTrue(2.<'11.'),
+    testFalse(20.<'11.'),
+    testTrue(2.=='2.'),
+    testFalse(2.=='11.'),
+    // string, int
+    testFalse('2'<2),
+    testTrue('2'<11),
+    testFalse('20'<11),
+    testTrue('2'==2),
+    testFalse('2'==11),
+    // string, scalar
+    testFalse('2'<2.),
+    testTrue('2'<11.),
+    testFalse('20'<11.),
+    testTrue('2'==2.),
+    testFalse('2'==11.),
+#endif
+    // string, string
+    testFalse('2'<'2'),
+    testFalse('2'<'11'),
+    testFalse('20'<'11'),
+    testTrue('2'=='2'),
+    testFalse('2'=='11'),
+    // logic
+    testInt(1?2:3),
+    testInt(0?2:3),
+    testInt(1&&2||3),
+    testInt(1&&0||3),
+    testInt(1&&0||0),
+    testInt(1||0&&3),
+    testInt(0||0&&3),
+    testInt(0||1&&3),
+    testInt(0&&1?2:3)
+#ifdef SK_CAN_USE_FLOAT
+    , {    "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2 }
+#endif
+};
+#endif // build for brew
+
+#define SkScriptNAnswer_testCount    SK_ARRAY_COUNT(scriptTests)
+
+void SkScriptEngine2::UnitTest() {
+#if !defined(SK_BUILD_FOR_BREW) && defined(SK_SUPPORT_UNITTEST)
+    ValidateDecompileTable();
+    for (int index = 0; index < SkScriptNAnswer_testCount; index++) {
+        SkScriptEngine2 engine(scriptTests[index].fType);
+        SkScriptValue2 value;
+        const char* script = scriptTests[index].fScript;
+        const char* scriptPtr = script;
+        SkASSERT(engine.evaluateScript(&scriptPtr, &value) == true);
+        SkASSERT(value.fType == scriptTests[index].fType);
+        SkScalar error;
+        switch (value.fType) {
+            case SkOperand2::kS32:
+                if (value.fOperand.fS32 != scriptTests[index].fIntAnswer)
+                    SkDEBUGF(("script '%s' == value %d != expected answer %d\n", script, value.fOperand.fS32, scriptTests[index].fIntAnswer));
+                SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
+                break;
+            case SkOperand2::kScalar:
+                error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
+#ifdef SK_CAN_USE_FLOAT
+                if (error >= SK_Scalar1 / 10000)
+                    SkDEBUGF(("script '%s' == value %g != expected answer %g\n", script, value.fOperand.fScalar / (1.0f * SK_Scalar1), scriptTests[index].fScalarAnswer / (1.0f * SK_Scalar1)));
+#endif
+                SkASSERT(error < SK_Scalar1 / 10000);
+                break;
+            case SkOperand2::kString:
+                SkASSERT(value.fOperand.fString->equals(scriptTests[index].fStringAnswer));
+                break;
+            default:
+                SkASSERT(0);
+        }
+    }
+#endif
+}
+#endif
diff --git a/src/animator/SkSnapshot.cpp b/src/animator/SkSnapshot.cpp
new file mode 100644
index 0000000..b65c517
--- /dev/null
+++ b/src/animator/SkSnapshot.cpp
@@ -0,0 +1,74 @@
+/* libs/graphics/animator/SkSnapshot.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 "SkTypes.h"
+
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+
+#include "SkSnapshot.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkSnapshot::fInfo[] = {
+    SK_MEMBER(filename, String),
+    SK_MEMBER(quality, Float),
+    SK_MEMBER(sequence, Boolean),
+    SK_MEMBER(type, BitmapEncoding)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkSnapshot);
+
+SkSnapshot::SkSnapshot()
+{
+    quality     = 100 * SK_Scalar1;
+    type        = (SkImageEncoder::Type) -1;
+    sequence    = false;
+    fSeqVal     = 0;
+}
+
+#include "SkDevice.h"
+
+bool SkSnapshot::draw(SkAnimateMaker& maker) {
+    SkASSERT(type >= 0);
+    SkASSERT(filename.size() > 0);
+    SkImageEncoder* encoder = SkImageEncoder::Create((SkImageEncoder::Type) type);
+
+    SkString name(filename);
+    if (sequence) {
+        char num[4] = "000";
+        num[0] = (char) (num[0] + fSeqVal / 100);
+        num[1] = (char) (num[1] + fSeqVal / 10 % 10);
+        num[2] = (char) (num[2] + fSeqVal % 10);
+        name.append(num);
+        if (++fSeqVal > 999)
+            sequence = false;
+    }
+    if (type == SkImageEncoder::kJPEG_Type)
+        name.append(".jpg");
+    else if (type == SkImageEncoder::kPNG_Type)
+        name.append(".png");
+    encoder->encodeFile(name.c_str(),
+                        maker.fCanvas->getDevice()->accessBitmap(false),
+                        SkScalarFloor(quality));
+    return false;
+}
+
+#endif
diff --git a/src/animator/SkSnapshot.h b/src/animator/SkSnapshot.h
new file mode 100644
index 0000000..c459fbb
--- /dev/null
+++ b/src/animator/SkSnapshot.h
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkSnapshot.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 SkSnapShot_DEFINED
+#define SkSnapShot_DEFINED
+
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+
+#include "SkDrawable.h"
+#include "SkImageDecoder.h"
+#include "SkMemberInfo.h"
+#include "SkString.h"
+
+class SkSnapshot: public SkDrawable {
+    DECLARE_MEMBER_INFO(Snapshot);
+    SkSnapshot();
+    virtual bool draw(SkAnimateMaker& );
+    private:
+    SkString filename;
+    SkScalar quality;
+    SkBool sequence;
+    int /*SkImageEncoder::Type*/    type;
+    int fSeqVal;
+};
+
+#endif // SK_SUPPORT_IMAGE_ENCODE
+#endif // SkSnapShot_DEFINED
+
diff --git a/src/animator/SkTDArray_Experimental.h b/src/animator/SkTDArray_Experimental.h
new file mode 100644
index 0000000..94d7871
--- /dev/null
+++ b/src/animator/SkTDArray_Experimental.h
@@ -0,0 +1,150 @@
+/* libs/graphics/animator/SkTDArray_Experimental.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 SkTDArray_Experimental_DEFINED
+#define SkTDArray_Experimental_DEFINED
+
+#include "SkTypes.h"
+
+#ifdef SK_BUILD_FOR_UNIX
+#define SK_BUILD_FOR_ADS_12
+#endif
+
+#ifndef SK_BUILD_FOR_ADS_12
+#define SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT 1
+#else
+#define SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT 0
+#endif
+
+#if SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT == 0
+#include "SkTDArray.h"
+#define SkIntArray(type) SkTDArray<type>
+#define SkLongArray(type) SkTDArray<type>
+#else
+
+class SkDS32Array {
+protected:
+    SkDS32Array();
+    SkDS32Array(const SkDS32Array& src);
+    SkDS32Array(const int32_t src[], U16CPU count);
+    SkDS32Array& operator=(const SkDS32Array& src);
+    friend int operator==(const SkDS32Array& a, const SkDS32Array& b);
+    int32_t* append() { return this->append(1, NULL); }
+    int32_t* append(U16CPU count, const int32_t* src = NULL);
+
+    int32_t* appendClear() 
+    { 
+        int32_t* result = this->append(); 
+        *result = 0;
+        return result;
+    }
+
+    int find(const int32_t& elem) const;
+    int32_t* insert(U16CPU index, U16CPU count, const int32_t* src);
+    int rfind(const int32_t& elem) const;
+    void swap(SkDS32Array& other);
+public:
+    bool isEmpty() const { return fCount == 0; }
+    int count() const { return fCount; }
+
+    void remove(U16CPU index, U16CPU count = 1)
+    {
+        SkASSERT(index + count <= fCount);
+        fCount = SkToU16(fCount - count);
+        memmove(fArray + index, fArray + index + count, sizeof(int32_t) * (fCount - index));
+    }
+
+    void reset()
+    {
+        if (fArray)
+        {
+            sk_free(fArray);
+            fArray = NULL;
+#ifdef SK_DEBUG
+            fData = NULL;
+#endif
+            fReserve = fCount = 0;
+        }
+        else
+        {
+            SkASSERT(fReserve == 0 && fCount == 0);
+        }
+    }
+
+    void setCount(U16CPU count)
+    {
+        if (count > fReserve)
+            this->growBy(count - fCount);
+        else
+            fCount = SkToU16(count);
+    }
+protected:
+#ifdef SK_DEBUG
+    enum {
+        kDebugArraySize = 24
+    };
+    int32_t(* fData)[kDebugArraySize];
+#endif
+    int32_t*    fArray;
+    uint16_t    fReserve, fCount;
+    void growBy(U16CPU extra);
+};
+
+#ifdef SK_DEBUG
+    #define SYNC() fTData = (T (*)[kDebugArraySize]) fArray
+#else
+    #define SYNC()
+#endif
+
+template <typename T> class SkTDS32Array : public SkDS32Array {
+public:
+    SkTDS32Array() { SkDEBUGCODE(fTData=NULL); SkASSERT(sizeof(T) == sizeof(int32_t)); }
+    SkTDS32Array(const SkTDS32Array<T>& src) : SkDS32Array(src) {}
+    ~SkTDS32Array() { sk_free(fArray); }
+    T&  operator[](int index) const { SYNC(); SkASSERT((unsigned)index < fCount); return ((T*) fArray)[index]; }
+    SkTDS32Array<T>& operator=(const SkTDS32Array<T>& src) { 
+        return (SkTDS32Array<T>&) SkDS32Array::operator=(src); }
+    friend int operator==(const SkTDS32Array<T>& a, const SkTDS32Array<T>& b) { 
+        return operator==((const SkDS32Array&) a, (const SkDS32Array&) b); }
+    T* append() { return (T*) SkDS32Array::append(); }
+    T* appendClear() { return (T*) SkDS32Array::appendClear(); }
+    T* append(U16CPU count, const T* src = NULL) { return (T*) SkDS32Array::append(count, (const int32_t*) src); }
+    T*  begin() const { SYNC(); return (T*) fArray; }
+    T*  end() const { return (T*) (fArray ? fArray + fCount : NULL); }
+    int find(const T& elem) const { return SkDS32Array::find((const int32_t&) elem); }
+    T* insert(U16CPU index) { return this->insert(index, 1, NULL); }
+    T* insert(U16CPU index, U16CPU count, const T* src = NULL) {
+        return (T*) SkDS32Array::insert(index, count, (const int32_t*) src); }
+    int rfind(const T& elem) const { return SkDS32Array::rfind((const int32_t&) elem); }
+    T*          push() { return this->append(); }
+    void        push(T& elem) { *this->append() = elem; }
+    const T&    top() const { return (*this)[fCount - 1]; }
+    T&          top() { return (*this)[fCount - 1]; }
+    void        pop(T* elem) { if (elem) *elem = (*this)[fCount - 1]; --fCount; }
+    void        pop() { --fCount; }
+private:
+#ifdef SK_DEBUG
+    mutable T(* fTData)[kDebugArraySize];
+#endif
+};
+
+#define SkIntArray(type) SkTDS32Array<type> // holds 32 bit data types
+#define SkLongArray(type) SkTDS32Array<type>    // holds 32/64 bit data types depending on pointer size
+
+#endif // SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT
+
+#endif // SkTDArray_Experimental_DEFINED
diff --git a/src/animator/SkTextOnPath.cpp b/src/animator/SkTextOnPath.cpp
new file mode 100644
index 0000000..1a06746
--- /dev/null
+++ b/src/animator/SkTextOnPath.cpp
@@ -0,0 +1,47 @@
+/* libs/graphics/animator/SkTextOnPath.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 "SkTextOnPath.h"
+#include "SkAnimateMaker.h"
+#include "SkCanvas.h"
+#include "SkDrawPath.h"
+#include "SkDrawText.h"
+#include "SkPaint.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkTextOnPath::fInfo[] = {
+    SK_MEMBER(offset, Float),
+    SK_MEMBER(path, Path),
+    SK_MEMBER(text, Text)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkTextOnPath);
+
+SkTextOnPath::SkTextOnPath() : offset(0), path(NULL), text(NULL) {
+}
+
+bool SkTextOnPath::draw(SkAnimateMaker& maker) {
+    SkASSERT(text);
+    SkASSERT(path);
+    SkBoundableAuto boundable(this, maker);
+    maker.fCanvas->drawTextOnPathHV(text->getText(), text->getSize(), 
+                                    path->getPath(), offset, 0, *maker.fPaint);
+    return false;
+}
diff --git a/src/animator/SkTextOnPath.h b/src/animator/SkTextOnPath.h
new file mode 100644
index 0000000..3c78b5b
--- /dev/null
+++ b/src/animator/SkTextOnPath.h
@@ -0,0 +1,38 @@
+/* libs/graphics/animator/SkTextOnPath.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 SkTextOnPath_DEFINED
+#define SkTextOnPath_DEFINED
+
+#include "SkBoundable.h"
+#include "SkMemberInfo.h"
+
+class SkDrawPath;
+class SkText;
+
+class SkTextOnPath : public SkBoundable {
+    DECLARE_MEMBER_INFO(TextOnPath);
+    SkTextOnPath();
+    virtual bool draw(SkAnimateMaker& );
+private:
+    SkScalar offset;
+    SkDrawPath* path;
+    SkText* text;
+    typedef SkBoundable INHERITED;
+};
+
+#endif // SkTextOnPath_DEFINED
diff --git a/src/animator/SkTextToPath.cpp b/src/animator/SkTextToPath.cpp
new file mode 100644
index 0000000..44036a3
--- /dev/null
+++ b/src/animator/SkTextToPath.cpp
@@ -0,0 +1,56 @@
+/* libs/graphics/animator/SkTextToPath.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 "SkTextToPath.h"
+#include "SkAnimateMaker.h"
+#include "SkDrawPaint.h"
+#include "SkDrawPath.h"
+#include "SkDrawText.h"
+#include "SkPaint.h"
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkTextToPath::fInfo[] = {
+    SK_MEMBER(paint, Paint),
+    SK_MEMBER(path, Path),
+    SK_MEMBER(text, Text)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkTextToPath);
+
+SkTextToPath::SkTextToPath() : paint(NULL), path(NULL), text(NULL) {
+}
+
+bool SkTextToPath::draw(SkAnimateMaker& maker) {
+    path->draw(maker);
+    return false;
+}
+
+void SkTextToPath::onEndElement(SkAnimateMaker& maker) {
+    if (paint == NULL || path == NULL || text == NULL) {
+        // !!! add error message here
+        maker.setErrorCode(SkDisplayXMLParserError::kErrorInAttributeValue);
+        return;
+    }
+    SkPaint realPaint;
+    paint->setupPaint(&realPaint);
+    realPaint.getTextPath(text->getText(), text->getSize(), text->x, 
+        text->y, &path->getPath());
+}
+
diff --git a/src/animator/SkTextToPath.h b/src/animator/SkTextToPath.h
new file mode 100644
index 0000000..6d93239
--- /dev/null
+++ b/src/animator/SkTextToPath.h
@@ -0,0 +1,40 @@
+/* libs/graphics/animator/SkTextToPath.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 SkTextToPath_DEFINED
+#define SkTextToPath_DEFINED
+
+#include "SkDrawPath.h"
+#include "SkMemberInfo.h"
+
+class SkDrawPaint;
+class SkDrawPath;
+class SkText;
+
+class SkTextToPath : public SkDrawable {
+    DECLARE_MEMBER_INFO(TextToPath);
+    SkTextToPath();
+    virtual bool draw(SkAnimateMaker& );
+    virtual void onEndElement(SkAnimateMaker& );
+private:
+    SkDrawPaint* paint;
+    SkDrawPath* path;
+    SkText* text;
+};
+
+#endif // SkTextToPath_DEFINED
+
diff --git a/src/animator/SkTime.cpp b/src/animator/SkTime.cpp
new file mode 100644
index 0000000..a36a95a
--- /dev/null
+++ b/src/animator/SkTime.cpp
@@ -0,0 +1,89 @@
+/* libs/graphics/animator/SkTime.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 "SkTime.h"
+
+#ifdef SK_BUILD_FOR_WIN
+
+#ifdef SK_DEBUG
+SkMSec gForceTickCount = (SkMSec) -1;
+#endif
+
+void SkTime::GetDateTime(DateTime* t)
+{
+    if (t)
+    {
+        SYSTEMTIME  syst;
+
+        ::GetLocalTime(&syst);
+        t->fYear        = SkToU16(syst.wYear);
+        t->fMonth       = SkToU8(syst.wMonth);
+        t->fDayOfWeek   = SkToU8(syst.wDayOfWeek);
+        t->fDay         = SkToU8(syst.wDay);
+        t->fHour        = SkToU8(syst.wHour);
+        t->fMinute      = SkToU8(syst.wMinute);
+        t->fSecond      = SkToU8(syst.wSecond);
+    }
+}
+
+SkMSec SkTime::GetMSecs()
+{
+#ifdef SK_DEBUG
+    if (gForceTickCount != (SkMSec) -1)
+        return gForceTickCount;
+#endif
+    return ::GetTickCount();
+}
+
+#elif defined(xSK_BUILD_FOR_MAC)
+
+#include <time.h>
+
+void SkTime::GetDateTime(DateTime* t)
+{
+    if (t)
+    {
+        tm      syst;
+        time_t  tm;
+        
+        time(&tm);
+        localtime_r(&tm, &syst);
+        t->fYear        = SkToU16(syst.tm_year);
+        t->fMonth       = SkToU8(syst.tm_mon + 1);
+        t->fDayOfWeek   = SkToU8(syst.tm_wday);
+        t->fDay         = SkToU8(syst.tm_mday);
+        t->fHour        = SkToU8(syst.tm_hour);
+        t->fMinute      = SkToU8(syst.tm_min);
+        t->fSecond      = SkToU8(syst.tm_sec);
+    }
+}
+
+#include "Sk64.h"
+
+SkMSec SkTime::GetMSecs()
+{
+    UnsignedWide    wide;
+    Sk64            s;
+
+    ::Microseconds(&wide);
+    s.set(wide.hi, wide.lo);
+    s.div(1000, Sk64::kRound_DivOption);
+    return s.get32();
+}
+
+#endif
+
diff --git a/src/animator/SkTypedArray.cpp b/src/animator/SkTypedArray.cpp
new file mode 100644
index 0000000..075c607
--- /dev/null
+++ b/src/animator/SkTypedArray.cpp
@@ -0,0 +1,187 @@
+/* libs/graphics/animator/SkTypedArray.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 "SkTypedArray.h"
+
+SkTypedArray::SkTypedArray() : fType(SkType_Unknown) {
+}
+
+SkTypedArray::SkTypedArray(SkDisplayTypes type) : fType(type) {
+}
+
+bool SkTypedArray::getIndex(int index, SkOperand* operand) {
+    if (index >= count()) {
+        SkASSERT(0);
+        return false;
+    }
+    *operand = begin()[index];
+    return true;
+}
+
+
+#if SK_SMALLER_ARRAY_TEMPLATE_EXPERIMENT == 1
+SkDS32Array::SkDS32Array()
+{
+    fReserve = fCount = 0;
+    fArray = NULL;
+#ifdef SK_DEBUG
+    fData = NULL;
+#endif
+}
+
+SkDS32Array::SkDS32Array(const SkDS32Array& src)
+{
+    fReserve = fCount = 0;
+    fArray = NULL;
+#ifdef SK_DEBUG
+    fData = NULL;
+#endif
+    SkDS32Array tmp(src.fArray, src.fCount);
+    this->swap(tmp);
+}
+
+SkDS32Array::SkDS32Array(const int32_t src[], U16CPU count)
+{
+    SkASSERT(src || count == 0);
+
+    fReserve = fCount = 0;
+    fArray = NULL;
+#ifdef SK_DEBUG
+    fData = NULL;
+#endif
+    if (count)
+    {
+        fArray = (int32_t*)sk_malloc_throw(count * sizeof(int32_t));
+#ifdef SK_DEBUG
+        fData = (int32_t (*)[kDebugArraySize]) fArray;
+#endif
+        memcpy(fArray, src, sizeof(int32_t) * count);
+        fReserve = fCount = SkToU16(count);
+    }
+}
+
+SkDS32Array& SkDS32Array::operator=(const SkDS32Array& src)
+{
+    if (this != &src)
+    {
+        if (src.fCount > fReserve)
+        {
+            SkDS32Array tmp(src.fArray, src.fCount);
+            this->swap(tmp);
+        }
+        else
+        {
+            memcpy(fArray, src.fArray, sizeof(int32_t) * src.fCount);
+            fCount = src.fCount;
+        }
+    }
+    return *this;
+}
+
+int operator==(const SkDS32Array& a, const SkDS32Array& b)
+{
+    return a.fCount == b.fCount &&
+            (a.fCount == 0 || !memcmp(a.fArray, b.fArray, a.fCount * sizeof(int32_t)));
+}
+
+void SkDS32Array::swap(SkDS32Array& other)
+{
+    SkTSwap(fArray, other.fArray);
+#ifdef SK_DEBUG
+    SkTSwap(fData, other.fData);
+#endif
+    SkTSwap(fReserve, other.fReserve);
+    SkTSwap(fCount, other.fCount);
+}
+
+int32_t* SkDS32Array::append(U16CPU count, const int32_t* src)
+{
+    unsigned oldCount = fCount;
+    if (count)
+    {
+        SkASSERT(src == NULL || fArray == NULL ||
+                src + count <= fArray || fArray + count <= src);
+
+        this->growBy(count);
+        if (src)
+            memcpy(fArray + oldCount, src, sizeof(int32_t) * count);
+    }
+    return fArray + oldCount;
+}
+
+int SkDS32Array::find(const int32_t& elem) const
+{
+    const int32_t* iter = fArray;
+    const int32_t* stop = fArray + fCount;
+
+    for (; iter < stop; iter++)
+    {
+        if (*iter == elem)
+            return (int) (iter - fArray);
+    }
+    return -1;
+}
+
+void SkDS32Array::growBy(U16CPU extra)
+{
+    SkASSERT(extra);
+    SkASSERT(fCount + extra <= 0xFFFF);
+
+    if (fCount + extra > fReserve)
+    {
+        size_t size = fCount + extra + 4;
+        size += size >> 2;
+        int32_t* array = (int32_t*)sk_malloc_throw(size * sizeof(int32_t));
+        memcpy(array, fArray, fCount * sizeof(int32_t));
+
+        sk_free(fArray);
+        fArray = array;
+#ifdef SK_DEBUG
+        fData = (int32_t (*)[kDebugArraySize]) fArray;
+#endif
+        fReserve = SkToU16((U16CPU)size);
+    }
+    fCount = SkToU16(fCount + extra);
+}
+
+int32_t* SkDS32Array::insert(U16CPU index, U16CPU count, const int32_t* src)
+{
+    SkASSERT(count);
+    int oldCount = fCount;
+    this->growBy(count);
+    int32_t* dst = fArray + index;
+    memmove(dst + count, dst, sizeof(int32_t) * (oldCount - index));
+    if (src)
+        memcpy(dst, src, sizeof(int32_t) * count);
+    return dst;
+}
+
+
+    int SkDS32Array::rfind(const int32_t& elem) const
+    {
+        const int32_t* iter = fArray + fCount;
+        const int32_t* stop = fArray;
+
+        while (iter > stop)
+        {
+            if (*--iter == elem)
+                return (int) (iter - stop);
+        }
+        return -1;
+    }
+
+#endif
diff --git a/src/animator/SkTypedArray.h b/src/animator/SkTypedArray.h
new file mode 100644
index 0000000..a658f29
--- /dev/null
+++ b/src/animator/SkTypedArray.h
@@ -0,0 +1,39 @@
+/* libs/graphics/animator/SkTypedArray.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 SkTypedArray_DEFINED
+#define SkTypedArray_DEFINED
+
+#include "SkScript.h"
+#include "SkTDArray_Experimental.h"
+
+class SkTypedArray : public SkTDOperandArray {
+public:
+    SkTypedArray();
+    SkTypedArray(SkDisplayTypes type);
+    bool getIndex(int index, SkOperand* operand);
+    SkDisplayTypes getType() { return fType; }
+    SkScriptEngine::SkOpType getOpType() { return SkScriptEngine::ToOpType(fType); }
+    void setType(SkDisplayTypes type) { 
+    //  SkASSERT(count() == 0);
+        fType = type;
+    }
+protected:
+    SkDisplayTypes fType;
+};
+
+#endif // SkTypedArray_DEFINED
diff --git a/src/animator/SkXMLAnimatorWriter.cpp b/src/animator/SkXMLAnimatorWriter.cpp
new file mode 100644
index 0000000..b26bc73
--- /dev/null
+++ b/src/animator/SkXMLAnimatorWriter.cpp
@@ -0,0 +1,91 @@
+/* libs/graphics/animator/SkXMLAnimatorWriter.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 "SkXMLAnimatorWriter.h"
+#include "SkAnimator.h"
+#include "SkAnimateMaker.h"
+#include "SkDisplayXMLParser.h"
+
+SkXMLAnimatorWriter::SkXMLAnimatorWriter(SkAnimator* animator) : fAnimator(animator)
+{
+    fParser = new SkDisplayXMLParser(*fAnimator->fMaker);
+}
+
+SkXMLAnimatorWriter::~SkXMLAnimatorWriter() {
+    delete fParser;
+}
+
+void SkXMLAnimatorWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
+{
+    fParser->onAddAttributeLen(name, value, length);
+}
+
+void SkXMLAnimatorWriter::onEndElement()
+{
+    Elem* elem = getEnd();
+    fParser->onEndElement(elem->fName.c_str());
+    doEnd(elem);
+}
+
+void SkXMLAnimatorWriter::onStartElementLen(const char name[], size_t length)
+{
+    doStart(name, length);
+    fParser->onStartElementLen(name, length);
+}
+
+void SkXMLAnimatorWriter::writeHeader()
+{
+}
+
+#ifdef SK_DEBUG
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+void SkXMLAnimatorWriter::UnitTest(SkCanvas* canvas)
+{
+    SkAnimator  s;
+    SkXMLAnimatorWriter     w(&s);
+    w.startElement("screenplay");
+        w.startElement("animateField");
+            w.addAttribute("field", "x1");
+            w.addAttribute("id", "to100");
+            w.addAttribute("from", "0");
+            w.addAttribute("to", "100");
+            w.addAttribute("dur", "1");
+        w.endElement();
+        w.startElement("event");
+            w.addAttribute("kind", "onLoad");
+            w.startElement("line");
+                w.addAttribute("id", "line");
+                w.addAttribute("x1", "-1");
+                w.addAttribute("y1", "20");
+                w.addAttribute("x2", "150");
+                w.addAttribute("y2", "40");
+            w.endElement();
+            w.startElement("apply");
+                w.addAttribute("animator", "to100");
+                w.addAttribute("scope", "line");
+            w.endElement();
+        w.endElement();
+    w.endElement();
+    SkPaint paint;
+    canvas->drawColor(SK_ColorWHITE);
+    s.draw(canvas, &paint, 0);
+}
+
+#endif
+
diff --git a/src/animator/SkXMLAnimatorWriter.h b/src/animator/SkXMLAnimatorWriter.h
new file mode 100644
index 0000000..c5830cd
--- /dev/null
+++ b/src/animator/SkXMLAnimatorWriter.h
@@ -0,0 +1,42 @@
+/* libs/graphics/animator/SkXMLAnimatorWriter.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 SkXMLAnimatorWriter_DEFINED
+#define SkXMLAnimatorWriter_DEFINED
+
+#include "SkXMLWriter.h"
+
+class SkAnimator;
+class SkDisplayXMLParser;
+
+class SkXMLAnimatorWriter : public SkXMLWriter {
+public:
+    SkXMLAnimatorWriter(SkAnimator*);
+    virtual ~SkXMLAnimatorWriter();
+    virtual void    writeHeader();
+    SkDEBUGCODE(static void UnitTest(class SkCanvas* canvas);)
+protected:
+    virtual void onAddAttributeLen(const char name[], const char value[], size_t length);
+    virtual void onEndElement();
+    virtual void onStartElementLen(const char elem[], size_t length);
+private:
+    SkAnimator* fAnimator;
+    SkDisplayXMLParser* fParser;
+};
+
+#endif // SkXMLAnimatorWriter_DEFINED
+
diff --git a/src/animator/thingstodo.txt b/src/animator/thingstodo.txt
new file mode 100644
index 0000000..8d0d47a
--- /dev/null
+++ b/src/animator/thingstodo.txt
@@ -0,0 +1,21 @@
+things to do:
+	figure out where endless or very deep recursion is possible
+	at these points, generate an error if actual physical stack gets too large
+	candidates are scripts
+		eval(eval(eval...	user callouts
+		(((((	operator precedence or similar making stack deep
+		groups within groups
+		very large apply create or apply immediate steps
+
+	write tests for math functions
+		looks like random takes a parameter when it should take zero parameters
+		
+	add Math, Number files to perforce for docs
+	alphabetize attributes in docs
+	
+	manually modified tools/screenplayDocs/xmlToJPEG.cpp
+	
+	fix docs where lines are stitched together (insert space)
+	
+	naked <data> outside of <post> asserts on name
+	handle errors for all element not contained by correct parents
\ No newline at end of file
diff --git a/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h b/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
new file mode 100644
index 0000000..c7e23af
--- /dev/null
+++ b/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
@@ -0,0 +1,171 @@
+
+class ARGB32_Clamp_Bilinear_BitmapShader : public SkBitmapShader {
+public:
+    ARGB32_Clamp_Bilinear_BitmapShader(const SkBitmap& src)
+        : SkBitmapShader(src, true,
+                         SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)
+    {}
+
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+};
+
+SkPMColor sample_bilerp(SkFixed fx, SkFixed fy, unsigned srcMaxX, unsigned srcMaxY,
+                        const SkPMColor* srcPixels, int srcRB, const SkFilterPtrProc* proc_table);
+SkPMColor sample_bilerp(SkFixed fx, SkFixed fy, unsigned srcMaxX, unsigned srcMaxY,
+                        const SkPMColor* srcPixels, int srcRB, const SkFilterPtrProc* proc_table)
+{
+    int ix = fx >> 16;
+    int iy = fy >> 16;
+
+    const SkPMColor *p00, *p01, *p10, *p11;
+
+    p00 = p01 = ((const SkPMColor*)((const char*)srcPixels
+                                    + SkClampMax(iy, srcMaxY) * srcRB))
+                                    + SkClampMax(ix, srcMaxX);
+
+    if ((unsigned)ix < srcMaxX)
+        p01 += 1;
+    p10 = p00;
+    p11 = p01;
+    if ((unsigned)iy < srcMaxY)
+    {
+        p10 = (const SkPMColor*)((const char*)p10 + srcRB);
+        p11 = (const SkPMColor*)((const char*)p11 + srcRB);
+    }
+
+    SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(proc_table, fx, fy);
+    return proc(p00, p01, p10, p11);
+}
+
+static inline SkPMColor sample_bilerpx(SkFixed fx, unsigned srcMaxX, const SkPMColor* srcPixels,
+                                       int srcRB, const SkFilterPtrProc* proc_table)
+{
+    int ix = fx >> 16;
+    
+    const SkPMColor *p00, *p01, *p10, *p11;
+    
+    p00 = p01 = srcPixels + SkClampMax(ix, srcMaxX);
+    if ((unsigned)ix < srcMaxX)
+        p01 += 1;
+
+    p10 = (const SkPMColor*)((const char*)p00 + srcRB);
+    p11 = (const SkPMColor*)((const char*)p01 + srcRB);
+    
+    SkFilterPtrProc proc = SkGetBilinearFilterPtrXProc(proc_table, fx);
+    return proc(p00, p01, p10, p11);
+}
+
+void ARGB32_Clamp_Bilinear_BitmapShader::shadeSpan(int x, int y, SkPMColor dstC[], int count)
+{
+    SkASSERT(count > 0);
+    
+    unsigned srcScale = SkAlpha255To256(this->getPaintAlpha());
+
+    const SkMatrix& inv = this->getTotalInverse();
+    const SkBitmap& srcBitmap = this->getSrcBitmap();
+    unsigned        srcMaxX = srcBitmap.width() - 1;
+    unsigned        srcMaxY = srcBitmap.height() - 1;
+    unsigned        srcRB = srcBitmap.rowBytes();
+
+    const SkFilterPtrProc* proc_table = SkGetBilinearFilterPtrProcTable();
+    const SkPMColor* srcPixels = (const SkPMColor*)srcBitmap.getPixels();
+
+    if (this->getInverseClass() == kPerspective_MatrixClass)
+    {
+        SkPerspIter   iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                SkIntToScalar(y) + SK_ScalarHalf, count);
+
+        if (256 == srcScale)
+        {
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+                while (--count >= 0)
+                {
+                    SkFixed fx = *srcXY++ - SK_FixedHalf;
+                    SkFixed fy = *srcXY++ - SK_FixedHalf;
+                    *dstC++ = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+                }
+            }
+        }
+        else    // scale by srcScale
+        {
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+                while (--count >= 0)
+                {
+                    SkFixed fx = *srcXY++ - SK_FixedHalf;
+                    SkFixed fy = *srcXY++ - SK_FixedHalf;
+                    SkPMColor c = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+                    *dstC++ = SkAlphaMulQ(c, srcScale);
+                }
+            }
+        }
+    }
+    else    // linear case
+    {
+        SkFixed fx, fy, dx, dy;
+
+        // now init fx, fy, dx, dy
+        {
+            SkPoint srcPt;
+            this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                             SkIntToScalar(y) + SK_ScalarHalf,
+                                             &srcPt);
+
+            fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf;
+            fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf;
+
+            if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+                (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+            else
+            {
+                dx = SkScalarToFixed(inv.getScaleX());
+                dy = SkScalarToFixed(inv.getSkewY());
+            }
+        }
+
+        if (dy == 0 && (unsigned)(fy >> 16) < srcMaxY)
+        {
+            srcPixels = (const SkPMColor*)((const char*)srcPixels + (fy >> 16) * srcRB);
+            proc_table = SkGetBilinearFilterPtrProcYTable(proc_table, fy);
+            if (256 == srcScale)
+            {
+                do {
+                    *dstC++ = sample_bilerpx(fx, srcMaxX, srcPixels, srcRB, proc_table);
+                    fx += dx;
+                } while (--count != 0);
+            }
+            else
+            {
+                do {
+                    SkPMColor c = sample_bilerpx(fx, srcMaxX, srcPixels, srcRB, proc_table);
+                    *dstC++ = SkAlphaMulQ(c, srcScale);
+                    fx += dx;
+                } while (--count != 0);
+            }
+        }
+        else    // dy is != 0
+        {
+            if (256 == srcScale)
+            {
+                do {
+                    *dstC++ = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+                    fx += dx;
+                    fy += dy;
+                } while (--count != 0);
+            }
+            else
+            {
+                do {
+                    SkPMColor c = sample_bilerp(fx, fy, srcMaxX, srcMaxY, srcPixels, srcRB, proc_table);
+                    *dstC++ = SkAlphaMulQ(c, srcScale);
+                    fx += dx;
+                    fy += dy;
+                } while (--count != 0);
+            }
+        }
+    }
+}
+
diff --git a/src/core/Sk64.cpp b/src/core/Sk64.cpp
new file mode 100644
index 0000000..6013bd7
--- /dev/null
+++ b/src/core/Sk64.cpp
@@ -0,0 +1,575 @@
+/* libs/corecg/Sk64.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 "Sk64.h"
+
+#define shift_left(hi, lo)          \
+    hi = (hi << 1) | (lo >> 31);    \
+    lo <<= 1
+
+#define shift_left_bits(hi, lo, bits)           \
+    SkASSERT((unsigned)(bits) < 31);                \
+    hi = (hi << (bits)) | (lo >> (32 - (bits)));    \
+    lo <<= (bits)
+
+//////////////////////////////////////////////////////////////////////
+
+int Sk64::getClzAbs() const
+{
+    int32_t     hi = fHi;
+    uint32_t    lo = fLo;
+
+    // get abs
+    if (hi < 0)
+    {
+        hi = -hi - Sk32ToBool(lo);
+        lo = 0 - lo;
+    }
+    return hi ? SkCLZ(hi) : SkCLZ(lo) + 32;
+}
+
+void Sk64::shiftLeft(unsigned bits)
+{
+    SkASSERT(bits <= 63);
+    if (bits == 0)
+        return;
+
+    if (bits >= 32)
+    {
+        fHi = fLo << (bits - 32);
+        fLo = 0;
+    }
+    else
+    {
+        fHi = (fHi << bits) | (fLo >> (32 - bits));
+        fLo <<= bits;
+    }
+}
+
+int32_t Sk64::getShiftRight(unsigned bits) const
+{
+    SkASSERT(bits <= 63);
+
+    if (bits == 0)
+        return fLo;
+
+    if (bits >= 32)
+        return fHi >> (bits - 32);
+    else
+    {
+#ifdef SK_DEBUG
+        int32_t tmp = fHi >> bits;
+        SkASSERT(tmp == 0 || tmp == -1);
+#endif
+        return (fHi << (32 - bits)) | (fLo >> bits);
+    }
+}
+
+void Sk64::shiftRight(unsigned bits)
+{
+    SkASSERT(bits <= 63);
+    if (bits == 0)
+        return;
+
+    if (bits >= 32)
+    {
+        fLo = fHi >> (bits - 32);
+        fHi >>= 31;
+    }
+    else
+    {
+        fLo = (fHi << (32 - bits)) | (fLo >> bits);
+        fHi >>= bits;
+    }
+}
+
+void Sk64::roundRight(unsigned bits)
+{
+    SkASSERT(bits <= 63);
+    if (bits)
+    {
+        Sk64 one;
+        one.set(1);
+        one.shiftLeft(bits - 1);
+        this->add(one);
+        this->shiftRight(bits);
+    }
+}
+
+int Sk64::shiftToMake32() const
+{
+    int32_t     hi = fHi;
+    uint32_t    lo = fLo;
+
+    if (hi < 0) // make it positive
+    {
+        hi = -hi - Sk32ToBool(lo);
+        lo = 0 - lo;
+    }
+
+    if (hi == 0)
+        return lo >> 31;
+    else
+        return 33 - SkCLZ(hi);
+}
+
+void Sk64::negate()
+{
+    fHi = -fHi - Sk32ToBool(fLo);
+    fLo = 0 - fLo;
+}
+
+void Sk64::abs()
+{
+    if (fHi < 0)
+    {
+        fHi = -fHi - Sk32ToBool(fLo);
+        fLo = 0 - fLo;
+    }
+}
+
+////////////////////////////////////////////////////////////////
+
+static inline int32_t round_right_16(int32_t hi, uint32_t lo)
+{
+    uint32_t sum = lo + (1 << 15);
+    hi += (sum < lo);
+    return (hi << 16) | (sum >> 16);
+}
+
+SkBool Sk64::isFixed() const
+{
+    Sk64 tmp = *this;
+    tmp.roundRight(16);
+    return tmp.is32();
+}
+
+SkFract Sk64::getFract() const
+{
+    Sk64 tmp = *this;
+    tmp.roundRight(30);
+    return tmp.get32();
+}
+
+void Sk64::sub(const Sk64& a)
+{
+    fHi = fHi - a.fHi - (fLo < a.fLo);
+    fLo = fLo - a.fLo;
+}
+
+void Sk64::rsub(const Sk64& a)
+{
+    fHi = a.fHi - fHi - (a.fLo < fLo);
+    fLo = a.fLo - fLo;
+}
+
+void Sk64::setMul(int32_t a, int32_t b)
+{
+    int sa = a >> 31;
+    int sb = b >> 31;
+    // now make them positive
+    a = (a ^ sa) - sa;
+    b = (b ^ sb) - sb;
+
+    uint32_t    ah = a >> 16;
+    uint32_t    al = a & 0xFFFF;
+    uint32_t bh = b >> 16;
+    uint32_t bl = b & 0xFFFF;
+
+    uint32_t A = ah * bh;
+    uint32_t B = ah * bl + al * bh;
+    uint32_t C = al * bl;
+
+    /*  [  A  ]
+           [  B  ]
+              [  C  ]
+    */
+    fLo = C + (B << 16);
+    fHi = A + (B >>16) + (fLo < C);
+
+    if (sa != sb)
+        this->negate();
+}
+
+void Sk64::div(int32_t denom, DivOptions option)
+{
+    SkASSERT(denom);
+
+    int32_t     hi = fHi;
+    uint32_t    lo = fLo;
+    int         sign = denom ^ hi;
+
+    denom = SkAbs32(denom);
+    if (hi < 0)
+    {
+        hi = -hi - Sk32ToBool(lo);
+        lo = 0 - lo;
+    }
+
+    if (option == kRound_DivOption) // add denom/2
+    {
+        uint32_t newLo = lo + (denom >> 1);
+        hi += (newLo < lo);
+        lo = newLo;
+    }
+
+    if (hi == 0)    // fast-case
+    {
+        if (lo < (uint32_t)denom)
+            this->set(0, 0);
+        else
+        {
+            this->set(0, lo / denom);
+            if (sign < 0)
+                this->negate();
+        }
+        return;
+    }
+
+    int bits;
+
+    {
+        int dbits = SkCLZ(denom);
+        int nbits = SkCLZ(hi);
+
+        bits = 32 + dbits - nbits;
+        SkASSERT(bits <= 63);
+        if (bits <= 0)
+        {
+            this->set(0, 0);
+            return;
+        }
+        denom <<= (dbits - 1);
+        shift_left_bits(hi, lo, nbits - 1);
+    }
+
+    int32_t     rhi = 0;
+    uint32_t    rlo = 0;
+
+    do {
+        shift_left(rhi, rlo);
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+        if ((uint32_t)denom <= (uint32_t)hi)
+        {
+            hi -= denom;
+            rlo |= 1;
+        }
+#else
+        int32_t diff = (denom - hi - 1) >> 31;
+        hi -= denom & diff;
+        rlo -= diff;
+#endif
+        shift_left(hi, lo);
+    } while (--bits >= 0);
+    SkASSERT(rhi >= 0);
+
+    fHi = rhi;
+    fLo = rlo;
+    if (sign < 0)
+        this->negate();
+}
+
+#define shift_left_2(a, b, c)   \
+    a = (a << 2) | (b >> 30);   \
+    b = (b << 2) | (c >> 30);   \
+    c <<= 2
+
+int32_t Sk64::getSqrt() const
+{
+    SkASSERT(!this->isNeg());
+
+    uint32_t    hi = fHi;
+    uint32_t lo = fLo;
+    uint32_t    sqr = 0;
+    uint32_t root = 0;
+    int count = 31;
+
+    do {
+        root <<= 1;
+        shift_left_2(sqr, hi, lo);
+
+        uint32_t testDiv = (root << 1) + 1;
+        if (sqr >= testDiv)
+        {
+            sqr -= testDiv;
+            root++;
+        }
+    } while (--count >= 0);
+    SkASSERT((int32_t)root >= 0);
+
+    return root;
+}
+
+#ifdef SkLONGLONG
+    SkLONGLONG Sk64::getLongLong() const
+    {
+        SkLONGLONG value = fHi;
+        value <<= 32;
+        return value | fLo;
+    }
+#endif
+
+SkFixed Sk64::getFixedDiv(const Sk64& denom) const
+{
+    Sk64    N = *this;
+    Sk64    D = denom;
+    int32_t sign = SkExtractSign(N.fHi ^ D.fHi);
+    SkFixed result;
+
+    N.abs();
+    D.abs();
+
+    // need to knock D down to just 31 bits
+    // either by rounding it to the right, or shifting N to the left
+    // then we can just call 64/32 div
+
+    int nclz = N.fHi ? SkCLZ(N.fHi) : 32;
+    int dclz = D.fHi ? SkCLZ(D.fHi) : (33 - (D.fLo >> 31));
+
+    int shiftN = nclz - 1;
+    SkASSERT(shiftN >= 0);
+    int shiftD = 33 - dclz;
+    SkASSERT(shiftD >= 0);
+
+    if (shiftD + shiftN < 16)
+        shiftD = 16 - shiftN;
+    else
+        shiftN = 16 - shiftD;
+
+    D.roundRight(shiftD);
+    if (D.isZero())
+        result = SK_MaxS32;
+    else
+    {
+        if (shiftN >= 0)
+            N.shiftLeft(shiftN);
+        else
+            N.roundRight(-shiftN);
+        N.div(D.get32(), Sk64::kTrunc_DivOption);
+        if (N.is32())
+            result = N.get32();
+        else
+            result = SK_MaxS32;
+    }
+    return SkApplySign(result, sign);
+}
+
+///////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#include "SkRandom.h"
+#include <math.h>
+
+#ifdef SK_SUPPORT_UNITTEST
+struct BoolTable {
+    int8_t  zero, pos, neg, toBool, sign;
+};
+
+static void bool_table_test(const Sk64& a, const BoolTable& table)
+{
+    SkASSERT(a.isZero() != a.nonZero());
+
+    SkASSERT(!a.isZero() == !table.zero);
+    SkASSERT(!a.isPos() == !table.pos);
+    SkASSERT(!a.isNeg() == !table.neg);
+    SkASSERT(a.sign() == table.sign);
+}
+
+#ifdef SkLONGLONG
+    static SkLONGLONG asLL(const Sk64& a)
+    {
+        return ((SkLONGLONG)a.fHi << 32) | a.fLo;
+    }
+#endif
+#endif
+
+void Sk64::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    enum BoolTests {
+        kZero_BoolTest,
+        kPos_BoolTest,
+        kNeg_BoolTest
+    };
+    static const BoolTable gBoolTable[] = {
+        { 1, 0, 0, 0, 0 },
+        { 0, 1, 0, 1, 1 },
+        { 0, 0, 1, 1, -1 }
+    };
+
+    Sk64    a, b, c;
+
+    a.fHi = a.fLo = 0;
+    b.set(0);
+    c.setZero();
+    SkASSERT(a == b);
+    SkASSERT(a == c);
+    bool_table_test(a, gBoolTable[kZero_BoolTest]);
+
+    a.fHi = 0;  a.fLo = 5;
+    b.set(5);
+    SkASSERT(a == b);
+    SkASSERT(a.is32() && a.get32() == 5 && !a.is64());
+    bool_table_test(a, gBoolTable[kPos_BoolTest]);
+
+    a.fHi = -1; a.fLo = (uint32_t)-5;
+    b.set(-5);
+    SkASSERT(a == b);
+    SkASSERT(a.is32() && a.get32() == -5 && !a.is64());
+    bool_table_test(a, gBoolTable[kNeg_BoolTest]);
+
+    a.setZero();
+    b.set(6);
+    c.set(-6);
+    SkASSERT(a != b && b != c && a != c);
+    SkASSERT(!(a == b) && !(a == b) && !(a == b));
+    SkASSERT(a < b && b > a && a <= b && b >= a);
+    SkASSERT(c < a && a > c && c <= a && a >= c);
+    SkASSERT(c < b && b > c && c <= b && b >= c);
+
+    // Now test add/sub
+
+    SkRandom    rand;
+    int         i;
+
+    for (i = 0; i < 1000; i++)
+    {
+        int aa = rand.nextS() >> 1;
+        int bb = rand.nextS() >> 1;
+        a.set(aa);
+        b.set(bb);
+        SkASSERT(a.get32() == aa && b.get32() == bb);
+        c = a; c.add(bb);
+        SkASSERT(c.get32() == aa + bb);
+        c = a; c.add(-bb);
+        SkASSERT(c.get32() == aa - bb);
+        c = a; c.add(b);
+        SkASSERT(c.get32() == aa + bb);
+        c = a; c.sub(b);
+        SkASSERT(c.get32() == aa - bb);
+    }
+
+#ifdef SkLONGLONG
+    for (i = 0; i < 1000; i++)
+    {
+        rand.next64(&a); //a.fHi >>= 1; // avoid overflow
+        rand.next64(&b); //b.fHi >>= 1; // avoid overflow
+
+        if (!(i & 3))   // want to explicitly test these cases
+        {
+            a.fLo = 0;
+            b.fLo = 0;
+        }
+        else if (!(i & 7))  // want to explicitly test these cases
+        {
+            a.fHi = 0;
+            b.fHi = 0;
+        }
+
+        SkLONGLONG aa = asLL(a);
+        SkLONGLONG bb = asLL(b);
+
+        SkASSERT((a < b) == (aa < bb));
+        SkASSERT((a <= b) == (aa <= bb));
+        SkASSERT((a > b) == (aa > bb));
+        SkASSERT((a >= b) == (aa >= bb));
+        SkASSERT((a == b) == (aa == bb));
+        SkASSERT((a != b) == (aa != bb));
+
+        c = a; c.add(b);
+        SkASSERT(asLL(c) == aa + bb);
+        c = a; c.sub(b);
+        SkASSERT(asLL(c) == aa - bb);
+        c = a; c.rsub(b);
+        SkASSERT(asLL(c) == bb - aa);
+        c = a; c.negate();
+        SkASSERT(asLL(c) == -aa);
+
+        int bits = rand.nextU() & 63;
+        c = a; c.shiftLeft(bits);
+        SkASSERT(asLL(c) == (aa << bits));
+        c = a; c.shiftRight(bits);
+        SkASSERT(asLL(c) == (aa >> bits));
+        c = a; c.roundRight(bits);
+
+        SkLONGLONG tmp;
+
+        tmp = aa;
+        if (bits > 0)
+            tmp += (SkLONGLONG)1 << (bits - 1);
+        SkASSERT(asLL(c) == (tmp >> bits));
+
+        c.setMul(a.fHi, b.fHi);
+        tmp = (SkLONGLONG)a.fHi * b.fHi;
+        SkASSERT(asLL(c) == tmp);
+    }
+
+
+    for (i = 0; i < 100000; i++)
+    {
+        Sk64    wide;
+        int32_t denom = rand.nextS();
+
+        while (denom == 0)
+            denom = rand.nextS();
+        wide.setMul(rand.nextS(), rand.nextS());
+        SkLONGLONG check = wide.getLongLong();
+
+        wide.div(denom, Sk64::kTrunc_DivOption);
+        check /= denom;
+        SkLONGLONG w = wide.getLongLong();
+
+        SkASSERT(check == w);
+
+#ifdef SK_CAN_USE_FLOATx
+        wide.setMul(rand.nextS(), rand.nextS());
+        wide.abs();
+        denom = wide.getSqrt();
+        int32_t ck = (int32_t)sqrt((double)wide.getLongLong());
+        int diff = denom - ck;
+        SkASSERT(SkAbs32(diff) <= 1);
+
+        wide.setMul(rand.nextS(), rand.nextS());
+        Sk64    dwide;
+        dwide.setMul(rand.nextS(), rand.nextS());
+        SkFixed fixdiv = wide.getFixedDiv(dwide);
+        double dnumer = (double)wide.getLongLong();
+        double ddenom = (double)dwide.getLongLong();
+        double ddiv = dnumer / ddenom;
+        SkFixed dfixdiv;
+        if (ddiv >= (double)SK_MaxS32 / (double)SK_Fixed1)
+            dfixdiv = SK_MaxS32;
+        else if (ddiv <= -(double)SK_MaxS32 / (double)SK_Fixed1)
+            dfixdiv = SK_MinS32;
+        else
+            dfixdiv = SkFloatToFixed(dnumer / ddenom);
+        diff = fixdiv - dfixdiv;
+        
+        if (SkAbs32(diff) > 1) {
+            SkDebugf(" %d === numer %g denom %g div %g xdiv %x fxdiv %x\n",
+                     i, dnumer, ddenom, ddiv, dfixdiv, fixdiv);
+        }
+//        SkASSERT(SkAbs32(diff) <= 1);
+#endif
+    }
+#endif
+#endif
+}
+
+#endif
+
diff --git a/src/core/SkAlphaRuns.cpp b/src/core/SkAlphaRuns.cpp
new file mode 100644
index 0000000..46b0206
--- /dev/null
+++ b/src/core/SkAlphaRuns.cpp
@@ -0,0 +1,185 @@
+/* libs/graphics/sgl/SkAlphaRuns.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 "SkAntiRun.h"
+
+void SkAlphaRuns::reset(int width)
+{
+    SkASSERT(width > 0);
+
+    fRuns[0] = SkToS16(width);
+    fRuns[width] = 0;
+    fAlpha[0] = 0;
+
+    SkDEBUGCODE(fWidth = width;)
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkAlphaRuns::Break(int16_t runs[], uint8_t alpha[], int x, int count)
+{
+    SkASSERT(count > 0 && x >= 0);
+
+//  SkAlphaRuns::BreakAt(runs, alpha, x);
+//  SkAlphaRuns::BreakAt(&runs[x], &alpha[x], count);
+
+    int16_t* next_runs = runs + x;
+    uint8_t*  next_alpha = alpha + x;
+
+    while (x > 0)
+    {
+        int n = runs[0];
+        SkASSERT(n > 0);
+
+        if (x < n)
+        {
+            alpha[x] = alpha[0];
+            runs[0] = SkToS16(x);
+            runs[x] = SkToS16(n - x);
+            break;
+        }
+        runs += n;
+        alpha += n;
+        x -= n;
+    }
+
+    runs = next_runs;
+    alpha = next_alpha;
+    x = count;
+
+    for (;;)
+    {
+        int n = runs[0];
+        SkASSERT(n > 0);
+
+        if (x < n)
+        {
+            alpha[x] = alpha[0];
+            runs[0] = SkToS16(x);
+            runs[x] = SkToS16(n - x);
+            break;
+        }
+        x -= n;
+        if (x <= 0)
+            break;
+
+        runs += n;
+        alpha += n;
+    }
+}
+
+void SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue)
+{
+    SkASSERT(middleCount >= 0);
+    SkASSERT(x >= 0 && x + (startAlpha != 0) + middleCount + (stopAlpha != 0) <= fWidth);
+
+    int16_t*    runs = fRuns;
+    uint8_t*     alpha = fAlpha;
+
+    if (startAlpha)
+    {
+        SkAlphaRuns::Break(runs, alpha, x, 1);
+        /*  I should be able to just add alpha[x] + startAlpha.
+            However, if the trailing edge of the previous span and the leading
+            edge of the current span round to the same super-sampled x value,
+            I might overflow to 256 with this add, hence the funny subtract (crud).
+        */
+        unsigned tmp = alpha[x] + startAlpha;
+        SkASSERT(tmp <= 256);
+        alpha[x] = SkToU8(tmp - (tmp >> 8));    // was (tmp >> 7), but that seems wrong if we're trying to catch 256
+
+        runs += x + 1;
+        alpha += x + 1;
+        x = 0;
+        SkDEBUGCODE(this->validate();)
+    }
+    if (middleCount)
+    {
+        SkAlphaRuns::Break(runs, alpha, x, middleCount);
+        alpha += x;
+        runs += x;
+        x = 0;
+        do {
+            alpha[0] = SkToU8(alpha[0] + maxValue);
+            int n = runs[0];
+            SkASSERT(n <= middleCount);
+            alpha += n;
+            runs += n;
+            middleCount -= n;
+        } while (middleCount > 0);
+        SkDEBUGCODE(this->validate();)
+    }
+    if (stopAlpha)
+    {
+        SkAlphaRuns::Break(runs, alpha, x, 1);
+        alpha[x] = SkToU8(alpha[x] + stopAlpha);
+        SkDEBUGCODE(this->validate();)
+    }
+}
+
+#ifdef SK_DEBUG
+    void SkAlphaRuns::assertValid(int y, int maxStep) const
+    {
+        int max = (y + 1) * maxStep - (y == maxStep - 1);
+
+        const int16_t* runs = fRuns;
+        const uint8_t*   alpha = fAlpha;
+
+        while (*runs)
+        {
+            SkASSERT(*alpha <= max);
+            alpha += *runs;
+            runs += *runs;
+        }
+    }
+
+    void SkAlphaRuns::dump() const
+    {
+        const int16_t* runs = fRuns;
+        const uint8_t* alpha = fAlpha;
+
+        SkDebugf("Runs");
+        while (*runs)
+        {
+            int n = *runs;
+
+            SkDebugf(" %02x", *alpha);
+            if (n > 1)
+                SkDebugf(",%d", n);
+            alpha += n;
+            runs += n;
+        }
+        SkDebugf("\n");
+    }
+
+    void SkAlphaRuns::validate() const
+    {
+        SkASSERT(fWidth > 0);
+
+        int         count = 0;
+        const int16_t*  runs = fRuns;
+
+        while (*runs)
+        {
+            SkASSERT(*runs > 0);
+            count += *runs;
+            SkASSERT(count <= fWidth);
+            runs += *runs;
+        }
+        SkASSERT(count == fWidth);
+    }
+#endif
+
diff --git a/src/core/SkAntiRun.h b/src/core/SkAntiRun.h
new file mode 100644
index 0000000..12930e6
--- /dev/null
+++ b/src/core/SkAntiRun.h
@@ -0,0 +1,185 @@
+/* libs/graphics/sgl/SkAntiRun.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 SkAntiRun_DEFINED
+#define SkAntiRun_DEFINED
+
+#include "SkBlitter.h"
+
+inline int sk_make_nonzero_neg_one(int x)
+{
+    return (x | -x) >> 31;
+}
+
+#if 0
+template <int kShift> class SkAntiRun {
+    static uint8_t coverage_to_alpha(int aa)
+    {
+        aa <<= 8 - 2*kShift;
+        aa -= aa >> (8 - kShift - 1);
+        return SkToU8(aa);
+    }
+public:
+    void set(int start, int stop)
+    {
+        SkASSERT(start >= 0 && stop > start);
+
+#if 1
+        int fb = start & kMask;
+        int fe = stop & kMask;
+        int n = (stop >> kShift) - (start >> kShift) - 1;
+
+        if (n < 0)
+        {
+            fb = fe - fb;
+            n = 0;
+            fe = 0;
+        }
+        else
+        {
+            if (fb == 0)
+                n += 1;
+            else
+                fb = (1 << kShift) - fb;
+        }
+
+        fStartAlpha = coverage_to_alpha(fb);
+        fMiddleCount = n;
+        fStopAlpha = coverage_to_alpha(fe);
+#else
+        int x0 = start >> kShift;
+        int x1 = (stop - 1) >> kShift;
+        int middle = x1 - x0;
+        int aa;
+
+        if (middle == 0)
+        {
+            aa = stop - start;
+            aa <<= 8 - 2*kShift;
+            aa -= aa >> (8 - kShift - 1);
+            SkASSERT(aa > 0 && aa < kMax);
+            fStartAlpha = SkToU8(aa);
+            fMiddleCount = 0;
+            fStopAlpha = 0;
+        }
+        else
+        {
+            int aa = start & kMask;
+            aa <<= 8 - 2*kShift;
+            aa -= aa >> (8 - kShift - 1);
+            SkASSERT(aa >= 0 && aa < kMax);
+            if (aa)
+                fStartAlpha = SkToU8(kMax - aa);
+            else
+            {
+                fStartAlpha = 0;
+                middle += 1;
+            }
+            aa = stop & kMask;
+            aa <<= 8 - 2*kShift;
+            aa -= aa >> (8 - kShift - 1);
+            SkASSERT(aa >= 0 && aa < kMax);
+            middle += sk_make_nonzero_neg_one(aa);
+
+            fStopAlpha = SkToU8(aa);
+            fMiddleCount = middle;
+        }
+        SkASSERT(fStartAlpha < kMax);
+        SkASSERT(fStopAlpha < kMax);
+#endif
+    }
+
+    void blit(int x, int y, SkBlitter* blitter)
+    {
+        int16_t runs[2];
+        runs[0] = 1;
+        runs[1] = 0;
+
+        if (fStartAlpha)
+        {
+            blitter->blitAntiH(x, y, &fStartAlpha, runs);
+            x += 1;
+        }
+        if (fMiddleCount)
+        {
+            blitter->blitH(x, y, fMiddleCount);
+            x += fMiddleCount;
+        }
+        if (fStopAlpha)
+            blitter->blitAntiH(x, y, &fStopAlpha, runs);
+    }
+
+    uint8_t  getStartAlpha() const { return fStartAlpha; }
+    int getMiddleCount() const { return fMiddleCount; }
+    uint8_t  getStopAlpha() const { return fStopAlpha; }
+
+private:
+    uint8_t  fStartAlpha, fStopAlpha;
+    int fMiddleCount;
+
+    enum {
+        kMask = (1 << kShift) - 1,
+        kMax = (1 << (8 - kShift)) - 1
+    };
+};
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class SkAlphaRuns {
+public:
+    int16_t*    fRuns;
+    uint8_t*     fAlpha;
+
+    bool    empty() const
+    {
+        SkASSERT(fRuns[0] > 0);
+        return fAlpha[0] == 0 && fRuns[fRuns[0]] == 0;
+    }
+    void    reset(int width);
+    void    add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue);
+    SkDEBUGCODE(void assertValid(int y, int maxStep) const;)
+    SkDEBUGCODE(void dump() const;)
+
+    static void Break(int16_t runs[], uint8_t alpha[], int x, int count);
+    static void BreakAt(int16_t runs[], uint8_t alpha[], int x)
+    {
+        while (x > 0)
+        {
+            int n = runs[0];
+            SkASSERT(n > 0);
+
+            if (x < n)
+            {
+                alpha[x] = alpha[0];
+                runs[0] = SkToS16(x);
+                runs[x] = SkToS16(n - x);
+                break;
+            }
+            runs += n;
+            alpha += n;
+            x -= n;
+        }
+    }
+
+private:
+    SkDEBUGCODE(int fWidth;)
+    SkDEBUGCODE(void validate() const;)
+};
+
+#endif
+
diff --git a/src/core/SkAutoKern.h b/src/core/SkAutoKern.h
new file mode 100644
index 0000000..023cb6b
--- /dev/null
+++ b/src/core/SkAutoKern.h
@@ -0,0 +1,62 @@
+/* libs/graphics/sgl/SkAutoKern.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 SkAutoKern_DEFINED
+#define SkAutoKern_DEFINED
+
+#include "SkScalerContext.h"
+
+#define SkAutoKern_AdjustF(prev, next)    (((next) - (prev) + 32) >> 6 << 16)
+#define SkAutoKern_AdjustS(prev, next)    SkIntToScalar(((next) - (prev) + 32) >> 6)
+
+/* this is a helper class to perform auto-kerning
+ * the adjust() method returns a SkFixed corresponding
+ * to a +1/0/-1 pixel adjustment
+ */
+
+class SkAutoKern {
+public:
+    SkAutoKern() : fPrevRsbDelta(0) {}
+
+    SkFixed  adjust(const SkGlyph&  glyph) 
+    {
+//        if (SkAbs32(glyph.fLsbDelta) > 47 || SkAbs32(glyph.fRsbDelta) > 47)
+//            printf("------- %d> L %d R %d\n", glyph.f_GlyphID, glyph.fLsbDelta, glyph.fRsbDelta);
+
+#if 0
+        int  distort = fPrevRsbDelta - glyph.fLsbDelta;
+
+        fPrevRsbDelta = glyph.fRsbDelta;
+
+        if (distort >= 32)
+            return -SK_Fixed1;
+        else if (distort < -32)
+            return +SK_Fixed1;
+        else
+            return 0;
+#else
+        SkFixed adjust = SkAutoKern_AdjustF(fPrevRsbDelta, glyph.fLsbDelta);
+        fPrevRsbDelta = glyph.fRsbDelta;
+        return adjust;
+#endif
+    }
+private:
+    int   fPrevRsbDelta;
+};
+
+#endif
+
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
new file mode 100644
index 0000000..5ca3601
--- /dev/null
+++ b/src/core/SkBitmap.cpp
@@ -0,0 +1,1255 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkFlattenable.h"
+#include "SkMallocPixelRef.h"
+#include "SkMask.h"
+#include "SkPixelRef.h"
+#include "SkThread.h"
+#include "SkUtils.h"
+#include "SkPackBits.h"
+#include <new>
+
+#ifdef SK_SUPPORT_MIPMAP
+struct MipLevel {
+    void*       fPixels;
+    uint32_t    fRowBytes;
+    uint16_t    fWidth, fHeight;
+};
+
+struct SkBitmap::MipMap : SkNoncopyable {
+    int32_t fRefCnt;
+    int     fLevelCount;
+//  MipLevel    fLevel[fLevelCount];
+//  Pixels[]
+    
+    static MipMap* Alloc(int levelCount, size_t pixelSize) {
+        MipMap* mm = (MipMap*)sk_malloc_throw(sizeof(MipMap) +
+                                              levelCount * sizeof(MipLevel) +
+                                              pixelSize);
+        mm->fRefCnt = 1;
+        mm->fLevelCount = levelCount;
+        return mm;
+    }
+
+    const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
+    MipLevel* levels() { return (MipLevel*)(this + 1); }
+
+    const void* pixels() const { return levels() + fLevelCount; }
+    void* pixels() { return levels() + fLevelCount; }
+    
+    void safeRef() {
+        if (this) {
+            SkASSERT(fRefCnt > 0);
+            sk_atomic_inc(&fRefCnt);
+        }
+    }
+    void safeUnref() {
+        if (this) {
+            SkASSERT(fRefCnt > 0);
+            if (sk_atomic_dec(&fRefCnt) == 1) {
+                sk_free(this);
+            }
+        }
+    }
+};
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmap::SkBitmap() {
+    bzero(this, sizeof(*this));
+}
+
+SkBitmap::SkBitmap(const SkBitmap& src) {
+    SkDEBUGCODE(src.validate();)
+    bzero(this, sizeof(*this));
+    *this = src;
+    SkDEBUGCODE(this->validate();)
+}
+
+SkBitmap::~SkBitmap() {
+    SkDEBUGCODE(this->validate();)
+    this->freePixels();
+}
+
+SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
+    if (this != &src) {
+        this->freePixels();
+        memcpy(this, &src, sizeof(src));
+
+        // inc src reference counts
+        src.fPixelRef->safeRef();
+#ifdef SK_SUPPORT_MIPMAP
+        src.fMipMap->safeRef();
+#endif
+
+        // we reset our locks if we get blown away
+        fPixelLockCount = 0;
+        
+        /*  The src could be in 3 states
+            1. no pixelref, in which case we just copy/ref the pixels/ctable
+            2. unlocked pixelref, pixels/ctable should be null
+            3. locked pixelref, we should lock the ref again ourselves
+        */
+        if (NULL == fPixelRef) {
+            // leave fPixels as it is
+            fColorTable->safeRef(); // ref the user's ctable if present
+        } else {    // we have a pixelref, so pixels/ctable reflect it
+            // ignore the values from the memcpy
+            fPixels = NULL;
+            fColorTable = NULL;
+        }
+    }
+
+    SkDEBUGCODE(this->validate();)
+    return *this;
+}
+
+void SkBitmap::swap(SkBitmap& other) {
+    SkTSwap<SkColorTable*>(fColorTable, other.fColorTable);
+    SkTSwap<SkPixelRef*>(fPixelRef, other.fPixelRef);
+    SkTSwap<size_t>(fPixelRefOffset, other.fPixelRefOffset);
+    SkTSwap<int>(fPixelLockCount, other.fPixelLockCount);
+#ifdef SK_SUPPORT_MIPMAP
+    SkTSwap<MipMap*>(fMipMap, other.fMipMap);
+#endif
+    SkTSwap<void*>(fPixels, other.fPixels);
+    SkTSwap<uint16_t>(fWidth, other.fWidth);
+    SkTSwap<uint16_t>(fHeight, other.fHeight);
+    SkTSwap<uint32_t>(fRowBytes, other.fRowBytes);
+    SkTSwap<uint8_t>(fConfig, other.fConfig);
+    SkTSwap<uint8_t>(fFlags, other.fFlags);
+    SkTSwap<uint8_t>(fBytesPerPixel, other.fBytesPerPixel);
+
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkBitmap::reset() {
+    this->freePixels();
+    bzero(this, sizeof(*this));
+}
+
+int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
+    int bpp;
+    switch (config) {
+        case kNo_Config:
+        case kA1_Config:
+            bpp = 0;   // not applicable
+            break;
+        case kRLE_Index8_Config:
+        case kA8_Config:
+        case kIndex8_Config:
+            bpp = 1;
+            break;
+        case kRGB_565_Config:
+        case kARGB_4444_Config:
+            bpp = 2;
+            break;
+        case kARGB_8888_Config:
+            bpp = 4;
+            break;
+        default:
+            SkASSERT(!"unknown config");
+            bpp = 0;   // error
+            break;
+    }
+    return bpp;
+}
+
+int SkBitmap::ComputeRowBytes(Config c, int width) {
+    int rowBytes = 0;
+
+    switch (c) {
+        case kNo_Config:
+        case kRLE_Index8_Config:
+            // assume that the bitmap has no pixels to draw to
+            rowBytes = 0;
+            break;
+        case kA1_Config:
+            rowBytes = (width + 7) >> 3;
+            break;
+        case kA8_Config:
+        case kIndex8_Config:
+            rowBytes = width;
+            break;
+        case kRGB_565_Config:
+        case kARGB_4444_Config:
+            rowBytes = width << 1;
+            break;
+        case kARGB_8888_Config:
+            rowBytes = width << 2;
+            break;
+        default:
+            SkASSERT(!"unknown config");
+            break;
+    }
+    return rowBytes;
+}
+
+Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
+    Sk64 size;
+    size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
+    return size;
+}
+
+size_t SkBitmap::ComputeSize(Config c, int width, int height) {
+    Sk64 size = SkBitmap::ComputeSize64(c, width, height);
+    if (size.isNeg() || !size.is32()) {
+        return 0;
+    }
+    return size.get32();
+}
+
+void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
+    this->freePixels();
+
+    if (rowBytes == 0) {
+        rowBytes = SkBitmap::ComputeRowBytes(c, width);
+    }
+    fConfig     = SkToU8(c);
+    fWidth      = SkToU16(width);
+    fHeight     = SkToU16(height);
+    fRowBytes   = rowBytes;
+
+    fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
+
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkBitmap::updatePixelsFromRef() const {
+    if (NULL != fPixelRef) {
+        if (fPixelLockCount > 0) {
+            SkASSERT(fPixelRef->getLockCount() > 0);
+            
+            void* p = fPixelRef->pixels();
+            if (NULL != p) {
+                p = (char*)p + fPixelRefOffset;
+            }
+            fPixels = p;
+            SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
+        } else {
+            SkASSERT(0 == fPixelLockCount);
+            fPixels = NULL;
+            fColorTable->safeUnref();
+            fColorTable = NULL;
+        }
+    }
+}
+
+SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
+    // do this first, we that we never have a non-zero offset with a null ref
+    if (NULL == pr) {
+        offset = 0;
+    }
+
+    if (fPixelRef != pr || fPixelRefOffset != offset) {
+        if (fPixelRef != pr) {
+            this->freePixels();
+            SkASSERT(NULL == fPixelRef);
+            
+            pr->safeRef();
+            fPixelRef = pr;
+        }
+        fPixelRefOffset = offset;
+        this->updatePixelsFromRef();
+    }
+
+    SkDEBUGCODE(this->validate();)
+    return pr;
+}
+
+void SkBitmap::lockPixels() const {
+    if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
+        fPixelRef->lockPixels();
+        this->updatePixelsFromRef();
+    }
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkBitmap::unlockPixels() const {
+    SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
+
+    if (NULL != fPixelRef && 0 == --fPixelLockCount) {
+        fPixelRef->unlockPixels();
+        this->updatePixelsFromRef();
+    }
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
+    this->freePixels();
+    fPixels = p;
+    SkRefCnt_SafeAssign(fColorTable, ctable);
+
+    SkDEBUGCODE(this->validate();)
+}
+
+bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
+    HeapAllocator stdalloc;
+
+    if (NULL == allocator) {
+        allocator = &stdalloc;
+    }
+    return allocator->allocPixelRef(this, ctable);
+}
+
+void SkBitmap::freePixels() {
+    // if we're gonna free the pixels, we certainly need to free the mipmap
+    this->freeMipMap();
+
+    fColorTable->safeUnref();
+    fColorTable = NULL;
+
+    if (NULL != fPixelRef) {
+        if (fPixelLockCount > 0) {
+            fPixelRef->unlockPixels();
+        }
+        fPixelRef->unref();
+        fPixelRef = NULL;
+        fPixelRefOffset = 0;
+    }
+    fPixelLockCount = 0;
+    fPixels = NULL;
+}
+
+void SkBitmap::freeMipMap() {
+#ifdef SK_SUPPORT_MIPMAP
+    fMipMap->safeUnref();
+    fMipMap = NULL;
+#endif
+}
+
+uint32_t SkBitmap::getGenerationID() const {
+    return fPixelRef ? fPixelRef->getGenerationID() : 0;
+}
+
+void SkBitmap::notifyPixelsChanged() const {
+    if (fPixelRef) {
+        fPixelRef->notifyPixelsChanged();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size,
+                                   SkColorTable* ctable) {
+    SkASSERT(storage);
+    fStorage = storage;
+    fSize = size;
+    fCTable = ctable;
+    ctable->safeRef();
+}
+
+SkMallocPixelRef::~SkMallocPixelRef() {
+    fCTable->safeUnref();
+    sk_free(fStorage);
+}
+
+void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) {
+    *ct = fCTable;
+    return fStorage;
+}
+
+void SkMallocPixelRef::onUnlockPixels() {
+    // nothing to do
+}
+
+void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    
+    buffer.write32(fSize);
+    buffer.writePad(fStorage, fSize);
+    if (fCTable) {
+        buffer.writeBool(true);
+        fCTable->flatten(buffer);
+    } else {
+        buffer.writeBool(false);
+    }
+}
+
+SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer) : INHERITED(buffer, NULL) {
+    fSize = buffer.readU32();
+    fStorage = sk_malloc_throw(fSize);
+    buffer.read(fStorage, fSize);
+    if (buffer.readBool()) {
+        fCTable = SkNEW_ARGS(SkColorTable, (buffer));
+    } else {
+        fCTable = NULL;
+    }
+}
+
+static SkPixelRef::Registrar reg("SkMallocPixelRef",
+                                 SkMallocPixelRef::Create);
+
+/** We explicitly use the same allocator for our pixels that SkMask does,
+ so that we can freely assign memory allocated by one class to the other.
+ */
+bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
+                                            SkColorTable* ctable) {
+    Sk64 size = dst->getSize64();
+    if (size.isNeg() || !size.is32()) {
+        return false;
+    }
+    
+    void* addr = sk_malloc_flags(size.get32(), 0);  // returns NULL on failure
+    if (NULL == addr) {
+        return false;
+    }
+    
+    dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
+    // since we're already allocated, we lockPixels right away
+    dst->lockPixels();
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkBitmap::isOpaque() const {
+    switch (fConfig) {
+        case kNo_Config:
+            return true;
+
+        case kA1_Config:
+        case kA8_Config:
+        case kARGB_4444_Config:
+        case kARGB_8888_Config:
+            return (fFlags & kImageIsOpaque_Flag) != 0;
+
+        case kIndex8_Config:
+        case kRLE_Index8_Config: {
+                uint32_t flags = 0;
+
+                this->lockPixels();
+                // if lockPixels failed, we may not have a ctable ptr
+                if (fColorTable) {
+                    flags = fColorTable->getFlags();
+                }
+                this->unlockPixels();
+
+                return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
+            }
+
+        case kRGB_565_Config:
+            return true;
+
+        default:
+            SkASSERT(!"unknown bitmap config pased to isOpaque");
+            return false;
+    }
+}
+
+void SkBitmap::setIsOpaque(bool isOpaque) {
+    /*  we record this regardless of fConfig, though it is ignored in
+        isOpaque() for configs that can't support per-pixel alpha.
+    */
+    if (isOpaque) {
+        fFlags |= kImageIsOpaque_Flag;
+    } else {
+        fFlags &= ~kImageIsOpaque_Flag;
+    }
+}
+
+void* SkBitmap::getAddr(int x, int y) const {
+    SkASSERT((unsigned)x < (unsigned)this->width());
+    SkASSERT((unsigned)y < (unsigned)this->height());
+
+    char* base = (char*)this->getPixels();
+    if (base) {
+        base += y * this->rowBytes();
+        switch (this->config()) {
+            case SkBitmap::kARGB_8888_Config:
+                base += x << 2;
+                break;
+            case SkBitmap::kARGB_4444_Config:
+            case SkBitmap::kRGB_565_Config:
+                base += x << 1;
+                break;
+            case SkBitmap::kA8_Config:
+            case SkBitmap::kIndex8_Config:
+                base += x;
+                break;
+            case SkBitmap::kA1_Config:
+                base += x >> 3;
+                break;
+            case kRLE_Index8_Config:
+                SkASSERT(!"Can't return addr for kRLE_Index8_Config");
+                base = NULL;
+                break;
+            default:
+                SkASSERT(!"Can't return addr for config");
+                base = NULL;
+                break;
+        }
+    }
+    return base;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
+    SkDEBUGCODE(this->validate();)
+
+    if (0 == fWidth || 0 == fHeight ||
+            kNo_Config == fConfig || kIndex8_Config == fConfig) {
+        return;
+    }
+
+    SkAutoLockPixels alp(*this);
+    // perform this check after the lock call
+    if (!this->readyToDraw()) {
+        return;
+    }
+
+    int height = fHeight;
+    const int width = fWidth;
+    const int rowBytes = fRowBytes;
+
+    // make rgb premultiplied
+    if (255 != a) {
+        r = SkAlphaMul(r, a);
+        g = SkAlphaMul(g, a);
+        b = SkAlphaMul(b, a);
+    }
+
+    switch (fConfig) {
+        case kA1_Config: {
+            uint8_t* p = (uint8_t*)fPixels;
+            const int count = (width + 7) >> 3;
+            a = (a >> 7) ? 0xFF : 0;
+            SkASSERT(count <= rowBytes);
+            while (--height >= 0) {
+                memset(p, a, count);
+                p += rowBytes;
+            }
+            break;
+        }
+        case kA8_Config: {
+            uint8_t* p = (uint8_t*)fPixels;
+            while (--height >= 0) {
+                memset(p, a, width);
+                p += rowBytes;
+            }
+            break;
+        }
+        case kARGB_4444_Config:
+        case kRGB_565_Config: {
+            uint16_t* p = (uint16_t*)fPixels;
+            uint16_t v;
+            
+            if (kARGB_4444_Config == fConfig) {
+                v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
+            } else {    // kRGB_565_Config
+                v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
+                                b >> (8 - SK_B16_BITS));
+            }
+            while (--height >= 0) {
+                sk_memset16(p, v, width);
+                p = (uint16_t*)((char*)p + rowBytes);
+            }
+            break;
+        }
+        case kARGB_8888_Config: {
+            uint32_t* p = (uint32_t*)fPixels;
+            uint32_t  v = SkPackARGB32(a, r, g, b);
+
+            while (--height >= 0) {
+                sk_memset32(p, v, width);
+                p = (uint32_t*)((char*)p + rowBytes);
+            }
+            break;
+        }
+    }
+    
+    this->notifyPixelsChanged();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////
+
+#define SUB_OFFSET_FAILURE  ((size_t)-1)
+
+static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
+    SkASSERT((unsigned)x < (unsigned)bm.width());
+    SkASSERT((unsigned)y < (unsigned)bm.height());
+    
+    switch (bm.getConfig()) {
+        case SkBitmap::kA8_Config:
+        case SkBitmap:: kIndex8_Config:
+            // x is fine as is for the calculation
+            break;
+
+        case SkBitmap::kRGB_565_Config:
+        case SkBitmap::kARGB_4444_Config:
+            x <<= 1;
+            break;
+
+        case SkBitmap::kARGB_8888_Config:
+            x <<= 2;
+            break;
+
+        case SkBitmap::kNo_Config:
+        case SkBitmap::kA1_Config:
+        default:
+            return SUB_OFFSET_FAILURE;
+    }
+    return y * bm.rowBytes() + x;
+}
+
+bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
+    SkDEBUGCODE(this->validate();)
+
+    if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
+        return false;   // no src pixels
+    }
+
+    SkIRect srcRect, r;
+    srcRect.set(0, 0, this->width(), this->height());
+    if (!r.intersect(srcRect, subset)) {
+        return false;   // r is empty (i.e. no intersection)
+    }
+
+    if (kRLE_Index8_Config == fConfig) {
+        SkAutoLockPixels alp(*this);
+        // don't call readyToDraw(), since we can operate w/o a colortable
+        // at this stage
+        if (this->getPixels() == NULL) {
+            return false;
+        }
+        SkBitmap bm;
+        
+        bm.setConfig(kIndex8_Config, r.width(), r.height());
+        bm.allocPixels(this->getColorTable());
+        if (NULL == bm.getPixels()) {
+            return false;
+        }
+        
+        const RLEPixels* rle = (const RLEPixels*)this->getPixels();
+        uint8_t* dst = bm.getAddr8(0, 0);
+        const int width = bm.width();
+        const int rowBytes = bm.rowBytes();
+            
+        for (int y = r.fTop; y < r.fBottom; y++) {
+            SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
+            dst += rowBytes;
+        }
+        result->swap(bm);
+        return true;
+    }
+
+    size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
+    if (SUB_OFFSET_FAILURE == offset) {
+        return false;   // config not supported
+    }
+
+    SkBitmap dst;
+    dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
+
+    if (fPixelRef) {
+        // share the pixelref with a custom offset
+        dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
+    } else {
+        // share the pixels (owned by the caller)
+        dst.setPixels((char*)fPixels + offset, this->getColorTable());
+    }
+    SkDEBUGCODE(dst.validate();)
+
+    // we know we're good, so commit to result
+    result->swap(dst);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
+    if (NULL == dst || this->width() == 0 || this->height() == 0) {
+        return false;
+    }
+
+    switch (dstConfig) {
+        case kA8_Config:
+        case kARGB_4444_Config:
+        case kRGB_565_Config:
+        case kARGB_8888_Config:
+            break;
+        default:
+            return false;
+    }
+    
+    SkBitmap    tmp;
+    
+    tmp.setConfig(dstConfig, this->width(), this->height());
+    // pass null for colortable, since we don't support Index8 config for dst
+    if (!tmp.allocPixels(alloc, NULL)) {
+        return false;
+    }
+    
+    SkAutoLockPixels srclock(*this);
+    SkAutoLockPixels dstlock(tmp);
+    
+    if (!this->readyToDraw() || !tmp.readyToDraw()) {
+        // allocator/lock failed
+        return false;
+    }
+
+    // if the src has alpha, we have to clear the dst first
+    if (!this->isOpaque()) {
+        tmp.eraseColor(0);
+    }
+
+    SkCanvas canvas(tmp);
+    SkPaint  paint;
+    
+    paint.setDither(true);
+    canvas.drawBitmap(*this, 0, 0, &paint);
+    
+    dst->swap(tmp);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
+                                 const SkBitmap& src) {
+    x <<= 1;
+    y <<= 1;
+    const SkPMColor* p = src.getAddr32(x, y);
+    SkPMColor c, ag, rb;
+
+    c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
+    if (x < src.width() - 1) {
+        p += 1;
+    }
+    c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
+
+    if (y < src.height() - 1) {
+        p = src.getAddr32(x, y + 1);
+    }
+    c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
+    if (x < src.width() - 1) {
+        p += 1;
+    }
+    c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
+
+    *dst->getAddr32(x >> 1, y >> 1) =
+        ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
+}
+
+static inline uint32_t expand16(U16CPU c) {
+    return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
+}
+
+// returns dirt in the top 16bits, but we don't care, since we only
+// store the low 16bits.
+static inline U16CPU pack16(uint32_t c) {
+    return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
+}
+
+static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
+                                 const SkBitmap& src) {
+    x <<= 1;
+    y <<= 1;
+    const uint16_t* p = src.getAddr16(x, y);
+    SkPMColor       c;
+    
+    c = expand16(*p);
+    if (x < (int)src.width() - 1) {
+        p += 1;
+    }
+    c += expand16(*p);
+    
+    if (y < (int)src.height() - 1) {
+        p = src.getAddr16(x, y + 1);
+    }
+    c += expand16(*p);
+    if (x < (int)src.width() - 1) {
+        p += 1;
+    }
+    c += expand16(*p);
+    
+    *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
+}
+
+static uint32_t expand4444(U16CPU c) {
+    return (c & 0xF0F) | ((c & ~0xF0F) << 12);
+}
+
+static U16CPU collaps4444(uint32_t c) {
+    return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
+}
+
+static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
+                                   const SkBitmap& src) {
+    x <<= 1;
+    y <<= 1;
+    const uint16_t* p = src.getAddr16(x, y);
+    uint32_t        c;
+    
+    c = expand4444(*p);
+    if (x < src.width() - 1) {
+        p += 1;
+    }
+    c += expand4444(*p);
+    
+    if (y < src.height() - 1) {
+        p = src.getAddr16(x, y + 1);
+    }
+    c += expand4444(*p);
+    if (x < src.width() - 1) {
+        p += 1;
+    }
+    c += expand4444(*p);
+    
+    *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
+}
+
+void SkBitmap::buildMipMap(bool forceRebuild) {
+#ifdef SK_SUPPORT_MIPMAP
+    if (forceRebuild)
+        this->freeMipMap();
+    else if (fMipMap)
+        return; // we're already built
+
+    SkASSERT(NULL == fMipMap);
+
+    void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
+
+    const SkBitmap::Config config = this->getConfig();
+
+    switch (config) {
+        case kARGB_8888_Config:
+            proc = downsampleby2_proc32;
+            break;
+        case kRGB_565_Config:
+            proc = downsampleby2_proc16;
+            break;
+        case kARGB_4444_Config:
+            proc = downsampleby2_proc4444;
+            break;
+        case kIndex8_Config:
+        case kA8_Config:
+        default:
+            return; // don't build mipmaps for these configs
+    }
+
+    // whip through our loop to compute the exact size needed
+    size_t  size = 0;
+    int     maxLevels = 0;
+    {
+        unsigned    width = this->width();
+        unsigned    height = this->height();
+        for (;;) {
+            width >>= 1;
+            height >>= 1;
+            if (0 == width || 0 == height) {
+                break;
+            }
+            size += ComputeRowBytes(config, width) * height;
+            maxLevels += 1;
+        }
+    }
+    if (0 == maxLevels) {
+        return;
+    }
+
+    MipMap*     mm = MipMap::Alloc(maxLevels, size);
+    MipLevel*   level = mm->levels();
+    uint8_t*    addr = (uint8_t*)mm->pixels();
+
+    unsigned    width = this->width();
+    unsigned    height = this->height();
+    unsigned    rowBytes = this->rowBytes();
+    SkBitmap    srcBM(*this), dstBM;
+
+    srcBM.lockPixels();
+
+    for (int i = 0; i < maxLevels; i++) {
+        width >>= 1;
+        height >>= 1;
+        rowBytes = ComputeRowBytes(config, width);
+
+        level[i].fPixels   = addr;
+        level[i].fWidth    = SkToU16(width);
+        level[i].fHeight   = SkToU16(height);
+        level[i].fRowBytes = SkToU16(rowBytes);
+
+        dstBM.setConfig(config, width, height, rowBytes);
+        dstBM.setPixels(addr);
+    
+        for (unsigned y = 0; y < height; y++) {
+            for (unsigned x = 0; x < width; x++) {
+                proc(&dstBM, x, y, srcBM);
+            }
+        }
+
+        srcBM = dstBM;
+        addr += height * rowBytes;
+    }
+    SkASSERT(addr == (uint8_t*)mm->pixels() + size);
+    fMipMap = mm;
+#endif
+}
+
+bool SkBitmap::hasMipMap() const {
+#ifdef SK_SUPPORT_MIPMAP
+    return fMipMap != NULL;
+#else
+    return false;
+#endif
+}
+
+int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
+#ifdef SK_SUPPORT_MIPMAP
+    if (NULL == fMipMap)
+        return 0;
+    
+    int level = ComputeMipLevel(sx, sy) >> 16;
+    SkASSERT(level >= 0);
+    if (level <= 0) {
+        return 0;
+    }
+
+    if (level >= fMipMap->fLevelCount) {
+        level = fMipMap->fLevelCount - 1;
+    }
+    if (dst) {
+        const MipLevel& mip = fMipMap->levels()[level - 1];
+        dst->setConfig((SkBitmap::Config)this->config(),
+                       mip.fWidth, mip.fHeight, mip.fRowBytes);
+        dst->setPixels(mip.fPixels);
+    }
+    return level;
+#else
+    return 0;
+#endif
+}
+
+SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
+#ifdef SK_SUPPORT_MIPMAP
+    sx = SkAbs32(sx);
+    sy = SkAbs32(sy);
+    if (sx < sy) {
+        sx = sy;
+    }
+    if (sx < SK_Fixed1) {
+        return 0;
+    }
+    int clz = SkCLZ(sx);
+    SkASSERT(clz >= 1 && clz <= 15);
+    return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
+#else
+    return 0;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void GetBitmapAlpha(const SkBitmap& src, uint8_t SK_RESTRICT alpha[],
+                           int alphaRowBytes) {
+    SkASSERT(alpha != NULL);
+    SkASSERT(alphaRowBytes >= src.width());
+
+    SkBitmap::Config config = src.getConfig();
+    int              w = src.width();
+    int              h = src.height();
+    int              rb = src.rowBytes();
+
+    if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
+        const uint8_t* s = src.getAddr8(0, 0);
+        while (--h >= 0) {
+            memcpy(alpha, s, w);
+            s += rb;
+            alpha += alphaRowBytes;
+        }
+    } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
+        const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
+        while (--h >= 0) {
+            for (int x = 0; x < w; x++) {
+                alpha[x] = SkGetPackedA32(s[x]);
+            }
+            s = (const SkPMColor*)((const char*)s + rb);
+            alpha += alphaRowBytes;
+        }
+    } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
+        const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
+        while (--h >= 0) {
+            for (int x = 0; x < w; x++) {
+                alpha[x] = SkPacked4444ToA32(s[x]);
+            }
+            s = (const SkPMColor16*)((const char*)s + rb);
+            alpha += alphaRowBytes;
+        }
+    } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
+        SkColorTable* ct = src.getColorTable();
+        if (ct) {
+            const SkPMColor* SK_RESTRICT table = ct->lockColors();
+            const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
+            while (--h >= 0) {
+                for (int x = 0; x < w; x++) {
+                    alpha[x] = SkGetPackedA32(table[s[x]]);
+                }
+                s += rb;
+                alpha += alphaRowBytes;
+            }
+            ct->unlockColors(false);
+        }
+    } else {    // src is opaque, so just fill alpha[] with 0xFF
+        memset(alpha, 0xFF, h * alphaRowBytes);
+    }
+}
+
+#include "SkPaint.h"
+#include "SkMaskFilter.h"
+#include "SkMatrix.h"
+
+void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
+                            SkIPoint* offset) const {
+    SkDEBUGCODE(this->validate();)
+
+    SkMatrix    identity;
+    SkMask      srcM, dstM;
+
+    srcM.fBounds.set(0, 0, this->width(), this->height());
+    srcM.fRowBytes = SkAlign4(this->width());
+    srcM.fFormat = SkMask::kA8_Format;
+
+    SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
+
+    // compute our (larger?) dst bounds if we have a filter
+    if (NULL != filter) {
+        identity.reset();
+        srcM.fImage = NULL;
+        if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
+            goto NO_FILTER_CASE;
+        }
+        dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
+    } else {
+    NO_FILTER_CASE:
+        dst->setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
+                       srcM.fRowBytes);
+        dst->allocPixels();        
+        GetBitmapAlpha(*this, dst->getAddr8(0, 0), srcM.fRowBytes);
+        if (offset) {
+            offset->set(0, 0);
+        }
+        return;
+    }
+
+    SkAutoMaskImage srcCleanup(&srcM, true);
+
+    GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
+    if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
+        goto NO_FILTER_CASE;
+    }
+
+    SkAutoMaskImage dstCleanup(&dstM, false);
+
+    dst->setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
+                   dstM.fBounds.height(), dstM.fRowBytes);
+    dst->allocPixels();
+    memcpy(dst->getPixels(), dstM.fImage, dstM.computeImageSize());
+    if (offset) {
+        offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
+    }
+    SkDEBUGCODE(dst->validate();)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum {
+    SERIALIZE_PIXELTYPE_NONE,
+    SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
+    SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
+    SERIALIZE_PIXELTYPE_REF_DATA,
+    SERIALIZE_PIXELTYPE_REF_PTR,
+};
+
+static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
+    size_t len = strlen(str);
+    buffer.write32(len);
+    buffer.writePad(str, len);
+}
+
+static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
+    size_t len = buffer.readInt();
+    SkAutoSMalloc<256> storage(len + 1);
+    char* str = (char*)storage.get();
+    buffer.read(str, len);
+    str[len] = 0;
+    return SkPixelRef::NameToFactory(str);
+}
+
+/*
+    It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
+    we just have pixels, then we can only flatten the pixels, or write out an
+    empty bitmap.
+ 
+    With a pixelref, we still have the question of recognizing when two sitings
+    of the same pixelref are the same, and when they are different. Perhaps we
+    should look at the generationID and keep a record of that in some dictionary
+    associated with the buffer. SkGLTextureCache does this sort of thing to know
+    when to create a new texture.
+*/
+void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
+    buffer.write32(fWidth);
+    buffer.write32(fHeight);
+    buffer.write32(fRowBytes);
+    buffer.write8(fConfig);
+    buffer.writeBool(this->isOpaque());
+    
+    /*  If we are called in this mode, then it is up to the caller to manage
+        the owner-counts on the pixelref, as we just record the ptr itself.
+    */
+    if (!buffer.persistBitmapPixels()) {
+        if (fPixelRef) {
+            buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
+            buffer.write32(fPixelRefOffset);
+            buffer.writeRefCnt(fPixelRef);
+            return;
+        } else {
+            // we ignore the non-persist request, since we don't have a ref
+            // ... or we could just write an empty bitmap...
+            // (true) will write an empty bitmap, (false) will flatten the pix
+            if (true) {
+                buffer.write8(SERIALIZE_PIXELTYPE_NONE);
+                return;
+            }
+        }
+    }
+
+    if (fPixelRef) {
+        SkPixelRef::Factory fact = fPixelRef->getFactory();
+        if (fact) {
+            const char* name = SkPixelRef::FactoryToName(fact);
+            if (name && *name) {
+                buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
+                buffer.write32(fPixelRefOffset);
+                writeString(buffer, name);
+                fPixelRef->flatten(buffer);
+                return;
+            }
+        }
+        // if we get here, we can't record the pixels
+        buffer.write8(SERIALIZE_PIXELTYPE_NONE);
+    } else if (fPixels) {
+        if (fColorTable) {
+            buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
+            fColorTable->flatten(buffer);
+        } else {
+            buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
+        }
+        buffer.writePad(fPixels, this->getSize());
+    } else {
+        buffer.write8(SERIALIZE_PIXELTYPE_NONE);
+    }
+}
+
+void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
+    this->reset();
+    
+    int width = buffer.readInt();
+    int height = buffer.readInt();
+    int rowBytes = buffer.readInt();
+    int config = buffer.readU8();
+    
+    this->setConfig((Config)config, width, height, rowBytes);
+    this->setIsOpaque(buffer.readBool());
+    
+    size_t size = this->getSize();
+    int reftype = buffer.readU8();
+    switch (reftype) {
+        case SERIALIZE_PIXELTYPE_REF_PTR: {
+            size_t offset = buffer.readU32();
+            SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
+            this->setPixelRef(pr, offset);
+            break;
+        }
+        case SERIALIZE_PIXELTYPE_REF_DATA: {
+            size_t offset = buffer.readU32();
+            SkPixelRef::Factory fact = deserialize_factory(buffer);
+            SkPixelRef* pr = fact(buffer);
+            this->setPixelRef(pr, offset)->safeUnref();
+            break;
+        }
+        case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
+        case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
+            SkColorTable* ctable = NULL;
+            if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
+                ctable = SkNEW_ARGS(SkColorTable, (buffer));
+            }
+            if (this->allocPixels(ctable)) {
+                this->lockPixels();
+                buffer.read(this->getPixels(), size);
+                this->unlockPixels();
+            } else {
+                buffer.skip(size);
+            }
+            ctable->safeUnref();
+            break;
+        }
+        case SERIALIZE_PIXELTYPE_NONE:
+            break;
+        default:
+            SkASSERT(!"unrecognized pixeltype in serialized data");
+            sk_throw();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmap::RLEPixels::RLEPixels(int width, int height) {
+    fHeight = height;
+    fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
+    bzero(fYPtrs, height * sizeof(uint8_t*));
+}
+
+SkBitmap::RLEPixels::~RLEPixels() {
+    sk_free(fYPtrs);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+void SkBitmap::validate() const {
+    SkASSERT(fConfig < kConfigCount);
+    SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
+    SkASSERT(fFlags <= kImageIsOpaque_Flag);
+    SkASSERT(fPixelLockCount >= 0);
+    SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
+    SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
+
+#if 0   // these asserts are not thread-correct, so disable for now
+    if (fPixelRef) {
+        if (fPixelLockCount > 0) {
+            SkASSERT(fPixelRef->getLockCount() > 0);
+        } else {
+            SkASSERT(NULL == fPixels);
+            SkASSERT(NULL == fColorTable);
+        }
+    }
+#endif
+}
+#endif
+
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
new file mode 100644
index 0000000..6d7d581
--- /dev/null
+++ b/src/core/SkBitmapProcShader.cpp
@@ -0,0 +1,231 @@
+#include "SkBitmapProcShader.h"
+#include "SkColorPriv.h"
+#include "SkPixelRef.h"
+
+bool SkBitmapProcShader::CanDo(const SkBitmap& bm, TileMode tx, TileMode ty) {
+    switch (bm.config()) {
+        case SkBitmap::kA8_Config:
+        case SkBitmap::kRGB_565_Config:
+        case SkBitmap::kIndex8_Config:
+        case SkBitmap::kARGB_8888_Config:
+    //        if (tx == ty && (kClamp_TileMode == tx || kRepeat_TileMode == tx))
+                return true;
+        default:
+            break;
+    }
+    return false;
+}
+
+SkBitmapProcShader::SkBitmapProcShader(const SkBitmap& src,
+                                       TileMode tmx, TileMode tmy) {
+    fRawBitmap = src;
+    fState.fTileModeX = (uint8_t)tmx;
+    fState.fTileModeY = (uint8_t)tmy;
+}
+
+SkBitmapProcShader::SkBitmapProcShader(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {
+    fRawBitmap.unflatten(buffer);
+    fState.fTileModeX = buffer.readU8();
+    fState.fTileModeY = buffer.readU8();
+}
+
+void SkBitmapProcShader::beginSession() {
+    this->INHERITED::beginSession();
+
+    fRawBitmap.lockPixels();
+}
+
+void SkBitmapProcShader::endSession() {
+    fRawBitmap.unlockPixels();
+
+    this->INHERITED::endSession();
+}
+
+bool SkBitmapProcShader::asABitmap(SkBitmap* texture, SkMatrix* texM,
+                                   TileMode xy[]) {
+    if (texture) {
+        *texture = fRawBitmap;
+    }
+    if (texM) {
+        texM->reset();
+    }
+    if (xy) {
+        xy[0] = (TileMode)fState.fTileModeX;
+        xy[1] = (TileMode)fState.fTileModeY;
+    }
+    return true;
+}
+
+void SkBitmapProcShader::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+
+    fRawBitmap.flatten(buffer);
+    buffer.write8(fState.fTileModeX);
+    buffer.write8(fState.fTileModeY);
+}
+
+bool SkBitmapProcShader::setContext(const SkBitmap& device,
+                                    const SkPaint& paint,
+                                    const SkMatrix& matrix) {
+    // do this first, so we have a correct inverse matrix
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+
+    fState.fOrigBitmap = fRawBitmap;
+    fState.fOrigBitmap.lockPixels();
+    if (fState.fOrigBitmap.getPixels() == NULL) {
+        fState.fOrigBitmap.unlockPixels();
+        return false;
+    }
+
+    if (!fState.chooseProcs(this->getTotalInverse(), paint)) {
+        return false;
+    }
+
+    bool bitmapIsOpaque = fState.fBitmap->isOpaque();
+    
+    // filtering doesn't guarantee that opaque stays opaque (finite precision)
+    // so pretend we're not opaque if we're being asked to filter. If we had
+    // more blit-procs, we could specialize on opaque src, and just OR in 0xFF
+    // after the filter to be sure...
+    if (paint.isFilterBitmap()) {
+        bitmapIsOpaque = false;
+    }
+
+    // update fFlags
+    fFlags = 0; // this should happen in SkShader.cpp
+
+    if (bitmapIsOpaque && (255 == this->getPaintAlpha())) {
+        fFlags |= kOpaqueAlpha_Flag;
+    }
+
+    switch (fState.fBitmap->config()) {
+        case SkBitmap::kRGB_565_Config:
+            fFlags |= (kHasSpan16_Flag | kIntrinsicly16_Flag);
+            break;
+        case SkBitmap::kIndex8_Config:
+        case SkBitmap::kARGB_8888_Config:
+            if (bitmapIsOpaque) {
+                fFlags |= kHasSpan16_Flag;
+            }
+            break;
+        case SkBitmap::kA8_Config:
+            break;  // never set kHasSpan16_Flag
+        default:
+            break;
+    }
+    return true;
+}
+
+#define BUF_MAX     128
+
+void SkBitmapProcShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
+    uint32_t buffer[BUF_MAX];
+
+    const SkBitmapProcState&        state = fState;
+    SkBitmapProcState::MatrixProc   mproc = state.fMatrixProc;
+    SkBitmapProcState::SampleProc32 sproc = state.fSampleProc32;
+    int max = fState.fDoFilter ? (BUF_MAX >> 1) : BUF_MAX;
+
+    SkASSERT(state.fBitmap->getPixels());
+    SkASSERT(state.fBitmap->pixelRef() == NULL ||
+             state.fBitmap->pixelRef()->getLockCount());
+
+    for (;;) {
+        int n = count;
+        if (n > max) {
+            n = max;
+        }
+        mproc(state, buffer, n, x, y);
+        sproc(state, buffer, n, dstC);
+        
+        if ((count -= n) == 0) {
+            break;
+        }
+        x += n;
+        dstC += n;
+    }
+}
+
+void SkBitmapProcShader::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
+    uint32_t buffer[BUF_MAX];
+    
+    const SkBitmapProcState&        state = fState;
+    SkBitmapProcState::MatrixProc   mproc = state.fMatrixProc;
+    SkBitmapProcState::SampleProc16 sproc = state.fSampleProc16;
+    int max = fState.fDoFilter ? (BUF_MAX >> 1) : BUF_MAX;
+
+    SkASSERT(state.fBitmap->getPixels());
+    SkASSERT(state.fBitmap->pixelRef() == NULL ||
+             state.fBitmap->pixelRef()->getLockCount());
+
+    for (;;) {
+        int n = count;
+        if (n > max) {
+            n = max;
+        }
+        mproc(state, buffer, n, x, y);
+        sproc(state, buffer, n, dstC);
+        
+        if ((count -= n) == 0) {
+            break;
+        }
+        x += n;
+        dstC += n;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTemplatesPriv.h"
+
+SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
+                                       TileMode tmx, TileMode tmy,
+                                       void* storage, size_t storageSize) {
+    SkShader* shader;
+    SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
+                          storageSize, (src, tmx, tmy));
+    return shader;
+}
+
+static SkFlattenable::Registrar gBitmapProcShaderReg("SkBitmapProcShader",
+                                               SkBitmapProcShader::CreateProc);
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const char* gTileModeName[] = {
+    "clamp", "repeat", "mirror"
+};
+
+bool SkBitmapProcShader::toDumpString(SkString* str) const {
+    str->printf("BitmapShader: [%d %d %d",
+                fRawBitmap.width(), fRawBitmap.height(),
+                fRawBitmap.bytesPerPixel());
+    
+    // add the pixelref
+    SkPixelRef* pr = fRawBitmap.pixelRef();
+    if (pr) {
+        const char* uri = pr->getURI();
+        if (uri) {
+            str->appendf(" \"%s\"", uri);
+        }
+    }
+    
+    // add the (optional) matrix
+    {
+        SkMatrix m;
+        if (this->getLocalMatrix(&m)) {
+            SkString info;
+            m.toDumpString(&info);
+            str->appendf(" %s", info.c_str());
+        }
+    }
+    
+    str->appendf(" [%s %s]]",
+                 gTileModeName[fState.fTileModeX],
+                 gTileModeName[fState.fTileModeY]);
+    return true;
+}
+
diff --git a/src/core/SkBitmapProcShader.h b/src/core/SkBitmapProcShader.h
new file mode 100644
index 0000000..09d53af
--- /dev/null
+++ b/src/core/SkBitmapProcShader.h
@@ -0,0 +1,59 @@
+/* libs/graphics/sgl/SkBitmapShader.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 SkBitmapProcShader_DEFINED
+#define SkBitmapProcShader_DEFINED
+
+#include "SkShader.h"
+#include "SkBitmapProcState.h"
+
+class SkBitmapProcShader : public SkShader {
+public:
+    SkBitmapProcShader(const SkBitmap& src, TileMode tx, TileMode ty);
+
+    // overrides from SkShader
+    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
+    virtual uint32_t getFlags() { return fFlags; }
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
+    virtual void beginSession();
+    virtual void endSession();
+    virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
+
+    static bool CanDo(const SkBitmap&, TileMode tx, TileMode ty);
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { 
+        return SkNEW_ARGS(SkBitmapProcShader, (buffer));
+    }
+
+    // override from flattenable
+    virtual bool toDumpString(SkString* str) const;
+
+protected:
+    SkBitmapProcShader(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+
+    SkBitmap          fRawBitmap;   // experimental for RLE encoding
+    SkBitmapProcState fState;
+    uint32_t          fFlags;
+
+private:    
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
new file mode 100644
index 0000000..428921d
--- /dev/null
+++ b/src/core/SkBitmapProcState.cpp
@@ -0,0 +1,476 @@
+#include "SkBitmapProcState.h"
+#include "SkColorPriv.h"
+#include "SkFilterProc.h"
+#include "SkPaint.h"
+#include "SkShader.h"   // for tilemodes
+
+#ifdef SK_CPU_BENDIAN
+    #define UNPACK_PRIMARY_SHORT(packed)    ((uint32_t)(packed) >> 16)
+    #define UNPACK_SECONDARY_SHORT(packed)  ((packed) & 0xFFFF)
+#else
+    #define UNPACK_PRIMARY_SHORT(packed)    ((packed) & 0xFFFF)
+    #define UNPACK_SECONDARY_SHORT(packed)  ((uint32_t)(packed) >> 16)
+#endif
+
+static inline SkPMColor Filter_32(unsigned x, unsigned y,
+                                  SkPMColor a00, SkPMColor a01,
+                                  SkPMColor a10, SkPMColor a11) {
+    SkASSERT((unsigned)x <= 0xF);
+    SkASSERT((unsigned)y <= 0xF);
+    
+    int xy = x * y;
+    uint32_t mask = gMask_00FF00FF; //0xFF00FF;
+    
+    int scale = 256 - 16*y - 16*x + xy;
+    uint32_t lo = (a00 & mask) * scale;
+    uint32_t hi = ((a00 >> 8) & mask) * scale;
+    
+    scale = 16*x - xy;
+    lo += (a01 & mask) * scale;
+    hi += ((a01 >> 8) & mask) * scale;
+    
+    scale = 16*y - xy;
+    lo += (a10 & mask) * scale;
+    hi += ((a10 >> 8) & mask) * scale;
+    
+    lo += (a11 & mask) * xy;
+    hi += ((a11 >> 8) & mask) * xy;
+    
+    return ((lo >> 8) & mask) | (hi & ~mask);
+}
+
+// returns expanded * 5bits
+static inline uint32_t Filter_565_Expanded(unsigned x, unsigned y,
+                                           uint32_t a00, uint32_t a01,
+                                           uint32_t a10, uint32_t a11) {
+    SkASSERT((unsigned)x <= 0xF);
+    SkASSERT((unsigned)y <= 0xF);
+    
+    a00 = SkExpand_rgb_16(a00);
+    a01 = SkExpand_rgb_16(a01);
+    a10 = SkExpand_rgb_16(a10);
+    a11 = SkExpand_rgb_16(a11);
+    
+    int xy = x * y >> 3;
+    return  a00 * (32 - 2*y - 2*x + xy) +
+            a01 * (2*x - xy) +
+            a10 * (2*y - xy) +
+            a11 * xy;
+}
+
+// turn an expanded 565 * 5bits into SkPMColor
+// g:11 | r:10 | x:1 | b:10
+static inline SkPMColor SkExpanded_565_To_PMColor(uint32_t c) {
+    unsigned r = (c >> 13) & 0xFF;
+    unsigned g = (c >> 24);
+    unsigned b = (c >> 2) & 0xFF;
+    return SkPackARGB32(0xFF, r, g, b);
+}
+
+// returns answer in SkPMColor format
+static inline SkPMColor Filter_4444_D32(unsigned x, unsigned y,
+                                        uint32_t a00, uint32_t a01,
+                                        uint32_t a10, uint32_t a11) {
+    SkASSERT((unsigned)x <= 0xF);
+    SkASSERT((unsigned)y <= 0xF);
+    
+    a00 = SkExpand_4444(a00);
+    a01 = SkExpand_4444(a01);
+    a10 = SkExpand_4444(a10);
+    a11 = SkExpand_4444(a11);
+
+    int xy = x * y >> 4;
+    uint32_t result =   a00 * (16 - y - x + xy) +
+                        a01 * (x - xy) +
+                        a10 * (y - xy) +
+                        a11 * xy;
+
+    return SkCompact_8888(result);
+}
+
+static inline U8CPU Filter_8(unsigned x, unsigned y,
+                             U8CPU a00, U8CPU a01,
+                             U8CPU a10, U8CPU a11) {
+    SkASSERT((unsigned)x <= 0xF);
+    SkASSERT((unsigned)y <= 0xF);
+    
+    int xy = x * y;
+    unsigned result =   a00 * (256 - 16*y - 16*x + xy) +
+                        a01 * (16*x - xy) +
+                        a10 * (16*y - xy) +
+                        a11 * xy;
+    
+    return result >> 8;
+}
+
+/*****************************************************************************
+ *
+ *  D32 functions
+ *
+ */
+
+// SRC == 8888
+
+#define FILTER_PROC(x, y, a, b, c, d)   Filter_32(x, y, a, b, c, d)
+
+#define MAKENAME(suffix)        S32_opaque_D32 ## suffix
+#define DSTSIZE                 32
+#define SRCTYPE                 SkPMColor
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
+                                SkASSERT(state.fAlphaScale == 256)
+#define RETURNDST(src)          src
+#define SRC_TO_FILTER(src)      src
+#define FILTER_TO_DST(c)        c
+#include "SkBitmapProcState_sample.h"
+
+#define MAKENAME(suffix)        S32_alpha_D32 ## suffix
+#define DSTSIZE                 32
+#define SRCTYPE                 SkPMColor
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
+                                SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state)         unsigned scale = state.fAlphaScale
+#define RETURNDST(src)          SkAlphaMulQ(src, scale)
+#define SRC_TO_FILTER(src)      src
+#define FILTER_TO_DST(c)        SkAlphaMulQ(c, scale)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == 565
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d)   Filter_565_Expanded(x, y, a, b, c, d)
+
+#define MAKENAME(suffix)        S16_opaque_D32 ## suffix
+#define DSTSIZE                 32
+#define SRCTYPE                 uint16_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config); \
+                                SkASSERT(state.fAlphaScale == 256)
+#define RETURNDST(src)          SkPixel16ToPixel32(src)
+#define SRC_TO_FILTER(src)      src
+#define FILTER_TO_DST(c)        SkExpanded_565_To_PMColor(c)
+#include "SkBitmapProcState_sample.h"
+
+#define MAKENAME(suffix)        S16_alpha_D32 ## suffix
+#define DSTSIZE                 32
+#define SRCTYPE                 uint16_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config); \
+                                SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state)         unsigned scale = state.fAlphaScale
+#define RETURNDST(src)          SkAlphaMulQ(SkPixel16ToPixel32(src), scale)
+#define SRC_TO_FILTER(src)      src
+#define FILTER_TO_DST(c)        SkAlphaMulQ(SkExpanded_565_To_PMColor(c), scale)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == Index8
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d)   Filter_32(x, y, a, b, c, d)
+
+#define MAKENAME(suffix)        SI8_opaque_D32 ## suffix
+#define DSTSIZE                 32
+#define SRCTYPE                 uint8_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
+                                SkASSERT(state.fAlphaScale == 256)
+#define PREAMBLE(state)         const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors()
+#define RETURNDST(src)          table[src]
+#define SRC_TO_FILTER(src)      table[src]
+#define FILTER_TO_DST(c)        c
+#define POSTAMBLE(state)        state.fBitmap->getColorTable()->unlockColors(false)
+#include "SkBitmapProcState_sample.h"
+
+#define MAKENAME(suffix)        SI8_alpha_D32 ## suffix
+#define DSTSIZE                 32
+#define SRCTYPE                 uint8_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
+                                SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state)         unsigned scale = state.fAlphaScale; \
+                                const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors()
+#define RETURNDST(src)          SkAlphaMulQ(table[src], scale)
+#define SRC_TO_FILTER(src)      table[src]
+#define FILTER_TO_DST(c)        SkAlphaMulQ(c, scale)
+#define POSTAMBLE(state)        state.fBitmap->getColorTable()->unlockColors(false)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == 4444
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d)   Filter_4444_D32(x, y, a, b, c, d)
+
+#define MAKENAME(suffix)        S4444_opaque_D32 ## suffix
+#define DSTSIZE                 32
+#define SRCTYPE                 SkPMColor16
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_4444_Config); \
+SkASSERT(state.fAlphaScale == 256)
+#define RETURNDST(src)          SkPixel4444ToPixel32(src)
+#define SRC_TO_FILTER(src)      src
+#define FILTER_TO_DST(c)        c
+#include "SkBitmapProcState_sample.h"
+
+#define MAKENAME(suffix)        S4444_alpha_D32 ## suffix
+#define DSTSIZE                 32
+#define SRCTYPE                 SkPMColor16
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_4444_Config); \
+SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state)         unsigned scale = state.fAlphaScale
+#define RETURNDST(src)          SkAlphaMulQ(SkPixel4444ToPixel32(src), scale)
+#define SRC_TO_FILTER(src)      src
+#define FILTER_TO_DST(c)        SkAlphaMulQ(c, scale)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == A8
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d)   Filter_8(x, y, a, b, c, d)
+
+#define MAKENAME(suffix)        SA8_alpha_D32 ## suffix
+#define DSTSIZE                 32
+#define SRCTYPE                 uint8_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kA8_Config); \
+                                SkASSERT(state.fAlphaScale == 256)
+#define PREAMBLE(state)         const SkPMColor pmColor = state.fPaintPMColor;
+#define RETURNDST(src)          SkAlphaMulQ(pmColor, SkAlpha255To256(src))
+#define SRC_TO_FILTER(src)      src
+#define FILTER_TO_DST(c)        SkAlphaMulQ(pmColor, SkAlpha255To256(c))
+#include "SkBitmapProcState_sample.h"
+
+/*****************************************************************************
+ *
+ *  D16 functions
+ *
+ */
+
+// SRC == 8888
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d)   Filter_32(x, y, a, b, c, d)
+
+#define MAKENAME(suffix)        S32_D16 ## suffix
+#define DSTSIZE                 16
+#define SRCTYPE                 SkPMColor
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
+                                SkASSERT(state.fBitmap->isOpaque())
+#define RETURNDST(src)          SkPixel32ToPixel16(src)
+#define SRC_TO_FILTER(src)      src
+#define FILTER_TO_DST(c)        SkPixel32ToPixel16(c)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == 565
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d)   Filter_565_Expanded(x, y, a, b, c, d)
+
+#define MAKENAME(suffix)        S16_D16 ## suffix
+#define DSTSIZE                 16
+#define SRCTYPE                 uint16_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config)
+#define RETURNDST(src)          src
+#define SRC_TO_FILTER(src)      src
+#define FILTER_TO_DST(c)        SkCompact_rgb_16((c) >> 5)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == Index8
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d)   Filter_565_Expanded(x, y, a, b, c, d)
+
+#define MAKENAME(suffix)        SI8_D16 ## suffix
+#define DSTSIZE                 16
+#define SRCTYPE                 uint8_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
+                                SkASSERT(state.fBitmap->isOpaque())
+#define PREAMBLE(state)         const uint16_t* SK_RESTRICT table = state.fBitmap->getColorTable()->lock16BitCache()
+#define RETURNDST(src)          table[src]
+#define SRC_TO_FILTER(src)      table[src]
+#define FILTER_TO_DST(c)        SkCompact_rgb_16(c >> 5)
+#define POSTAMBLE(state)        state.fBitmap->getColorTable()->unlock16BitCache()
+#include "SkBitmapProcState_sample.h"
+
+static bool valid_for_filtering(unsigned dimension) {
+    // for filtering, width and height must fit in 14bits, since we use steal
+    // 2 bits from each to store our 4bit subpixel data
+    return (dimension & ~0x3FFF) == 0;
+}
+
+bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
+    if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) {
+        return false;
+    }
+    const SkMatrix* m;
+    
+    if (SkShader::kClamp_TileMode == fTileModeX &&
+            SkShader::kClamp_TileMode == fTileModeY) {
+        m = &inv;
+    } else {
+        fUnitInvMatrix = inv;
+        fUnitInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
+        m = &fUnitInvMatrix;
+    }
+    
+    fBitmap = &fOrigBitmap;
+#ifdef SK_SUPPORT_MIPMAP
+    if (fOrigBitmap.hasMipMap()) {
+        int shift = fOrigBitmap.extractMipLevel(&fMipBitmap,
+                                                SkScalarToFixed(m->getScaleX()),
+                                                SkScalarToFixed(m->getSkewY()));
+        
+        if (shift > 0) {
+            if (m != &fUnitInvMatrix) {
+                fUnitInvMatrix = *m;
+                m = &fUnitInvMatrix;
+            }
+
+            SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift);
+            fUnitInvMatrix.postScale(scale, scale);
+            
+            // now point here instead of fOrigBitmap
+            fBitmap = &fMipBitmap;
+        }
+    }
+#endif
+
+    fInvMatrix      = m;
+    fInvProc        = m->getMapXYProc();
+    fInvType        = m->getType();
+    fInvSx          = SkScalarToFixed(m->getScaleX());
+    fInvSy          = SkScalarToFixed(m->getScaleY());
+    fInvKy          = SkScalarToFixed(m->getSkewY());
+    fInvTxPlusHalf  = SkScalarToFixed(m->getTranslateX()) + (fInvSx >> 1);
+    fInvTyPlusHalf  = SkScalarToFixed(m->getTranslateY()) + (fInvSy >> 1);
+
+    /*  the -1 keeps us symetric with general policy for rounding, which is
+        (x + 1/2) >> 16. This sends exact halves to the next large pixel
+        e.g. x==3.5, round(x) == 4. However, our state is working with the
+        inverse matrix, and so to match the result of "normal" rounding, we
+        subtract 1 so that we in effect behave the same at the half-way point.
+        To compare, try drawing a bitmap with y == exact-half using the sprite
+        blitters and with us. Without the -1, we will draw the colors a whole
+        pixel shifted up (yikes).
+    */
+    fInvTxPlusHalf -= 1;
+    fInvTyPlusHalf -= 1;
+
+    fAlphaScale = SkAlpha255To256(paint.getAlpha());
+
+    // pick-up filtering from the paint, but only if the matrix is
+    // more complex than identity/translate (i.e. no need to pay the cost
+    // of filtering if we're not scaled etc.).
+    // note: we explicitly check inv, since m might be scaled due to unitinv
+    //       trickery, but we don't want to see that for this test
+    fDoFilter = paint.isFilterBitmap() &&
+                (inv.getType() > SkMatrix::kTranslate_Mask &&
+                 valid_for_filtering(fBitmap->width() | fBitmap->height()));
+
+    fMatrixProc = this->chooseMatrixProc();
+    if (NULL == fMatrixProc) {
+        return false;
+    }
+
+    ///////////////////////////////////////////////////////////////////////
+    
+    int index = 0;
+    if (fAlphaScale < 256) {  // note: this distinction is not used for D16
+        index |= 1;
+    }
+    if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
+        index |= 2;
+    }
+    if (fDoFilter) {
+        index |= 4;
+    }
+    // bits 3,4,5 encoding the source bitmap format
+    switch (fBitmap->config()) {
+        case SkBitmap::kARGB_8888_Config:
+            index |= 0;
+            break;
+        case SkBitmap::kRGB_565_Config:
+            index |= 8;
+            break;
+        case SkBitmap::kIndex8_Config:
+            index |= 16;
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            index |= 24;
+            break;
+        case SkBitmap::kA8_Config:
+            index |= 32;
+            fPaintPMColor = SkPreMultiplyColor(paint.getColor());
+        default:
+            return false;
+    }
+
+    static const SampleProc32 gSample32[] = {
+        S32_opaque_D32_nofilter_DXDY,
+        S32_alpha_D32_nofilter_DXDY,
+        S32_opaque_D32_nofilter_DX,
+        S32_alpha_D32_nofilter_DX,
+        S32_opaque_D32_filter_DXDY,
+        S32_alpha_D32_filter_DXDY,
+        S32_opaque_D32_filter_DX,
+        S32_alpha_D32_filter_DX,
+        
+        S16_opaque_D32_nofilter_DXDY,
+        S16_alpha_D32_nofilter_DXDY,
+        S16_opaque_D32_nofilter_DX,
+        S16_alpha_D32_nofilter_DX,
+        S16_opaque_D32_filter_DXDY,
+        S16_alpha_D32_filter_DXDY,
+        S16_opaque_D32_filter_DX,
+        S16_alpha_D32_filter_DX,
+        
+        SI8_opaque_D32_nofilter_DXDY,
+        SI8_alpha_D32_nofilter_DXDY,
+        SI8_opaque_D32_nofilter_DX,
+        SI8_alpha_D32_nofilter_DX,
+        SI8_opaque_D32_filter_DXDY,
+        SI8_alpha_D32_filter_DXDY,
+        SI8_opaque_D32_filter_DX,
+        SI8_alpha_D32_filter_DX,
+        
+        S4444_opaque_D32_nofilter_DXDY,
+        S4444_alpha_D32_nofilter_DXDY,
+        S4444_opaque_D32_nofilter_DX,
+        S4444_alpha_D32_nofilter_DX,
+        S4444_opaque_D32_filter_DXDY,
+        S4444_alpha_D32_filter_DXDY,
+        S4444_opaque_D32_filter_DX,
+        S4444_alpha_D32_filter_DX,
+        
+        // A8 treats alpha/opauqe the same (equally efficient)
+        SA8_alpha_D32_nofilter_DXDY,
+        SA8_alpha_D32_nofilter_DXDY,
+        SA8_alpha_D32_nofilter_DX,
+        SA8_alpha_D32_nofilter_DX,
+        SA8_alpha_D32_filter_DXDY,
+        SA8_alpha_D32_filter_DXDY,
+        SA8_alpha_D32_filter_DX,
+        SA8_alpha_D32_filter_DX
+    };
+    
+    static const SampleProc16 gSample16[] = {
+        S32_D16_nofilter_DXDY,
+        S32_D16_nofilter_DX,
+        S32_D16_filter_DXDY,
+        S32_D16_filter_DX,
+        
+        S16_D16_nofilter_DXDY,
+        S16_D16_nofilter_DX,
+        S16_D16_filter_DXDY,
+        S16_D16_filter_DX,
+        
+        SI8_D16_nofilter_DXDY,
+        SI8_D16_nofilter_DX,
+        SI8_D16_filter_DXDY,
+        SI8_D16_filter_DX,
+        
+        // Don't support 4444 -> 565
+        NULL, NULL, NULL, NULL,
+        // Don't support A8 -> 565
+        NULL, NULL, NULL, NULL
+    };
+    
+    fSampleProc32 = gSample32[index];
+    index >>= 1;    // shift away any opaque/alpha distinction
+    fSampleProc16 = gSample16[index];
+
+    return true;
+}
+
diff --git a/src/core/SkBitmapProcState.h b/src/core/SkBitmapProcState.h
new file mode 100644
index 0000000..1366d3b
--- /dev/null
+++ b/src/core/SkBitmapProcState.h
@@ -0,0 +1,78 @@
+/*
+** Copyright 2007, 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 SkBitmapProcState_DEFINED
+#define SkBitmapProcState_DEFINED
+
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+
+class SkPaint;
+
+struct SkBitmapProcState {
+
+    typedef void (*MatrixProc)(const SkBitmapProcState&,
+                               uint32_t bitmapXY[],
+                               int count,
+                               int x, int y);
+    
+    typedef void (*SampleProc32)(const SkBitmapProcState&,
+                                 const uint32_t[],
+                                 int count,
+                                 SkPMColor colors[]);
+
+    typedef void (*SampleProc16)(const SkBitmapProcState&,
+                                 const uint32_t[],
+                                 int count,
+                                 uint16_t colors[]);
+    
+    typedef U16CPU (*FixedTileProc)(SkFixed);   // returns 0..0xFFFF
+    
+    MatrixProc          fMatrixProc;        // chooseProcs
+    SampleProc32        fSampleProc32;      // chooseProcs
+    SampleProc16        fSampleProc16;      // chooseProcs
+
+    SkMatrix            fUnitInvMatrix;     // chooseProcs
+    FixedTileProc       fTileProcX;         // chooseProcs
+    FixedTileProc       fTileProcY;         // chooseProcs
+    SkFixed             fFilterOneX;
+    SkFixed             fFilterOneY;
+
+    const SkBitmap*     fBitmap;            // chooseProcs - orig or mip
+    SkBitmap            fOrigBitmap;        // CONSTRUCTOR
+#ifdef SK_SUPPORT_MIPMAP
+    SkBitmap            fMipBitmap;
+#endif
+    SkPMColor           fPaintPMColor;      // chooseProcs - A8 config
+    const SkMatrix*     fInvMatrix;         // chooseProcs
+    SkMatrix::MapXYProc fInvProc;           // chooseProcs
+    SkFixed             fInvSx, fInvSy;     // chooseProcs
+    SkFixed             fInvKy;             // chooseProcs
+    SkFixed             fInvTxPlusHalf;     // chooseProcs
+    SkFixed             fInvTyPlusHalf;     // chooseProcs
+    uint16_t            fAlphaScale;        // chooseProcs
+    uint8_t             fInvType;           // chooseProcs
+    uint8_t             fTileModeX;         // CONSTRUCTOR
+    uint8_t             fTileModeY;         // CONSTRUCTOR
+    SkBool8             fDoFilter;          // chooseProcs
+    
+    bool chooseProcs(const SkMatrix& inv, const SkPaint&);
+
+private:
+    MatrixProc chooseMatrixProc();
+};
+
+#endif
diff --git a/src/core/SkBitmapProcState_matrix.h b/src/core/SkBitmapProcState_matrix.h
new file mode 100644
index 0000000..f54f8b1
--- /dev/null
+++ b/src/core/SkBitmapProcState_matrix.h
@@ -0,0 +1,271 @@
+
+#define SCALE_NOFILTER_NAME     MAKENAME(_nofilter_scale)
+#define SCALE_FILTER_NAME       MAKENAME(_filter_scale)
+#define AFFINE_NOFILTER_NAME    MAKENAME(_nofilter_affine)
+#define AFFINE_FILTER_NAME      MAKENAME(_filter_affine)
+#define PERSP_NOFILTER_NAME     MAKENAME(_nofilter_persp)
+#define PERSP_FILTER_NAME       MAKENAME(_filter_persp)
+
+#define PACK_FILTER_X_NAME  MAKENAME(_pack_filter_x)
+#define PACK_FILTER_Y_NAME  MAKENAME(_pack_filter_y)
+
+#ifndef PREAMBLE
+    #define PREAMBLE(state)
+    #define PREAMBLE_PARAM_X
+    #define PREAMBLE_PARAM_Y
+    #define PREAMBLE_ARG_X
+    #define PREAMBLE_ARG_Y
+#endif
+
+static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
+                                uint32_t xy[], int count, int x, int y) {
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask)) == 0);
+
+    PREAMBLE(s);
+    // we store y, x, x, x, x, x
+
+    const unsigned maxX = s.fBitmap->width() - 1;
+    const SkFixed dx = s.fInvSx;
+    SkFixed fx;
+    {
+        SkPoint pt;
+        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
+        fx = SkScalarToFixed(pt.fY);
+        const unsigned maxY = s.fBitmap->height() - 1;
+        *xy++ = TILEY_PROCF(fx, maxY);
+        fx = SkScalarToFixed(pt.fX);
+    }
+
+#ifdef CHECK_FOR_DECAL
+    // test if we don't need to apply the tile proc
+    if ((unsigned)(fx >> 16) <= maxX &&
+        (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) {
+        decal_nofilter_scale(xy, fx, dx, count);
+    } else
+#endif
+    {
+        int i;
+        for (i = (count >> 2); i > 0; --i) {
+            unsigned a, b;
+            a = TILEX_PROCF(fx, maxX); fx += dx;
+            b = TILEX_PROCF(fx, maxX); fx += dx;
+#ifdef SK_CPU_BENDIAN
+            *xy++ = (a << 16) | b;
+#else
+            *xy++ = (b << 16) | a;
+#endif
+            a = TILEX_PROCF(fx, maxX); fx += dx;
+            b = TILEX_PROCF(fx, maxX); fx += dx;
+#ifdef SK_CPU_BENDIAN
+            *xy++ = (a << 16) | b;
+#else
+            *xy++ = (b << 16) | a;
+#endif
+        }
+        uint16_t* xx = (uint16_t*)xy;
+        for (i = (count & 3); i > 0; --i) {
+            *xx++ = TILEX_PROCF(fx, maxX); fx += dx;
+        }
+    }
+}
+
+// note: we could special-case on a matrix which is skewed in X but not Y.
+// this would require a more general setup thatn SCALE does, but could use
+// SCALE's inner loop that only looks at dx
+
+static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s,
+                                 uint32_t xy[], int count, int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask |
+                             SkMatrix::kAffine_Mask)) == 0);
+    
+    PREAMBLE(s);
+    SkPoint srcPt;
+    s.fInvProc(*s.fInvMatrix,
+               SkIntToScalar(x) + SK_ScalarHalf,
+               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+    
+    SkFixed fx = SkScalarToFixed(srcPt.fX);
+    SkFixed fy = SkScalarToFixed(srcPt.fY);
+    SkFixed dx = s.fInvSx;
+    SkFixed dy = s.fInvKy;
+    int maxX = s.fBitmap->width() - 1;
+    int maxY = s.fBitmap->height() - 1;
+    
+    for (int i = count; i > 0; --i) {
+        *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX);
+        fx += dx; fy += dy;
+    }
+}
+
+static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s,
+                                uint32_t* SK_RESTRICT xy,
+                                int count, int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
+    
+    PREAMBLE(s);
+    int maxX = s.fBitmap->width() - 1;
+    int maxY = s.fBitmap->height() - 1;
+    
+    SkPerspIter   iter(*s.fInvMatrix,
+                       SkIntToScalar(x) + SK_ScalarHalf,
+                       SkIntToScalar(y) + SK_ScalarHalf, count);
+    
+    while ((count = iter.next()) != 0) {
+        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
+        while (--count >= 0) {
+            *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) |
+                     TILEX_PROCF(srcXY[0], maxX);
+            srcXY += 2;
+        }
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max,
+                                          SkFixed one PREAMBLE_PARAM_Y) {
+    unsigned i = TILEY_PROCF(f, max);
+    i = (i << 4) | TILEY_LOW_BITS(f, max);
+    return (i << 14) | (TILEY_PROCF((f + one), max));
+}
+
+static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max,
+                                          SkFixed one PREAMBLE_PARAM_X) {
+    unsigned i = TILEX_PROCF(f, max);
+    i = (i << 4) | TILEX_LOW_BITS(f, max);
+    return (i << 14) | (TILEX_PROCF((f + one), max));
+}
+
+static void SCALE_FILTER_NAME(const SkBitmapProcState& s,
+                              uint32_t xy[], int count, int x, int y) {
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask)) == 0);
+    SkASSERT(s.fInvKy == 0);
+
+    PREAMBLE(s);
+    
+    const unsigned maxX = s.fBitmap->width() - 1;
+    const SkFixed one = s.fFilterOneX;
+    const SkFixed dx = s.fInvSx;
+    SkFixed fx;
+
+    {
+        SkPoint pt;
+        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
+        const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
+        const unsigned maxY = s.fBitmap->height() - 1;
+        // compute our two Y values up front
+        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
+        // now initialize fx
+        fx = SkScalarToFixed(pt.fX) - (one >> 1);
+    }
+
+#ifdef CHECK_FOR_DECAL
+    // test if we don't need to apply the tile proc
+    if (dx > 0 &&
+            (unsigned)(fx >> 16) <= maxX &&
+            (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) {
+        decal_filter_scale(xy, fx, dx, count);
+    } else
+#endif
+    {
+        do {
+            *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X);
+            fx += dx;
+        } while (--count != 0);
+    }
+}
+
+static void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
+                               uint32_t xy[], int count, int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask |
+                             SkMatrix::kAffine_Mask)) == 0);
+    
+    PREAMBLE(s);
+    SkPoint srcPt;
+    s.fInvProc(*s.fInvMatrix,
+               SkIntToScalar(x) + SK_ScalarHalf,
+               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+    
+    SkFixed oneX = s.fFilterOneX;
+    SkFixed oneY = s.fFilterOneY;
+    SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
+    SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1);
+    SkFixed dx = s.fInvSx;
+    SkFixed dy = s.fInvKy;
+    unsigned maxX = s.fBitmap->width() - 1;
+    unsigned maxY = s.fBitmap->height() - 1;
+    
+    do {
+        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
+        fy += dy;
+        *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
+        fx += dx;
+    } while (--count != 0);
+}
+
+static void PERSP_FILTER_NAME(const SkBitmapProcState& s,
+                              uint32_t* SK_RESTRICT xy, int count,
+                              int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
+    
+    PREAMBLE(s);
+    unsigned maxX = s.fBitmap->width() - 1;
+    unsigned maxY = s.fBitmap->height() - 1;
+    SkFixed oneX = s.fFilterOneX;
+    SkFixed oneY = s.fFilterOneY;
+    
+    SkPerspIter   iter(*s.fInvMatrix,
+                       SkIntToScalar(x) + SK_ScalarHalf,
+                       SkIntToScalar(y) + SK_ScalarHalf, count);
+    
+    while ((count = iter.next()) != 0) {
+        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
+        do {
+            *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY,
+                                       oneY PREAMBLE_ARG_Y);
+            *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX,
+                                       oneX PREAMBLE_ARG_X);
+            srcXY += 2;
+        } while (--count != 0);
+    }
+}
+
+static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = {
+    SCALE_NOFILTER_NAME,
+    SCALE_FILTER_NAME,
+    AFFINE_NOFILTER_NAME,
+    AFFINE_FILTER_NAME,
+    PERSP_NOFILTER_NAME,
+    PERSP_FILTER_NAME
+};
+
+#undef MAKENAME
+#undef TILEX_PROCF
+#undef TILEY_PROCF
+#ifdef CHECK_FOR_DECAL
+    #undef CHECK_FOR_DECAL
+#endif
+
+#undef SCALE_NOFILTER_NAME
+#undef SCALE_FILTER_NAME
+#undef AFFINE_NOFILTER_NAME
+#undef AFFINE_FILTER_NAME
+#undef PERSP_NOFILTER_NAME
+#undef PERSP_FILTER_NAME
+
+#undef PREAMBLE
+#undef PREAMBLE_PARAM_X
+#undef PREAMBLE_PARAM_Y
+#undef PREAMBLE_ARG_X
+#undef PREAMBLE_ARG_Y
+
+#undef TILEX_LOW_BITS
+#undef TILEY_LOW_BITS
diff --git a/src/core/SkBitmapProcState_matrixProcs.cpp b/src/core/SkBitmapProcState_matrixProcs.cpp
new file mode 100644
index 0000000..beb21c8
--- /dev/null
+++ b/src/core/SkBitmapProcState_matrixProcs.cpp
@@ -0,0 +1,199 @@
+#include "SkBitmapProcState.h"
+#include "SkPerspIter.h"
+#include "SkShader.h"
+
+void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count);
+void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count);
+
+#ifdef SK_CPU_BENDIAN
+    #define PACK_TWO_SHORTS(pri, sec) ((pri) << 16 | (sec))
+#else
+    #define PACK_TWO_SHORTS(pri, sec) ((pri) | ((sec) << 16))
+#endif
+
+#ifdef SK_DEBUG
+    static uint32_t pack_two_shorts(U16CPU pri, U16CPU sec)
+    {
+        SkASSERT((uint16_t)pri == pri);
+        SkASSERT((uint16_t)sec == sec);
+        return PACK_TWO_SHORTS(pri, sec);
+    }
+#else
+    #define pack_two_shorts(pri, sec)   PACK_TWO_SHORTS(pri, sec)
+#endif
+
+#define MAKENAME(suffix)        ClampX_ClampY ## suffix
+#define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
+#define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
+#define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
+#define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
+#define CHECK_FOR_DECAL
+#include "SkBitmapProcState_matrix.h"
+
+#define MAKENAME(suffix)        RepeatX_RepeatY ## suffix
+#define TILEX_PROCF(fx, max)    (((fx) & 0xFFFF) * ((max) + 1) >> 16)
+#define TILEY_PROCF(fy, max)    (((fy) & 0xFFFF) * ((max) + 1) >> 16)
+#define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
+#define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
+#include "SkBitmapProcState_matrix.h"
+
+#define MAKENAME(suffix)        GeneralXY ## suffix
+#define PREAMBLE(state)         SkBitmapProcState::FixedTileProc tileProcX = (state).fTileProcX; \
+                                SkBitmapProcState::FixedTileProc tileProcY = (state).fTileProcY
+#define PREAMBLE_PARAM_X        , SkBitmapProcState::FixedTileProc tileProcX
+#define PREAMBLE_PARAM_Y        , SkBitmapProcState::FixedTileProc tileProcY
+#define PREAMBLE_ARG_X          , tileProcX
+#define PREAMBLE_ARG_Y          , tileProcY
+#define TILEX_PROCF(fx, max)    (tileProcX(fx) * ((max) + 1) >> 16)
+#define TILEY_PROCF(fy, max)    (tileProcY(fy) * ((max) + 1) >> 16)
+#define TILEX_LOW_BITS(fx, max) ((tileProcX(fx) * ((max) + 1) >> 12) & 0xF)
+#define TILEY_LOW_BITS(fy, max) ((tileProcY(fy) * ((max) + 1) >> 12) & 0xF)
+#include "SkBitmapProcState_matrix.h"
+
+static inline U16CPU fixed_clamp(SkFixed x)
+{
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (x >> 16)
+        x = 0xFFFF;
+    if (x < 0)
+        x = 0;
+#else
+    if (x >> 16)
+    {
+        if (x < 0)
+            x = 0;
+        else
+            x = 0xFFFF;
+    }
+#endif
+    return x;
+}
+
+static inline U16CPU fixed_repeat(SkFixed x)
+{
+    return x & 0xFFFF;
+}
+
+static inline U16CPU fixed_mirror(SkFixed x)
+{
+    SkFixed s = x << 15 >> 31;
+    // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
+    return (x ^ s) & 0xFFFF;
+}
+
+static SkBitmapProcState::FixedTileProc choose_tile_proc(unsigned m)
+{
+    if (SkShader::kClamp_TileMode == m)
+        return fixed_clamp;
+    if (SkShader::kRepeat_TileMode == m)
+        return fixed_repeat;
+    SkASSERT(SkShader::kMirror_TileMode == m);
+    return fixed_mirror;
+}
+
+SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc()
+{
+    int index = 0;
+    if (fDoFilter)
+        index = 1;
+    if (fInvType & SkMatrix::kPerspective_Mask)
+        index |= 4;
+    else if (fInvType & SkMatrix::kAffine_Mask)
+        index |= 2;
+
+    if (SkShader::kClamp_TileMode == fTileModeX &&
+        SkShader::kClamp_TileMode == fTileModeY)
+    {
+        // clamp gets special version of filterOne
+        fFilterOneX = SK_Fixed1;
+        fFilterOneY = SK_Fixed1;
+        return ClampX_ClampY_Procs[index];
+    }
+    
+    // all remaining procs use this form for filterOne
+    fFilterOneX = SK_Fixed1 / fBitmap->width();
+    fFilterOneY = SK_Fixed1 / fBitmap->height();
+
+    if (SkShader::kRepeat_TileMode == fTileModeX &&
+        SkShader::kRepeat_TileMode == fTileModeY)
+    {
+        return RepeatX_RepeatY_Procs[index];
+    }
+
+    // only general needs these procs
+    fTileProcX = choose_tile_proc(fTileModeX);
+    fTileProcY = choose_tile_proc(fTileModeY);
+    return GeneralXY_Procs[index];
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count)
+{
+    int i;
+
+    for (i = (count >> 2); i > 0; --i)
+    {
+        *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16);
+        fx += dx+dx;
+        *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16);
+        fx += dx+dx;
+    }
+    uint16_t* xx = (uint16_t*)dst;
+
+    for (i = (count & 3); i > 0; --i)
+    {
+        *xx++ = SkToU16(fx >> 16); fx += dx;
+    }
+}
+
+void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count)
+{
+    if (count & 1)
+    {
+        SkASSERT((fx >> (16 + 14)) == 0);
+        *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
+        fx += dx;
+    }
+    while ((count -= 2) >= 0)
+    {
+        SkASSERT((fx >> (16 + 14)) == 0);
+        *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
+        fx += dx;
+
+        *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
+        fx += dx;
+    }
+}
+
+///////////////////////////////////
+
+void repeat_nofilter_identity(uint32_t dst[], int x, int width, int count)
+{
+    if (x >= width)
+        x %= width;
+
+    int i;
+    uint16_t* xx = (uint16_t*)dst;
+
+    // do the first partial run
+    int n = width - x;
+    if (n > count)
+        n = count;
+    
+    count -= n;
+    n += x;
+    for (i = x; i < n; i++)
+        *xx++ = SkToU16(i);
+
+    // do all the full-width runs
+    while ((count -= width) >= 0)
+        for (i = 0; i < width; i++)
+            *xx++ = SkToU16(i);
+
+    // final cleanup run
+    count += width;
+    for (i = 0; i < count; i++)
+        *xx++ = SkToU16(i);
+}
+
diff --git a/src/core/SkBitmapProcState_sample.h b/src/core/SkBitmapProcState_sample.h
new file mode 100644
index 0000000..122ccf8
--- /dev/null
+++ b/src/core/SkBitmapProcState_sample.h
@@ -0,0 +1,207 @@
+
+#if DSTSIZE==32
+    #define DSTTYPE SkPMColor
+#elif DSTSIZE==16
+    #define DSTTYPE uint16_t
+#else
+    #error "need DSTSIZE to be 32 or 16"
+#endif
+
+static void MAKENAME(_nofilter_DXDY)(const SkBitmapProcState& s,
+                                     const uint32_t* SK_RESTRICT xy,
+                                     int count, DSTTYPE* SK_RESTRICT colors) {
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(s.fDoFilter == false);
+    SkDEBUGCODE(CHECKSTATE(s);)
+
+#ifdef PREAMBLE
+    PREAMBLE(s);
+#endif
+    const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels();
+    int i, rb = s.fBitmap->rowBytes();
+
+    uint32_t XY;
+    SRCTYPE src;
+    
+    for (i = (count >> 1); i > 0; --i) {
+        XY = *xy++;
+        SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() &&
+                 (XY & 0xFFFF) < (unsigned)s.fBitmap->width());
+        src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF];
+        *colors++ = RETURNDST(src);
+        
+        XY = *xy++;
+        SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() &&
+                 (XY & 0xFFFF) < (unsigned)s.fBitmap->width());
+        src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF];
+        *colors++ = RETURNDST(src);
+    }
+    if (count & 1) {
+        XY = *xy++;
+        SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() &&
+                 (XY & 0xFFFF) < (unsigned)s.fBitmap->width());
+        src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF];
+        *colors++ = RETURNDST(src);
+    }
+
+#ifdef POSTAMBLE
+    POSTAMBLE(s);
+#endif
+}
+
+static void MAKENAME(_nofilter_DX)(const SkBitmapProcState& s,
+                                   const uint32_t* SK_RESTRICT xy,
+                                   int count, DSTTYPE* SK_RESTRICT colors) {
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
+    SkASSERT(s.fDoFilter == false);
+    SkDEBUGCODE(CHECKSTATE(s);)
+
+#ifdef PREAMBLE
+    PREAMBLE(s);
+#endif
+    const SRCTYPE* SK_RESTRICT srcAddr = (const SRCTYPE*)s.fBitmap->getPixels();
+    int i;
+
+    // bump srcAddr to the proper row, since we're told Y never changes
+    SkASSERT((unsigned)xy[0] < (unsigned)s.fBitmap->height());
+    srcAddr = (const SRCTYPE*)((const char*)srcAddr +
+                                                xy[0] * s.fBitmap->rowBytes());
+    // buffer is y32, x16, x16, x16, x16, x16
+    const uint16_t* SK_RESTRICT xx = (const uint16_t*)(xy + 1);
+    
+    SRCTYPE src;
+    
+    for (i = (count >> 2); i > 0; --i) {
+        SkASSERT(*xx < (unsigned)s.fBitmap->width());
+        src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
+        
+        SkASSERT(*xx < (unsigned)s.fBitmap->width());
+        src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
+        
+        SkASSERT(*xx < (unsigned)s.fBitmap->width());
+        src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
+        
+        SkASSERT(*xx < (unsigned)s.fBitmap->width());
+        src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
+    }
+    for (i = (count & 3); i > 0; --i) {
+        SkASSERT(*xx < (unsigned)s.fBitmap->width());
+        src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
+    }
+    
+#ifdef POSTAMBLE
+    POSTAMBLE(s);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void MAKENAME(_filter_DX)(const SkBitmapProcState& s,
+                                 const uint32_t* SK_RESTRICT xy,
+                                  int count, DSTTYPE* SK_RESTRICT colors) {
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(s.fDoFilter);
+    SkDEBUGCODE(CHECKSTATE(s);)
+
+#ifdef PREAMBLE
+    PREAMBLE(s);
+#endif
+    const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels();
+    unsigned rb = s.fBitmap->rowBytes();
+    unsigned subY;
+    const SRCTYPE* SK_RESTRICT row0;
+    const SRCTYPE* SK_RESTRICT row1;
+
+    // setup row ptrs and update proc_table
+    {
+        uint32_t XY = *xy++;
+        unsigned y0 = XY >> 14;
+        row0 = (const SRCTYPE*)(srcAddr + (y0 >> 4) * rb);
+        row1 = (const SRCTYPE*)(srcAddr + (XY & 0x3FFF) * rb);
+        subY = y0 & 0xF;
+    }
+    
+    do {
+        uint32_t XX = *xy++;    // x0:14 | 4 | x1:14
+        unsigned x0 = XX >> 14;
+        unsigned x1 = XX & 0x3FFF;
+        unsigned subX = x0 & 0xF;        
+        x0 >>= 4;
+
+        uint32_t c = FILTER_PROC(subX, subY,
+                                 SRC_TO_FILTER(row0[x0]),
+                                 SRC_TO_FILTER(row0[x1]),
+                                 SRC_TO_FILTER(row1[x0]),
+                                 SRC_TO_FILTER(row1[x1]));
+        *colors++ = FILTER_TO_DST(c);
+
+    } while (--count != 0);
+    
+#ifdef POSTAMBLE
+    POSTAMBLE(s);
+#endif
+}
+static void MAKENAME(_filter_DXDY)(const SkBitmapProcState& s,
+                                   const uint32_t* SK_RESTRICT xy,
+                                   int count, DSTTYPE* SK_RESTRICT colors) {
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(s.fDoFilter);
+    SkDEBUGCODE(CHECKSTATE(s);)
+        
+#ifdef PREAMBLE
+        PREAMBLE(s);
+#endif
+    const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels();
+    int rb = s.fBitmap->rowBytes();
+    
+    do {
+        uint32_t data = *xy++;
+        unsigned y0 = data >> 14;
+        unsigned y1 = data & 0x3FFF;
+        unsigned subY = y0 & 0xF;
+        y0 >>= 4;
+        
+        data = *xy++;
+        unsigned x0 = data >> 14;
+        unsigned x1 = data & 0x3FFF;
+        unsigned subX = x0 & 0xF;
+        x0 >>= 4;
+        
+        const SRCTYPE* SK_RESTRICT row0 = (const SRCTYPE*)(srcAddr + y0 * rb);
+        const SRCTYPE* SK_RESTRICT row1 = (const SRCTYPE*)(srcAddr + y1 * rb);
+        
+        uint32_t c = FILTER_PROC(subX, subY,
+                                 SRC_TO_FILTER(row0[x0]),
+                                 SRC_TO_FILTER(row0[x1]),
+                                 SRC_TO_FILTER(row1[x0]),
+                                 SRC_TO_FILTER(row1[x1]));
+        *colors++ = FILTER_TO_DST(c);
+    } while (--count != 0);
+    
+#ifdef POSTAMBLE
+    POSTAMBLE(s);
+#endif
+}
+
+#undef MAKENAME
+#undef DSTSIZE
+#undef DSTTYPE
+#undef SRCTYPE
+#undef CHECKSTATE
+#undef RETURNDST
+#undef SRC_TO_FILTER
+#undef FILTER_TO_DST
+
+#ifdef PREAMBLE
+    #undef PREAMBLE
+#endif
+#ifdef POSTAMBLE
+    #undef POSTAMBLE
+#endif
+
+#undef FILTER_PROC_TYPE
+#undef GET_FILTER_TABLE
+#undef GET_FILTER_ROW
+#undef GET_FILTER_ROW_PROC
+#undef GET_FILTER_PROC
diff --git a/src/core/SkBitmapSampler.cpp b/src/core/SkBitmapSampler.cpp
new file mode 100644
index 0000000..045efd1
--- /dev/null
+++ b/src/core/SkBitmapSampler.cpp
@@ -0,0 +1,423 @@
+/* libs/graphics/sgl/SkBitmapSampler.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 "SkBitmapSampler.h"
+
+static SkTileModeProc get_tilemode_proc(SkShader::TileMode mode)
+{
+    switch (mode) {
+    case SkShader::kClamp_TileMode:
+        return do_clamp;
+    case SkShader::kRepeat_TileMode:
+        return do_repeat_mod;
+    case SkShader::kMirror_TileMode:
+        return do_mirror_mod;
+    default:
+        SkASSERT(!"unknown mode");
+        return NULL;
+    }
+}
+
+SkBitmapSampler::SkBitmapSampler(const SkBitmap& bm, bool filter,
+                                 SkShader::TileMode tmx, SkShader::TileMode tmy)
+    : fBitmap(bm), fFilterBitmap(filter), fTileModeX(tmx), fTileModeY(tmy)
+{
+    SkASSERT(bm.width() > 0 && bm.height() > 0);
+
+    fMaxX = SkToU16(bm.width() - 1);
+    fMaxY = SkToU16(bm.height() - 1);
+    
+    fTileProcX = get_tilemode_proc(tmx);
+    fTileProcY = get_tilemode_proc(tmy);
+}
+
+void SkBitmapSampler::setPaint(const SkPaint& paint)
+{
+}
+
+class SkNullBitmapSampler : public SkBitmapSampler {
+public:
+    SkNullBitmapSampler(const SkBitmap& bm, bool filter,
+                        SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, filter, tmx, tmy) {}
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const { return 0; }
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+
+#define BITMAP_CLASSNAME_PREFIX(name)           ARGB32##name
+#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y)   *bitmap.getAddr32(x, y)
+#include "SkBitmapSamplerTemplate.h"
+
+#include "SkColorPriv.h"
+
+#define BITMAP_CLASSNAME_PREFIX(name)           RGB16##name
+#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y)   SkPixel16ToPixel32(*bitmap.getAddr16(x, y))
+#include "SkBitmapSamplerTemplate.h"
+
+#define BITMAP_CLASSNAME_PREFIX(name)           Index8##name
+#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y)   bitmap.getIndex8Color(x, y)
+#include "SkBitmapSamplerTemplate.h"
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+///////////////// The Bilinear versions
+
+#include "SkFilterProc.h"
+
+class ARGB32_Bilinear_Sampler : public SkBitmapSampler {
+public:
+    ARGB32_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, true, tmx, tmy)
+    {
+        fPtrProcTable = SkGetBilinearFilterPtrProcTable();
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        const uint32_t *p00, *p01, *p10, *p11;
+
+        // turn pixel centers into the top-left of our filter-box
+        x -= SK_FixedHalf;
+        y -= SK_FixedHalf;
+    
+        // compute our pointers
+        {
+            const SkBitmap* bitmap = &fBitmap;
+            int ix = x >> 16;
+            int iy = y >> 16;
+            
+            int             maxX = fMaxX;
+            SkTileModeProc  procX = fTileProcX;
+            int             maxY = fMaxY;
+            SkTileModeProc  procY = fTileProcY;
+
+            int tmpx = procX(ix, maxX);
+            int tmpy = procY(iy, maxY);
+            p00 = bitmap->getAddr32(tmpx, tmpy);
+
+            int tmpx1 = procX(ix + 1, maxX);
+            p01 = bitmap->getAddr32(tmpx1, tmpy);
+
+            int tmpy1 = procY(iy + 1, maxY);
+            p10 = bitmap->getAddr32(tmpx, tmpy1);
+
+            p11 = bitmap->getAddr32(tmpx1, tmpy1);
+        }
+
+        SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y);
+        return proc(p00, p01, p10, p11);
+    }
+    
+private:
+    const SkFilterPtrProc* fPtrProcTable;
+};
+
+class RGB16_Bilinear_Sampler : public SkBitmapSampler {
+public:
+    RGB16_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, true, tmx, tmy)
+    {
+        fProcTable = SkGetBilinearFilterProcTable();
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        const uint16_t *p00, *p01, *p10, *p11;
+
+        // turn pixel centers into the top-left of our filter-box
+        x -= SK_FixedHalf;
+        y -= SK_FixedHalf;
+    
+        // compute our pointers
+        {
+            const SkBitmap* bitmap = &fBitmap;
+            int ix = x >> 16;
+            int iy = y >> 16;
+            
+            int             maxX = fMaxX;
+            SkTileModeProc  procX = fTileProcX;
+            int             maxY = fMaxY;
+            SkTileModeProc  procY = fTileProcY;
+
+            int tmpx = procX(ix, maxX);
+            int tmpy = procY(iy, maxY);
+            p00 = bitmap->getAddr16(tmpx, tmpy);
+
+            int tmpx1 = procX(ix + 1, maxX);
+            p01 = bitmap->getAddr16(tmpx1, tmpy);
+
+            int tmpy1 = procY(iy + 1, maxY);
+            p10 = bitmap->getAddr16(tmpx, tmpy1);
+
+            p11 = bitmap->getAddr16(tmpx1, tmpy1);
+        }
+
+        SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y);
+        uint32_t c = proc(SkExpand_rgb_16(*p00), SkExpand_rgb_16(*p01),
+                          SkExpand_rgb_16(*p10), SkExpand_rgb_16(*p11));
+
+        return SkPixel16ToPixel32((uint16_t)SkCompact_rgb_16(c));
+    }
+    
+private:
+    const SkFilterProc* fProcTable;
+};
+
+// If we had a init/term method on sampler, we could avoid the per-pixel
+// call to lockColors/unlockColors
+
+class Index8_Bilinear_Sampler : public SkBitmapSampler {
+public:
+    Index8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, true, tmx, tmy)
+    {
+        fPtrProcTable = SkGetBilinearFilterPtrProcTable();
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        const SkBitmap* bitmap = &fBitmap;
+
+        const uint8_t *p00, *p01, *p10, *p11;
+
+         // turn pixel centers into the top-left of our filter-box
+        x -= SK_FixedHalf;
+        y -= SK_FixedHalf;
+    
+       // compute our pointers
+        {
+            int ix = x >> 16;
+            int iy = y >> 16;
+            
+            int             maxX = fMaxX;
+            SkTileModeProc  procX = fTileProcX;
+            int             maxY = fMaxY;
+            SkTileModeProc  procY = fTileProcY;
+
+            int tmpx = procX(ix, maxX);
+            int tmpy = procY(iy, maxY);
+            p00 = bitmap->getAddr8(tmpx, tmpy);
+
+            int tmpx1 = procX(ix + 1, maxX);
+            p01 = bitmap->getAddr8(tmpx1, tmpy);
+
+            int tmpy1 = procY(iy + 1, maxY);
+            p10 = bitmap->getAddr8(tmpx, tmpy1);
+
+            p11 = bitmap->getAddr8(tmpx1, tmpy1);
+        }
+
+        const SkPMColor* colors = bitmap->getColorTable()->lockColors();
+
+        SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y);
+        uint32_t c = proc(&colors[*p00], &colors[*p01], &colors[*p10], &colors[*p11]);
+
+        bitmap->getColorTable()->unlockColors(false);
+
+        return c;
+    }
+    
+private:
+    const SkFilterPtrProc* fPtrProcTable;
+};
+
+class A8_Bilinear_Sampler : public SkBitmapSampler {
+public:
+    A8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, true, tmx, tmy)
+    {
+        fProcTable = SkGetBilinearFilterProcTable();
+    }
+
+    virtual void setPaint(const SkPaint& paint)
+    {
+        fColor = SkPreMultiplyColor(paint.getColor());
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        const uint8_t *p00, *p01, *p10, *p11;
+
+        // turn pixel centers into the top-left of our filter-box
+        x -= SK_FixedHalf;
+        y -= SK_FixedHalf;
+    
+        // compute our pointers
+        {
+            const SkBitmap* bitmap = &fBitmap;
+            int ix = x >> 16;
+            int iy = y >> 16;
+            
+            int             maxX = fMaxX;
+            SkTileModeProc  procX = fTileProcX;
+            int             maxY = fMaxY;
+            SkTileModeProc  procY = fTileProcY;
+
+            int tmpx = procX(ix, maxX);
+            int tmpy = procY(iy, maxY);
+            p00 = bitmap->getAddr8(tmpx, tmpy);
+
+            int tmpx1 = procX(ix + 1, maxX);
+            p01 = bitmap->getAddr8(tmpx1, tmpy);
+
+            int tmpy1 = procY(iy + 1, maxY);
+            p10 = bitmap->getAddr8(tmpx, tmpy1);
+
+            p11 = bitmap->getAddr8(tmpx1, tmpy1);
+        }
+
+        SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y);
+        int alpha = proc(*p00, *p01, *p10, *p11);
+        return SkAlphaMulQ(fColor, SkAlpha255To256(alpha));
+    }
+    
+private:
+    const SkFilterProc* fProcTable;
+    SkPMColor           fColor;
+};
+
+class A8_NoFilter_Sampler : public SkBitmapSampler {
+public:
+    A8_NoFilter_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, false, tmx, tmy)
+    {
+    }
+
+    virtual void setPaint(const SkPaint& paint)
+    {
+        fColor = SkPreMultiplyColor(paint.getColor());
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        int ix = SkFixedFloor(x);
+        int iy = SkFixedFloor(y);
+        
+        int alpha = *fBitmap.getAddr8(fTileProcX(ix, fMaxX), fTileProcY(iy, fMaxY));
+        return SkAlphaMulQ(fColor, SkAlpha255To256(alpha));
+    }
+    
+private:
+    const SkFilterProc* fProcTable;
+    SkPMColor           fColor;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmapSampler* SkBitmapSampler::Create(const SkBitmap& bm, bool doFilter,
+                                         SkShader::TileMode tmx,
+                                         SkShader::TileMode tmy)
+{
+    switch (bm.getConfig()) {
+    case SkBitmap::kARGB_8888_Config:
+        if (doFilter)
+            return SkNEW_ARGS(ARGB32_Bilinear_Sampler, (bm, tmx, tmy));
+
+        if (tmx == tmy) {
+            switch (tmx) {
+            case SkShader::kClamp_TileMode:
+                return SkNEW_ARGS(ARGB32_Point_Clamp_Sampler, (bm));
+            case SkShader::kRepeat_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(ARGB32_Point_Repeat_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(ARGB32_Point_Repeat_Mod_Sampler, (bm));
+            case SkShader::kMirror_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(ARGB32_Point_Mirror_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(ARGB32_Point_Mirror_Mod_Sampler, (bm));
+            default:
+                SkASSERT(!"unknown mode");
+            }
+        }
+        else {  // tmx != tmy
+            return SkNEW_ARGS(ARGB32_Point_Sampler, (bm, tmx, tmy));
+        }
+        break;
+
+    case SkBitmap::kRGB_565_Config:
+        if (doFilter)
+            return SkNEW_ARGS(RGB16_Bilinear_Sampler, (bm, tmx, tmy));
+
+        if (tmx == tmy) {
+            switch (tmx) {
+            case SkShader::kClamp_TileMode:
+                return SkNEW_ARGS(RGB16_Point_Clamp_Sampler, (bm));
+            case SkShader::kRepeat_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(RGB16_Point_Repeat_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(RGB16_Point_Repeat_Mod_Sampler, (bm));
+            case SkShader::kMirror_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(RGB16_Point_Mirror_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(RGB16_Point_Mirror_Mod_Sampler, (bm));
+            default:
+                SkASSERT(!"unknown mode");
+            }
+        }
+        else {  // tmx != tmy
+            return SkNEW_ARGS(RGB16_Point_Sampler, (bm, tmx, tmy));
+        }
+        break;
+
+    case SkBitmap::kIndex8_Config:
+        if (doFilter)
+            return SkNEW_ARGS(Index8_Bilinear_Sampler, (bm, tmx, tmy));
+
+        if (tmx == tmy) {
+            switch (tmx) {
+            case SkShader::kClamp_TileMode:
+                return SkNEW_ARGS(Index8_Point_Clamp_Sampler, (bm));
+            case SkShader::kRepeat_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(Index8_Point_Repeat_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(Index8_Point_Repeat_Mod_Sampler, (bm));
+            case SkShader::kMirror_TileMode:
+                if (is_pow2(bm.width()) && is_pow2(bm.height()))
+                    return SkNEW_ARGS(Index8_Point_Mirror_Pow2_Sampler, (bm));
+                else
+                    return SkNEW_ARGS(Index8_Point_Mirror_Mod_Sampler, (bm));
+            default:
+                SkASSERT(!"unknown mode");
+            }
+        }
+        else {  // tmx != tmy
+            return SkNEW_ARGS(Index8_Point_Sampler, (bm, tmx, tmy));
+        }
+        break;
+
+    case SkBitmap::kA8_Config:
+        if (doFilter)
+            return SkNEW_ARGS(A8_Bilinear_Sampler, (bm, tmx, tmy));
+        else
+            return SkNEW_ARGS(A8_NoFilter_Sampler, (bm, tmx, tmy));
+        break;
+
+    default:
+        SkASSERT(!"unknown device");
+    }
+    return SkNEW_ARGS(SkNullBitmapSampler, (bm, doFilter, tmx, tmy));
+}
+
diff --git a/src/core/SkBitmapSampler.h b/src/core/SkBitmapSampler.h
new file mode 100644
index 0000000..eeef3b3
--- /dev/null
+++ b/src/core/SkBitmapSampler.h
@@ -0,0 +1,170 @@
+/* libs/graphics/sgl/SkBitmapSampler.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 SkBitmapSampler_DEFINED
+#define SkBitmapSampler_DEFINED
+
+#include "SkBitmap.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+typedef int (*SkTileModeProc)(int value, unsigned max);
+
+class SkBitmapSampler {
+public:
+    SkBitmapSampler(const SkBitmap&, bool filter, SkShader::TileMode tmx, SkShader::TileMode tmy);
+    virtual ~SkBitmapSampler() {}
+
+    const SkBitmap&     getBitmap() const { return fBitmap; }
+    bool                getFilterBitmap() const { return fFilterBitmap; }
+    SkShader::TileMode  getTileModeX() const { return fTileModeX; }
+    SkShader::TileMode  getTileModeY() const { return fTileModeY; }
+
+    /** Given a pixel center at [x,y], return the color sample
+    */
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const = 0;
+
+    virtual void setPaint(const SkPaint& paint);
+
+    // This is the factory for finding an optimal subclass
+    static SkBitmapSampler* Create(const SkBitmap&, bool filter,
+                                   SkShader::TileMode tmx, SkShader::TileMode tmy);
+
+protected:
+    const SkBitmap&     fBitmap;
+    uint16_t            fMaxX, fMaxY;
+    bool                fFilterBitmap;
+    SkShader::TileMode  fTileModeX;
+    SkShader::TileMode  fTileModeY;
+    SkTileModeProc      fTileProcX;
+    SkTileModeProc      fTileProcY;
+
+    // illegal
+    SkBitmapSampler& operator=(const SkBitmapSampler&);
+};
+
+static inline int fixed_clamp(SkFixed x)
+{
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (x >> 16)
+        x = 0xFFFF;
+    if (x < 0)
+        x = 0;
+#else
+    if (x >> 16)
+    {
+        if (x < 0)
+            x = 0;
+        else
+            x = 0xFFFF;
+    }
+#endif
+    return x;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+static inline int fixed_repeat(SkFixed x)
+{
+    return x & 0xFFFF;
+}
+
+static inline int fixed_mirror(SkFixed x)
+{
+    SkFixed s = x << 15 >> 31;
+    // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
+    return (x ^ s) & 0xFFFF;
+}
+
+static inline bool is_pow2(int count)
+{
+    SkASSERT(count > 0);
+    return (count & (count - 1)) == 0;
+}
+
+static inline int do_clamp(int index, unsigned max)
+{
+    SkASSERT((int)max >= 0);
+
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (index > (int)max)
+        index = max;
+    if (index < 0)
+        index = 0;
+#else
+    if ((unsigned)index > max)
+    {
+        if (index < 0)
+            index = 0;
+        else
+            index = max;
+    }
+#endif
+    return index;
+}
+
+static inline int do_repeat_mod(int index, unsigned max)
+{
+    SkASSERT((int)max >= 0);
+
+    if ((unsigned)index > max)
+    {
+        if (index < 0)
+            index = max - (~index % (max + 1));
+        else
+            index = index % (max + 1);
+    }
+    return index;
+}
+
+static inline int do_repeat_pow2(int index, unsigned max)
+{
+    SkASSERT((int)max >= 0 && is_pow2(max + 1));
+
+    return index & max;
+}
+
+static inline int do_mirror_mod(int index, unsigned max)
+{
+    SkASSERT((int)max >= 0);
+
+    // have to handle negatives so that
+    // -1 -> 0, -2 -> 1, -3 -> 2, etc.
+    // so we can't just cal abs
+    index ^= index >> 31;
+
+    if ((unsigned)index > max)
+    {
+        int mod = (max + 1) << 1;
+        index = index % mod;
+        if ((unsigned)index > max)
+            index = mod - index - 1;
+    }
+    return index;
+}
+
+static inline int do_mirror_pow2(int index, unsigned max)
+{
+    SkASSERT((int)max >= 0 && is_pow2(max + 1));
+
+    int s = (index & (max + 1)) - 1;
+    s = ~(s >> 31);
+    // at this stage, s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
+    return (index ^ s) & max;
+}
+
+#endif
diff --git a/src/core/SkBitmapSamplerTemplate.h b/src/core/SkBitmapSamplerTemplate.h
new file mode 100644
index 0000000..00df10c
--- /dev/null
+++ b/src/core/SkBitmapSamplerTemplate.h
@@ -0,0 +1,116 @@
+/* libs/graphics/sgl/SkBitmapSamplerTemplate.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.
+*/
+
+/*  this guy is pulled in multiple times, with the following symbols defined each time:
+
+    #define BITMAP_CLASSNAME_PREFIX(name)           ARGB32##name
+    #defube BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y)   *bitmap.getAddr32(x, y)
+*/
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Sampler)(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
+        : SkBitmapSampler(bm, false, tmx, tmy)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = fTileProcX(SkFixedFloor(x), fMaxX);
+        y = fTileProcY(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Clamp_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Clamp_Sampler)(const SkBitmap& bm)
+        : SkBitmapSampler(bm, false, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = do_clamp(SkFixedFloor(x), fMaxX);
+        y = do_clamp(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Pow2_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Pow2_Sampler)(const SkBitmap& bm)
+        : SkBitmapSampler(bm, false, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = do_repeat_pow2(SkFixedFloor(x), fMaxX);
+        y = do_repeat_pow2(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Mod_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Repeat_Mod_Sampler)(const SkBitmap& bm)
+        : SkBitmapSampler(bm, false, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = do_repeat_mod(SkFixedFloor(x), fMaxX);
+        y = do_repeat_mod(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Pow2_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Pow2_Sampler)(const SkBitmap& bm)
+        : SkBitmapSampler(bm, false, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = do_mirror_pow2(SkFixedFloor(x), fMaxX);
+        y = do_mirror_pow2(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+class BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Mod_Sampler) : public SkBitmapSampler {
+public:
+    BITMAP_CLASSNAME_PREFIX(_Point_Mirror_Mod_Sampler)(const SkBitmap& bm)
+        : SkBitmapSampler(bm, false, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode)
+    {
+    }
+
+    virtual SkPMColor sample(SkFixed x, SkFixed y) const
+    {
+        x = do_mirror_mod(SkFixedFloor(x), fMaxX);
+        y = do_mirror_mod(SkFixedFloor(y), fMaxY);
+        return BITMAP_PIXEL_TO_PMCOLOR(fBitmap, x, y);
+    }
+};
+
+#undef BITMAP_CLASSNAME_PREFIX
+#undef BITMAP_PIXEL_TO_PMCOLOR
diff --git a/src/core/SkBitmapShader.cpp b/src/core/SkBitmapShader.cpp
new file mode 100644
index 0000000..5d70dd3
--- /dev/null
+++ b/src/core/SkBitmapShader.cpp
@@ -0,0 +1,822 @@
+/* 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.
+*/
+
+#if 0
+
+#include "SkBitmapShader.h"
+#include "SkBitmapSampler.h"
+
+#ifdef SK_SUPPORT_MIPMAP
+static SkFixed find_mip_level(SkFixed dx, SkFixed dy)
+{
+    dx = SkAbs32(dx);
+    dy = SkAbs32(dy);
+    if (dx < dy)
+        dx = dy;
+    
+    if (dx < SK_Fixed1)
+        return 0;
+    
+    int clz = SkCLZ(dx);
+    SkASSERT(clz >= 1 && clz <= 15);
+    return SkIntToFixed(15 - clz) + ((unsigned)(dx << (clz + 1)) >> 16);
+}
+#endif
+
+SkBitmapShader::SkBitmapShader(const SkBitmap& src, bool doFilter,
+                               TileMode tmx, TileMode tmy)
+    :
+#ifdef SK_SUPPORT_MIPMAP
+    fMipLevel(0), fMipSrcBitmap(src),
+#endif
+    fOrigSrcBitmap(src)
+    
+{
+    fFilterBitmap = doFilter;
+    fTileModeX = SkToU8(tmx);
+    fTileModeY = SkToU8(tmy);
+}
+
+SkBitmapShader::SkBitmapShader(SkFlattenableReadBuffer& buffer) :
+    INHERITED(buffer)
+{
+    Bitmap src;
+    buffer.readBitmap(&src); 
+#ifdef SK_SUPPORT_MIPMAP
+    fMipLevel = 0;
+    fMipSrcBitmap = src;
+#endif
+    fOrigSrcBitmap = src;
+    fFilterBitmap = buffer.readU8();
+    fTileModeX = buffer.readU8();
+    fTileModeY = buffer.readU8();
+}
+
+void SkBitmapShader::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    this->INHERITED::flatten(buffer);
+    buffer.writeBitmap(&fOrigSrcBitmap);
+    buffer.write8(fFilterBitmap);
+    buffer.write8(fTileModeX);
+    buffer.write8(fTileModeY);
+}
+
+bool SkBitmapShader::setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix)
+{
+    // do this first, so we have a correct inverse matrix
+    if (!this->INHERITED::setContext(device, paint, matrix))
+        return false;
+
+    if (fOrigSrcBitmap.getConfig() == SkBitmap::kNo_Config ||
+        fOrigSrcBitmap.width() == 0 ||
+        fOrigSrcBitmap.height() == 0)
+        return false;
+
+    SkBitmap& bm = fOrigSrcBitmap;
+
+#ifdef SK_SUPPORT_MIPMAP
+    if (fOrigSrcBitmap.countMipLevels())
+    {
+        const SkMatrix& inv = this->getTotalInverse();
+
+        fMipLevel = SkMin32(find_mip_level( SkScalarToFixed(inv.getScaleX()),
+                                            SkScalarToFixed(inv.getSkewY())),
+                            SkIntToFixed(fOrigSrcBitmap.countMipLevels() - 1));
+
+//        SkDEBUGF(("BitmapShader miplevel=%x\n", fMipLevel));
+
+        const SkBitmap::MipLevel* mm = fOrigSrcBitmap.getMipLevel(fMipLevel >> 16);
+        
+        fMipSrcBitmap.setConfig(fOrigSrcBitmap.getConfig(),
+                                mm->fWidth,
+                                mm->fHeight,
+                                mm->fRowBytes);
+        fMipSrcBitmap.setPixels(mm->fPixels);
+        bm = fMipSrcBitmap;
+    }
+    else
+    {
+        fMipLevel = 0;
+        fMipSrcBitmap = fOrigSrcBitmap;
+    }
+#endif
+
+    fFlags = 0;
+    if (paint.getAlpha() == 255 && bm.isOpaque())
+        fFlags |= kOpaqueAlpha_Flag;
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+#include "SkColorPriv.h"
+#include "SkBitmapSampler.h"
+#include "SkPerspIter.h"
+
+class Sampler_BitmapShader : public SkBitmapShader {
+public:
+    Sampler_BitmapShader(const SkBitmap& src, bool doFilter,
+                         TileMode tmx, TileMode tmy)
+        : SkBitmapShader(src, doFilter, tmx, tmy)
+    {
+        // make sure to pass our copy of the src bitmap to the sampler, and not the
+        // original parameter (which might go away).
+        fSampler = NULL;
+    }
+
+    virtual ~Sampler_BitmapShader()
+    {
+        SkDELETE(fSampler);
+    }
+    
+    virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix)
+    {
+        if (this->INHERITED::setContext(device, paint, matrix))
+        {
+            SkDELETE(fSampler);
+            fSampler = SkBitmapSampler::Create(this->getSrcBitmap(), this->getFilterBitmap(),
+                                               this->getTileModeX(), this->getTileModeY());
+            fSampler->setPaint(paint);
+            return true;
+        }
+        return false;
+    }
+    
+    enum {
+        kMaxPointStorageCount = 32
+    };
+
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
+    {
+        unsigned            scale = SkAlpha255To256(this->getPaintAlpha());
+        const SkMatrix&     inv = this->getTotalInverse();
+        SkMatrix::MapPtProc proc = this->getInverseMapPtProc();
+        SkBitmapSampler*     sampler = fSampler;
+        MatrixClass         mc = this->getInverseClass();
+
+        SkPoint srcPt;
+
+        if (mc != kPerspective_MatrixClass)
+        {
+            proc(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                      SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+            SkFixed fx = SkScalarToFixed(srcPt.fX);
+            SkFixed fy = SkScalarToFixed(srcPt.fY);
+            SkFixed dx, dy;
+
+            if (mc == kLinear_MatrixClass)
+            {
+                dx = SkScalarToFixed(inv.getScaleX());
+                dy = SkScalarToFixed(inv.getSkewY());
+            }
+            else
+                (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+
+#if defined(SK_SUPPORT_MIPMAP)
+            {   int level = this->getMipLevel() >> 16;
+                fx >>= level;
+                fy >>= level;
+                dx >>= level;
+                dy >>= level;
+            }
+#endif
+            if (scale == 256)
+            {
+                for (int i = 0; i < count; i++)
+                {
+                    dstC[i] = sampler->sample(fx, fy);
+                    fx += dx;
+                    fy += dy;
+                }
+            }
+            else
+            {
+                for (int i = 0; i < count; i++)
+                {
+                    uint32_t    c = sampler->sample(fx, fy);
+                    dstC[i] = SkAlphaMulQ(c, scale);
+                    fx += dx;
+                    fy += dy;
+                }
+            }
+        }
+        else
+        {
+            SkPerspIter   iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                    SkIntToScalar(y) + SK_ScalarHalf, count);
+            if (scale == 256)
+            {
+                while ((count = iter.next()) != 0)
+                {
+                    const SkFixed* src = iter.getXY();
+                    for (int i = 0; i < count; i++)
+                    {
+                        *dstC++ = sampler->sample(src[0], src[1]);
+                        src += 2;
+                    }
+                }
+            }
+            else
+            {
+                while ((count = iter.next()) != 0)
+                {
+                    const SkFixed* src = iter.getXY();
+                    for (int i = 0; i < count; i++)
+                    {
+                        uint32_t c = sampler->sample(src[0] - SK_FixedHalf, src[1] - SK_FixedHalf);
+                        *dstC++ = SkAlphaMulQ(c, scale);
+                        src += 2;
+                    }
+                }
+            }
+        }
+    }
+
+protected:
+
+    const SkMatrix& getUnitInverse() const { return fUnitInverse; }
+    SkMatrix::MapPtProc getUnitInverseProc() const { return fUnitInverseProc; }
+
+    /* takes computed inverse (from setContext) and computes fUnitInverse,
+        taking srcBitmap width/height into account, so that fUnitInverse
+        walks 0...1, allowing the tile modes to all operate in a fast 16bit
+        space (no need for mod). The resulting coords need to be scaled by
+        width/height to get back into src space (coord * width >> 16).
+    */
+    void computeUnitInverse()
+    {
+        const SkBitmap& src = getSrcBitmap();
+        fUnitInverse = this->getTotalInverse();
+        fUnitInverse.postIDiv(src.width(), src.height());
+        fUnitInverseProc = fUnitInverse.getMapPtProc();
+    }
+
+private:
+    SkBitmapSampler*    fSampler;
+    SkMatrix            fUnitInverse;
+    SkMatrix::MapPtProc fUnitInverseProc;
+    
+    typedef SkBitmapShader INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+class HasSpan16_Sampler_BitmapShader : public Sampler_BitmapShader {
+public:
+    HasSpan16_Sampler_BitmapShader(const SkBitmap& src, bool doFilter,
+                                   TileMode tmx, TileMode tmy)
+        : Sampler_BitmapShader(src, doFilter, tmx, tmy)
+    {
+    }
+
+    virtual uint32_t getFlags()
+    {
+        uint32_t flags = this->INHERITED::getFlags();
+        
+        switch (this->getSrcBitmap().getConfig()) {
+        case SkBitmap::kRGB_565_Config:
+            flags |= kHasSpan16_Flag;
+            break;
+        case SkBitmap::kIndex8_Config:
+        case SkBitmap::kARGB_8888_Config:
+            if (this->getSrcBitmap().isOpaque())
+                flags |= kHasSpan16_Flag;
+            break;
+        default:
+            break;
+        }
+        return flags;
+    }
+    
+    const SkBitmap& revealSrcBitmap() const { return this->getSrcBitmap(); }
+    uint8_t         revealPaintAlpha() const { return this->getPaintAlpha(); }
+    const SkMatrix& revealTotalInverse() const { return this->getTotalInverse(); }
+
+private:
+    typedef Sampler_BitmapShader INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+static void Index8_RepeatTile_Sprite16(HasSpan16_Sampler_BitmapShader* shader,
+                                       int x, int y, uint16_t dstC[], int count)
+{
+    const SkMatrix& inv = shader->revealTotalInverse();
+    const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+    int             width = srcBitmap.width();
+    int             height = srcBitmap.height();
+
+    SkColorTable* ctable = srcBitmap.getColorTable();
+    const uint16_t* colors = ctable->lock16BitCache();
+
+    x += SkScalarRound(inv[SkMatrix::kMTransX]);
+    y += SkScalarRound(inv[SkMatrix::kMTransY]);
+    
+    x = do_repeat_mod(x, width - 1);
+    y = do_repeat_mod(y, height - 1);
+    const uint8_t* row = srcBitmap.getAddr8(0, y);
+    const uint8_t* src = row + x;
+
+    // do the first partial run
+    int n = width - x;
+    if (n > count) n = count;
+    count -= n;
+    SkASSERT(n > 0);
+    do {
+        *dstC++ = colors[*src++];
+    } while (--n > 0);
+
+    // do 1 complete run
+    if (count >= width)
+    {
+        uint16_t* baseDstC = dstC;  // remember the first complete run start
+        n = width;
+        count -= width;
+        src = row;
+        do {
+            *dstC++ = colors[*src++];
+        } while (--n > 0);
+
+        // do the rest of the complete runs
+        while (count >= width)
+        {
+            count -= width;
+            memcpy(dstC, baseDstC, width << 1);
+            dstC += width;
+        }
+        // do final partial run
+        if (count > 0)
+            memcpy(dstC, baseDstC, count << 1);
+    }
+    else // do final partial
+    {
+        if (count > 0)
+        {
+            src = row;
+            do {
+                *dstC++ = colors[*src++];
+            } while (--count > 0);
+        }
+    }
+    
+    ctable->unlock16BitCache();
+}
+
+static void Index8_RepeatTile_Sprite32(HasSpan16_Sampler_BitmapShader* shader,
+                                       int x, int y, SkPMColor dstC[], int count)
+{
+    const SkMatrix& inv = shader->revealTotalInverse();
+    const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+    int             width = srcBitmap.width();
+    int             height = srcBitmap.height();
+
+    SkColorTable* ctable = srcBitmap.getColorTable();
+    const SkPMColor* colors = ctable->lockColors();
+
+    x += SkScalarRound(inv[SkMatrix::kMTransX]);
+    y += SkScalarRound(inv[SkMatrix::kMTransY]);
+    
+    x = do_repeat_mod(x, width - 1);
+    y = do_repeat_mod(y, height - 1);
+
+    const uint8_t* row = srcBitmap.getAddr8(0, y);
+    const uint8_t* src = row + x;
+
+    // do the first partial run
+    int n = width - x;
+    if (n > count) n = count;
+    count -= n;
+    SkASSERT(n > 0);
+    do {
+        *dstC++ = colors[*src++];
+    } while (--n > 0);
+
+    // do 1 complete run
+    if (count >= width)
+    {
+        SkPMColor* baseDstC = dstC;  // remember the first complete run start
+        n = width;
+        count -= width;
+        src = row;
+        do {
+            *dstC++ = colors[*src++];
+        } while (--n > 0);
+
+        // do the rest of the complete runs
+        while (count >= width)
+        {
+            count -= width;
+            memcpy(dstC, baseDstC, width << 2);
+            dstC += width;
+        }
+        // do final partial run
+        if (count > 0)
+            memcpy(dstC, baseDstC, count << 2);
+    }
+    else // do final partial
+    {
+        if (count > 0)
+        {
+            src = row;
+            do {
+                *dstC++ = colors[*src++];
+            } while (--count > 0);
+        }
+    }
+    
+    ctable->unlockColors(false);
+}
+
+static void RGB16_RepeatTile_Sprite16(HasSpan16_Sampler_BitmapShader* shader,
+                                       int x, int y, uint16_t dstC[], int count)
+{
+    SkASSERT(count > 0);
+
+    const SkMatrix& inv = shader->revealTotalInverse();
+    const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+    int             width = srcBitmap.width();
+    int             height = srcBitmap.height();
+    
+    SkASSERT(width > 0 && height > 0);
+
+    x += SkScalarRound(inv[SkMatrix::kMTransX]);
+    y += SkScalarRound(inv[SkMatrix::kMTransY]);
+    
+    x = do_repeat_mod(x, width - 1);
+    y = do_repeat_mod(y, height - 1);
+
+    const uint16_t* row = srcBitmap.getAddr16(0, y);
+    const uint16_t* src = row + x;
+
+    int n = SkMin32(width - x, count);
+
+    for (;;)
+    {
+        SkASSERT(n > 0 && count >= n);
+        memcpy(dstC, src, n << 1);
+        count -= n;
+        if (count == 0)
+            break;
+        dstC += n;
+        src = row;
+        n = SkMin32(width, count);
+    }
+}
+
+static void RGB16_RepeatTile_Sprite32(HasSpan16_Sampler_BitmapShader* shader,
+                                       int x, int y, SkPMColor dstC[], int count)
+{
+    SkASSERT(count > 0);
+
+    const SkMatrix& inv = shader->revealTotalInverse();
+    const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+    int             width = srcBitmap.width();
+    int             height = srcBitmap.height();
+    
+    SkASSERT(width > 0 && height > 0);
+
+    x += SkScalarRound(inv[SkMatrix::kMTransX]);
+    y += SkScalarRound(inv[SkMatrix::kMTransY]);
+    
+    x = do_repeat_mod(x, width - 1);
+    y = do_repeat_mod(y, height - 1);
+
+    const uint16_t* row = srcBitmap.getAddr16(0, y);
+    const uint16_t* src = row + x;
+
+    int n = SkMin32(width - x, count);
+
+    // do the first partial run
+    count -= n;
+    SkASSERT(n > 0);
+    do {
+        *dstC++ = SkPixel16ToPixel32(*src++);
+    } while (--n > 0);
+
+    // do 1 complete run
+    if (count >= width)
+    {
+        SkPMColor* baseDstC = dstC;  // remember the first complete run start
+        n = width;
+        count -= width;
+        src = row;
+        do {
+            *dstC++ = SkPixel16ToPixel32(*src++);
+        } while (--n > 0);
+
+        // do the rest of the complete runs
+        while (count >= width)
+        {
+            count -= width;
+            memcpy(dstC, baseDstC, width << 2);
+            dstC += width;
+        }
+        // do final partial run
+        if (count > 0)
+            memcpy(dstC, baseDstC, count << 2);
+    }
+    else // do final partial
+    {
+        if (count > 0)
+        {
+            src = row;
+            do {
+                *dstC++ = SkPixel16ToPixel32(*src++);;
+            } while (--count > 0);
+        }
+    }
+}
+
+static void ARGB32_RepeatTile_Sprite16(HasSpan16_Sampler_BitmapShader* shader,
+                                       int x, int y, uint16_t dstC[], int count)
+{
+    SkASSERT(count > 0);
+
+    const SkMatrix& inv = shader->revealTotalInverse();
+    const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+    int             width = srcBitmap.width();
+    int             height = srcBitmap.height();
+    
+    SkASSERT(width > 0 && height > 0);
+
+    x += SkScalarRound(inv[SkMatrix::kMTransX]);
+    y += SkScalarRound(inv[SkMatrix::kMTransY]);
+    
+    x = do_repeat_mod(x, width - 1);
+    y = do_repeat_mod(y, height - 1);
+
+    const SkPMColor* row = srcBitmap.getAddr32(0, y);
+    const SkPMColor* src = row + x;
+
+    int n = SkMin32(width - x, count);
+
+    // do the first partial run
+    count -= n;
+    SkASSERT(n > 0);
+    do {
+        *dstC++ = SkPixel32ToPixel16(*src++);
+    } while (--n > 0);
+
+    // do 1 complete run
+    if (count >= width)
+    {
+        uint16_t* baseDstC = dstC;  // remember the first complete run start
+        n = width;
+        count -= width;
+        src = row;
+        do {
+            *dstC++ = SkPixel32ToPixel16(*src++);
+        } while (--n > 0);
+
+        // do the rest of the complete runs
+        while (count >= width)
+        {
+            count -= width;
+            memcpy(dstC, baseDstC, width << 1);
+            dstC += width;
+        }
+        // do final partial run
+        if (count > 0)
+            memcpy(dstC, baseDstC, count << 1);
+    }
+    else // do final partial
+    {
+        if (count > 0)
+        {
+            src = row;
+            do {
+                *dstC++ = SkPixel32ToPixel16(*src++);;
+            } while (--count > 0);
+        }
+    }
+}
+
+static void ARGB32_RepeatTile_Sprite32(HasSpan16_Sampler_BitmapShader* shader,
+                                       int x, int y, SkPMColor dstC[], int count)
+{
+    SkASSERT(count > 0);
+
+    const SkMatrix& inv = shader->revealTotalInverse();
+    const SkBitmap& srcBitmap = shader->revealSrcBitmap();
+    int             width = srcBitmap.width();
+    int             height = srcBitmap.height();
+    
+    SkASSERT(width > 0 && height > 0);
+
+    x += SkScalarRound(inv[SkMatrix::kMTransX]);
+    y += SkScalarRound(inv[SkMatrix::kMTransY]);
+    
+    x = do_repeat_mod(x, width - 1);
+    y = do_repeat_mod(y, height - 1);
+
+    const SkPMColor* row = srcBitmap.getAddr32(0, y);
+    const SkPMColor* src = row + x;
+
+    int n = SkMin32(width - x, count);
+
+    for (;;)
+    {
+        SkASSERT(n > 0 && count >= n);
+        memcpy(dstC, src, n << 2);
+        count -= n;
+        if (count == 0)
+            break;
+        dstC += n;
+        src = row;
+        n = SkMin32(width, count);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+#define NOFILTER_BITMAP_SHADER_CLASS                    Index8_NoFilter_RepeatTile_BitmapShader
+#define NOFILTER_BITMAP_SHADER_TILEMODE                 SkShader::kRepeat_TileMode
+#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max)         (fixed_repeat(x) * (max + 1) >> 16)
+#define NOFILTER_BITMAP_SHADER_TYPE                     uint8_t
+#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x)           colors32[p[x]]
+#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb)   colors32[p[x + y * rb]]
+#define NOFILTER_BITMAP_SHADER_PREAMBLE(bitmap, rb)     const SkPMColor* colors32 = bitmap.getColorTable()->lockColors()
+#define NOFILTER_BITMAP_SHADER_POSTAMBLE(bitmap)        bitmap.getColorTable()->unlockColors(false)
+#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x)         colors16[p[x]]
+#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) colors16[p[x + y * rb]]
+#define NOFILTER_BITMAP_SHADER_PREAMBLE16(bitmap, rb)   const uint16_t* colors16 = bitmap.getColorTable()->lock16BitCache()
+#define NOFILTER_BITMAP_SHADER_POSTAMBLE16(bitmap)      bitmap.getColorTable()->unlock16BitCache()
+#define NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC16             Index8_RepeatTile_Sprite16
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC32             Index8_RepeatTile_Sprite32
+#include "SkBitmapShaderTemplate.h"
+
+#define NOFILTER_BITMAP_SHADER_CLASS                    U16_NoFilter_RepeatTile_BitmapShader
+#define NOFILTER_BITMAP_SHADER_TILEMODE                 SkShader::kRepeat_TileMode
+#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max)         (fixed_repeat(x) * (max + 1) >> 16)
+#define NOFILTER_BITMAP_SHADER_TYPE                     uint16_t
+#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x)           SkPixel16ToPixel32(p[x])
+#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb)   SkPixel16ToPixel32(*(const uint16_t*)((const char*)p + y * rb + (x << 1)))
+#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x)         p[x]
+#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) *(const uint16_t*)((const char*)p + y * rb + (x << 1))
+#define NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC16             RGB16_RepeatTile_Sprite16
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC32             RGB16_RepeatTile_Sprite32
+#include "SkBitmapShaderTemplate.h"
+
+#define NOFILTER_BITMAP_SHADER_CLASS                    U32_NoFilter_RepeatTile_BitmapShader
+#define NOFILTER_BITMAP_SHADER_TILEMODE                 SkShader::kRepeat_TileMode
+#define NOFILTER_BITMAP_SHADER_TILEPROC(x, max)         (fixed_repeat(x) * (max + 1) >> 16)
+#define NOFILTER_BITMAP_SHADER_TYPE                     uint32_t
+#define NOFILTER_BITMAP_SHADER_SAMPLE_X(p, x)           p[x]
+#define NOFILTER_BITMAP_SHADER_SAMPLE_XY(p, x, y, rb)   *(const uint32_t*)((const char*)p + y * rb + (x << 2))
+#define NOFILTER_BITMAP_SHADER_SAMPLE_X16(p, x)         SkPixel32ToPixel16_ToU16(p[x])
+#define NOFILTER_BITMAP_SHADER_SAMPLE_XY16(p, x, y, rb) SkPixel32ToPixel16_ToU16(*(const uint32_t*)((const char*)p + y * rb + (x << 2)))
+#define NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC16             ARGB32_RepeatTile_Sprite16
+#define NOFILTER_BITMAP_SHADER_SPRITEPROC32             ARGB32_RepeatTile_Sprite32
+#include "SkBitmapShaderTemplate.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static inline SkPMColor expanded_rgb16_to_8888(uint32_t c, U8CPU alpha)
+{
+//    GGGG Gggg gggR RRRR rrrr r|BB BBBb bbbb
+    SkASSERT(alpha <= 255);
+
+#if 1
+    int scale = SkAlpha255To256(alpha);
+    int r = (c & 0xF800) * scale >> 16;
+    int g = ((c >> 21) & 0x3F) * scale >> 6;
+    int b = (c & 0x1F) * scale >> 5;
+    return SkPackARGB32(alpha, r, g, b);
+#else
+    int scale = SkAlpha255To256(alpha) >> 3;
+    c &= 0x07E0F81F;
+    c = c * scale;
+    int r = (c >> 13) & 0xFF;
+    int g = (c >> 24) & 0xFF;
+    int b = (c >> 2) & 0xFF;
+    return SkPackARGB32(alpha, r, g, b);
+#endif
+}
+
+#define BILERP_BITMAP16_SHADER_CLASS            U16_Bilerp_BitmapShader
+#define BILERP_BITMAP16_SHADER_TYPE             uint16_t
+#define BILERP_BITMAP16_SHADER_PREAMBLE(bm)
+#define BILERP_BITMAP16_SHADER_PIXEL(c)         (c)
+#define BILERP_BITMAP16_SHADER_POSTAMBLE(bm)
+#include "SkBitmapShader16BilerpTemplate.h"
+
+#define BILERP_BITMAP16_SHADER_CLASS            Index8_Bilerp_BitmapShader
+#define BILERP_BITMAP16_SHADER_TYPE             uint8_t
+#define BILERP_BITMAP16_SHADER_PREAMBLE(bm)     SkColorTable* ctable = (bm).getColorTable(); const uint16_t* colors16 = ctable->lock16BitCache()
+#define BILERP_BITMAP16_SHADER_PIXEL(c)         colors16[c]
+#define BILERP_BITMAP16_SHADER_POSTAMBLE(bm)    ctable->unlock16BitCache()
+#include "SkBitmapShader16BilerpTemplate.h"
+
+#include "ARGB32_Clamp_Bilinear_BitmapShader.h"
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+#include "SkBitmapProcShader.h"
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+#include "SkTemplatesPriv.h"
+
+SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
+                                       bool doFilter,
+                                       TileMode tmx, TileMode tmy,
+                                       void* storage, size_t storageSize)
+{
+#if 1
+    
+    SkShader* shader;
+    SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
+                          storageSize, (src, doFilter, tmx, tmy));
+    return shader;
+#else
+
+    if (!doFilter)
+    {
+        if (kClamp_TileMode == tmx && kClamp_TileMode == tmy)
+        {
+            SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
+                                  storageSize, (src, doFilter, tmx, tmy));
+        }
+        else if (kRepeat_TileMode == tmx && kRepeat_TileMode == tmy)
+        {
+    #if 1
+            SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
+                                  storageSize, (src, doFilter, tmx, tmy));
+    #else
+            switch (src.getConfig()) {
+            case SkBitmap::kIndex8_Config:
+                SK_PLACEMENT_NEW_ARGS(shader, Index8_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src));
+                break;
+            case SkBitmap::kRGB_565_Config:
+                SK_PLACEMENT_NEW_ARGS(shader, U16_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src));
+                break;
+            case SkBitmap::kARGB_8888_Config:
+                SK_PLACEMENT_NEW_ARGS(shader, U32_NoFilter_RepeatTile_BitmapShader, storage, storageSize, (src));
+                break;
+            default:
+                break;
+            }
+    #endif
+        }
+    }
+    else if (kClamp_TileMode == tmx && kClamp_TileMode == tmy)
+    {
+#if 1
+        if (SkBitmapProcShader::CanDo(src, tmx, tmy))
+        {
+            SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
+                                  storageSize, (src, doFilter, tmx, tmy));
+        }
+#else
+        switch (src.getConfig()) {
+        case SkBitmap::kIndex8_Config:
+            if (src.isOpaque())
+                SK_PLACEMENT_NEW_ARGS(shader, Index8_Bilerp_BitmapShader, storage, storageSize, (src));
+            break;
+        case SkBitmap::kRGB_565_Config:
+            SK_PLACEMENT_NEW_ARGS(shader, U16_Bilerp_BitmapShader, storage, storageSize, (src));
+            break;
+        case SkBitmap::kARGB_8888_Config:
+            SK_PLACEMENT_NEW_ARGS(shader, ARGB32_Clamp_Bilinear_BitmapShader, storage, storageSize, (src));
+            break;
+        default:
+            break;
+        }
+#endif
+    }
+    
+    // if shader is null, then none of the special cases could handle the request
+    // so fall through to our slow-general case
+    if (shader == NULL)
+        SK_PLACEMENT_NEW_ARGS(shader, Sampler_BitmapShader, storage, storageSize,
+                              (src, doFilter, tmx, tmy));
+    return shader;
+#endif
+}
+
+SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, bool doFilter,
+                                       TileMode tmx, TileMode tmy)
+{
+    return SkShader::CreateBitmapShader(src, doFilter, tmx, tmy, NULL, 0);
+}
+
+#endif
diff --git a/src/core/SkBitmapShader.h b/src/core/SkBitmapShader.h
new file mode 100644
index 0000000..8d40a4b
--- /dev/null
+++ b/src/core/SkBitmapShader.h
@@ -0,0 +1,73 @@
+/* libs/graphics/sgl/SkBitmapShader.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 SkBitmapShader_DEFINED
+#define SkBitmapShader_DEFINED
+
+#include "SkShader.h"
+#include "SkBitmap.h"
+#include "SkPaint.h"
+
+class SkBitmapShader : public SkShader {
+public:
+    SkBitmapShader( const SkBitmap& src,
+                    bool doFilter, TileMode tx, TileMode ty);
+
+    virtual bool        setContext(const SkBitmap&, const SkPaint& paint, const SkMatrix&);
+    virtual uint32_t    getFlags() { return fFlags; }
+
+protected:
+    SkBitmapShader(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    virtual Factory getFactory() { return CreateProc; }
+    const SkBitmap&     getSrcBitmap() const
+    {
+#ifdef SK_SUPPORT_MIPMAP
+        return fMipSrcBitmap;
+#else
+        return fOrigSrcBitmap;
+#endif
+    }
+    bool        getFilterBitmap() const { return fFilterBitmap != 0; }
+    TileMode    getTileModeX() const { return (TileMode)fTileModeX; }
+    TileMode    getTileModeY() const { return (TileMode)fTileModeY; }
+    SkFixed     getMipLevel() const 
+    {
+#ifdef SK_SUPPORT_MIPMAP
+        return fMipLevel;
+#else
+        return 0;
+#endif
+    }
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { 
+        return SkNEW_ARGS(SkBitmapShader, (buffer)); }
+#ifdef SK_SUPPORT_MIPMAP
+    SkFixed     fMipLevel;
+    SkBitmap    fMipSrcBitmap; // the chosen level (in setContext)
+#endif
+    SkBitmap    fOrigSrcBitmap;
+    uint8_t     fFilterBitmap;
+    uint8_t     fTileModeX;
+    uint8_t     fTileModeY;
+    uint8_t     fFlags;
+
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/src/core/SkBitmapShader16BilerpTemplate.h b/src/core/SkBitmapShader16BilerpTemplate.h
new file mode 100644
index 0000000..b70801e
--- /dev/null
+++ b/src/core/SkBitmapShader16BilerpTemplate.h
@@ -0,0 +1,253 @@
+/* libs/graphics/sgl/SkBitmapShader16BilerpTemplate.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.
+*/
+
+#include "SkFilterProc.h"
+
+class BILERP_BITMAP16_SHADER_CLASS : public HasSpan16_Sampler_BitmapShader {
+public:
+    BILERP_BITMAP16_SHADER_CLASS(const SkBitmap& src)
+        : HasSpan16_Sampler_BitmapShader(src, true,
+                                         SkShader::kClamp_TileMode,
+                                         SkShader::kClamp_TileMode)
+    {
+    }
+
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
+    {
+        SkASSERT(count > 0);
+        
+        U8CPU alpha = this->getPaintAlpha();
+
+        const SkMatrix& inv = this->getTotalInverse();
+        const SkBitmap& srcBitmap = this->getSrcBitmap();
+        unsigned        srcMaxX = srcBitmap.width() - 1;
+        unsigned        srcMaxY = srcBitmap.height() - 1;
+        unsigned        srcRB = srcBitmap.rowBytes();
+
+        BILERP_BITMAP16_SHADER_PREAMBLE(srcBitmap);
+
+        const SkFilterProc* proc_table = SkGetBilinearFilterProcTable();
+        const BILERP_BITMAP16_SHADER_TYPE* srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)srcBitmap.getPixels();
+
+        if (this->getInverseClass() == kPerspective_MatrixClass)
+        {
+            SkPerspIter   iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                    SkIntToScalar(y) + SK_ScalarHalf, count);
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+                while (--count >= 0)
+                {
+                    SkFixed fx = *srcXY++ - SK_FixedHalf;
+                    SkFixed fy = *srcXY++ - SK_FixedHalf;
+                    int ix = fx >> 16;
+                    int iy = fy >> 16;
+                    int x = SkClampMax(ix, srcMaxX);
+                    int y = SkClampMax(iy, srcMaxY);
+
+                    const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11;
+
+                    p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels + y * srcRB)) + x;
+                    if ((unsigned)ix < srcMaxX)
+                        p01 += 1;
+                    p10 = p00;
+                    p11 = p01;
+                    if ((unsigned)iy < srcMaxY)
+                    {
+                        p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+                        p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+                    }
+
+                    SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+                    uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+
+                    *dstC++ = expanded_rgb16_to_8888(c, alpha);
+                }
+            }
+        }
+        else    // linear case
+        {
+            SkFixed fx, fy, dx, dy;
+
+            // now init fx, fy, dx, dy
+            {
+                SkPoint srcPt;
+                this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                                 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+                fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf;
+                fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf;
+
+                if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+                    (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+                else
+                {
+                    dx = SkScalarToFixed(inv.getScaleX());
+                    dy = SkScalarToFixed(inv.getSkewY());
+                }
+            }
+
+            do {
+                int ix = fx >> 16;
+                int iy = fy >> 16;
+
+                const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11;
+
+                p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels +
+                                                                   SkClampMax(iy, srcMaxY) * srcRB)) +
+                                                                   SkClampMax(ix, srcMaxX);
+                if ((unsigned)ix < srcMaxX)
+                    p01 += 1;
+                p10 = p00;
+                p11 = p01;
+                if ((unsigned)iy < srcMaxY)
+                {
+                    p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+                    p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+                }
+
+                SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+                uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+                *dstC++ = expanded_rgb16_to_8888(c, alpha);
+
+                fx += dx;
+                fy += dy;
+            } while (--count != 0);
+        }
+        BILERP_BITMAP16_SHADER_POSTAMBLE(srcBitmap);
+    }
+
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
+    {
+        SkASSERT(count > 0);
+
+        const SkMatrix& inv = this->getTotalInverse();
+        const SkBitmap& srcBitmap = this->getSrcBitmap();
+        unsigned        srcMaxX = srcBitmap.width() - 1;
+        unsigned        srcMaxY = srcBitmap.height() - 1;
+        unsigned        srcRB = srcBitmap.rowBytes();
+
+        BILERP_BITMAP16_SHADER_PREAMBLE(srcBitmap);
+
+        const SkFilterProc* proc_table = SkGetBilinearFilterProcTable();
+        const BILERP_BITMAP16_SHADER_TYPE* srcPixels = (const BILERP_BITMAP16_SHADER_TYPE*)srcBitmap.getPixels();
+
+        if (this->getInverseClass() == kPerspective_MatrixClass)
+        {
+            SkPerspIter   iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                    SkIntToScalar(y) + SK_ScalarHalf, count);
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+                while (--count >= 0)
+                {
+                    SkFixed fx = *srcXY++ - SK_FixedHalf;
+                    SkFixed fy = *srcXY++ - SK_FixedHalf;
+                    int ix = fx >> 16;
+                    int iy = fy >> 16;
+                    
+                    const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11;
+
+                    p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels +
+                                                                      SkClampMax(iy, srcMaxY) * srcRB)) +
+                                                                      SkClampMax(ix, srcMaxX);
+                    if ((unsigned)ix < srcMaxX)
+                        p01 += 1;
+                    p10 = p00;
+                    p11 = p01;
+                    if ((unsigned)iy < srcMaxY)
+                    {
+                        p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+                        p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+                    }
+
+                    SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+                    uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+                                      SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+                    *dstC++ = SkCompact_rgb_16(c);
+                }
+            }
+        }
+        else    // linear case
+        {
+            SkFixed fx, fy, dx, dy;
+
+            // now init fx, fy, dx, dy
+            {
+                SkPoint srcPt;
+                this->getInverseMapPtProc()(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                                 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+                fx = SkScalarToFixed(srcPt.fX) - SK_FixedHalf;
+                fy = SkScalarToFixed(srcPt.fY) - SK_FixedHalf;
+
+                if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+                    (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+                else
+                {
+                    dx = SkScalarToFixed(inv.getScaleX());
+                    dy = SkScalarToFixed(inv.getSkewY());
+                }
+            }
+
+            do {
+                int ix = fx >> 16;
+                int iy = fy >> 16;
+                
+                const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11;
+
+                p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels +
+                                                                  SkClampMax(iy, srcMaxY) * srcRB)) +
+                                                                  SkClampMax(ix, srcMaxX);
+                if ((unsigned)ix < srcMaxX)
+                    p01 += 1;
+                p10 = p00;
+                p11 = p01;
+                if ((unsigned)iy < srcMaxY)
+                {
+                    p10 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p10 + srcRB);
+                    p11 = (const BILERP_BITMAP16_SHADER_TYPE*)((const char*)p11 + srcRB);
+                }
+
+                SkFilterProc proc = SkGetBilinearFilterProc(proc_table, fx, fy);
+                uint32_t c = proc(SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p00)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p01)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p10)),
+                                  SkExpand_rgb_16(BILERP_BITMAP16_SHADER_PIXEL(*p11)));
+                *dstC++ = SkCompact_rgb_16(c);
+
+                fx += dx;
+                fy += dy;
+            } while (--count != 0);
+        }
+        BILERP_BITMAP16_SHADER_POSTAMBLE(srcBitmap);
+    }
+};
+
+#undef BILERP_BITMAP16_SHADER_CLASS
+#undef BILERP_BITMAP16_SHADER_TYPE
+#undef BILERP_BITMAP16_SHADER_PREAMBLE
+#undef BILERP_BITMAP16_SHADER_PIXEL
+#undef BILERP_BITMAP16_SHADER_POSTAMBLE
diff --git a/src/core/SkBitmapShaderTemplate.h b/src/core/SkBitmapShaderTemplate.h
new file mode 100644
index 0000000..0174138
--- /dev/null
+++ b/src/core/SkBitmapShaderTemplate.h
@@ -0,0 +1,314 @@
+/* libs/graphics/sgl/SkBitmapShaderTemplate.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 NOFILTER_BITMAP_SHADER_PREAMBLE
+    #define NOFILTER_BITMAP_SHADER_PREAMBLE(bitmap, rb)
+#endif
+#ifndef NOFILTER_BITMAP_SHADER_POSTAMBLE
+    #define NOFILTER_BITMAP_SHADER_POSTAMBLE(bitmap)
+#endif
+#ifndef NOFILTER_BITMAP_SHADER_PREAMBLE16
+    #define NOFILTER_BITMAP_SHADER_PREAMBLE16(bitmap, rb)
+#endif
+#ifndef NOFILTER_BITMAP_SHADER_POSTAMBLE16
+    #define NOFILTER_BITMAP_SHADER_POSTAMBLE16(bitmap)
+#endif
+
+class NOFILTER_BITMAP_SHADER_CLASS : public HasSpan16_Sampler_BitmapShader {
+public:
+    NOFILTER_BITMAP_SHADER_CLASS(const SkBitmap& src)
+        : HasSpan16_Sampler_BitmapShader(src, false,
+                                         NOFILTER_BITMAP_SHADER_TILEMODE,
+                                         NOFILTER_BITMAP_SHADER_TILEMODE)
+    {
+    }
+    
+    virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix)
+    {
+        if (!this->INHERITED::setContext(device, paint, matrix))
+            return false;
+
+#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+        this->computeUnitInverse();
+#endif
+        return true;
+    }
+
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
+    {
+        SkASSERT(count > 0);
+
+#ifdef NOFILTER_BITMAP_SHADER_SPRITEPROC32
+        if ((this->getTotalInverse().getType() & ~SkMatrix::kTranslate_Mask) == 0)
+        {
+            NOFILTER_BITMAP_SHADER_SPRITEPROC32(this, x, y, dstC, count);
+            return;
+        }
+#endif
+
+        unsigned        scale = SkAlpha255To256(this->getPaintAlpha());
+#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+        const SkMatrix& inv = this->getUnitInverse();
+        SkMatrix::MapPtProc invProc = this->getUnitInverseProc();
+#else
+        const SkMatrix& inv = this->getTotalInverse();
+        SkMatrix::MapPtProc invProc = this->getInverseMapPtProc();
+#endif
+        const SkBitmap& srcBitmap = this->getSrcBitmap();
+        unsigned        srcMaxX = srcBitmap.width() - 1;
+        unsigned        srcMaxY = srcBitmap.height() - 1;
+        unsigned        srcRB = srcBitmap.rowBytes();
+        SkFixed         fx, fy, dx, dy;
+
+        const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels();
+        NOFILTER_BITMAP_SHADER_PREAMBLE(srcBitmap, srcRB);
+
+        if (this->getInverseClass() == kPerspective_MatrixClass)
+        {
+            SkPerspIter   iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                    SkIntToScalar(y) + SK_ScalarHalf, count);
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+
+/*  Do I need this?
+#ifndef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+            fx >>= level;
+            fy >>= level;
+#endif
+*/
+                if (256 == scale)
+                {
+                    while (--count >= 0)
+                    {
+                        fx = *srcXY++;
+                        fy = *srcXY++;
+                        unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                        unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                        *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB);
+                    }
+                }
+                else
+                {
+                    while (--count >= 0)
+                    {
+                        fx = *srcXY++;
+                        fy = *srcXY++;
+                        unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                        unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                        uint32_t c = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB);
+                        *dstC++ = SkAlphaMulQ(c, scale);
+                    }
+                }
+            }
+            return;
+        }
+
+        // now init fx, fy, dx, dy
+        {
+            SkPoint srcPt;
+            invProc(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+            fx = SkScalarToFixed(srcPt.fX);
+            fy = SkScalarToFixed(srcPt.fY);
+
+            if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+                (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+            else
+            {
+                dx = SkScalarToFixed(inv.getScaleX());
+                dy = SkScalarToFixed(inv.getSkewY());
+            }
+        }
+
+#if defined(SK_SUPPORT_MIPMAP) && !defined(NOFILTER_BITMAP_SHADER_USE_UNITINVERSE)
+        {   int level = this->getMipLevel() >> 16;
+            fx >>= level;
+            fy >>= level;
+            dx >>= level;
+            dy >>= level;
+        }
+#endif
+
+        if (dy == 0)
+        {
+            int y_index = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+//          SkDEBUGF(("fy = %g, srcMaxY = %d, y_index = %d\n", SkFixedToFloat(fy), srcMaxY, y_index));
+            srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)((const char*)srcPixels + y_index * srcRB);
+            if (scale == 256)
+                while (--count >= 0)
+                {
+                    unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                    fx += dx;
+                    *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_X(srcPixels, x);
+                }
+            else
+                while (--count >= 0)
+                {
+                    unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                    SkPMColor c = NOFILTER_BITMAP_SHADER_SAMPLE_X(srcPixels, x);
+                    fx += dx;
+                    *dstC++ = SkAlphaMulQ(c, scale);
+                }
+        }
+        else    // dy != 0
+        {
+            if (scale == 256)
+                while (--count >= 0)
+                {
+                    unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                    unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                    fx += dx;
+                    fy += dy;
+                    *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB);
+                }
+            else
+                while (--count >= 0)
+                {
+                    unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                    unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                    SkPMColor c = NOFILTER_BITMAP_SHADER_SAMPLE_XY(srcPixels, x, y, srcRB);
+                    fx += dx;
+                    fy += dy;
+                    *dstC++ = SkAlphaMulQ(c, scale);
+                }
+        }
+
+        NOFILTER_BITMAP_SHADER_POSTAMBLE(srcBitmap);
+    }
+
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
+    {
+        SkASSERT(count > 0);
+        SkASSERT(this->getFlags() & SkShader::kHasSpan16_Flag);
+
+#ifdef NOFILTER_BITMAP_SHADER_SPRITEPROC16
+        if ((this->getTotalInverse().getType() & ~SkMatrix::kTranslate_Mask) == 0)
+        {
+            NOFILTER_BITMAP_SHADER_SPRITEPROC16(this, x, y, dstC, count);
+            return;
+        }
+#endif
+
+#ifdef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+        const SkMatrix& inv = this->getUnitInverse();
+        SkMatrix::MapPtProc invProc = this->getUnitInverseProc();
+#else
+        const SkMatrix& inv = this->getTotalInverse();
+        SkMatrix::MapPtProc invProc = this->getInverseMapPtProc();
+#endif
+        const SkBitmap& srcBitmap = this->getSrcBitmap();
+        unsigned        srcMaxX = srcBitmap.width() - 1;
+        unsigned        srcMaxY = srcBitmap.height() - 1;
+        unsigned        srcRB = srcBitmap.rowBytes();
+        SkFixed         fx, fy, dx, dy;
+
+        const NOFILTER_BITMAP_SHADER_TYPE* srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)srcBitmap.getPixels();
+        NOFILTER_BITMAP_SHADER_PREAMBLE16(srcBitmap, srcRB);
+
+        if (this->getInverseClass() == kPerspective_MatrixClass)
+        {
+            SkPerspIter   iter(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                                    SkIntToScalar(y) + SK_ScalarHalf, count);
+            while ((count = iter.next()) != 0)
+            {
+                const SkFixed* srcXY = iter.getXY();
+                
+                while (--count >= 0)
+                {
+                    fx = *srcXY++;
+                    fy = *srcXY++;
+                    unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                    unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                    *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY16(srcPixels, x, y, srcRB);
+                }
+            }
+            return;
+        }
+
+        // now init fx, fy, dx, dy
+        {
+            SkPoint srcPt;
+            invProc(inv, SkIntToScalar(x) + SK_ScalarHalf,
+                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+            fx = SkScalarToFixed(srcPt.fX);
+            fy = SkScalarToFixed(srcPt.fY);
+
+            if (this->getInverseClass() == kFixedStepInX_MatrixClass)
+                (void)inv.fixedStepInX(SkIntToScalar(y), &dx, &dy);
+            else
+            {
+                dx = SkScalarToFixed(inv.getScaleX());
+                dy = SkScalarToFixed(inv.getSkewY());
+            }
+        }
+
+#if defined(SK_SUPPORT_MIPMAP) && !defined(NOFILTER_BITMAP_SHADER_USE_UNITINVERSE)
+        {   int level = this->getMipLevel() >> 16;
+            fx >>= level;
+            fy >>= level;
+            dx >>= level;
+            dy >>= level;
+        }
+#endif
+
+        if (dy == 0)
+        {
+            srcPixels = (const NOFILTER_BITMAP_SHADER_TYPE*)((const char*)srcPixels + NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY) * srcRB);
+            do {
+                unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                fx += dx;
+                *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_X16(srcPixels, x);
+            } while (--count != 0);
+        }
+        else    // dy != 0
+        {
+            do {
+                unsigned x = NOFILTER_BITMAP_SHADER_TILEPROC(fx, srcMaxX);
+                unsigned y = NOFILTER_BITMAP_SHADER_TILEPROC(fy, srcMaxY);
+                fx += dx;
+                fy += dy;
+                *dstC++ = NOFILTER_BITMAP_SHADER_SAMPLE_XY16(srcPixels, x, y, srcRB);
+            } while (--count != 0);
+        }
+
+        NOFILTER_BITMAP_SHADER_POSTAMBLE16(srcBitmap);
+    }
+private:
+    typedef HasSpan16_Sampler_BitmapShader INHERITED;
+};
+
+#undef NOFILTER_BITMAP_SHADER_CLASS
+#undef NOFILTER_BITMAP_SHADER_TYPE
+#undef NOFILTER_BITMAP_SHADER_PREAMBLE
+#undef NOFILTER_BITMAP_SHADER_POSTAMBLE
+#undef NOFILTER_BITMAP_SHADER_SAMPLE_X      //(x)
+#undef NOFILTER_BITMAP_SHADER_SAMPLE_XY     //(x, y, rowBytes)
+#undef NOFILTER_BITMAP_SHADER_TILEMODE
+#undef NOFILTER_BITMAP_SHADER_TILEPROC
+
+#undef NOFILTER_BITMAP_SHADER_PREAMBLE16
+#undef NOFILTER_BITMAP_SHADER_POSTAMBLE16
+#undef NOFILTER_BITMAP_SHADER_SAMPLE_X16        //(x)
+#undef NOFILTER_BITMAP_SHADER_SAMPLE_XY16       //(x, y, rowBytes)
+
+#undef NOFILTER_BITMAP_SHADER_USE_UNITINVERSE
+#undef NOFILTER_BITMAP_SHADER_SPRITEPROC16
+#undef NOFILTER_BITMAP_SHADER_SPRITEPROC32
diff --git a/src/core/SkBitmap_scroll.cpp b/src/core/SkBitmap_scroll.cpp
new file mode 100644
index 0000000..f9f197d
--- /dev/null
+++ b/src/core/SkBitmap_scroll.cpp
@@ -0,0 +1,107 @@
+#include "SkBitmap.h"
+#include "SkRegion.h"
+
+bool SkBitmap::scrollRect(const SkIRect* subset, int dx, int dy,
+                          SkRegion* inval) const
+{
+    if (NULL != subset) {
+        SkBitmap tmp;
+
+        return  this->extractSubset(&tmp, *subset) &&
+                // now call again with no rectangle
+                tmp.scrollRect(NULL, dx, dy, inval);
+    }
+
+    int shift;
+
+    switch (this->config()) {
+    case kIndex8_Config:
+    case kA8_Config:
+        shift = 0;
+        break;
+    case kARGB_4444_Config:
+    case kRGB_565_Config:
+        shift = 1;
+        break;
+    case kARGB_8888_Config:
+        shift = 2;
+        break;
+    default:
+        // can't scroll this config
+        return false;
+    }
+
+    int width = this->width();
+    int height = this->height();    
+    
+    // check if there's nothing to do
+    if ((dx | dy) == 0 || width <= 0 || height <= 0) {
+        if (NULL != inval) {
+            inval->setEmpty();
+        }
+        return true;
+    }
+
+    // compute the inval region now, before we see if there are any pixels
+    if (NULL != inval) {
+        SkIRect r;
+        
+        r.set(0, 0, width, height);
+        // initial the region with the entire bounds
+        inval->setRect(r);
+        // do the "scroll"
+        r.offset(dx, dy);
+        
+        // check if we scrolled completely away
+        if (!SkIRect::Intersects(r, inval->getBounds())) {
+            // inval has already been updated...
+            return true;
+        }
+        
+        // compute the dirty area
+        inval->op(r, SkRegion::kDifference_Op);
+    }
+    
+    SkAutoLockPixels    alp(*this);
+    // if we have no pixels, just return (inval is already updated)
+    // don't call readyToDraw(), since we don't require a colortable per se
+    if (this->getPixels() == NULL) {
+        return true;
+    }
+
+    // if we get this far, then we need to shift the pixels
+    
+    char*       dst = (char*)this->getPixels();
+    const char* src = dst;
+    int         rowBytes = this->rowBytes();    // need rowBytes to be signed
+
+    if (dy <= 0) {
+        src -= dy * rowBytes;
+        height += dy;
+    } else {
+        dst += dy * rowBytes;
+        height -= dy;
+        // now jump src/dst to the last scanline
+        src += (height - 1) * rowBytes;
+        dst += (height - 1) * rowBytes;
+        // now invert rowbytes so we copy backwards in the loop
+        rowBytes = -rowBytes;
+    }
+    
+    if (dx <= 0) {
+        src -= dx << shift;
+        width += dx;
+    } else {
+        dst += dx << shift;
+        width -= dx;
+    }
+
+    width <<= shift;    // now width is the number of bytes to move per line
+    while (--height >= 0) {
+        memmove(dst, src, width);
+        dst += rowBytes;
+        src += rowBytes;
+    }
+    return true;
+}
+
diff --git a/src/core/SkBlitBWMaskTemplate.h b/src/core/SkBlitBWMaskTemplate.h
new file mode 100644
index 0000000..e433d36
--- /dev/null
+++ b/src/core/SkBlitBWMaskTemplate.h
@@ -0,0 +1,137 @@
+/* libs/graphics/sgl/SkBlitBWMaskTemplate.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.
+*/
+
+#include "SkBitmap.h"
+#include "SkMask.h"
+
+#ifndef ClearLow3Bits_DEFINED
+#define ClearLow3Bits_DEFINED
+    #define ClearLow3Bits(x)    ((unsigned)(x) >> 3 << 3)
+#endif
+
+/*
+    SK_BLITBWMASK_NAME          name of function(const SkBitmap& bitmap, const SkMask& mask, const SkIRect& clip, SK_BLITBWMASK_ARGS)
+    SK_BLITBWMASK_ARGS          list of additional arguments to SK_BLITBWMASK_NAME, beginning with a comma
+    SK_BLITBWMASK_BLIT8         name of function(U8CPU byteMask, SK_BLITBWMASK_DEVTYPE* dst, int x, int y)
+    SK_BLITBWMASK_GETADDR       either getAddr32 or getAddr16 or getAddr8
+    SK_BLITBWMASK_DEVTYPE       either U32 or U16 or U8
+*/
+
+static void SK_BLITBWMASK_NAME(const SkBitmap& bitmap, const SkMask& srcMask, const SkIRect& clip SK_BLITBWMASK_ARGS)
+{
+    SkASSERT(clip.fRight <= srcMask.fBounds.fRight);
+
+    int cx = clip.fLeft;
+    int cy = clip.fTop;
+    int maskLeft = srcMask.fBounds.fLeft;
+    unsigned mask_rowBytes = srcMask.fRowBytes;
+    unsigned bitmap_rowBytes = bitmap.rowBytes();
+    unsigned height = clip.height();
+
+    SkASSERT(mask_rowBytes != 0);
+    SkASSERT(bitmap_rowBytes != 0);
+    SkASSERT(height != 0);
+
+    const uint8_t* bits = srcMask.getAddr1(cx, cy);
+    SK_BLITBWMASK_DEVTYPE* device = bitmap.SK_BLITBWMASK_GETADDR(cx, cy);
+
+    if (cx == maskLeft && clip.fRight == srcMask.fBounds.fRight)
+    {
+        do {
+            SK_BLITBWMASK_DEVTYPE* dst = device;
+            unsigned rb = mask_rowBytes;
+            do {
+                U8CPU mask = *bits++;
+                SK_BLITBWMASK_BLIT8(mask, dst);
+                dst += 8;
+            } while (--rb != 0);
+            device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes);
+        } while (--height != 0);
+    }
+    else
+    {
+        int left_edge = cx - maskLeft;
+        SkASSERT(left_edge >= 0);
+        int rite_edge = clip.fRight - maskLeft;
+        SkASSERT(rite_edge > left_edge);
+
+        int left_mask = 0xFF >> (left_edge & 7);
+        int rite_mask = 0xFF << (8 - (rite_edge & 7));
+        int full_runs = (rite_edge >> 3) - ((left_edge + 7) >> 3);
+
+        // check for empty right mask, so we don't read off the end (or go slower than we need to)
+        if (rite_mask == 0)
+        {
+            SkASSERT(full_runs >= 0);
+            full_runs -= 1;
+            rite_mask = 0xFF;
+        }
+        if (left_mask == 0xFF)
+            full_runs -= 1;
+
+        // back up manually so we can keep in sync with our byte-aligned src
+        // and not trigger an assert from the getAddr## function
+        device -= left_edge & 7;
+        // have cx reflect our actual starting x-coord
+        cx -= left_edge & 7;
+
+        if (full_runs < 0)
+        {
+            left_mask &= rite_mask;
+            SkASSERT(left_mask != 0);
+            do {
+                U8CPU mask = *bits & left_mask;
+                SK_BLITBWMASK_BLIT8(mask, device);
+                bits += mask_rowBytes;
+                device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes);
+            } while (--height != 0);
+        }
+        else
+        {
+            do {
+                int runs = full_runs;
+                SK_BLITBWMASK_DEVTYPE* dst = device;
+                const uint8_t* b = bits;
+                U8CPU   mask;
+
+                mask = *b++ & left_mask;
+                SK_BLITBWMASK_BLIT8(mask, dst);
+                dst += 8;
+
+                while (--runs >= 0)
+                {
+                    mask = *b++;
+                    SK_BLITBWMASK_BLIT8(mask, dst);
+                    dst += 8;
+                }
+
+                mask = *b & rite_mask;
+                SK_BLITBWMASK_BLIT8(mask, dst);
+
+                bits += mask_rowBytes;
+                device = (SK_BLITBWMASK_DEVTYPE*)((char*)device + bitmap_rowBytes);
+            } while (--height != 0);
+        }
+    }
+}   
+
+#undef SK_BLITBWMASK_NAME
+#undef SK_BLITBWMASK_ARGS
+#undef SK_BLITBWMASK_BLIT8
+#undef SK_BLITBWMASK_GETADDR
+#undef SK_BLITBWMASK_DEVTYPE
+#undef SK_BLITBWMASK_DOROWSETUP
diff --git a/src/core/SkBlitRow.h b/src/core/SkBlitRow.h
new file mode 100644
index 0000000..bb6a29b
--- /dev/null
+++ b/src/core/SkBlitRow.h
@@ -0,0 +1,22 @@
+#ifndef SkBlitRow_DEFINED
+#define SkBlitRow_DEFINED
+
+#include "SkBitmap.h"
+#include "SkColor.h"
+
+class SkBlitRow {
+public:
+    enum {
+        kGlobalAlpha_Flag   = 0x01,
+        kSrcPixelAlpha_Flag = 0x02,
+        kDither_Flag        = 0x04
+    };
+
+    typedef void (*Proc)(uint16_t* SK_RESTRICT dst,
+                         const SkPMColor* SK_RESTRICT src,
+                         int count, U8CPU alpha, int x, int y);
+
+    static Proc Factory(unsigned flags, SkBitmap::Config);
+};
+
+#endif
diff --git a/src/core/SkBlitRow_D16.cpp b/src/core/SkBlitRow_D16.cpp
new file mode 100644
index 0000000..f40df36
--- /dev/null
+++ b/src/core/SkBlitRow_D16.cpp
@@ -0,0 +1,258 @@
+#include "SkBlitRow.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void S32_D565_Opaque(uint16_t* SK_RESTRICT dst,
+                            const SkPMColor* SK_RESTRICT src, int count,
+                            U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 == alpha);
+
+    if (count > 0) {
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+            *dst++ = SkPixel32ToPixel16_ToU16(c);
+        } while (--count != 0);
+    }
+}
+
+static void S32_D565_Blend(uint16_t* SK_RESTRICT dst,
+                             const SkPMColor* SK_RESTRICT src, int count,
+                             U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 > alpha);
+
+    if (count > 0) {
+        int scale = SkAlpha255To256(alpha);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+            uint16_t d = *dst;
+            *dst++ = SkPackRGB16(
+                    SkAlphaBlend(SkPacked32ToR16(c), SkGetPackedR16(d), scale),
+                    SkAlphaBlend(SkPacked32ToG16(c), SkGetPackedG16(d), scale),
+                    SkAlphaBlend(SkPacked32ToB16(c), SkGetPackedB16(d), scale));
+        } while (--count != 0);
+    }
+}
+
+static void S32A_D565_Opaque(uint16_t* SK_RESTRICT dst,
+                               const SkPMColor* SK_RESTRICT src, int count,
+                               U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 == alpha);
+
+    if (count > 0) {
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+//            if (__builtin_expect(c!=0, 1))
+            if (c) {
+                *dst = SkSrcOver32To16(c, *dst);
+            }
+            dst += 1;
+        } while (--count != 0);
+    }
+}
+
+static void S32A_D565_Blend(uint16_t* SK_RESTRICT dst,
+                              const SkPMColor* SK_RESTRICT src, int count,
+                               U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 > alpha);
+    
+    if (count > 0) {
+        int src_scale = SkAlpha255To256(alpha);
+        do {
+            SkPMColor sc = *src++;
+            SkPMColorAssert(sc);
+            if (sc)
+            {
+                uint16_t dc = *dst;
+                unsigned sa = SkGetPackedA32(sc);
+                unsigned dr, dg, db;
+                
+                if (sa == 255) {
+                    dr = SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), src_scale);
+                    dg = SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), src_scale);
+                    db = SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), src_scale);
+                } else {
+                    unsigned dst_scale = 255 - SkAlphaMul(sa, src_scale);
+                    dr = (SkPacked32ToR16(sc) * src_scale + SkGetPackedR16(dc) * dst_scale) >> 8;
+                    dg = (SkPacked32ToG16(sc) * src_scale + SkGetPackedG16(dc) * dst_scale) >> 8;
+                    db = (SkPacked32ToB16(sc) * src_scale + SkGetPackedB16(dc) * dst_scale) >> 8;
+                }
+                *dst = SkPackRGB16(dr, dg, db);
+            }
+            dst += 1;
+        } while (--count != 0);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+                               
+static void S32_D565_Opaque_Dither(uint16_t* SK_RESTRICT dst,
+                                     const SkPMColor* SK_RESTRICT src,
+                                     int count, U8CPU alpha, int x, int y) {
+    SkASSERT(255 == alpha);
+    
+    if (count > 0) {
+        DITHER_565_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+
+            unsigned dither = DITHER_VALUE(x);
+            *dst++ = SkDitherRGB32To565(c, dither);
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+static void S32_D565_Blend_Dither(uint16_t* SK_RESTRICT dst,
+                                    const SkPMColor* SK_RESTRICT src,
+                                    int count, U8CPU alpha, int x, int y) {
+    SkASSERT(255 > alpha);
+    
+    if (count > 0) {
+        int scale = SkAlpha255To256(alpha);
+        DITHER_565_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+
+            int dither = DITHER_VALUE(x);            
+            int sr = SkGetPackedR32(c);
+            int sg = SkGetPackedG32(c);
+            int sb = SkGetPackedB32(c);
+            sr = SkDITHER_R32To565(sr, dither);
+            sg = SkDITHER_G32To565(sg, dither);
+            sb = SkDITHER_B32To565(sb, dither);
+
+            uint16_t d = *dst;
+            *dst++ = SkPackRGB16(SkAlphaBlend(sr, SkGetPackedR16(d), scale),
+                                 SkAlphaBlend(sg, SkGetPackedG16(d), scale),
+                                 SkAlphaBlend(sb, SkGetPackedB16(d), scale));
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+static void S32A_D565_Opaque_Dither(uint16_t* SK_RESTRICT dst,
+                                      const SkPMColor* SK_RESTRICT src,
+                                      int count, U8CPU alpha, int x, int y) {
+    SkASSERT(255 == alpha);
+    
+    if (count > 0) {
+        DITHER_565_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            if (c) {
+                unsigned a = SkGetPackedA32(c);
+                
+                int d = SkAlphaMul(DITHER_VALUE(x), SkAlpha255To256(a));
+                
+                unsigned sr = SkGetPackedR32(c);
+                unsigned sg = SkGetPackedG32(c);
+                unsigned sb = SkGetPackedB32(c);
+                sr = SkDITHER_R32_FOR_565(sr, d);
+                sg = SkDITHER_G32_FOR_565(sg, d);
+                sb = SkDITHER_B32_FOR_565(sb, d);
+                
+                uint32_t src_expanded = (sg << 24) | (sr << 13) | (sb << 2);
+                uint32_t dst_expanded = SkExpand_rgb_16(*dst);
+                dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3);
+                // now src and dst expanded are in g:11 r:10 x:1 b:10
+                *dst = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5);
+            }
+            dst += 1;
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+static void S32A_D565_Blend_Dither(uint16_t* SK_RESTRICT dst,
+                                     const SkPMColor* SK_RESTRICT src,
+                                     int count, U8CPU alpha, int x, int y) {
+    SkASSERT(255 > alpha);
+    
+    if (count > 0) {
+        int src_scale = SkAlpha255To256(alpha);
+        DITHER_565_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            if (c)
+            {
+                unsigned d = *dst;
+                int sa = SkGetPackedA32(c);
+                int dst_scale = SkAlpha255To256(255 - SkAlphaMul(sa, src_scale));
+                int dither = DITHER_VALUE(x);
+                
+                int sr = SkGetPackedR32(c);
+                int sg = SkGetPackedG32(c);
+                int sb = SkGetPackedB32(c);
+                sr = SkDITHER_R32To565(sr, dither);
+                sg = SkDITHER_G32To565(sg, dither);
+                sb = SkDITHER_B32To565(sb, dither);
+                
+                int dr = (sr * src_scale + SkGetPackedR16(d) * dst_scale) >> 8;
+                int dg = (sg * src_scale + SkGetPackedG16(d) * dst_scale) >> 8;
+                int db = (sb * src_scale + SkGetPackedB16(d) * dst_scale) >> 8;
+                
+                *dst = SkPackRGB16(dr, dg, db);
+            }
+            dst += 1;
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef USE_T32CB16BLEND_ASM
+    extern "C" void scanline_t32cb16blend_arm(uint16_t*, uint32_t*, size_t);
+#endif
+
+static const SkBlitRow::Proc gProcs16[] = {
+    // no dither
+    S32_D565_Opaque,
+    S32_D565_Blend,
+
+#ifdef USE_T32CB16BLEND_ASM
+    (SkBlitRow::Proc)scanline_t32cb16blend_arm,
+#else
+    S32A_D565_Opaque,
+#endif
+
+    S32A_D565_Blend,
+    
+    // dither
+    S32_D565_Opaque_Dither,
+    S32_D565_Blend_Dither,
+    
+    S32A_D565_Opaque_Dither,
+    S32A_D565_Blend_Dither
+};
+
+extern SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags);
+    
+SkBlitRow::Proc SkBlitRow::Factory(unsigned flags, SkBitmap::Config config) {
+    SkASSERT(flags < SK_ARRAY_COUNT(gProcs16));
+    
+    switch (config) {
+        case SkBitmap::kRGB_565_Config:
+            return gProcs16[flags];
+        case SkBitmap::kARGB_4444_Config:
+            return SkBlitRow_Factory_4444(flags);
+        default:
+            break;
+    }
+    return NULL;
+}
+    
diff --git a/src/core/SkBlitRow_D4444.cpp b/src/core/SkBlitRow_D4444.cpp
new file mode 100644
index 0000000..e60c721
--- /dev/null
+++ b/src/core/SkBlitRow_D4444.cpp
@@ -0,0 +1,214 @@
+#include "SkBlitRow.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void S32_D4444_Opaque(uint16_t* SK_RESTRICT dst,
+                             const SkPMColor* SK_RESTRICT src, int count,
+                             U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 == alpha);
+
+    if (count > 0) {
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+            *dst++ = SkPixel32ToPixel4444(c);
+        } while (--count != 0);
+    }
+}
+
+static void S32_D4444_Blend(uint16_t* SK_RESTRICT dst,
+                            const SkPMColor* SK_RESTRICT src, int count,
+                            U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 > alpha);
+
+    if (count > 0) {
+        unsigned scale16 = SkAlpha255To256(alpha) >> 4;
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+
+            uint32_t src_expand = SkExpand32_4444(c);
+            uint32_t dst_expand = SkExpand_4444(*dst);
+            dst_expand += (src_expand - dst_expand) * scale16 >> 4;
+            *dst++ = SkCompact_4444(dst_expand);
+        } while (--count != 0);
+    }
+}
+
+static void S32A_D4444_Opaque(uint16_t* SK_RESTRICT dst,
+                              const SkPMColor* SK_RESTRICT src, int count,
+                              U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 == alpha);
+
+    if (count > 0) {
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+//            if (__builtin_expect(c!=0, 1))
+            if (c)
+            {
+                unsigned scale16 = SkAlpha255To256(255 - SkGetPackedA32(c)) >> 4;
+                uint32_t src_expand = SkExpand_8888(c);
+                uint32_t dst_expand = SkExpand_4444(*dst) * scale16;
+                *dst = SkCompact_4444((src_expand + dst_expand) >> 4);
+            }
+            dst += 1;
+        } while (--count != 0);
+    }
+}
+
+static void S32A_D4444_Blend(uint16_t* SK_RESTRICT dst,
+                             const SkPMColor* SK_RESTRICT src, int count,
+                             U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 > alpha);
+    
+    if (count > 0) {
+        int src_scale = SkAlpha255To256(alpha) >> 4;
+        do {
+            SkPMColor sc = *src++;
+            SkPMColorAssert(sc);
+
+            if (sc) {
+                unsigned dst_scale = 16 - (SkGetPackedA32(sc) * src_scale >> 8);
+                uint32_t src_expand = SkExpand32_4444(sc) * src_scale;
+                uint32_t dst_expand = SkExpand_4444(*dst) * dst_scale;
+                *dst = SkCompact_4444((src_expand + dst_expand) >> 4);
+            }
+            dst += 1;
+        } while (--count != 0);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+                               
+static void S32_D4444_Opaque_Dither(uint16_t* SK_RESTRICT dst,
+                                    const SkPMColor* SK_RESTRICT src,
+                                    int count, U8CPU alpha, int x, int y) {
+    SkASSERT(255 == alpha);
+    
+    if (count > 0) {
+        DITHER_4444_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+
+            unsigned dither = DITHER_VALUE(x);
+            *dst++ = SkDitherARGB32To4444(c, dither);
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+static void S32_D4444_Blend_Dither(uint16_t* SK_RESTRICT dst,
+                                   const SkPMColor* SK_RESTRICT src,
+                                   int count, U8CPU alpha, int x, int y) {
+    SkASSERT(255 > alpha);
+    
+    if (count > 0) {
+        int scale16 = SkAlpha255To256(alpha) >> 4;
+        DITHER_4444_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+
+            uint32_t src_expand = SkExpand32_4444(c) * scale16;
+            uint32_t dst_expand = SkExpand_4444(*dst) * (16 - scale16);
+            
+            c = SkCompact_8888(src_expand + dst_expand); // convert back to SkPMColor
+            *dst++ = SkDitherARGB32To4444(c, DITHER_VALUE(x));
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+static void S32A_D4444_Opaque_Dither(uint16_t* SK_RESTRICT dst,
+                                     const SkPMColor* SK_RESTRICT src,
+                                     int count, U8CPU alpha, int x, int y) {
+    SkASSERT(255 == alpha);
+    
+    if (count > 0) {
+        DITHER_4444_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            if (c) {
+                unsigned a = SkGetPackedA32(c);                
+                int d = SkAlphaMul(DITHER_VALUE(x), SkAlpha255To256(a));
+                
+                unsigned scale16 = SkAlpha255To256(255 - a) >> 4;
+                uint32_t src_expand = SkExpand_8888(c);
+                uint32_t dst_expand = SkExpand_4444(*dst) * scale16;
+                // convert back to SkPMColor
+                c = SkCompact_8888(src_expand + dst_expand);
+                *dst = SkDitherARGB32To4444(c, d);
+            }
+            dst += 1;
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+// need DitherExpand888To4444(expand, dither)
+
+static void S32A_D4444_Blend_Dither(uint16_t* SK_RESTRICT dst,
+                                    const SkPMColor* SK_RESTRICT src,
+                                    int count, U8CPU alpha, int x, int y) {
+    SkASSERT(255 > alpha);
+    
+    if (count > 0) {
+        int src_scale = SkAlpha255To256(alpha) >> 4;
+        DITHER_4444_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            if (c) {
+                unsigned a = SkAlpha255To256(SkGetPackedA32(c));
+                int d = SkAlphaMul(DITHER_VALUE(x), a);
+                
+                unsigned dst_scale = 16 - SkAlphaMul(src_scale, a);
+                uint32_t src_expand = SkExpand32_4444(c) * src_scale;
+                uint32_t dst_expand = SkExpand_4444(*dst) * dst_scale;
+                // convert back to SkPMColor
+                c = SkCompact_8888(src_expand + dst_expand);
+                *dst = SkDitherARGB32To4444(c, d);
+            }
+            dst += 1;
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkBlitRow::Proc gProcs4444[] = {
+    // no dither
+    S32_D4444_Opaque,
+    S32_D4444_Blend,
+    
+    S32A_D4444_Opaque,
+    S32A_D4444_Blend,
+    
+    // dither
+    S32_D4444_Opaque_Dither,
+    S32_D4444_Blend_Dither,
+    
+    S32A_D4444_Opaque_Dither,
+    S32A_D4444_Blend_Dither
+};
+    
+SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags);
+SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags)
+{
+    SkASSERT(flags < SK_ARRAY_COUNT(gProcs4444));
+    
+    return gProcs4444[flags];
+}
+    
+    
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
new file mode 100644
index 0000000..9208429
--- /dev/null
+++ b/src/core/SkBlitter.cpp
@@ -0,0 +1,923 @@
+/* libs/graphics/sgl/SkBlitter.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 "SkBlitter.h"
+#include "SkAntiRun.h"
+#include "SkColor.h"
+#include "SkColorFilter.h"
+#include "SkMask.h"
+#include "SkMaskFilter.h"
+#include "SkTemplatesPriv.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+SkBlitter::~SkBlitter()
+{
+}
+
+const SkBitmap* SkBlitter::justAnOpaqueColor(uint32_t* value)
+{
+    return NULL;
+}
+
+void SkBlitter::blitH(int x, int y, int width)
+{
+    SkASSERT(!"unimplemented");
+}
+
+void SkBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[])
+{
+    SkASSERT(!"unimplemented");
+}
+
+void SkBlitter::blitV(int x, int y, int height, SkAlpha alpha)
+{
+    if (alpha == 255)
+        this->blitRect(x, y, 1, height);
+    else
+    {
+        int16_t runs[2];
+        runs[0] = 1;
+        runs[1] = 0;
+
+        while (--height >= 0)
+            this->blitAntiH(x, y++, &alpha, runs);
+    }
+}
+
+void SkBlitter::blitRect(int x, int y, int width, int height)
+{
+    while (--height >= 0)
+        this->blitH(x, y++, width);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static inline void bits_to_runs(SkBlitter* blitter, int x, int y, const uint8_t bits[],
+                                U8CPU left_mask, int rowBytes, U8CPU right_mask)
+{
+    int inFill = 0;
+    int pos = 0;
+
+    while (--rowBytes >= 0)
+    {
+        unsigned b = *bits++ & left_mask;
+        if (rowBytes == 0)
+            b &= right_mask;
+
+        for (unsigned test = 0x80; test != 0; test >>= 1)
+        {
+            if (b & test)
+            {
+                if (!inFill)
+                {
+                    pos = x;
+                    inFill = true;
+                }
+            }
+            else
+            {
+                if (inFill)
+                {
+                    blitter->blitH(pos, y, x - pos);
+                    inFill = false;
+                }
+            }
+            x += 1;
+        }
+        left_mask = 0xFF;
+    }
+
+    // final cleanup
+    if (inFill)
+        blitter->blitH(pos, y, x - pos);
+}
+
+void SkBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
+{
+    SkASSERT(mask.fBounds.contains(clip));
+
+    if (mask.fFormat == SkMask::kBW_Format)
+    {
+        int cx = clip.fLeft;
+        int cy = clip.fTop;
+        int maskLeft = mask.fBounds.fLeft;
+        int mask_rowBytes = mask.fRowBytes;
+        int height = clip.height();
+
+        const uint8_t* bits = mask.getAddr1(cx, cy);
+
+        if (cx == maskLeft && clip.fRight == mask.fBounds.fRight)
+        {
+            while (--height >= 0)
+            {
+                bits_to_runs(this, cx, cy, bits, 0xFF, mask_rowBytes, 0xFF);
+                bits += mask_rowBytes;
+                cy += 1;
+            }
+        }
+        else
+        {
+            int left_edge = cx - maskLeft;
+            SkASSERT(left_edge >= 0);
+            int rite_edge = clip.fRight - maskLeft;
+            SkASSERT(rite_edge > left_edge);
+
+            int left_mask = 0xFF >> (left_edge & 7);
+            int rite_mask = 0xFF << (8 - (rite_edge & 7));
+            int full_runs = (rite_edge >> 3) - ((left_edge + 7) >> 3);
+
+            // check for empty right mask, so we don't read off the end (or go slower than we need to)
+            if (rite_mask == 0)
+            {
+                SkASSERT(full_runs >= 0);
+                full_runs -= 1;
+                rite_mask = 0xFF;
+            }
+            if (left_mask == 0xFF)
+                full_runs -= 1;
+
+            // back up manually so we can keep in sync with our byte-aligned src
+            // have cx reflect our actual starting x-coord
+            cx -= left_edge & 7;
+
+            if (full_runs < 0)
+            {
+                SkASSERT((left_mask & rite_mask) != 0);
+                while (--height >= 0)
+                {
+                    bits_to_runs(this, cx, cy, bits, left_mask, 1, rite_mask);
+                    bits += mask_rowBytes;
+                    cy += 1;
+                }
+            }
+            else
+            {
+                while (--height >= 0)
+                {
+                    bits_to_runs(this, cx, cy, bits, left_mask, full_runs + 2, rite_mask);
+                    bits += mask_rowBytes;
+                    cy += 1;
+                }
+            }
+        }
+    }
+    else
+    {
+        int                         width = clip.width();
+        SkAutoSTMalloc<64, int16_t> runStorage(width + 1);
+        int16_t*                    runs = runStorage.get();
+        const uint8_t*              aa = mask.getAddr(clip.fLeft, clip.fTop);
+
+        sk_memset16((uint16_t*)runs, 1, width);
+        runs[width] = 0;
+
+        int height = clip.height();
+        int y = clip.fTop;
+        while (--height >= 0)
+        {
+            this->blitAntiH(clip.fLeft, y, aa, runs);
+            aa += mask.fRowBytes;
+            y += 1;
+        }
+    }
+}
+
+/////////////////////// these guys are not virtual, just a helpers
+
+void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) {
+    if (clip.quickReject(mask.fBounds)) {
+        return;
+    }
+    
+    SkRegion::Cliperator clipper(clip, mask.fBounds);
+    
+    while (!clipper.done()) {
+        const SkIRect& cr = clipper.rect();
+        this->blitMask(mask, cr);
+        clipper.next();
+    }
+}
+
+void SkBlitter::blitRectRegion(const SkIRect& rect, const SkRegion& clip) {
+    SkRegion::Cliperator clipper(clip, rect);
+    
+    while (!clipper.done()) {
+        const SkIRect& cr = clipper.rect();
+        this->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
+        clipper.next();
+    }
+}
+
+void SkBlitter::blitRegion(const SkRegion& clip) {
+    SkRegion::Iterator iter(clip);
+    
+    while (!iter.done()) {
+        const SkIRect& cr = iter.rect();
+        this->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
+        iter.next();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+void SkNullBlitter::blitH(int x, int y, int width)
+{
+}
+
+void SkNullBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[])
+{
+}
+
+void SkNullBlitter::blitV(int x, int y, int height, SkAlpha alpha)
+{
+}
+
+void SkNullBlitter::blitRect(int x, int y, int width, int height)
+{
+}
+
+void SkNullBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
+{
+}
+
+const SkBitmap* SkNullBlitter::justAnOpaqueColor(uint32_t* value)
+{
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+static int compute_anti_width(const int16_t runs[])
+{
+    int width = 0;
+    
+    for (;;)
+    {
+        int count = runs[0];
+        
+        SkASSERT(count >= 0);
+        if (count == 0)
+            break;
+        width += count;
+        runs += count;
+        
+        SkASSERT(width < 20000);
+    }
+    return width;
+}
+
+static inline bool y_in_rect(int y, const SkIRect& rect)
+{
+    return (unsigned)(y - rect.fTop) < (unsigned)rect.height();
+}
+
+static inline bool x_in_rect(int x, const SkIRect& rect)
+{
+    return (unsigned)(x - rect.fLeft) < (unsigned)rect.width();
+}
+
+void SkRectClipBlitter::blitH(int left, int y, int width)
+{
+    SkASSERT(width > 0);
+
+    if (!y_in_rect(y, fClipRect))
+        return;
+
+    int right = left + width;
+
+    if (left < fClipRect.fLeft)
+        left = fClipRect.fLeft;
+    if (right > fClipRect.fRight)
+        right = fClipRect.fRight;
+
+    width = right - left;
+    if (width > 0)
+        fBlitter->blitH(left, y, width);
+}
+
+void SkRectClipBlitter::blitAntiH(int left, int y, const SkAlpha aa[], const int16_t runs[])
+{
+    if (!y_in_rect(y, fClipRect) || left >= fClipRect.fRight)
+        return;
+
+    int x0 = left;
+    int x1 = left + compute_anti_width(runs);
+
+    if (x1 <= fClipRect.fLeft)
+        return;
+
+    SkASSERT(x0 < x1);
+    if (x0 < fClipRect.fLeft)
+    {
+        int dx = fClipRect.fLeft - x0;
+        SkAlphaRuns::BreakAt((int16_t*)runs, (uint8_t*)aa, dx);
+        runs += dx;
+        aa += dx;
+        x0 = fClipRect.fLeft;
+    }
+
+    SkASSERT(x0 < x1 && runs[x1 - x0] == 0);
+    if (x1 > fClipRect.fRight)
+    {
+        x1 = fClipRect.fRight;
+        SkAlphaRuns::BreakAt((int16_t*)runs, (uint8_t*)aa, x1 - x0);
+        ((int16_t*)runs)[x1 - x0] = 0;
+    }
+
+    SkASSERT(x0 < x1 && runs[x1 - x0] == 0);
+    SkASSERT(compute_anti_width(runs) == x1 - x0);
+
+    fBlitter->blitAntiH(x0, y, aa, runs);
+}
+
+void SkRectClipBlitter::blitV(int x, int y, int height, SkAlpha alpha)
+{
+    SkASSERT(height > 0);
+
+    if (!x_in_rect(x, fClipRect))
+        return;
+
+    int y0 = y;
+    int y1 = y + height;
+
+    if (y0 < fClipRect.fTop)
+        y0 = fClipRect.fTop;
+    if (y1 > fClipRect.fBottom)
+        y1 = fClipRect.fBottom;
+
+    if (y0 < y1)
+        fBlitter->blitV(x, y0, y1 - y0, alpha);
+}
+
+void SkRectClipBlitter::blitRect(int left, int y, int width, int height)
+{
+    SkIRect    r;
+
+    r.set(left, y, left + width, y + height);
+    if (r.intersect(fClipRect))
+        fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
+}
+
+void SkRectClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
+{
+    SkASSERT(mask.fBounds.contains(clip));
+
+    SkIRect    r = clip;
+
+    if (r.intersect(fClipRect))
+        fBlitter->blitMask(mask, r);
+}
+
+const SkBitmap* SkRectClipBlitter::justAnOpaqueColor(uint32_t* value)
+{
+    return fBlitter->justAnOpaqueColor(value);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+void SkRgnClipBlitter::blitH(int x, int y, int width)
+{
+    SkRegion::Spanerator span(*fRgn, y, x, x + width);
+    int left, right;
+
+    while (span.next(&left, &right))
+    {
+        SkASSERT(left < right);
+        fBlitter->blitH(left, y, right - left);
+    }
+}
+
+void SkRgnClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t runs[])
+{
+    int width = compute_anti_width(runs);
+    SkRegion::Spanerator span(*fRgn, y, x, x + width);
+    int left, right;
+    SkDEBUGCODE(const SkIRect& bounds = fRgn->getBounds();)
+        
+    int prevRite = x;
+    while (span.next(&left, &right))
+    {
+        SkASSERT(x <= left);
+        SkASSERT(left < right);
+        SkASSERT(left >= bounds.fLeft && right <= bounds.fRight);
+        
+        SkAlphaRuns::Break((int16_t*)runs, (uint8_t*)aa, left - x, right - left);
+
+        // now zero before left
+        if (left > prevRite)
+        {
+            int index = prevRite - x;
+            ((uint8_t*)aa)[index] = 0;   // skip runs after right
+            ((int16_t*)runs)[index] = SkToS16(left - prevRite);
+        }
+        
+        prevRite = right;
+    }
+    
+    if (prevRite > x)
+    {
+        ((int16_t*)runs)[prevRite - x] = 0;
+        
+        if (x < 0) {
+            int skip = runs[0];
+            SkASSERT(skip >= -x);
+            aa += skip;
+            runs += skip;
+            x += skip;
+        }
+        fBlitter->blitAntiH(x, y, aa, runs);
+    }
+}
+
+void SkRgnClipBlitter::blitV(int x, int y, int height, SkAlpha alpha)
+{
+    SkIRect    bounds;
+    bounds.set(x, y, x + 1, y + height);
+
+    SkRegion::Cliperator    iter(*fRgn, bounds);
+
+    while (!iter.done())
+    {
+        const SkIRect& r = iter.rect();
+        SkASSERT(bounds.contains(r));
+
+        fBlitter->blitV(x, r.fTop, r.height(), alpha);
+        iter.next();
+    }
+}
+
+void SkRgnClipBlitter::blitRect(int x, int y, int width, int height)
+{
+    SkIRect    bounds;
+    bounds.set(x, y, x + width, y + height);
+
+    SkRegion::Cliperator    iter(*fRgn, bounds);
+
+    while (!iter.done())
+    {
+        const SkIRect& r = iter.rect();
+        SkASSERT(bounds.contains(r));
+
+        fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
+        iter.next();
+    }
+}
+
+void SkRgnClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
+{
+    SkASSERT(mask.fBounds.contains(clip));
+
+    SkRegion::Cliperator iter(*fRgn, clip);
+    const SkIRect&       r = iter.rect();
+    SkBlitter*           blitter = fBlitter;
+
+    while (!iter.done())
+    {
+        blitter->blitMask(mask, r);
+        iter.next();
+    }
+}
+
+const SkBitmap* SkRgnClipBlitter::justAnOpaqueColor(uint32_t* value)
+{
+    return fBlitter->justAnOpaqueColor(value);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+SkBlitter* SkBlitterClipper::apply(SkBlitter* blitter, const SkRegion* clip, const SkIRect* ir)
+{
+    if (clip)
+    {
+        const SkIRect& clipR = clip->getBounds();
+
+        if (clip->isEmpty() || (ir && !SkIRect::Intersects(clipR, *ir)))
+            blitter = &fNullBlitter;
+        else if (clip->isRect())
+        {
+            if (ir == NULL || !clipR.contains(*ir))
+            {
+                fRectBlitter.init(blitter, clipR);
+                blitter = &fRectBlitter;
+            }
+        }
+        else
+        {
+            fRgnBlitter.init(blitter, clip);
+            blitter = &fRgnBlitter;
+        }
+    }
+    return blitter;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorShader.h"
+#include "SkColorPriv.h"
+
+class Sk3DShader : public SkShader {
+public:
+    Sk3DShader(SkShader* proxy) : fProxy(proxy)
+    {
+        proxy->safeRef();
+        fMask = NULL;
+    }
+    virtual ~Sk3DShader()
+    {
+        fProxy->safeUnref();
+    }
+    void setMask(const SkMask* mask) { fMask = mask; }
+
+    virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix)
+    {
+        if (fProxy)
+            return fProxy->setContext(device, paint, matrix);
+        else
+        {
+            fPMColor = SkPreMultiplyColor(paint.getColor());
+            return this->INHERITED::setContext(device, paint, matrix);
+        }
+    }
+    virtual void shadeSpan(int x, int y, SkPMColor span[], int count)
+    {
+        if (fProxy)
+            fProxy->shadeSpan(x, y, span, count);
+
+        if (fMask == NULL)
+        {
+            if (fProxy == NULL)
+                sk_memset32(span, fPMColor, count);
+            return;
+        }
+
+        SkASSERT(fMask->fBounds.contains(x, y));
+        SkASSERT(fMask->fBounds.contains(x + count - 1, y));
+
+        size_t          size = fMask->computeImageSize();
+        const uint8_t*  alpha = fMask->getAddr(x, y);
+        const uint8_t*  mulp = alpha + size;
+        const uint8_t*  addp = mulp + size;
+
+        if (fProxy)
+        {
+            for (int i = 0; i < count; i++)
+            {
+                if (alpha[i])
+                {
+                    SkPMColor c = span[i];
+                    if (c)
+                    {
+                        unsigned a = SkGetPackedA32(c);
+                        unsigned r = SkGetPackedR32(c);
+                        unsigned g = SkGetPackedG32(c);
+                        unsigned b = SkGetPackedB32(c);
+
+                        unsigned mul = SkAlpha255To256(mulp[i]);
+                        unsigned add = addp[i];
+
+                        r = SkFastMin32(SkAlphaMul(r, mul) + add, a);
+                        g = SkFastMin32(SkAlphaMul(g, mul) + add, a);
+                        b = SkFastMin32(SkAlphaMul(b, mul) + add, a);
+
+                        span[i] = SkPackARGB32(a, r, g, b);
+                    }
+                }
+                else
+                    span[i] = 0;
+            }
+        }
+        else    // color
+        {
+            unsigned a = SkGetPackedA32(fPMColor);
+            unsigned r = SkGetPackedR32(fPMColor);
+            unsigned g = SkGetPackedG32(fPMColor);
+            unsigned b = SkGetPackedB32(fPMColor);
+            for (int i = 0; i < count; i++)
+            {
+                if (alpha[i])
+                {
+                    unsigned mul = SkAlpha255To256(mulp[i]);
+                    unsigned add = addp[i];
+
+                    span[i] = SkPackARGB32( a,
+                                            SkFastMin32(SkAlphaMul(r, mul) + add, a),
+                                            SkFastMin32(SkAlphaMul(g, mul) + add, a),
+                                            SkFastMin32(SkAlphaMul(b, mul) + add, a));
+                }
+                else
+                    span[i] = 0;
+            }
+        }
+    }
+    
+    virtual void beginSession()
+    {
+        this->INHERITED::beginSession();
+        if (fProxy)
+            fProxy->beginSession();
+    }
+    
+    virtual void endSession()
+    {
+        if (fProxy)
+            fProxy->endSession();
+        this->INHERITED::endSession();
+    }
+
+protected:
+    Sk3DShader(SkFlattenableReadBuffer& buffer) :
+        INHERITED(buffer)
+    {
+        fProxy = static_cast<SkShader*>(buffer.readFlattenable());
+        fPMColor = buffer.readU32();
+        fMask = NULL;
+    }
+    
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) 
+    {
+        this->INHERITED::flatten(buffer);
+        buffer.writeFlattenable(fProxy);
+        buffer.write32(fPMColor);
+    }
+    
+    virtual Factory getFactory() 
+    { 
+        return CreateProc;
+    }
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) 
+    {
+        return SkNEW_ARGS(Sk3DShader, (buffer));
+    }
+
+    SkShader*       fProxy;
+    SkPMColor       fPMColor;
+    const SkMask*   fMask;
+
+    typedef SkShader INHERITED;
+};
+
+class Sk3DBlitter : public SkBlitter {
+public:
+    Sk3DBlitter(SkBlitter* proxy, Sk3DShader* shader, void (*killProc)(void*))
+        : fProxy(proxy), f3DShader(shader), fKillProc(killProc)
+    {
+        shader->ref();
+    }
+    virtual ~Sk3DBlitter()
+    {
+        f3DShader->unref();
+        fKillProc(fProxy);
+    }
+
+    virtual void blitH(int x, int y, int width)
+    {
+        fProxy->blitH(x, y, width);
+    }
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[])
+    {
+        fProxy->blitAntiH(x, y, antialias, runs);
+    }
+    virtual void blitV(int x, int y, int height, SkAlpha alpha)
+    {
+        fProxy->blitV(x, y, height, alpha);
+    }
+    virtual void blitRect(int x, int y, int width, int height)
+    {
+        fProxy->blitRect(x, y, width, height);
+    }
+    virtual void blitMask(const SkMask& mask, const SkIRect& clip)
+    {
+        if (mask.fFormat == SkMask::k3D_Format)
+        {
+            f3DShader->setMask(&mask);
+
+            ((SkMask*)&mask)->fFormat = SkMask::kA8_Format;
+            fProxy->blitMask(mask, clip);
+            ((SkMask*)&mask)->fFormat = SkMask::k3D_Format;
+
+            f3DShader->setMask(NULL);
+        }
+        else
+            fProxy->blitMask(mask, clip);
+    }
+private:
+    SkBlitter*  fProxy;
+    Sk3DShader* f3DShader;
+    void        (*fKillProc)(void*);
+};
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkCoreBlitters.h"
+
+class SkAutoRestoreShader {
+public:
+    SkAutoRestoreShader(const SkPaint& p) : fPaint((SkPaint*)&p)
+    {
+        fShader = fPaint->getShader();
+        fShader->safeRef();
+    }
+    ~SkAutoRestoreShader()
+    {
+        fPaint->setShader(fShader);
+        fShader->safeUnref();
+    }
+private:
+    SkPaint*    fPaint;
+    SkShader*   fShader;
+};
+
+class SkAutoCallProc {
+public:
+    typedef void (*Proc)(void*);
+    SkAutoCallProc(void* obj, Proc proc)
+        : fObj(obj), fProc(proc)
+    {
+    }
+    ~SkAutoCallProc()
+    {
+        if (fObj && fProc)
+            fProc(fObj);
+    }
+    void* get() const { return fObj; }
+    void* detach()
+    {
+        void* obj = fObj;
+        fObj = NULL;
+        return obj;
+    }
+private:
+    void*   fObj;
+    Proc    fProc;
+};
+
+static void destroy_blitter(void* blitter)
+{
+    ((SkBlitter*)blitter)->~SkBlitter();
+}
+
+static void delete_blitter(void* blitter)
+{
+    SkDELETE((SkBlitter*)blitter);
+}
+
+SkBlitter* SkBlitter::Choose(const SkBitmap& device,
+                             const SkMatrix& matrix,
+                             const SkPaint& paint,
+                             void* storage, size_t storageSize)
+{
+    SkASSERT(storageSize == 0 || storage != NULL);
+
+    SkBlitter*  blitter = NULL;
+
+    // which check, in case we're being called by a client with a dummy device
+    // (e.g. they have a bounder that always aborts the draw)
+    if (SkBitmap::kNo_Config == device.getConfig())
+    {
+        SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+        return blitter;
+    }
+
+    SkAutoRestoreShader restore(paint);
+    SkShader* shader = paint.getShader();
+
+    Sk3DShader* shader3D = NULL;
+    if (paint.getMaskFilter() != NULL && paint.getMaskFilter()->getFormat() == SkMask::k3D_Format)
+    {
+        shader3D = SkNEW_ARGS(Sk3DShader, (shader));
+        ((SkPaint*)&paint)->setShader(shader3D)->unref();
+        shader = shader3D;
+    }
+
+    SkXfermode* mode = paint.getXfermode();
+    if (NULL == shader && (NULL != mode || paint.getColorFilter() != NULL))
+    {
+        // xfermodes require shaders for our current set of blitters
+        shader = SkNEW(SkColorShader);
+        ((SkPaint*)&paint)->setShader(shader)->unref();
+    }
+    
+    if (paint.getColorFilter() != NULL)
+    {
+        SkASSERT(shader);
+        shader = SkNEW_ARGS(SkFilterShader, (shader, paint.getColorFilter()));
+        ((SkPaint*)&paint)->setShader(shader)->unref();
+    }
+    
+    bool doDither = paint.isDither();
+
+    if (shader)
+    {
+        if (!shader->setContext(device, paint, matrix))
+            return SkNEW(SkNullBlitter);
+        
+        // disable dither if our shader is natively 16bit (no need to upsample)
+        if (shader->getFlags() & SkShader::kIntrinsicly16_Flag)
+            doDither = false;
+    }
+
+    switch (device.getConfig()) {
+    case SkBitmap::kA1_Config:
+        SK_PLACEMENT_NEW_ARGS(blitter, SkA1_Blitter, storage, storageSize, (device, paint));
+        break;
+
+    case SkBitmap::kA8_Config:
+        if (shader)
+            SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Shader_Blitter, storage, storageSize, (device, paint));
+        else
+            SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Blitter, storage, storageSize, (device, paint));
+        break;
+        
+    case SkBitmap::kARGB_4444_Config:
+        blitter = SkBlitter_ChooseD4444(device, paint, storage, storageSize);
+        break;
+
+    case SkBitmap::kRGB_565_Config:
+        if (shader)
+        {
+            if (mode)
+                SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Xfermode_Blitter, storage, storageSize, (device, paint));
+            else if (SkShader::CanCallShadeSpan16(shader->getFlags()) && !doDither)
+                SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader16_Blitter, storage, storageSize, (device, paint));
+            else
+                SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Shader_Blitter, storage, storageSize, (device, paint));
+        }
+        else if (paint.getColor() == SK_ColorBLACK)
+            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Black_Blitter, storage, storageSize, (device, paint));
+        else
+            SK_PLACEMENT_NEW_ARGS(blitter, SkRGB16_Blitter, storage, storageSize, (device, paint));
+        break;
+
+    case SkBitmap::kARGB_8888_Config:
+        if (shader)
+            SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Shader_Blitter, storage, storageSize, (device, paint));
+        else if (paint.getColor() == SK_ColorBLACK)
+            SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Black_Blitter, storage, storageSize, (device, paint));
+        else if (paint.getAlpha() == 0xFF)
+            SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Opaque_Blitter, storage, storageSize, (device, paint));
+        else
+            SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Blitter, storage, storageSize, (device, paint));
+        break;
+
+    default:
+        SkASSERT(!"unsupported device config");
+        SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+    }
+
+    if (shader3D)
+    {
+        void (*proc)(void*) = ((void*)storage == (void*)blitter) ? destroy_blitter : delete_blitter;
+        SkAutoCallProc  tmp(blitter, proc);
+
+        blitter = SkNEW_ARGS(Sk3DBlitter, (blitter, shader3D, proc));
+        (void)tmp.detach();
+    }
+    return blitter;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+const uint16_t gMask_0F0F = 0xF0F;
+const uint32_t gMask_00FF00FF = 0xFF00FF;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkShaderBlitter::SkShaderBlitter(const SkBitmap& device, const SkPaint& paint)
+    : INHERITED(device)
+{
+    fShader = paint.getShader();
+    SkASSERT(fShader);
+
+    fShader->ref();
+    fShader->beginSession();
+}
+
+SkShaderBlitter::~SkShaderBlitter()
+{
+    fShader->endSession();
+    fShader->unref();
+}
+
diff --git a/src/core/SkBlitter.h b/src/core/SkBlitter.h
new file mode 100644
index 0000000..11b84fd
--- /dev/null
+++ b/src/core/SkBlitter.h
@@ -0,0 +1,143 @@
+/* libs/graphics/sgl/SkBlitter.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 SkBlitter_DEFINED
+#define SkBlitter_DEFINED
+
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkRefCnt.h"
+#include "SkRegion.h"
+#include "SkMask.h"
+
+class SkBlitter {
+public:
+    virtual ~SkBlitter();
+
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha[], const int16_t runs[]);
+    virtual void blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&, const SkIRect& clip);
+
+    /*  If the blitter just sets a single value for each pixel, return the
+        bitmap it draws into, and assign value. If not, return NULL and ignore
+        the value parameter.
+    */
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
+
+    // not virtual, just helpers
+    void blitMaskRegion(const SkMask& mask, const SkRegion& clip);
+    void blitRectRegion(const SkIRect& rect, const SkRegion& clip);
+    void blitRegion(const SkRegion& clip);
+
+    // factories
+    static SkBlitter* Choose(const SkBitmap& device,
+                             const SkMatrix& matrix,
+                             const SkPaint& paint) {
+        return Choose(device, matrix, paint, NULL, 0);
+    }
+
+    static SkBlitter* Choose(const SkBitmap& device,
+                             const SkMatrix& matrix,
+                             const SkPaint& paint,
+                             void* storage, size_t storageSize);
+
+    static SkBlitter* ChooseSprite(const SkBitmap& device,
+                                   const SkPaint&,
+                                   const SkBitmap& src,
+                                   int left, int top,
+                                   void* storage, size_t storageSize);
+
+private:
+};
+
+/** This blitter silently never draws anything.
+*/
+class SkNullBlitter : public SkBlitter {
+public:
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha[], const int16_t runs[]);
+    virtual void blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&, const SkIRect& clip);
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
+};
+
+/** Wraps another (real) blitter, and ensures that the real blitter is only
+    called with coordinates that have been clipped by the specified clipRect.
+    This means the caller need not perform the clipping ahead of time.
+*/
+class SkRectClipBlitter : public SkBlitter {
+public:
+    void init(SkBlitter* blitter, const SkIRect& clipRect) {
+        SkASSERT(!clipRect.isEmpty());
+        fBlitter = blitter;
+        fClipRect = clipRect;
+    }
+
+    // overrides
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha[], const int16_t runs[]);
+    virtual void blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&, const SkIRect& clip);
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
+
+private:
+    SkBlitter*  fBlitter;
+    SkIRect     fClipRect;
+};
+
+/** Wraps another (real) blitter, and ensures that the real blitter is only
+called with coordinates that have been clipped by the specified clipRgn.
+This means the caller need not perform the clipping ahead of time.
+*/
+class SkRgnClipBlitter : public SkBlitter {
+public:
+    void init(SkBlitter* blitter, const SkRegion* clipRgn) {
+        SkASSERT(clipRgn && !clipRgn->isEmpty());
+        fBlitter = blitter;
+        fRgn = clipRgn;
+    }
+
+    // overrides
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha[], const int16_t runs[]);
+    virtual void blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&, const SkIRect& clip);
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
+
+private:
+    SkBlitter*      fBlitter;
+    const SkRegion* fRgn;
+};
+
+class SkBlitterClipper {
+public:
+    SkBlitter*  apply(SkBlitter* blitter, const SkRegion* clip,
+                      const SkIRect* bounds = NULL);
+
+private:
+    SkNullBlitter       fNullBlitter;
+    SkRectClipBlitter   fRectBlitter;
+    SkRgnClipBlitter    fRgnBlitter;
+};
+
+#endif
diff --git a/src/core/SkBlitter_4444.cpp b/src/core/SkBlitter_4444.cpp
new file mode 100644
index 0000000..cce94c5
--- /dev/null
+++ b/src/core/SkBlitter_4444.cpp
@@ -0,0 +1,501 @@
+/* libs/graphics/sgl/SkBlitter_ARGB32.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 "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkShader.h"
+#include "SkTemplatesPriv.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+inline SkPMColor SkBlendARGB4444(SkPMColor16 src, SkPMColor16 dst, U8CPU aa)
+{
+    SkASSERT((unsigned)aa <= 255);
+    
+    unsigned src_scale = SkAlpha255To256(aa) >> 4;
+    unsigned dst_scale = SkAlpha15To16(15 - SkAlphaMul4(SkGetPackedA4444(src), src_scale));
+    
+    uint32_t src32 = SkExpand_4444(src) * src_scale;
+    uint32_t dst32 = SkExpand_4444(dst) * dst_scale;
+    return SkCompact_4444((src32 + dst32) >> 4);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkARGB4444_Blitter : public SkRasterBlitter {
+public:
+    SkARGB4444_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+    virtual void blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&, const SkIRect&);
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+    
+protected:
+    SkPMColor16 fPMColor16, fPMColor16Other;
+    SkPMColor16 fRawColor16, fRawColor16Other;
+    uint8_t     fScale16;
+    
+private:
+    // illegal
+    SkARGB4444_Blitter& operator=(const SkARGB4444_Blitter&);
+    
+    typedef SkRasterBlitter INHERITED;
+};
+
+
+SkARGB4444_Blitter::SkARGB4444_Blitter(const SkBitmap& device, const SkPaint& paint)
+    : INHERITED(device)
+{
+    // cache premultiplied versions in 4444
+    SkPMColor c = SkPreMultiplyColor(paint.getColor());
+    fPMColor16 = SkPixel32ToPixel4444(c);
+    if (paint.isDither()) {
+        fPMColor16Other = SkDitherPixel32To4444(c);
+    } else {
+        fPMColor16Other = fPMColor16;
+    }
+
+    // cache raw versions in 4444
+    fRawColor16 = SkPackARGB4444(0xFF >> 4, SkColorGetR(c) >> 4,
+                                 SkColorGetG(c) >> 4, SkColorGetB(c) >> 4);
+    if (paint.isDither()) {
+        fRawColor16Other = SkDitherARGB32To4444(0xFF, SkColorGetR(c),
+                                                SkColorGetG(c), SkColorGetB(c));
+    } else {
+        fRawColor16Other = fRawColor16;
+    }
+    
+    // our dithered color will be the same or more opaque than the original
+    // so use dithered to compute our scale
+    SkASSERT(SkGetPackedA4444(fPMColor16Other) >= SkGetPackedA4444(fPMColor16));
+
+    fScale16 = SkAlpha15To16(SkGetPackedA4444(fPMColor16Other));
+    if (16 == fScale16) {
+        // force the original to also be opaque
+        fPMColor16 |= (0xF << SK_A4444_SHIFT);
+    }
+}
+
+const SkBitmap* SkARGB4444_Blitter::justAnOpaqueColor(uint32_t* value)
+{
+    if (16 == fScale16) {
+        *value = fPMColor16;
+        return &fDevice;
+    }
+    return NULL;
+}
+
+static void src_over_4444(SkPMColor16 dst[], SkPMColor16 color,
+                          SkPMColor16 other, unsigned invScale, int count)
+{
+    int twice = count >> 1;
+    while (--twice >= 0) {
+        *dst = color + SkAlphaMulQ4(*dst, invScale);
+        dst++;
+        *dst = other + SkAlphaMulQ4(*dst, invScale);
+        dst++;
+    }
+    if (color & 1) {
+        *dst = color + SkAlphaMulQ4(*dst, invScale);
+    }
+}
+
+static inline uint32_t SkExpand_4444_Replicate(SkPMColor16 c)
+{
+    uint32_t c32 = SkExpand_4444(c);
+    return c32 | (c32 << 4);
+}
+
+static void src_over_4444x(SkPMColor16 dst[], uint32_t color,
+                           uint32_t other, unsigned invScale, int count)
+{
+    int twice = count >> 1;
+    uint32_t tmp;
+    while (--twice >= 0) {
+        tmp = SkExpand_4444(*dst) * invScale;
+        *dst++ = SkCompact_4444((color + tmp) >> 4);
+        tmp = SkExpand_4444(*dst) * invScale;
+        *dst++ = SkCompact_4444((other + tmp) >> 4);
+    }
+    if (color & 1) {
+        tmp = SkExpand_4444(*dst) * invScale;
+        *dst = SkCompact_4444((color + tmp) >> 4);
+    }
+}
+
+void SkARGB4444_Blitter::blitH(int x, int y, int width)
+{
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
+    
+    if (0 == fScale16) {
+        return;
+    }
+    
+    SkPMColor16* device = fDevice.getAddr16(x, y);
+    SkPMColor16  color = fPMColor16;
+    SkPMColor16  other = fPMColor16Other;
+    
+    if ((x ^ y) & 1) {
+        SkTSwap<SkPMColor16>(color, other);
+    }
+    
+    if (16 == fScale16) {
+        sk_dither_memset16(device, color, other, width);
+    }
+    else {
+        src_over_4444x(device, SkExpand_4444_Replicate(color),
+                       SkExpand_4444_Replicate(other),
+                       16 - fScale16, width);
+    }
+}
+
+void SkARGB4444_Blitter::blitV(int x, int y, int height, SkAlpha alpha)
+{
+    if (0 == alpha || 0 == fScale16) {
+        return;
+    }
+    
+    SkPMColor16* device = fDevice.getAddr16(x, y);
+    SkPMColor16  color = fPMColor16;
+    SkPMColor16  other = fPMColor16Other;
+    unsigned     rb = fDevice.rowBytes();
+    
+    if ((x ^ y) & 1) {
+        SkTSwap<SkPMColor16>(color, other);
+    }
+
+    if (16 == fScale16 && 255 == alpha) {
+        while (--height >= 0) {
+            *device = color;
+            device = (SkPMColor16*)((char*)device + rb);
+            SkTSwap<SkPMColor16>(color, other);
+        }
+    } else {
+        unsigned alphaScale = SkAlpha255To256(alpha);
+        uint32_t c32 = SkExpand_4444(color) * (alphaScale >> 4);
+        // need to normalize the low nibble of each expanded component
+        // so we don't overflow the add with d32
+        c32 = SkCompact_4444(c32 >> 4);
+        unsigned invScale = 16 - SkAlpha15To16(SkGetPackedA4444(c32));
+        // now re-expand and replicate
+        c32 = SkExpand_4444_Replicate(c32);
+
+        while (--height >= 0) {
+            uint32_t d32 = SkExpand_4444(*device) * invScale;
+            *device = SkCompact_4444((c32 + d32) >> 4);
+            device = (SkPMColor16*)((char*)device + rb);
+        }
+    }
+}
+
+void SkARGB4444_Blitter::blitRect(int x, int y, int width, int height)
+{
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width() && y + height <= fDevice.height());
+    
+    if (0 == fScale16) {
+        return;
+    }
+    
+    SkPMColor16* device = fDevice.getAddr16(x, y);
+    SkPMColor16  color = fPMColor16;
+    SkPMColor16  other = fPMColor16Other;
+    
+    if ((x ^ y) & 1) {
+        SkTSwap<SkPMColor16>(color, other);
+    }
+    
+    if (16 == fScale16) {
+        while (--height >= 0) {
+            sk_dither_memset16(device, color, other, width);
+            device = (SkPMColor16*)((char*)device + fDevice.rowBytes());
+            SkTSwap<SkPMColor16>(color, other);
+        }
+    } else {
+        unsigned invScale = 16 - fScale16;
+
+        uint32_t c32 = SkExpand_4444_Replicate(color);
+        uint32_t o32 = SkExpand_4444_Replicate(other);
+        while (--height >= 0) {
+            src_over_4444x(device, c32, o32, invScale, width);
+            device = (SkPMColor16*)((char*)device + fDevice.rowBytes());
+            SkTSwap<uint32_t>(c32, o32);
+        }
+    }
+}
+
+void SkARGB4444_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[])
+{
+    if (0 == fScale16) {
+        return;
+    }
+    
+    SkPMColor16* device = fDevice.getAddr16(x, y);
+    SkPMColor16  color = fPMColor16;
+    SkPMColor16  other = fPMColor16Other;
+    
+    if ((x ^ y) & 1) {
+        SkTSwap<SkPMColor16>(color, other);
+    }
+    
+    for (;;) {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count <= 0) {
+            return;
+        }
+        
+        unsigned aa = antialias[0];
+        if (aa) {
+            if (0xFF == aa) {
+                if (16 == fScale16) {
+                    sk_dither_memset16(device, color, other, count);
+                } else {
+                    src_over_4444(device, color, other, 16 - fScale16, count);
+                }
+            } else {
+                // todo: respect dithering
+                aa = SkAlpha255To256(aa);   // FIX
+                SkPMColor16 src = SkAlphaMulQ4(color, aa >> 4);
+                unsigned dst_scale = SkAlpha15To16(15 - SkGetPackedA4444(src)); // FIX
+                int n = count;
+                do {
+                    --n;
+                    device[n] = src + SkAlphaMulQ4(device[n], dst_scale);
+                } while (n > 0);
+            }
+        }
+
+        runs += count;
+        antialias += count;
+        device += count;
+
+        if (count & 1) {
+            SkTSwap<SkPMColor16>(color, other);
+        }
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+#define solid_8_pixels(mask, dst, color)    \
+do {                                    \
+if (mask & 0x80) dst[0] = color;    \
+if (mask & 0x40) dst[1] = color;    \
+if (mask & 0x20) dst[2] = color;    \
+if (mask & 0x10) dst[3] = color;    \
+if (mask & 0x08) dst[4] = color;    \
+if (mask & 0x04) dst[5] = color;    \
+if (mask & 0x02) dst[6] = color;    \
+if (mask & 0x01) dst[7] = color;    \
+} while (0)
+
+#define SK_BLITBWMASK_NAME                  SkARGB4444_BlitBW
+#define SK_BLITBWMASK_ARGS                  , SkPMColor16 color
+#define SK_BLITBWMASK_BLIT8(mask, dst)      solid_8_pixels(mask, dst, color)
+#define SK_BLITBWMASK_GETADDR               getAddr16
+#define SK_BLITBWMASK_DEVTYPE               uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+#define blend_8_pixels(mask, dst, sc, dst_scale)                            \
+do {                                                                    \
+if (mask & 0x80) { dst[0] = sc + SkAlphaMulQ4(dst[0], dst_scale); }  \
+if (mask & 0x40) { dst[1] = sc + SkAlphaMulQ4(dst[1], dst_scale); }  \
+if (mask & 0x20) { dst[2] = sc + SkAlphaMulQ4(dst[2], dst_scale); }  \
+if (mask & 0x10) { dst[3] = sc + SkAlphaMulQ4(dst[3], dst_scale); }  \
+if (mask & 0x08) { dst[4] = sc + SkAlphaMulQ4(dst[4], dst_scale); }  \
+if (mask & 0x04) { dst[5] = sc + SkAlphaMulQ4(dst[5], dst_scale); }  \
+if (mask & 0x02) { dst[6] = sc + SkAlphaMulQ4(dst[6], dst_scale); }  \
+if (mask & 0x01) { dst[7] = sc + SkAlphaMulQ4(dst[7], dst_scale); }  \
+} while (0)
+
+#define SK_BLITBWMASK_NAME                  SkARGB4444_BlendBW
+#define SK_BLITBWMASK_ARGS                  , uint16_t sc, unsigned dst_scale
+#define SK_BLITBWMASK_BLIT8(mask, dst)      blend_8_pixels(mask, dst, sc, dst_scale)
+#define SK_BLITBWMASK_GETADDR               getAddr16
+#define SK_BLITBWMASK_DEVTYPE               uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+void SkARGB4444_Blitter::blitMask(const SkMask& mask, const SkIRect& clip)
+{
+    SkASSERT(mask.fBounds.contains(clip));
+    
+    if (0 == fScale16) {
+        return;
+    }
+    
+    if (mask.fFormat == SkMask::kBW_Format) {
+        if (16 == fScale16) {
+            SkARGB4444_BlitBW(fDevice, mask, clip, fPMColor16);
+        } else {
+            SkARGB4444_BlendBW(fDevice, mask, clip, fPMColor16, 16 - fScale16);
+        }
+        return;
+    }
+    
+    int x = clip.fLeft;
+    int y = clip.fTop;
+    int width = clip.width();
+    int height = clip.height();
+    
+    SkPMColor16*    device = fDevice.getAddr16(x, y);
+    const uint8_t*  alpha = mask.getAddr(x, y);
+    SkPMColor16     srcColor = fPMColor16;
+    unsigned        devRB = fDevice.rowBytes() - (width << 1);
+    unsigned        maskRB = mask.fRowBytes - width;
+    
+    do {
+        int w = width;
+        do {
+            unsigned aa = *alpha++;
+            *device = SkBlendARGB4444(srcColor, *device, aa);
+            device += 1;
+        } while (--w != 0);
+        device = (SkPMColor16*)((char*)device + devRB);
+        alpha += maskRB;
+    } while (--height != 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+class SkARGB4444_Shader_Blitter : public SkShaderBlitter {
+    SkXfermode*     fXfermode;
+    SkBlitRow::Proc fOpaqueProc;
+    SkBlitRow::Proc fAlphaProc;
+    SkPMColor*      fBuffer;
+    uint8_t*        fAAExpand;
+public:
+SkARGB4444_Shader_Blitter(const SkBitmap& device, const SkPaint& paint)
+    : INHERITED(device, paint)
+{
+    const int width = device.width();
+    fBuffer = (SkPMColor*)sk_malloc_throw(width * sizeof(SkPMColor) + width);
+    fAAExpand = (uint8_t*)(fBuffer + width);
+    
+    (fXfermode = paint.getXfermode())->safeRef();
+    
+    unsigned flags = 0;
+    if (!(fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+        flags |= SkBlitRow::kSrcPixelAlpha_Flag;
+    }
+    if (paint.isDither()) {
+        flags |= SkBlitRow::kDither_Flag;
+    }
+    fOpaqueProc = SkBlitRow::Factory(flags, SkBitmap::kARGB_4444_Config);
+    fAlphaProc = SkBlitRow::Factory(flags | SkBlitRow::kGlobalAlpha_Flag,
+                                    SkBitmap::kARGB_4444_Config);
+}
+
+virtual ~SkARGB4444_Shader_Blitter()
+{
+    fXfermode->safeUnref();
+    sk_free(fBuffer);
+}
+
+virtual void blitH(int x, int y, int width)
+{
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
+    
+    SkPMColor16* device = fDevice.getAddr16(x, y);    
+    SkPMColor*   span = fBuffer;
+    
+    fShader->shadeSpan(x, y, span, width);
+    if (fXfermode) {
+        fXfermode->xfer4444(device, span, width, NULL);
+    }
+    else {
+        fOpaqueProc(device, span, width, 0xFF, x, y);
+    }
+}
+
+virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[])
+{
+    SkPMColor* SK_RESTRICT span = fBuffer;
+    uint8_t* SK_RESTRICT aaExpand = fAAExpand;
+    SkPMColor16* device = fDevice.getAddr16(x, y);
+    SkShader*    shader = fShader;
+    SkXfermode*  xfer = fXfermode;
+    
+    if (NULL != xfer) {
+        for (;;) {
+            int count = *runs;
+            if (count <= 0)
+                break;
+            int aa = *antialias;
+            if (aa) {
+                shader->shadeSpan(x, y, span, count);
+                if (255 == aa) {
+                    xfer->xfer4444(device, span, count, NULL);
+                } else {
+                    const uint8_t* aaBuffer = antialias;
+                    if (count > 1) {
+                        memset(aaExpand, aa, count);
+                        aaBuffer = aaExpand;
+                    }
+                    xfer->xfer4444(device, span, count, aaBuffer);
+                }
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        } 
+    } else {    // no xfermode
+        for (;;) {
+            int count = *runs;
+            if (count <= 0)
+                break;
+            int aa = *antialias;
+            if (aa) {
+                fShader->shadeSpan(x, y, span, count);
+                if (255 == aa) {
+                    fOpaqueProc(device, span, count, aa, x, y);
+                } else {
+                    fAlphaProc(device, span, count, aa, x, y);
+                }
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        } 
+    }
+}
+
+private:
+    typedef SkShaderBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkBlitter* SkBlitter_ChooseD4444(const SkBitmap& device,
+                                 const SkPaint& paint,
+                                 void* storage, size_t storageSize)
+{
+    SkBlitter* blitter;
+
+    if (paint.getShader()) {
+        SK_PLACEMENT_NEW_ARGS(blitter, SkARGB4444_Shader_Blitter, storage, storageSize, (device, paint));
+    } else {
+        SK_PLACEMENT_NEW_ARGS(blitter, SkARGB4444_Blitter, storage, storageSize, (device, paint));
+    }
+    return blitter;
+}
+
diff --git a/src/core/SkBlitter_A1.cpp b/src/core/SkBlitter_A1.cpp
new file mode 100644
index 0000000..1a91a26
--- /dev/null
+++ b/src/core/SkBlitter_A1.cpp
@@ -0,0 +1,63 @@
+/* libs/graphics/sgl/SkBlitter_A1.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 "SkCoreBlitters.h"
+
+SkA1_Blitter::SkA1_Blitter(const SkBitmap& device, const SkPaint& paint)
+    : INHERITED(device)
+{
+    fSrcA = SkToU8(SkColorGetA(paint.getColor()));
+}
+
+void SkA1_Blitter::blitH(int x, int y, int width)
+{
+    SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width());
+
+    if (fSrcA <= 0x7F)
+        return;
+
+    uint8_t* dst = fDevice.getAddr1(x, y);
+    int right = x + width;
+
+    int left_mask = 0xFF >> (x & 7);
+    int rite_mask = 0xFF << (8 - (right & 7));
+    int full_runs = (right >> 3) - ((x + 7) >> 3);
+
+    // check for empty right mask, so we don't read off the end (or go slower than we need to)
+    if (rite_mask == 0)
+    {
+        SkASSERT(full_runs >= 0);
+        full_runs -= 1;
+        rite_mask = 0xFF;
+    }
+    if (left_mask == 0xFF)
+        full_runs -= 1;
+
+    if (full_runs < 0)
+    {
+        SkASSERT((left_mask & rite_mask) != 0);
+        *dst |= (left_mask & rite_mask);
+    }
+    else
+    {
+        *dst++ |= left_mask;
+        memset(dst, 0xFF, full_runs);
+        dst += full_runs;
+        *dst |= rite_mask;
+    }
+}
+
diff --git a/src/core/SkBlitter_A8.cpp b/src/core/SkBlitter_A8.cpp
new file mode 100644
index 0000000..18b0881
--- /dev/null
+++ b/src/core/SkBlitter_A8.cpp
@@ -0,0 +1,387 @@
+/* libs/graphics/sgl/SkBlitter_A8.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 "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+#include "SkXfermode.h"
+
+SkA8_Blitter::SkA8_Blitter(const SkBitmap& device, const SkPaint& paint)
+    : INHERITED(device)
+{
+    fSrcA = SkColorGetA(paint.getColor());
+}
+
+const SkBitmap* SkA8_Blitter::justAnOpaqueColor(uint32_t* value)
+{
+    if (255 == fSrcA)
+    {
+        *value = 255;
+        return &fDevice;
+    }
+    return NULL;
+}
+
+void SkA8_Blitter::blitH(int x, int y, int width)
+{
+    SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width());
+
+    if (fSrcA == 0)
+        return;
+
+    uint8_t* device = fDevice.getAddr8(x, y);
+
+    if (fSrcA == 255)
+    {
+        memset(device, 0xFF, width);
+    }
+    else
+    {
+        unsigned scale = 256 - SkAlpha255To256(fSrcA);
+        unsigned srcA = fSrcA;
+
+        for (int i = 0; i < width; i++)
+        {
+            device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale));
+        }
+    }
+}
+
+void SkA8_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[])
+{
+    if (fSrcA == 0)
+        return;
+
+    uint8_t*    device = fDevice.getAddr8(x, y);
+    unsigned    srcA = fSrcA;
+
+    for (;;)
+    {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count == 0)
+            return;
+        unsigned aa = antialias[0];
+
+        if (aa == 255 && srcA == 255)
+            memset(device, 0xFF, count);
+        else
+        {
+            unsigned sa = SkAlphaMul(srcA, SkAlpha255To256(aa));
+            unsigned scale = 256 - sa;
+
+            for (int i = 0; i < count; i++)
+            {
+                device[i] = SkToU8(sa + SkAlphaMul(device[i], scale));
+            }
+        }
+        runs += count;
+        antialias += count;
+        device += count;
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+#define solid_8_pixels(mask, dst)           \
+    do {                                    \
+        if (mask & 0x80) dst[0] = 0xFF;     \
+        if (mask & 0x40) dst[1] = 0xFF;     \
+        if (mask & 0x20) dst[2] = 0xFF;     \
+        if (mask & 0x10) dst[3] = 0xFF;     \
+        if (mask & 0x08) dst[4] = 0xFF;     \
+        if (mask & 0x04) dst[5] = 0xFF;     \
+        if (mask & 0x02) dst[6] = 0xFF;     \
+        if (mask & 0x01) dst[7] = 0xFF;     \
+    } while (0)
+
+#define SK_BLITBWMASK_NAME                  SkA8_BlitBW
+#define SK_BLITBWMASK_ARGS                  
+#define SK_BLITBWMASK_BLIT8(mask, dst)      solid_8_pixels(mask, dst)
+#define SK_BLITBWMASK_GETADDR               getAddr8
+#define SK_BLITBWMASK_DEVTYPE               uint8_t
+#include "SkBlitBWMaskTemplate.h"
+
+static inline void blend_8_pixels(U8CPU bw, uint8_t dst[], U8CPU sa, unsigned dst_scale)
+{
+    if (bw & 0x80) dst[0] = SkToU8(sa + SkAlphaMul(dst[0], dst_scale));
+    if (bw & 0x40) dst[1] = SkToU8(sa + SkAlphaMul(dst[1], dst_scale));
+    if (bw & 0x20) dst[2] = SkToU8(sa + SkAlphaMul(dst[2], dst_scale));
+    if (bw & 0x10) dst[3] = SkToU8(sa + SkAlphaMul(dst[3], dst_scale));
+    if (bw & 0x08) dst[4] = SkToU8(sa + SkAlphaMul(dst[4], dst_scale));
+    if (bw & 0x04) dst[5] = SkToU8(sa + SkAlphaMul(dst[5], dst_scale));
+    if (bw & 0x02) dst[6] = SkToU8(sa + SkAlphaMul(dst[6], dst_scale));
+    if (bw & 0x01) dst[7] = SkToU8(sa + SkAlphaMul(dst[7], dst_scale));
+}
+
+#define SK_BLITBWMASK_NAME                  SkA8_BlendBW
+#define SK_BLITBWMASK_ARGS                  , U8CPU sa, unsigned dst_scale
+#define SK_BLITBWMASK_BLIT8(mask, dst)      blend_8_pixels(mask, dst, sa, dst_scale)
+#define SK_BLITBWMASK_GETADDR               getAddr8
+#define SK_BLITBWMASK_DEVTYPE               uint8_t
+#include "SkBlitBWMaskTemplate.h"
+
+void SkA8_Blitter::blitMask(const SkMask& mask, const SkIRect& clip)
+{
+    if (fSrcA == 0)
+        return;
+
+    if (mask.fFormat == SkMask::kBW_Format)
+    {
+        if (fSrcA == 0xFF)
+            SkA8_BlitBW(fDevice, mask, clip);
+        else
+            SkA8_BlendBW(fDevice, mask, clip, fSrcA, SkAlpha255To256(255 - fSrcA));
+        return;
+    }
+
+    int x = clip.fLeft;
+    int y = clip.fTop;
+    int width = clip.width();
+    int height = clip.height();
+    uint8_t* device = fDevice.getAddr8(x, y);
+    const uint8_t* alpha = mask.getAddr(x, y);
+    unsigned    srcA = fSrcA;
+
+    while (--height >= 0)
+    {
+        for (int i = width - 1; i >= 0; --i)
+        {
+            unsigned sa;
+            // scale our src by the alpha value
+            {
+                int aa = alpha[i];
+                if (aa == 0)
+                    continue;
+
+                if (aa == 255)
+                {
+                    if (srcA == 255)
+                    {
+                        device[i] = 0xFF;
+                        continue;
+                    }
+                    sa = srcA;
+                }
+                else
+                    sa = SkAlphaMul(srcA, SkAlpha255To256(aa));
+            }
+
+            int scale = 256 - SkAlpha255To256(sa);
+            device[i] = SkToU8(sa + SkAlphaMul(device[i], scale));
+        }
+        device += fDevice.rowBytes();
+        alpha += mask.fRowBytes;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void SkA8_Blitter::blitV(int x, int y, int height, SkAlpha alpha)
+{
+    if (fSrcA == 0)
+        return;
+
+    unsigned sa = SkAlphaMul(fSrcA, SkAlpha255To256(alpha));
+    uint8_t* device = fDevice.getAddr8(x, y);
+    int      rowBytes = fDevice.rowBytes();
+
+    if (sa == 0xFF)
+    {
+        for (int i = 0; i < height; i++)
+        {
+            *device = SkToU8(sa);
+            device += rowBytes;
+        }
+    }
+    else
+    {
+        unsigned scale = 256 - SkAlpha255To256(sa);
+
+        for (int i = 0; i < height; i++)
+        {
+            *device = SkToU8(sa + SkAlphaMul(*device, scale));
+            device += rowBytes;
+        }
+    }
+}
+
+void SkA8_Blitter::blitRect(int x, int y, int width, int height)
+{
+    SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width() && (unsigned)(y + height) <= (unsigned)fDevice.height());
+
+    if (fSrcA == 0)
+        return;
+
+    uint8_t*    device = fDevice.getAddr8(x, y);
+    unsigned    srcA = fSrcA;
+
+    if (srcA == 255)
+    {
+        while (--height >= 0)
+        {
+            memset(device, 0xFF, width);
+            device += fDevice.rowBytes();
+        }
+    }
+    else
+    {
+        unsigned scale = 256 - SkAlpha255To256(srcA);
+
+        while (--height >= 0)
+        {
+            for (int i = 0; i < width; i++)
+            {
+                device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale));
+            }
+            device += fDevice.rowBytes();
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////
+
+SkA8_Shader_Blitter::SkA8_Shader_Blitter(const SkBitmap& device, const SkPaint& paint)
+    : INHERITED(device, paint)
+{
+    if ((fXfermode = paint.getXfermode()) != NULL)
+    {
+        fXfermode->ref();
+        SkASSERT(fShader);
+    }
+
+    int width = device.width();
+    fBuffer = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * (width + (SkAlign4(width) >> 2)));
+    fAAExpand = (uint8_t*)(fBuffer + width);
+}
+
+SkA8_Shader_Blitter::~SkA8_Shader_Blitter()
+{
+    fXfermode->safeUnref();
+    sk_free(fBuffer);
+}
+
+void SkA8_Shader_Blitter::blitH(int x, int y, int width)
+{
+    SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width());
+
+    uint8_t* device = fDevice.getAddr8(x, y);
+
+    if ((fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) && fXfermode == NULL)
+    {
+        memset(device, 0xFF, width);
+    }
+    else
+    {
+        SkPMColor*  span = fBuffer;
+
+        fShader->shadeSpan(x, y, span, width);
+        if (fXfermode)
+            fXfermode->xferA8(device, span, width, NULL);
+        else
+        {
+            for (int i = width - 1; i >= 0; --i)
+            {
+                unsigned    srcA = SkGetPackedA32(span[i]);
+                unsigned    scale = 256 - SkAlpha255To256(srcA);
+
+                device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale));
+            }
+        }
+    }
+}
+
+static inline uint8_t aa_blend8(SkPMColor src, U8CPU da, int aa)
+{
+    SkASSERT((unsigned)aa <= 255);
+
+    int src_scale = SkAlpha255To256(aa);
+    int sa = SkGetPackedA32(src);
+    int dst_scale = 256 - SkAlphaMul(sa, src_scale);
+
+    return SkToU8((sa * src_scale + da * dst_scale) >> 8);
+}
+
+void SkA8_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[])
+{
+    SkShader*   shader = fShader;
+    SkXfermode* mode = fXfermode;
+    uint8_t*    aaExpand = fAAExpand;
+    SkPMColor*  span = fBuffer;
+    uint8_t*    device = fDevice.getAddr8(x, y);
+    int         opaque = fShader->getFlags() & SkShader::kOpaqueAlpha_Flag;
+
+    for (;;)
+    {
+        int count = *runs;
+        if (count == 0)
+            break;
+        int aa = *antialias;
+        if (aa)
+        {
+            if (opaque && aa == 255 && mode == NULL)
+                memset(device, 0xFF, count);
+            else
+            {
+                shader->shadeSpan(x, y, span, count);
+                if (mode)
+                {
+                    memset(aaExpand, aa, count);
+                    mode->xferA8(device, span, count, aaExpand);
+                }
+                else
+                {
+                    for (int i = count - 1; i >= 0; --i)
+                        device[i] = aa_blend8(span[i], device[i], aa);
+                }
+            }
+        }
+        device += count;
+        runs += count;
+        antialias += count;
+        x += count;
+    } 
+}
+
+void SkA8_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip)
+{
+    if (mask.fFormat == SkMask::kBW_Format)
+    {
+        this->INHERITED::blitMask(mask, clip);
+        return;
+    }
+    
+    int x = clip.fLeft;
+    int y = clip.fTop;
+    int width = clip.width();
+    int height = clip.height();
+    uint8_t* device = fDevice.getAddr8(x, y);
+    const uint8_t* alpha = mask.getAddr(x, y);
+
+    SkPMColor*  span = fBuffer;
+
+    while (--height >= 0)
+    {
+        fShader->shadeSpan(x, y, span, width);
+        fXfermode->xferA8(device, span, width, alpha);
+        
+        y += 1;
+        device += fDevice.rowBytes();
+        alpha += mask.fRowBytes;
+    }
+}
+
diff --git a/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp
new file mode 100644
index 0000000..ed2fc0e
--- /dev/null
+++ b/src/core/SkBlitter_ARGB32.cpp
@@ -0,0 +1,485 @@
+/* libs/graphics/sgl/SkBlitter_ARGB32.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 "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+SkARGB32_Blitter::SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint)
+        : INHERITED(device) {
+    uint32_t color = paint.getColor();
+
+    fSrcA = SkColorGetA(color);
+    unsigned scale = SkAlpha255To256(fSrcA);
+    fSrcR = SkAlphaMul(SkColorGetR(color), scale);
+    fSrcG = SkAlphaMul(SkColorGetG(color), scale);
+    fSrcB = SkAlphaMul(SkColorGetB(color), scale);
+
+    fPMColor = SkPackARGB32(fSrcA, fSrcR, fSrcG, fSrcB);
+}
+
+const SkBitmap* SkARGB32_Blitter::justAnOpaqueColor(uint32_t* value) {
+    if (255 == fSrcA) {
+        *value = fPMColor;
+        return &fDevice;
+    }
+    return NULL;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable warning : local variable used without having been initialized
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+void SkARGB32_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
+
+    if (fSrcA == 0) {
+        return;
+    }
+
+    uint32_t* device = fDevice.getAddr32(x, y);
+
+    if (fSrcA == 255) {
+        sk_memset32(device, fPMColor, width);
+    } else {
+        uint32_t color = fPMColor;
+        unsigned dst_scale = SkAlpha255To256(255 - fSrcA);
+        uint32_t prevDst = ~device[0];  // so we always fail the test the first time
+        uint32_t result SK_INIT_TO_AVOID_WARNING;
+
+        for (int i = 0; i < width; i++) {
+            uint32_t currDst = device[i];
+            if (currDst != prevDst) {
+                result = color + SkAlphaMulQ(currDst, dst_scale);
+                prevDst = currDst;
+            }
+            device[i] = result;
+        }
+    }
+}
+
+void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+                                 const int16_t runs[]) {
+    if (fSrcA == 0) {
+        return;
+    }
+
+    uint32_t    color = fPMColor;
+    uint32_t*   device = fDevice.getAddr32(x, y);
+    unsigned    opaqueMask = fSrcA; // if fSrcA is 0xFF, then we will catch the fast opaque case
+
+    for (;;) {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count <= 0) {
+            return;
+        }
+        unsigned aa = antialias[0];
+        if (aa) {
+            if ((opaqueMask & aa) == 255) {
+                sk_memset32(device, color, count);
+            } else {
+                uint32_t sc = SkAlphaMulQ(color, aa);
+                unsigned dst_scale = 255 - SkGetPackedA32(sc);
+                int n = count;
+                do {
+                    --n;
+                    device[n] = sc + SkAlphaMulQ(device[n], dst_scale);
+                } while (n > 0);
+            }
+        }
+        runs += count;
+        antialias += count;
+        device += count;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+#define solid_8_pixels(mask, dst, color)    \
+    do {                                    \
+        if (mask & 0x80) dst[0] = color;    \
+        if (mask & 0x40) dst[1] = color;    \
+        if (mask & 0x20) dst[2] = color;    \
+        if (mask & 0x10) dst[3] = color;    \
+        if (mask & 0x08) dst[4] = color;    \
+        if (mask & 0x04) dst[5] = color;    \
+        if (mask & 0x02) dst[6] = color;    \
+        if (mask & 0x01) dst[7] = color;    \
+    } while (0)
+
+#define SK_BLITBWMASK_NAME                  SkARGB32_BlitBW
+#define SK_BLITBWMASK_ARGS                  , SkPMColor color
+#define SK_BLITBWMASK_BLIT8(mask, dst)      solid_8_pixels(mask, dst, color)
+#define SK_BLITBWMASK_GETADDR               getAddr32
+#define SK_BLITBWMASK_DEVTYPE               uint32_t
+#include "SkBlitBWMaskTemplate.h"
+
+#define blend_8_pixels(mask, dst, sc, dst_scale)                            \
+    do {                                                                    \
+        if (mask & 0x80) { dst[0] = sc + SkAlphaMulQ(dst[0], dst_scale); }  \
+        if (mask & 0x40) { dst[1] = sc + SkAlphaMulQ(dst[1], dst_scale); }  \
+        if (mask & 0x20) { dst[2] = sc + SkAlphaMulQ(dst[2], dst_scale); }  \
+        if (mask & 0x10) { dst[3] = sc + SkAlphaMulQ(dst[3], dst_scale); }  \
+        if (mask & 0x08) { dst[4] = sc + SkAlphaMulQ(dst[4], dst_scale); }  \
+        if (mask & 0x04) { dst[5] = sc + SkAlphaMulQ(dst[5], dst_scale); }  \
+        if (mask & 0x02) { dst[6] = sc + SkAlphaMulQ(dst[6], dst_scale); }  \
+        if (mask & 0x01) { dst[7] = sc + SkAlphaMulQ(dst[7], dst_scale); }  \
+    } while (0)
+
+#define SK_BLITBWMASK_NAME                  SkARGB32_BlendBW
+#define SK_BLITBWMASK_ARGS                  , uint32_t sc, unsigned dst_scale
+#define SK_BLITBWMASK_BLIT8(mask, dst)      blend_8_pixels(mask, dst, sc, dst_scale)
+#define SK_BLITBWMASK_GETADDR               getAddr32
+#define SK_BLITBWMASK_DEVTYPE               uint32_t
+#include "SkBlitBWMaskTemplate.h"
+
+void SkARGB32_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
+    SkASSERT(mask.fBounds.contains(clip));
+    SkASSERT(fSrcA != 0xFF);
+
+    if (fSrcA == 0) {
+        return;
+    }
+
+    if (mask.fFormat == SkMask::kBW_Format) {
+        SkARGB32_BlendBW(fDevice, mask, clip, fPMColor, SkAlpha255To256(255 - fSrcA));
+        return;
+    }
+
+    int x = clip.fLeft;
+    int y = clip.fTop;
+    int width = clip.width();
+    int height = clip.height();
+
+    uint32_t*       device = fDevice.getAddr32(x, y);
+    const uint8_t*  alpha = mask.getAddr(x, y);
+    uint32_t        srcColor = fPMColor;
+    unsigned        devRB = fDevice.rowBytes() - (width << 2);
+    unsigned        maskRB = mask.fRowBytes - width;
+
+    do {
+        int w = width;
+        do {
+            unsigned aa = *alpha++;
+            *device = SkBlendARGB32(srcColor, *device, aa);
+            device += 1;
+        } while (--w != 0);
+        device = (uint32_t*)((char*)device + devRB);
+        alpha += maskRB;
+    } while (--height != 0);
+}
+
+void SkARGB32_Opaque_Blitter::blitMask(const SkMask& mask,
+                                       const SkIRect& clip) {
+    SkASSERT(mask.fBounds.contains(clip));
+
+    if (mask.fFormat == SkMask::kBW_Format) {
+        SkARGB32_BlitBW(fDevice, mask, clip, fPMColor);
+        return;
+    }
+
+    int x = clip.fLeft;
+    int y = clip.fTop;
+    int width = clip.width();
+    int height = clip.height();
+
+    uint32_t*       device = fDevice.getAddr32(x, y);
+    const uint8_t*  alpha = mask.getAddr(x, y);
+    uint32_t        srcColor = fPMColor;
+    unsigned        devRB = fDevice.rowBytes() - (width << 2);
+    unsigned        maskRB = mask.fRowBytes - width;
+
+    do {
+        int w = width;
+        do {
+            unsigned aa = *alpha++;
+            *device = SkAlphaMulQ(srcColor, SkAlpha255To256(aa)) + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa));
+            device += 1;
+        } while (--w != 0);
+        device = (uint32_t*)((char*)device + devRB);
+        alpha += maskRB;
+    } while (--height != 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+void SkARGB32_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    if (alpha == 0 || fSrcA == 0) {
+        return;
+    }
+
+    uint32_t* device = fDevice.getAddr32(x, y);
+    uint32_t  color = fPMColor;
+
+    if (alpha != 255) {
+        color = SkAlphaMulQ(color, SkAlpha255To256(alpha));
+    }
+
+    unsigned dst_scale = 255 - SkGetPackedA32(color);
+    uint32_t prevDst = ~device[0];
+    uint32_t result  SK_INIT_TO_AVOID_WARNING;
+    uint32_t rowBytes = fDevice.rowBytes();
+
+    while (--height >= 0) {
+        uint32_t dst = device[0];
+        if (dst != prevDst) {
+            result = color + SkAlphaMulQ(dst, dst_scale);
+            prevDst = dst;
+        }
+        device[0] = result;
+        device = (uint32_t*)((char*)device + rowBytes);
+    }
+}
+
+void SkARGB32_Blitter::blitRect(int x, int y, int width, int height) {
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width() && y + height <= fDevice.height());
+
+    if (fSrcA == 0) {
+        return;
+    }
+
+    uint32_t*   device = fDevice.getAddr32(x, y);
+    uint32_t    color = fPMColor;
+
+    if (fSrcA == 255) {
+        while (--height >= 0) {
+            sk_memset32(device, color, width);
+            device = (uint32_t*)((char*)device + fDevice.rowBytes());
+        }
+    } else {
+        unsigned dst_scale = SkAlpha255To256(255 - fSrcA);
+
+        while (--height >= 0) {
+            uint32_t prevDst = ~device[0];
+            uint32_t result SK_INIT_TO_AVOID_WARNING;
+
+            for (int i = 0; i < width; i++) {
+                uint32_t dst = device[i];
+                if (dst != prevDst) {
+                    result = color + SkAlphaMulQ(dst, dst_scale);
+                    prevDst = dst;
+                }
+                device[i] = result;
+            }
+            device = (uint32_t*)((char*)device + fDevice.rowBytes());
+        }
+    }
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+///////////////////////////////////////////////////////////////////////
+
+void SkARGB32_Black_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
+    SkASSERT(mask.fBounds.contains(clip));
+
+    if (mask.fFormat == SkMask::kBW_Format) {
+        SkPMColor black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT);
+
+        SkARGB32_BlitBW(fDevice, mask, clip, black);
+    } else {
+        uint32_t*       device = fDevice.getAddr32(clip.fLeft, clip.fTop);
+        const uint8_t*  alpha = mask.getAddr(clip.fLeft, clip.fTop);
+        unsigned        width = clip.width();
+        unsigned        height = clip.height();
+        unsigned        deviceRB = fDevice.rowBytes() - (width << 2);
+        unsigned        maskRB = mask.fRowBytes - width;
+
+        SkASSERT((int)height > 0);
+        SkASSERT((int)width > 0);
+        SkASSERT((int)deviceRB >= 0);
+        SkASSERT((int)maskRB >= 0);
+
+        do {
+            unsigned w = width;
+            do {
+                unsigned aa = *alpha++;
+                *device = (aa << SK_A32_SHIFT) + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa));
+                device += 1;
+            } while (--w != 0);
+            device = (uint32_t*)((char*)device + deviceRB);
+            alpha += maskRB;
+        } while (--height != 0);
+    }
+}
+
+void SkARGB32_Black_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+                                       const int16_t runs[]) {
+    uint32_t*   device = fDevice.getAddr32(x, y);
+    SkPMColor   black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT);
+
+    for (;;) {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count <= 0) {
+            return;
+        }
+        unsigned aa = antialias[0];
+        if (aa) {
+            if (aa == 255) {
+                sk_memset32(device, black, count);
+            } else {
+                SkPMColor src = aa << SK_A32_SHIFT;
+                unsigned dst_scale = 256 - aa;
+                int n = count;
+                do {
+                    --n;
+                    device[n] = src + SkAlphaMulQ(device[n], dst_scale);
+                } while (n > 0);
+            }
+        }
+        runs += count;
+        antialias += count;
+        device += count;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+SkARGB32_Shader_Blitter::SkARGB32_Shader_Blitter(const SkBitmap& device,
+                                                 const SkPaint& paint)
+        : INHERITED(device, paint) {
+    fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor)));
+
+    (fXfermode = paint.getXfermode())->safeRef();
+}
+
+SkARGB32_Shader_Blitter::~SkARGB32_Shader_Blitter() {
+    fXfermode->safeUnref();
+    sk_free(fBuffer);
+}
+
+void SkARGB32_Shader_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
+
+    uint32_t*   device = fDevice.getAddr32(x, y);
+
+    if (fXfermode == NULL && (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+        fShader->shadeSpan(x, y, device, width);
+    } else {
+        SkPMColor*  span = fBuffer;
+        fShader->shadeSpan(x, y, span, width);
+        if (fXfermode) {
+            fXfermode->xfer32(device, span, width, NULL);
+        } else {
+            for (int i = 0; i < width; i++) {
+                uint32_t src = span[i];
+                if (src) {
+                    unsigned srcA = SkGetPackedA32(src);
+                    if (srcA != 0xFF) {
+                        src += SkAlphaMulQ(device[i], SkAlpha255To256(255 - srcA));
+                    }
+                    device[i] = src;
+                }
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkARGB32_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
+                                        const int16_t runs[]) {
+    SkPMColor*  span = fBuffer;
+    uint32_t*   device = fDevice.getAddr32(x, y);
+    SkShader*   shader = fShader;
+
+    if (fXfermode) {
+        for (;;) {
+            SkXfermode* xfer = fXfermode;
+
+            int count = *runs;
+            if (count <= 0)
+                break;
+            int aa = *antialias;
+            if (aa) {
+                shader->shadeSpan(x, y, span, count);
+                if (aa == 255) {
+                    xfer->xfer32(device, span, count, NULL);
+                } else {
+                    // count is almost always 1
+                    for (int i = count - 1; i >= 0; --i) {
+                        xfer->xfer32(&device[i], &span[i], 1, antialias);
+                    }
+                }
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        } 
+    } else if (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) {
+        for (;;) {
+            int count = *runs;
+            if (count <= 0) {
+                break;
+            }
+            int aa = *antialias;
+            if (aa) {
+                if (aa == 255) {  // cool, have the shader draw right into the device
+                    shader->shadeSpan(x, y, device, count);
+                } else {
+                    shader->shadeSpan(x, y, span, count);
+                    for (int i = count - 1; i >= 0; --i) {
+                        if (span[i]) {
+                            device[i] = SkBlendARGB32(span[i], device[i], aa);
+                        }
+                    }
+                }
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        } 
+    } else {    // no xfermode but we are not opaque
+        for (;;) {
+            int count = *runs;
+            if (count <= 0) {
+                break;
+            }
+            int aa = *antialias;
+            if (aa) {
+                fShader->shadeSpan(x, y, span, count);
+                if (aa == 255) {
+                    for (int i = count - 1; i >= 0; --i) {
+                        if (span[i]) {
+                            device[i] = SkPMSrcOver(span[i], device[i]);
+                        }
+                    }
+                } else {
+                    for (int i = count - 1; i >= 0; --i) {
+                        if (span[i]) {
+                            device[i] = SkBlendARGB32(span[i], device[i], aa);
+                        }
+                    }
+                }
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        } 
+    }
+}
+
diff --git a/src/core/SkBlitter_RGB16.cpp b/src/core/SkBlitter_RGB16.cpp
new file mode 100644
index 0000000..b253662
--- /dev/null
+++ b/src/core/SkBlitter_RGB16.cpp
@@ -0,0 +1,782 @@
+/* libs/graphics/sgl/SkBlitter_RGB16.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 "SkBlitRow.h"
+#include "SkCoreBlitters.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+void sk_dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
+                        int count) {
+    if (count > 0) {
+        // see if we need to write one short before we can cast to an 4byte ptr
+        // (we do this subtract rather than (unsigned)dst so we don't get warnings
+        //  on 64bit machines)
+        if (((char*)dst - (char*)0) & 2) {
+            *dst++ = value;
+            count -= 1;
+            SkTSwap(value, other);
+        }
+        
+        // fast way to set [value,other] pairs
+#ifdef SK_CPU_BENDIAN
+        sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
+#else
+        sk_memset32((uint32_t*)dst, (other << 16) | value, count >> 1);
+#endif
+        
+        if (count & 1) {
+            dst[count - 1] = value;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Black_Blitter::SkRGB16_Black_Blitter(const SkBitmap& device, const SkPaint& paint)
+    : SkRGB16_Blitter(device, paint) {
+    SkASSERT(paint.getShader() == NULL);
+    SkASSERT(paint.getColorFilter() == NULL);
+    SkASSERT(paint.getXfermode() == NULL);
+    SkASSERT(paint.getColor() == SK_ColorBLACK);
+}
+
+#if 1
+#define black_8_pixels(mask, dst)       \
+    do {                                \
+        if (mask & 0x80) dst[0] = 0;    \
+        if (mask & 0x40) dst[1] = 0;    \
+        if (mask & 0x20) dst[2] = 0;    \
+        if (mask & 0x10) dst[3] = 0;    \
+        if (mask & 0x08) dst[4] = 0;    \
+        if (mask & 0x04) dst[5] = 0;    \
+        if (mask & 0x02) dst[6] = 0;    \
+        if (mask & 0x01) dst[7] = 0;    \
+    } while (0)
+#else
+static inline black_8_pixels(U8CPU mask, uint16_t dst[])
+{
+    if (mask & 0x80) dst[0] = 0;
+    if (mask & 0x40) dst[1] = 0;
+    if (mask & 0x20) dst[2] = 0;
+    if (mask & 0x10) dst[3] = 0;
+    if (mask & 0x08) dst[4] = 0;
+    if (mask & 0x04) dst[5] = 0;
+    if (mask & 0x02) dst[6] = 0;
+    if (mask & 0x01) dst[7] = 0;
+}
+#endif
+
+#define SK_BLITBWMASK_NAME                  SkRGB16_Black_BlitBW
+#define SK_BLITBWMASK_ARGS
+#define SK_BLITBWMASK_BLIT8(mask, dst)      black_8_pixels(mask, dst)
+#define SK_BLITBWMASK_GETADDR               getAddr16
+#define SK_BLITBWMASK_DEVTYPE               uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+void SkRGB16_Black_Blitter::blitMask(const SkMask& SK_RESTRICT mask,
+                                     const SkIRect& SK_RESTRICT clip)
+                                     SK_RESTRICT {
+    if (mask.fFormat == SkMask::kBW_Format) {
+        SkRGB16_Black_BlitBW(fDevice, mask, clip);
+    } else {
+        uint16_t* SK_RESTRICT device = fDevice.getAddr16(clip.fLeft, clip.fTop);
+        const uint8_t* SK_RESTRICT alpha = mask.getAddr(clip.fLeft, clip.fTop);
+        unsigned width = clip.width();
+        unsigned height = clip.height();
+        unsigned deviceRB = fDevice.rowBytes() - (width << 1);
+        unsigned maskRB = mask.fRowBytes - width;
+
+        SkASSERT((int)height > 0);
+        SkASSERT((int)width > 0);
+        SkASSERT((int)deviceRB >= 0);
+        SkASSERT((int)maskRB >= 0);
+
+        do {
+            unsigned w = width;
+            do {
+                unsigned aa = *alpha++;
+                *device = SkAlphaMulRGB16(*device, SkAlpha255To256(255 - aa));
+                device += 1;
+            } while (--w != 0);
+            device = (uint16_t*)((char*)device + deviceRB);
+            alpha += maskRB;
+        } while (--height != 0);
+    }
+}
+
+void SkRGB16_Black_Blitter::blitAntiH(int x, int y,
+                                      const SkAlpha* SK_RESTRICT antialias,
+                                      const int16_t* SK_RESTRICT runs)
+                                      SK_RESTRICT {
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+    for (;;) {
+        int count = runs[0];
+        SkASSERT(count >= 0);
+        if (count <= 0) {
+            return;
+        }
+        runs += count;
+
+        unsigned aa = antialias[0];
+        antialias += count;
+        if (aa) {
+            if (aa == 255) {
+                memset(device, 0, count << 1);
+            } else {
+                aa = SkAlpha255To256(255 - aa);
+                do {
+                    *device = SkAlphaMulRGB16(*device, aa);
+                    device += 1;
+                } while (--count != 0);
+                continue;
+            }
+        }
+        device += count;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Blitter::SkRGB16_Blitter(const SkBitmap& device, const SkPaint& paint)
+    : INHERITED(device) {
+    SkColor color = paint.getColor();
+
+    fSrcColor32 = SkPreMultiplyColor(color);
+    fScale = SkAlpha255To256(SkColorGetA(color));
+
+    int r = SkColorGetR(color);
+    int g = SkColorGetG(color);
+    int b = SkColorGetB(color);
+
+    fRawColor16 = fRawDither16 = SkPack888ToRGB16(r, g, b);
+    // if we're dithered, use fRawDither16 to hold that.
+    if ((fDoDither = paint.isDither()) != false) {
+        fRawDither16 = SkDitherPack888ToRGB16(r, g, b);
+    }
+    
+    fColor16 = SkPackRGB16( SkAlphaMul(r, fScale) >> (8 - SK_R16_BITS),
+                            SkAlphaMul(g, fScale) >> (8 - SK_G16_BITS),
+                            SkAlphaMul(b, fScale) >> (8 - SK_B16_BITS));
+}
+
+const SkBitmap* SkRGB16_Blitter::justAnOpaqueColor(uint32_t* value) {
+    if (!fDoDither && 256 == fScale) {
+        *value = fRawColor16;
+        return &fDevice;
+    }
+    return NULL;
+}
+
+void SkRGB16_Blitter::blitH(int x, int y, int width) SK_RESTRICT {
+    SkASSERT(width > 0);
+    SkASSERT(x + width <= fDevice.width());
+
+    if (fScale == 0) {
+        return;
+    }
+
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+    uint16_t srcColor = fColor16;
+
+    if (256 == fScale) {
+        SkASSERT(fRawColor16 == srcColor);
+        if (fDoDither) {
+            uint16_t ditherColor = fRawDither16;
+            if ((x ^ y) & 1) {
+                SkTSwap(ditherColor, srcColor);
+            }
+            sk_dither_memset16(device, srcColor, ditherColor, width);
+        } else {
+            sk_memset16(device, srcColor, width);
+        }
+    } else {
+        // TODO: respect fDoDither
+        SkPMColor src32 = fSrcColor32;
+        do {
+            *device = SkSrcOver32To16(src32, *device);
+            device += 1;
+        } while (--width != 0);
+    }
+}
+
+// return 1 or 0 from a bool
+static int Bool2Int(bool value) {
+    return !!value;
+}
+
+void SkRGB16_Blitter::blitAntiH(int x, int y,
+                                const SkAlpha* SK_RESTRICT antialias,
+                                const int16_t* SK_RESTRICT runs) SK_RESTRICT {
+    if (fScale == 0) {
+        return;
+    }
+
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+    uint16_t    srcColor = fRawColor16;
+    unsigned    scale = fScale;
+    int         ditherInt = Bool2Int(fDoDither);
+
+    if (256 == scale) {
+        uint16_t    ditherColor = fRawDither16;
+        // if we have no dithering, this will always fail
+        if ((x ^ y) & ditherInt) {
+            SkTSwap(ditherColor, srcColor);
+        }
+        for (;;) {
+            int count = runs[0];
+            SkASSERT(count >= 0);
+            if (count <= 0) {
+                return;
+            }
+            runs += count;
+
+            unsigned aa = antialias[0];
+            antialias += count;
+            if (aa) {
+                if (aa == 255) {
+                    if (ditherInt) {
+                        sk_dither_memset16(device, srcColor,
+                                           ditherColor, count);
+                    } else {
+                        sk_memset16(device, srcColor, count);
+                    }
+                } else {
+                    // TODO: respect fDoDither
+                    unsigned scale5 = SkAlpha255To256(aa) >> 3;
+                    uint32_t src32 = SkExpand_rgb_16(srcColor) * scale5;
+                    scale5 = 32 - scale5; // now we can use it on the device
+                    int n = count;
+                    do {
+                        uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
+                        *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
+                    } while (--n != 0);
+                    goto DONE;
+                }
+            }
+            device += count;
+
+            DONE:
+            // if we have no dithering, this will always fail
+            if (count & ditherInt) {
+                SkTSwap(ditherColor, srcColor);
+            }
+        }
+    } else {
+        // TODO: respect fDoDither
+        for (;;) {
+            int count = runs[0];
+            SkASSERT(count >= 0);
+            if (count <= 0) {
+                return;
+            }
+            runs += count;
+
+            unsigned aa = antialias[0];
+            antialias += count;
+            if (aa) {
+                unsigned scale5 = SkAlpha255To256(aa) * scale >> (8 + 3);
+                uint32_t src32 =  SkExpand_rgb_16(srcColor) * scale5;
+                scale5 = 32 - scale5;
+                do {
+                    uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
+                    *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
+                } while (--count != 0);
+                continue;
+            }
+            device += count;
+        }
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+#define solid_8_pixels(mask, dst, color)    \
+    do {                                    \
+        if (mask & 0x80) dst[0] = color;    \
+        if (mask & 0x40) dst[1] = color;    \
+        if (mask & 0x20) dst[2] = color;    \
+        if (mask & 0x10) dst[3] = color;    \
+        if (mask & 0x08) dst[4] = color;    \
+        if (mask & 0x04) dst[5] = color;    \
+        if (mask & 0x02) dst[6] = color;    \
+        if (mask & 0x01) dst[7] = color;    \
+    } while (0)
+
+#define SK_BLITBWMASK_NAME                  SkRGB16_BlitBW
+#define SK_BLITBWMASK_ARGS                  , uint16_t color
+#define SK_BLITBWMASK_BLIT8(mask, dst)      solid_8_pixels(mask, dst, color)
+#define SK_BLITBWMASK_GETADDR               getAddr16
+#define SK_BLITBWMASK_DEVTYPE               uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+static inline void blend_8_pixels(U8CPU bw, uint16_t dst[], unsigned dst_scale,
+                                  U16CPU srcColor) {
+    if (bw & 0x80) dst[0] = srcColor + SkAlphaMulRGB16(dst[0], dst_scale);
+    if (bw & 0x40) dst[1] = srcColor + SkAlphaMulRGB16(dst[1], dst_scale);
+    if (bw & 0x20) dst[2] = srcColor + SkAlphaMulRGB16(dst[2], dst_scale);
+    if (bw & 0x10) dst[3] = srcColor + SkAlphaMulRGB16(dst[3], dst_scale);
+    if (bw & 0x08) dst[4] = srcColor + SkAlphaMulRGB16(dst[4], dst_scale);
+    if (bw & 0x04) dst[5] = srcColor + SkAlphaMulRGB16(dst[5], dst_scale);
+    if (bw & 0x02) dst[6] = srcColor + SkAlphaMulRGB16(dst[6], dst_scale);
+    if (bw & 0x01) dst[7] = srcColor + SkAlphaMulRGB16(dst[7], dst_scale);
+}
+
+#define SK_BLITBWMASK_NAME                  SkRGB16_BlendBW
+#define SK_BLITBWMASK_ARGS                  , unsigned dst_scale, U16CPU src_color
+#define SK_BLITBWMASK_BLIT8(mask, dst)      blend_8_pixels(mask, dst, dst_scale, src_color)
+#define SK_BLITBWMASK_GETADDR               getAddr16
+#define SK_BLITBWMASK_DEVTYPE               uint16_t
+#include "SkBlitBWMaskTemplate.h"
+
+static U16CPU blend_compact(uint32_t src32, uint32_t dst32, unsigned scale5) {
+    return SkCompact_rgb_16(dst32 + ((src32 - dst32) * scale5 >> 5));
+}
+
+void SkRGB16_Blitter::blitMask(const SkMask& SK_RESTRICT mask,
+                               const SkIRect& SK_RESTRICT clip) SK_RESTRICT {
+    if (fScale == 0) {
+        return;
+    }
+    if (mask.fFormat == SkMask::kBW_Format) {
+        if (fScale == 256) {
+            SkRGB16_BlitBW(fDevice, mask, clip, fColor16);
+        } else {
+            SkRGB16_BlendBW(fDevice, mask, clip, 256 - fScale, fColor16);
+        }
+        return;
+    }
+
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(clip.fLeft, clip.fTop);
+    const uint8_t* SK_RESTRICT alpha = mask.getAddr(clip.fLeft, clip.fTop);
+    int width = clip.width();
+    int height = clip.height();
+    unsigned    deviceRB = fDevice.rowBytes() - (width << 1);
+    unsigned    maskRB = mask.fRowBytes - width;
+    uint32_t    color32 = SkExpand_rgb_16(fRawColor16);
+
+    if (256 == fScale) {
+        do {
+            int w = width;
+            do {
+                *device = blend_compact(color32, SkExpand_rgb_16(*device),
+                                        SkAlpha255To256(*alpha++) >> 3);
+                device += 1;
+            } while (--w != 0);
+            device = (uint16_t*)((char*)device + deviceRB);
+            alpha += maskRB;
+        } while (--height != 0);
+    } else {   // scale < 256
+        unsigned scale256 = fScale;
+        do {
+            int w = width;
+            do {
+                unsigned aa = *alpha++;
+                unsigned scale = SkAlpha255To256(aa) * scale256 >> (8 + 3);
+                uint32_t src32 = color32 * scale;
+                uint32_t dst32 = SkExpand_rgb_16(*device) * (32 - scale);
+                *device++ = SkCompact_rgb_16((src32 + dst32) >> 5);
+            } while (--w != 0);
+            device = (uint16_t*)((char*)device + deviceRB);
+            alpha += maskRB;
+        } while (--height != 0);
+    }
+}
+
+void SkRGB16_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    if (fScale == 0) {
+        return;
+    }
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+    uint16_t    color16 = fRawColor16;
+    unsigned    deviceRB = fDevice.rowBytes();
+
+    if (alpha + fScale == (255 + 256)) {
+        if (fDoDither) {
+            uint16_t ditherColor = fRawDither16;
+            if ((x ^ y) & 1) {
+                SkTSwap(ditherColor, color16);
+            }
+            do {
+                device[0] = color16;
+                device = (uint16_t*)((char*)device + deviceRB);
+                SkTSwap(ditherColor, color16);
+            } while (--height != 0);
+        } else {
+            do {
+                device[0] = color16;
+                device = (uint16_t*)((char*)device + deviceRB);
+            } while (--height != 0);
+        }
+    } else {
+        // TODO: respect fDoDither
+        unsigned scale5 = SkAlpha255To256(alpha) * fScale >> (8 + 3);
+        uint32_t src32 =  SkExpand_rgb_16(color16) * scale5;
+        scale5 = 32 - scale5;
+        do {
+            uint32_t dst32 = SkExpand_rgb_16(*device) * scale5;
+            *device = SkCompact_rgb_16((src32 + dst32) >> 5);
+            device = (uint16_t*)((char*)device + deviceRB);
+        } while (--height != 0);
+    }
+}
+
+void SkRGB16_Blitter::blitRect(int x, int y, int width, int height) {
+    SkASSERT(x + width <= fDevice.width() && y + height <= fDevice.height());
+
+    if (fScale == 0) {
+        return;
+    }
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+    unsigned    deviceRB = fDevice.rowBytes();
+    uint16_t    color16 = fColor16;
+
+    if (256 == fScale) {
+        if (fDoDither) {
+            uint16_t ditherColor = fRawDither16;
+            if ((x ^ y) & 1) {
+                SkTSwap(ditherColor, color16);
+            }
+            while (--height >= 0) {
+                sk_dither_memset16(device, color16, ditherColor, width);
+                SkTSwap(ditherColor, color16);
+                device = (uint16_t*)((char*)device + deviceRB);
+            }
+        } else {  // no dither
+            while (--height >= 0) {
+                sk_memset16(device, color16, width);
+                device = (uint16_t*)((char*)device + deviceRB);
+            }
+        }
+    } else {
+        SkPMColor src32 = fSrcColor32;
+        while (--height >= 0) {
+            for (int i = width - 1; i >= 0; --i) {
+                device[i] = SkSrcOver32To16(src32, device[i]);
+            }
+            device = (uint16_t*)((char*)device + deviceRB);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Shader16_Blitter::SkRGB16_Shader16_Blitter(const SkBitmap& device,
+                                                   const SkPaint& paint)
+    : SkRGB16_Shader_Blitter(device, paint) {
+    SkASSERT(SkShader::CanCallShadeSpan16(fShader->getFlags()));
+}
+
+void SkRGB16_Shader16_Blitter::blitH(int x, int y, int width) SK_RESTRICT {
+    SkASSERT(x + width <= fDevice.width());
+
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+    SkShader*   shader = fShader;
+
+    int alpha = shader->getSpan16Alpha();
+    if (0xFF == alpha) {
+        shader->shadeSpan16(x, y, device, width);
+    } else {
+        uint16_t* span16 = (uint16_t*)fBuffer;
+        shader->shadeSpan16(x, y, span16, width);
+        SkBlendRGB16(span16, device, SkAlpha255To256(alpha), width);
+    }
+}
+
+void SkRGB16_Shader16_Blitter::blitAntiH(int x, int y,
+                                         const SkAlpha* SK_RESTRICT antialias,
+                                         const int16_t* SK_RESTRICT runs)
+                                         SK_RESTRICT {
+    SkShader*   shader = fShader;
+    SkPMColor* SK_RESTRICT span = fBuffer;
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+    int alpha = shader->getSpan16Alpha();
+    uint16_t* span16 = (uint16_t*)span;
+
+    if (0xFF == alpha) {
+        for (;;) {
+            int count = *runs;
+            if (count <= 0) {
+                break;
+            }
+            SkASSERT(count <= fDevice.width()); // don't overrun fBuffer
+
+            int aa = *antialias;
+            if (aa == 255) {
+                // go direct to the device!
+                shader->shadeSpan16(x, y, device, count);
+            } else if (aa) {
+                shader->shadeSpan16(x, y, span16, count);
+                SkBlendRGB16(span16, device, SkAlpha255To256(aa), count);
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        }
+    } else {  // span alpha is < 255
+        alpha = SkAlpha255To256(alpha);
+        for (;;) {
+            int count = *runs;
+            if (count <= 0) {
+                break;
+            }
+            SkASSERT(count <= fDevice.width()); // don't overrun fBuffer
+
+            int aa = SkAlphaMul(*antialias, alpha);
+            if (aa) {
+                shader->shadeSpan16(x, y, span16, count);
+                SkBlendRGB16(span16, device, SkAlpha255To256(aa), count);
+            }
+
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRGB16_Shader_Blitter::SkRGB16_Shader_Blitter(const SkBitmap& device,
+                                               const SkPaint& paint)
+: INHERITED(device, paint) {
+    SkASSERT(paint.getXfermode() == NULL);
+
+    fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * sizeof(SkPMColor));
+
+    // compute SkBlitRow::Procs
+    unsigned flags = 0;
+    
+    uint32_t shaderFlags = fShader->getFlags();
+    // shaders take care of global alpha, so we never set it in SkBlitRow
+    if (!(shaderFlags & SkShader::kOpaqueAlpha_Flag)) {
+        flags |= SkBlitRow::kSrcPixelAlpha_Flag;
+        }
+    // don't dither if the shader is really 16bit
+    if (paint.isDither() && !(shaderFlags & SkShader::kIntrinsicly16_Flag)) {
+        flags |= SkBlitRow::kDither_Flag;
+    }
+    // used when we know our global alpha is 0xFF
+    fOpaqueProc = SkBlitRow::Factory(flags, SkBitmap::kRGB_565_Config);
+    // used when we know our global alpha is < 0xFF
+    fAlphaProc  = SkBlitRow::Factory(flags | SkBlitRow::kGlobalAlpha_Flag,
+                                     SkBitmap::kRGB_565_Config);
+}
+
+SkRGB16_Shader_Blitter::~SkRGB16_Shader_Blitter() {
+    sk_free(fBuffer);
+}
+
+void SkRGB16_Shader_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x + width <= fDevice.width());
+
+    fShader->shadeSpan(x, y, fBuffer, width);
+    // shaders take care of global alpha, so we pass 0xFF (should be ignored)
+    fOpaqueProc(fDevice.getAddr16(x, y), fBuffer, width, 0xFF, x, y);
+}
+
+static inline int count_nonzero_span(const int16_t runs[], const SkAlpha aa[]) {
+    int count = 0;
+    for (;;) {
+        int n = *runs;
+        if (n == 0 || *aa == 0) {
+            break;
+        }
+        runs += n;
+        aa += n;
+        count += n;
+    }
+    return count;
+}
+
+void SkRGB16_Shader_Blitter::blitAntiH(int x, int y,
+                                       const SkAlpha* SK_RESTRICT antialias,
+                                       const int16_t* SK_RESTRICT runs)
+                                       SK_RESTRICT {
+    SkShader*   shader = fShader;
+    SkPMColor* SK_RESTRICT span = fBuffer;
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+    for (;;) {
+        int count = *runs;
+        if (count <= 0) {
+            break;
+        }
+        int aa = *antialias;
+        if (0 == aa) {
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+            continue;
+        }
+
+        int nonZeroCount = count + count_nonzero_span(runs + count, antialias + count);
+
+        SkASSERT(nonZeroCount <= fDevice.width()); // don't overrun fBuffer
+        shader->shadeSpan(x, y, span, nonZeroCount);
+
+        SkPMColor* localSpan = span;
+        for (;;) {
+            SkBlitRow::Proc proc = (aa == 0xFF) ? fOpaqueProc : fAlphaProc;
+            proc(device, localSpan, count, aa, x, y);
+
+            x += count;
+            device += count;
+            runs += count;
+            antialias += count;
+            nonZeroCount -= count;
+            if (nonZeroCount == 0) {
+                break;
+            }
+            localSpan += count;
+            SkASSERT(nonZeroCount > 0);
+            count = *runs;
+            SkASSERT(count > 0);
+            aa = *antialias;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////
+
+SkRGB16_Shader_Xfermode_Blitter::SkRGB16_Shader_Xfermode_Blitter(
+                                const SkBitmap& device, const SkPaint& paint)
+: INHERITED(device, paint) {
+    fXfermode = paint.getXfermode();
+    SkASSERT(fXfermode);
+    fXfermode->ref();
+
+    int width = device.width();
+    fBuffer = (SkPMColor*)sk_malloc_throw((width + (SkAlign4(width) >> 2)) * sizeof(SkPMColor));
+    fAAExpand = (uint8_t*)(fBuffer + width);
+}
+
+SkRGB16_Shader_Xfermode_Blitter::~SkRGB16_Shader_Xfermode_Blitter() {
+    fXfermode->unref();
+    sk_free(fBuffer);
+}
+
+void SkRGB16_Shader_Xfermode_Blitter::blitH(int x, int y, int width) {
+    SkASSERT(x + width <= fDevice.width());
+
+    uint16_t*   device = fDevice.getAddr16(x, y);
+    SkPMColor*  span = fBuffer;
+
+    fShader->shadeSpan(x, y, span, width);
+    fXfermode->xfer16(device, span, width, NULL);
+}
+
+void SkRGB16_Shader_Xfermode_Blitter::blitAntiH(int x, int y,
+                                const SkAlpha* SK_RESTRICT antialias,
+                                const int16_t* SK_RESTRICT runs) SK_RESTRICT {
+    SkShader*   shader = fShader;
+    SkXfermode* mode = fXfermode;
+    SkPMColor* SK_RESTRICT span = fBuffer;
+    uint8_t* SK_RESTRICT aaExpand = fAAExpand;
+    uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
+
+    for (;;) {
+        int count = *runs;
+        if (count <= 0) {
+            break;
+        }
+        int aa = *antialias;
+        if (0 == aa) {
+            device += count;
+            runs += count;
+            antialias += count;
+            x += count;
+            continue;
+        }
+
+        int nonZeroCount = count + count_nonzero_span(runs + count,
+                                                      antialias + count);
+
+        SkASSERT(nonZeroCount <= fDevice.width()); // don't overrun fBuffer
+        shader->shadeSpan(x, y, span, nonZeroCount);
+
+        x += nonZeroCount;
+        SkPMColor* localSpan = span;
+        for (;;) {
+            if (aa == 0xFF) {
+                mode->xfer16(device, localSpan, count, NULL);
+            } else {
+                SkASSERT(aa);
+                memset(aaExpand, aa, count);
+                mode->xfer16(device, localSpan, count, aaExpand);
+            }
+            device += count;
+            runs += count;
+            antialias += count;
+            nonZeroCount -= count;
+            if (nonZeroCount == 0) {
+                break;
+            }
+            localSpan += count;
+            SkASSERT(nonZeroCount > 0);
+            count = *runs;
+            SkASSERT(count > 0);
+            aa = *antialias;
+        }
+    } 
+}
+
+////////////////////////
+
+#if 0
+static inline uint16_t aa_blendS32D16(SkPMColor src, U16CPU dst, int aa
+#ifdef DITHER_SHADER
+                                      , int dither
+#endif
+                                      )
+{
+    SkASSERT((unsigned)aa <= 255);
+    
+    int src_scale = SkAlpha255To256(aa);
+    int sa = SkGetPackedA32(src);
+    int dst_scale = SkAlpha255To256(255 - SkAlphaMul(sa, src_scale));
+    
+#ifdef DITHER_SHADER
+    int sr = SkGetPackedR32(src);
+    int sg = SkGetPackedG32(src);
+    int sb = SkGetPackedB32(src);
+    sr = SkDITHER_R32To16(sr, dither);
+    sg = SkDITHER_G32To16(sg, dither);
+    sb = SkDITHER_B32To16(sb, dither);
+#else
+    int sr = SkPacked32ToR16(src);
+    int sg = SkPacked32ToG16(src);
+    int sb = SkPacked32ToB16(src);
+#endif
+    
+    int dr = (sr * src_scale + SkGetPackedR16(dst) * dst_scale) >> 8;
+    int dg = (sg * src_scale + SkGetPackedG16(dst) * dst_scale) >> 8;
+    int db = (sb * src_scale + SkGetPackedB16(dst) * dst_scale) >> 8;
+    
+    return SkPackRGB16(dr, dg, db);
+}
+#endif
+
diff --git a/src/core/SkBlitter_Sprite.cpp b/src/core/SkBlitter_Sprite.cpp
new file mode 100644
index 0000000..f0da166
--- /dev/null
+++ b/src/core/SkBlitter_Sprite.cpp
@@ -0,0 +1,101 @@
+/* libs/graphics/sgl/SkBlitter_Sprite.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 "SkSpriteBlitter.h"
+
+SkSpriteBlitter::SkSpriteBlitter(const SkBitmap& source)
+    : fSource(&source)
+{
+    fSource->lockPixels();
+}
+
+SkSpriteBlitter::~SkSpriteBlitter()
+{
+    fSource->unlockPixels();
+}
+
+void SkSpriteBlitter::setup(const SkBitmap& device, int left, int top,
+                            const SkPaint& paint)
+{
+    fDevice = &device;
+    fLeft = left;
+    fTop = top;
+    fPaint = &paint;
+}
+
+#ifdef SK_DEBUG
+void SkSpriteBlitter::blitH(int x, int y, int width)
+{
+    SkASSERT(!"how did we get here?");
+}
+
+void SkSpriteBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[])
+{
+    SkASSERT(!"how did we get here?");
+}
+
+void SkSpriteBlitter::blitV(int x, int y, int height, SkAlpha alpha)
+{
+    SkASSERT(!"how did we get here?");
+}
+
+void SkSpriteBlitter::blitMask(const SkMask&, const SkIRect& clip)
+{
+    SkASSERT(!"how did we get here?");
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+// returning null means the caller will call SkBlitter::Choose() and
+// have wrapped the source bitmap inside a shader
+SkBlitter* SkBlitter::ChooseSprite( const SkBitmap& device,
+                                    const SkPaint& paint,
+                                    const SkBitmap& source,
+                                    int left, int top,
+                                    void* storage, size_t storageSize)
+{
+    /*  We currently ignore antialiasing and filtertype, meaning we will take our
+        special blitters regardless of these settings. Ignoring filtertype seems fine
+        since by definition there is no scale in the matrix. Ignoring antialiasing is
+        a bit of a hack, since we "could" pass in the fractional left/top for the bitmap,
+        and respect that by blending the edges of the bitmap against the device. To support
+        this we could either add more special blitters here, or detect antialiasing in the
+        paint and return null if it is set, forcing the client to take the slow shader case
+        (which does respect soft edges).
+    */
+
+    SkSpriteBlitter* blitter;
+
+    switch (device.getConfig()) {
+    case SkBitmap::kRGB_565_Config:
+        blitter = SkSpriteBlitter::ChooseD16(source, paint, storage, storageSize);
+        break;
+    case SkBitmap::kARGB_8888_Config:
+        blitter = SkSpriteBlitter::ChooseD32(source, paint, storage, storageSize);
+        break;
+    default:
+        blitter = NULL;
+        break;
+    }
+
+    if (blitter)
+        blitter->setup(device, left, top, paint);
+    return blitter;
+}
+
diff --git a/src/core/SkBuffer.cpp b/src/core/SkBuffer.cpp
new file mode 100644
index 0000000..5768ca4
--- /dev/null
+++ b/src/core/SkBuffer.cpp
@@ -0,0 +1,137 @@
+/* libs/corecg/SkBuffer.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 "SkBuffer.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void SkRBuffer::readNoSizeCheck(void* buffer, size_t size)
+{
+    SkASSERT((fData != 0 && fStop == 0) || fPos + size <= fStop);
+    if (buffer)
+        memcpy(buffer, fPos, size);
+    fPos += size;
+}
+
+const void* SkRBuffer::skip(size_t size)
+{
+    const void* result = fPos;
+    readNoSizeCheck(NULL, size);
+    return result;
+}
+
+size_t SkRBuffer::skipToAlign4()
+{
+    size_t pos = this->pos();
+    size_t n = SkAlign4(pos) - pos;
+    fPos += n;
+    return n;
+}
+
+void* SkWBuffer::skip(size_t size)
+{
+    void* result = fPos;
+    writeNoSizeCheck(NULL, size);
+    return fData == NULL ? NULL : result;
+}
+
+void SkWBuffer::writeNoSizeCheck(const void* buffer, size_t size)
+{
+    SkASSERT(fData == 0 || fStop == 0 || fPos + size <= fStop);
+    if (fData && buffer)
+        memcpy(fPos, buffer, size);
+    fPos += size;
+}
+
+size_t SkWBuffer::padToAlign4()
+{
+    size_t pos = this->pos();
+    size_t n = SkAlign4(pos) - pos;
+
+    if (n && fData)
+    {
+        char* p = fPos;
+        char* stop = p + n;
+        do {
+            *p++ = 0;
+        } while (p < stop);
+    }
+    fPos += n;
+    return n;
+}
+
+#if 0
+#ifdef SK_DEBUG
+    static void AssertBuffer32(const void* buffer)
+    {
+        SkASSERT(buffer);
+        SkASSERT(((size_t)buffer & 3) == 0);
+    }
+#else
+    #define AssertBuffer32(buffer)
+#endif
+
+void* sk_buffer_write_int32(void* buffer, int32_t value)
+{
+    AssertBuffer32(buffer);
+    *(int32_t*)buffer = value;
+    return (char*)buffer + sizeof(int32_t);
+}
+
+void* sk_buffer_write_int32(void* buffer, const int32_t values[], int count)
+{
+    AssertBuffer32(buffer);
+    SkASSERT(count >= 0);
+
+    memcpy((int32_t*)buffer, values, count * sizeof(int32_t));
+    return (char*)buffer + count * sizeof(int32_t);
+}
+
+const void* sk_buffer_read_int32(const void* buffer, int32_t* value)
+{
+    AssertBuffer32(buffer);
+    if (value)
+        *value = *(const int32_t*)buffer;
+    return (const char*)buffer + sizeof(int32_t);
+}
+
+const void* sk_buffer_read_int32(const void* buffer, int32_t values[], int count)
+{
+    AssertBuffer32(buffer);
+    SkASSERT(count >= 0);
+
+    if (values)
+        memcpy(values, (const int32_t*)buffer, count * sizeof(int32_t));
+    return (const char*)buffer + count * sizeof(int32_t);
+}
+
+void* sk_buffer_write_ptr(void* buffer, void* ptr)
+{
+    AssertBuffer32(buffer);
+    *(void**)buffer = ptr;
+    return (char*)buffer + sizeof(void*);
+}
+
+const void* sk_buffer_read_ptr(const void* buffer, void** ptr)
+{
+    AssertBuffer32(buffer);
+    if (ptr)
+        *ptr = *(void**)buffer;
+    return (const char*)buffer + sizeof(void*);
+}
+
+#endif
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
new file mode 100644
index 0000000..4088416
--- /dev/null
+++ b/src/core/SkCanvas.cpp
@@ -0,0 +1,1420 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkCanvas.h"
+#include "SkBounder.h"
+#include "SkDevice.h"
+#include "SkDraw.h"
+#include "SkDrawFilter.h"
+#include "SkDrawLooper.h"
+#include "SkPicture.h"
+#include "SkScalarCompare.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include <new>
+
+//#define SK_TRACE_SAVERESTORE
+
+#ifdef SK_TRACE_SAVERESTORE
+    static int gLayerCounter;
+    static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
+    static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
+
+    static int gRecCounter;
+    static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
+    static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
+
+    static int gCanvasCounter;
+    static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
+    static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
+#else
+    #define inc_layer()
+    #define dec_layer()
+    #define inc_rec()
+    #define dec_rec()
+    #define inc_canvas()
+    #define dec_canvas()
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for computing fast bounds for quickReject tests
+
+static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
+    return paint != NULL && paint->isAntiAlias() ?
+            SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  This is the record we keep for each SkDevice that the user installs.
+    The clip/matrix/proc are fields that reflect the top of the save/restore
+    stack. Whenever the canvas changes, it marks a dirty flag, and then before
+    these are used (assuming we're not on a layer) we rebuild these cache
+    values: they reflect the top of the save stack, but translated and clipped
+    by the device's XY offset and bitmap-bounds.
+*/
+struct DeviceCM {
+    DeviceCM*           fNext;
+    SkDevice*           fDevice;
+    SkRegion            fClip;
+    const SkMatrix*     fMatrix;
+	SkPaint*			fPaint;	// may be null (in the future)
+    int16_t             fX, fY; // relative to base matrix/clip
+
+	DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
+            : fNext(NULL) {
+        if (NULL != device) {
+            device->ref();
+            device->lockPixels();
+        }
+        fDevice = device;        
+        fX = SkToS16(x);
+        fY = SkToS16(y);
+        fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
+	}
+
+	~DeviceCM() {
+        if (NULL != fDevice) {
+            fDevice->unlockPixels();
+            fDevice->unref();
+        }
+		SkDELETE(fPaint);
+	}
+    
+    void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
+                  SkRegion* updateClip) {
+        int x = fX;
+        int y = fY;
+        int width = fDevice->width();
+        int height = fDevice->height();
+    
+        if ((x | y) == 0) {
+            fMatrix = &totalMatrix;
+            fClip = totalClip;
+        } else {
+            fMatrixStorage = totalMatrix;
+            fMatrixStorage.postTranslate(SkIntToScalar(-x),
+                                         SkIntToScalar(-y));
+            fMatrix = &fMatrixStorage;
+            
+            totalClip.translate(-x, -y, &fClip);
+        }
+
+        fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
+
+        // intersect clip, but don't translate it (yet)
+        
+        if (updateClip) {
+            updateClip->op(x, y, x + width, y + height,
+                           SkRegion::kDifference_Op);
+        }
+        
+        fDevice->setMatrixClip(*fMatrix, fClip);
+
+#ifdef SK_DEBUG
+        if (!fClip.isEmpty()) {
+            SkIRect deviceR;
+            deviceR.set(0, 0, width, height);
+            SkASSERT(deviceR.contains(fClip.getBounds()));
+        }
+#endif
+    }
+    
+    void translateClip() {
+        if (fX | fY) {
+            fClip.translate(fX, fY);
+        }
+    }
+
+private:
+    SkMatrix    fMatrixStorage;
+};
+
+/*  This is the record we keep for each save/restore level in the stack.
+    Since a level optionally copies the matrix and/or stack, we have pointers
+    for these fields. If the value is copied for this level, the copy is
+    stored in the ...Storage field, and the pointer points to that. If the
+    value is not copied for this level, we ignore ...Storage, and just point
+    at the corresponding value in the previous level in the stack.
+*/
+class SkCanvas::MCRec {
+public:
+    MCRec*          fNext;
+    SkMatrix*       fMatrix;    // points to either fMatrixStorage or prev MCRec
+    SkRegion*       fRegion;    // points to either fRegionStorage or prev MCRec
+    SkDrawFilter*   fFilter;    // the current filter (or null)
+    
+    DeviceCM*   fLayer;
+    /*  If there are any layers in the stack, this points to the top-most
+        one that is at or below this level in the stack (so we know what
+        bitmap/device to draw into from this level. This value is NOT
+        reference counted, since the real owner is either our fLayer field,
+        or a previous one in a lower level.)
+    */
+    DeviceCM*	fTopLayer;
+
+    MCRec(const MCRec* prev, int flags) {
+        if (NULL != prev) {
+            if (flags & SkCanvas::kMatrix_SaveFlag) {
+                fMatrixStorage = *prev->fMatrix;
+                fMatrix = &fMatrixStorage;
+            } else {
+                fMatrix = prev->fMatrix;
+            }
+            
+            if (flags & SkCanvas::kClip_SaveFlag) {
+                fRegionStorage = *prev->fRegion;
+                fRegion = &fRegionStorage;
+            } else {
+                fRegion = prev->fRegion;
+            }
+
+            fFilter = prev->fFilter;
+            fFilter->safeRef();
+
+            fTopLayer = prev->fTopLayer;
+        } else {   // no prev
+            fMatrixStorage.reset();
+            
+            fMatrix     = &fMatrixStorage;
+            fRegion     = &fRegionStorage;
+            fFilter     = NULL;
+            fTopLayer   = NULL;
+        }
+        fLayer = NULL;
+
+        // don't bother initializing fNext
+        inc_rec();
+    }
+    ~MCRec() {
+        fFilter->safeUnref();
+        SkDELETE(fLayer);
+        dec_rec();
+    }
+	
+private:
+    SkMatrix    fMatrixStorage;
+    SkRegion    fRegionStorage;
+};
+
+class SkDrawIter : public SkDraw {
+public:
+    SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
+        fCanvas = canvas;
+        canvas->updateDeviceCMCache();
+
+        fBounder = canvas->getBounder();
+        fCurrLayer = canvas->fMCRec->fTopLayer;
+        fSkipEmptyClips = skipEmptyClips;
+    }
+    
+    bool next() {
+        // skip over recs with empty clips
+        if (fSkipEmptyClips) {
+            while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
+                fCurrLayer = fCurrLayer->fNext;
+            }
+        }
+
+        if (NULL != fCurrLayer) {
+            const DeviceCM* rec = fCurrLayer;
+
+            fMatrix = rec->fMatrix;
+            fClip   = &rec->fClip;
+            fDevice = rec->fDevice;
+            fBitmap = &fDevice->accessBitmap(true);
+            fLayerX = rec->fX;
+            fLayerY = rec->fY;
+            fPaint  = rec->fPaint;
+            SkDEBUGCODE(this->validate();)
+
+            fCurrLayer = rec->fNext;
+            if (fBounder) {
+                fBounder->setClip(fClip);
+            }
+
+            // fCurrLayer may be NULL now
+            
+            fCanvas->prepareForDeviceDraw(fDevice);
+            return true;
+        }
+        return false;
+    }
+    
+    int getX() const { return fLayerX; }
+    int getY() const { return fLayerY; }
+    SkDevice* getDevice() const { return fDevice; }
+    const SkMatrix& getMatrix() const { return *fMatrix; }
+    const SkRegion& getClip() const { return *fClip; }
+    const SkPaint* getPaint() const { return fPaint; }
+private:
+    SkCanvas*       fCanvas;
+    const DeviceCM* fCurrLayer;
+    const SkPaint*  fPaint;     // May be null.
+    int             fLayerX;
+    int             fLayerY;
+    SkBool8         fSkipEmptyClips;
+
+    typedef SkDraw INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class AutoDrawLooper {
+public:
+    AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, SkDrawFilter::Type t)
+            : fCanvas(canvas), fPaint((SkPaint*)&paint), fType(t) {
+        if ((fLooper = paint.getLooper()) != NULL) {
+            fLooper->init(canvas, (SkPaint*)&paint);
+        } else {
+            fOnce = true;
+        }
+        fFilter = canvas->getDrawFilter();
+        fNeedFilterRestore = false;
+    }
+
+    ~AutoDrawLooper() {
+        if (fNeedFilterRestore) {
+            SkASSERT(fFilter);
+            fFilter->restore(fCanvas, fPaint, fType);
+        }
+        if (NULL != fLooper) {
+            fLooper->restore();
+        }
+    }
+    
+    bool next() {
+        SkDrawFilter* filter = fFilter;
+
+        // if we drew earlier with a filter, then we need to restore first
+        if (fNeedFilterRestore) {
+            SkASSERT(filter);
+            filter->restore(fCanvas, fPaint, fType);
+            fNeedFilterRestore = false;
+        }
+            
+        bool result;
+        
+        if (NULL != fLooper) {
+            result = fLooper->next();
+        } else {
+            result = fOnce;
+            fOnce = false;
+        }
+
+        // if we're gonna draw, give the filter a chance to do its work
+        if (result && NULL != filter) {
+            fNeedFilterRestore = result = filter->filter(fCanvas, fPaint,
+                                                         fType);
+        }
+        return result;
+    }
+    
+private:
+    SkDrawLooper*   fLooper;
+    SkDrawFilter*   fFilter;
+    SkCanvas*       fCanvas;
+    SkPaint*        fPaint;
+    SkDrawFilter::Type  fType;
+    bool            fOnce;
+    bool            fNeedFilterRestore;
+    
+};
+
+/*  Stack helper for managing a SkBounder. In the destructor, if we were
+    given a bounder, we call its commit() method, signifying that we are
+    done accumulating bounds for that draw.
+*/
+class SkAutoBounderCommit {
+public:
+    SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
+    ~SkAutoBounderCommit() {
+        if (NULL != fBounder) {
+            fBounder->commit();
+        }
+    }
+private:
+    SkBounder*  fBounder;
+};
+
+#include "SkColorPriv.h"
+
+class AutoValidator {
+public:
+    AutoValidator(SkDevice* device) : fDevice(device) {}
+    ~AutoValidator() {
+#ifdef SK_DEBUG
+        const SkBitmap& bm = fDevice->accessBitmap(false);
+        if (bm.config() == SkBitmap::kARGB_4444_Config) {
+            for (int y = 0; y < bm.height(); y++) {
+                const SkPMColor16* p = bm.getAddr16(0, y);
+                for (int x = 0; x < bm.width(); x++) {
+                    SkPMColor16 c = p[x];
+                    SkPMColor16Assert(c);
+                }
+            }
+        }
+#endif
+    }
+private:
+    SkDevice* fDevice;
+};
+
+////////// macros to place around the internal draw calls //////////////////
+
+#define ITER_BEGIN(paint, type)                                     \
+/*    AutoValidator   validator(fMCRec->fTopLayer->fDevice); */     \
+    AutoDrawLooper  looper(this, paint, type);                      \
+    while (looper.next()) {                                         \
+        SkAutoBounderCommit ac(fBounder);                           \
+        SkDrawIter          iter(this);
+    
+#define ITER_END    }
+
+////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::init(SkDevice* device) {
+    fBounder = NULL;
+    fLocalBoundsCompareTypeDirty = true;
+
+    fMCRec = (MCRec*)fMCStack.push_back();
+    new (fMCRec) MCRec(NULL, 0);
+
+    fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
+    fMCRec->fTopLayer = fMCRec->fLayer;
+    fMCRec->fNext = NULL;
+
+    return this->setDevice(device);
+}
+
+SkCanvas::SkCanvas(SkDevice* device)
+        : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+    inc_canvas();
+
+    this->init(device);
+}
+
+SkCanvas::SkCanvas(const SkBitmap& bitmap)
+        : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+    inc_canvas();
+
+    this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
+}
+
+SkCanvas::~SkCanvas() {
+    // free up the contents of our deque
+    this->restoreToCount(1);    // restore everything but the last
+    this->internalRestore();    // restore the last, since we're going away
+
+    fBounder->safeUnref();
+    
+    dec_canvas();
+}
+
+SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
+    SkRefCnt_SafeAssign(fBounder, bounder);
+    return bounder;
+}
+
+SkDrawFilter* SkCanvas::getDrawFilter() const {
+    return fMCRec->fFilter;
+}
+
+SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
+    SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
+    return filter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::getDevice() const {
+    // return root device
+    SkDeque::Iter   iter(fMCStack);
+    MCRec*          rec = (MCRec*)iter.next();    
+    SkASSERT(rec && rec->fLayer);
+    return rec->fLayer->fDevice;
+}
+
+SkDevice* SkCanvas::setDevice(SkDevice* device) {
+    // return root device
+    SkDeque::Iter   iter(fMCStack);
+    MCRec*          rec = (MCRec*)iter.next();    
+    SkASSERT(rec && rec->fLayer);
+    SkDevice*       rootDevice = rec->fLayer->fDevice;
+
+    if (rootDevice == device) {
+        return device;
+    }
+    
+    /* Notify the devices that they are going in/out of scope, so they can do
+       things like lock/unlock their pixels, etc.
+    */
+    if (device) {
+        device->lockPixels();
+    }
+    if (rootDevice) {
+        rootDevice->unlockPixels();
+    }
+
+    SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
+    rootDevice = device;
+
+    fDeviceCMDirty = true;
+    
+    /*  Now we update our initial region to have the bounds of the new device,
+        and then intersect all of the clips in our stack with these bounds,
+        to ensure that we can't draw outside of the device's bounds (and trash
+                                                                     memory).
+        
+    NOTE: this is only a partial-fix, since if the new device is larger than
+        the previous one, we don't know how to "enlarge" the clips in our stack,
+        so drawing may be artificially restricted. Without keeping a history of 
+        all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
+        reconstruct the correct clips, so this approximation will have to do.
+        The caller really needs to restore() back to the base if they want to
+        accurately take advantage of the new device bounds.
+    */
+
+    if (NULL == device) {
+        rec->fRegion->setEmpty();
+        while ((rec = (MCRec*)iter.next()) != NULL) {
+            (void)rec->fRegion->setEmpty();
+        }
+    } else {
+        // compute our total bounds for all devices
+        SkIRect bounds;
+        
+        bounds.set(0, 0, device->width(), device->height());
+
+        // now jam our 1st clip to be bounds, and intersect the rest with that
+        rec->fRegion->setRect(bounds);
+        while ((rec = (MCRec*)iter.next()) != NULL) {
+            (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
+        }
+    }
+    return device;
+}
+
+SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
+    SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
+    device->unref();
+    return device;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::getViewport(SkIPoint* size) const {
+    return false;
+}
+
+bool SkCanvas::setViewport(int width, int height) {
+    return false;
+}
+
+void SkCanvas::updateDeviceCMCache() {
+    if (fDeviceCMDirty) {
+        const SkMatrix& totalMatrix = this->getTotalMatrix();
+        const SkRegion& totalClip = this->getTotalClip();
+        DeviceCM*       layer = fMCRec->fTopLayer;
+        
+        if (NULL == layer->fNext) {   // only one layer
+            layer->updateMC(totalMatrix, totalClip, NULL);
+        } else {
+            SkRegion clip;
+            clip = totalClip;  // make a copy
+            do {
+                layer->updateMC(totalMatrix, clip, &clip);
+            } while ((layer = layer->fNext) != NULL);
+        }
+        fDeviceCMDirty = false;
+    }
+}
+
+void SkCanvas::prepareForDeviceDraw(SkDevice* device) {
+    SkASSERT(device);
+    device->gainFocus(this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkCanvas::internalSave(SaveFlags flags) {
+    int saveCount = this->getSaveCount(); // record this before the actual save
+    
+    MCRec* newTop = (MCRec*)fMCStack.push_back();
+    new (newTop) MCRec(fMCRec, flags);    // balanced in restore()
+    
+    newTop->fNext = fMCRec;
+    fMCRec = newTop;
+    
+    return saveCount;
+}
+
+int SkCanvas::save(SaveFlags flags) {
+    // call shared impl
+    return this->internalSave(flags);
+}
+
+#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
+#define C16MASK (1 << SkBitmap::kRGB_565_Config)
+#define C8MASK  (1 << SkBitmap::kA8_Config)
+
+static SkBitmap::Config resolve_config(SkCanvas* canvas,
+                                       const SkIRect& bounds,
+                                       SkCanvas::SaveFlags flags,
+                                       bool* isOpaque) {
+    *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
+
+#if 0
+    // loop through and union all the configs we may draw into
+    uint32_t configMask = 0;
+    for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
+    {
+        SkDevice* device = canvas->getLayerDevice(i);
+        if (device->intersects(bounds))
+            configMask |= 1 << device->config();
+    }
+
+    // if the caller wants alpha or fullcolor, we can't return 565
+    if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
+                 SkCanvas::kHasAlphaLayer_SaveFlag))
+        configMask &= ~C16MASK;
+
+    switch (configMask) {
+    case C8MASK:    // if we only have A8, return that
+        return SkBitmap::kA8_Config;
+
+    case C16MASK:   // if we only have 565, return that
+        return SkBitmap::kRGB_565_Config;
+
+    default:
+        return SkBitmap::kARGB_8888_Config; // default answer
+    }
+#else
+    return SkBitmap::kARGB_8888_Config; // default answer
+#endif
+}
+
+static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
+    return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
+}
+
+int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                        SaveFlags flags) {
+    // do this before we create the layer. We don't call the public save() since
+    // that would invoke a possibly overridden virtual
+    int count = this->internalSave(flags);
+
+    fDeviceCMDirty = true;
+
+    SkIRect         ir;
+    const SkIRect&  clipBounds = this->getTotalClip().getBounds();
+
+    if (NULL != bounds) {
+        SkRect r;
+        
+        this->getTotalMatrix().mapRect(&r, *bounds);
+        r.roundOut(&ir);
+        // early exit if the layer's bounds are clipped out
+        if (!ir.intersect(clipBounds)) {
+            if (bounds_affects_clip(flags))
+                fMCRec->fRegion->setEmpty();
+            return count;
+        }
+    } else {    // no user bounds, so just use the clip
+        ir = clipBounds;
+    }
+
+    // early exit if the clip is now empty
+    if (bounds_affects_clip(flags) &&
+        !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
+        return count;
+    }
+
+    bool isOpaque;
+    SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
+
+    SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
+                                          isOpaque, true);
+    DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
+    device->unref();
+
+    layer->fNext = fMCRec->fTopLayer;
+    fMCRec->fLayer = layer;
+    fMCRec->fTopLayer = layer;    // this field is NOT an owner of layer
+
+    return count;
+}
+
+int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
+                             SaveFlags flags) {
+    if (0xFF == alpha) {
+        return this->saveLayer(bounds, NULL, flags);
+    } else {
+        SkPaint tmpPaint;
+        tmpPaint.setAlpha(alpha);
+        return this->saveLayer(bounds, &tmpPaint, flags);
+    }
+}
+
+void SkCanvas::restore() {
+    // check for underflow
+    if (fMCStack.count() > 1) {
+        this->internalRestore();
+    }
+}
+
+void SkCanvas::internalRestore() {
+    SkASSERT(fMCStack.count() != 0);
+
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+
+	// reserve our layer (if any)
+    DeviceCM* layer = fMCRec->fLayer;   // may be null
+    // now detach it from fMCRec so we can pop(). Gets freed after its drawn
+    fMCRec->fLayer = NULL;
+
+    // now do the normal restore()
+    fMCRec->~MCRec();       // balanced in save()
+    fMCStack.pop_back();
+    fMCRec = (MCRec*)fMCStack.back();
+
+    /*  Time to draw the layer's offscreen. We can't call the public drawSprite,
+        since if we're being recorded, we don't want to record this (the
+        recorder will have already recorded the restore).
+    */
+    if (NULL != layer) {
+        if (layer->fNext) {
+            this->drawDevice(layer->fDevice, layer->fX, layer->fY,
+                             layer->fPaint);
+            // reset this, since drawDevice will have set it to true
+            fDeviceCMDirty = true;
+        }
+        SkDELETE(layer);
+	}
+}
+
+int SkCanvas::getSaveCount() const {
+    return fMCStack.count();
+}
+
+void SkCanvas::restoreToCount(int count) {
+    // sanity check
+    if (count < 1) {
+        count = 1;
+    }
+    while (fMCStack.count() > count) {
+        this->restore();
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+// can't draw it if its empty, or its too big for a fixed-point width or height
+static bool reject_bitmap(const SkBitmap& bitmap) {
+    return  bitmap.width() <= 0 || bitmap.height() <= 0 ||
+            bitmap.width() > 32767 || bitmap.height() > 32767;
+}
+
+void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
+                                const SkMatrix& matrix, const SkPaint* paint) {
+    if (reject_bitmap(bitmap)) {
+        return;
+    }
+
+    if (NULL == paint) {
+        SkPaint tmpPaint;
+        this->commonDrawBitmap(bitmap, matrix, tmpPaint);
+    } else {
+        this->commonDrawBitmap(bitmap, matrix, *paint);
+    }
+}
+
+void SkCanvas::drawDevice(SkDevice* device, int x, int y,
+                          const SkPaint* paint) {
+    SkPaint tmp;
+    if (NULL == paint) {
+        tmp.setDither(true);
+        paint = &tmp;
+    }
+    
+    ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+    while (iter.next()) {
+        iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
+                                 *paint);
+    }
+    ITER_END
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preTranslate(dx, dy);
+}
+
+bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preScale(sx, sy);
+}
+
+bool SkCanvas::rotate(SkScalar degrees) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preRotate(degrees);
+}
+
+bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preSkew(sx, sy);
+}
+
+bool SkCanvas::concat(const SkMatrix& matrix) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preConcat(matrix);
+}
+
+void SkCanvas::setMatrix(const SkMatrix& matrix) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    *fMCRec->fMatrix = matrix;
+}
+
+// this is not virtual, so it must call a virtual method so that subclasses
+// will see its action
+void SkCanvas::resetMatrix() {
+    SkMatrix matrix;
+    
+    matrix.reset();
+    this->setMatrix(matrix);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+
+    if (fMCRec->fMatrix->rectStaysRect()) {
+        SkRect      r;
+        SkIRect     ir;
+
+        fMCRec->fMatrix->mapRect(&r, rect);
+        r.round(&ir);
+        return fMCRec->fRegion->op(ir, op);
+    } else {
+        SkPath  path;
+
+        path.addRect(rect);
+        return this->clipPath(path, op);
+    }
+}
+
+bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+
+    SkPath devPath;
+    path.transform(*fMCRec->fMatrix, &devPath);
+
+    if (SkRegion::kIntersect_Op == op) {
+        return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion);
+    } else {
+        SkRegion base;
+        const SkBitmap& bm = this->getDevice()->accessBitmap(false);
+        base.setRect(0, 0, bm.width(), bm.height());
+        
+        if (SkRegion::kReplace_Op == op) {
+            return fMCRec->fRegion->setPath(devPath, base);
+        } else {
+            SkRegion rgn;
+            rgn.setPath(devPath, base);
+            return fMCRec->fRegion->op(rgn, op);
+        }
+    }
+}
+
+bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+
+    return fMCRec->fRegion->op(rgn, op);
+}
+
+void SkCanvas::computeLocalClipBoundsCompareType() const {
+    SkRect r;
+    
+    if (!this->getClipBounds(&r, kAA_EdgeType)) {
+        fLocalBoundsCompareType.setEmpty();
+    } else {
+        fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
+                                    SkScalarToCompareType(r.fTop),
+                                    SkScalarToCompareType(r.fRight),
+                                    SkScalarToCompareType(r.fBottom));
+    }
+}
+
+bool SkCanvas::quickReject(const SkRect& rect, EdgeType) const {
+    /*  current impl ignores edgetype, and relies on
+        getLocalClipBoundsCompareType(), which always returns a value assuming
+        antialiasing (worst case)
+     */
+
+    if (fMCRec->fRegion->isEmpty()) {
+        return true;
+    }
+    
+    // check for empty user rect (horizontal)
+    SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
+    SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
+    if (userL >= userR) {
+        return true;
+    }
+
+    // check for empty user rect (vertical)
+    SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
+    SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
+    if (userT >= userB) {
+        return true;
+    }
+    
+    // check if we are completely outside of the local clip bounds
+    const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
+    return  userL >= clipR.fRight || userT >= clipR.fBottom ||
+            userR <= clipR.fLeft  || userB <= clipR.fTop;
+}
+
+bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
+    if (fMCRec->fRegion->isEmpty() || path.isEmpty()) {
+        return true;
+    }
+
+    if (fMCRec->fMatrix->rectStaysRect()) {
+        SkRect  r;
+        path.computeBounds(&r, SkPath::kFast_BoundsType);
+        return this->quickReject(r, et);
+    }
+
+    SkPath      dstPath;
+    SkRect      r;
+    SkIRect     ir;
+
+    path.transform(*fMCRec->fMatrix, &dstPath);
+    dstPath.computeBounds(&r, SkPath::kFast_BoundsType);
+    r.round(&ir);
+    if (kAA_EdgeType == et) {
+        ir.inset(-1, -1);
+    }
+    return fMCRec->fRegion->quickReject(ir);
+}
+
+bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
+    /*  current impl ignores edgetype, and relies on
+        getLocalClipBoundsCompareType(), which always returns a value assuming
+        antialiasing (worst case)
+     */
+
+    if (fMCRec->fRegion->isEmpty()) {
+        return true;
+    }
+    
+    SkScalarCompareType userT = SkScalarAs2sCompliment(top);
+    SkScalarCompareType userB = SkScalarAs2sCompliment(bottom);
+    
+    // check for invalid user Y coordinates (i.e. empty)
+    if (userT >= userB) {
+        return true;
+    }
+    
+    // check if we are above or below the local clip bounds
+    const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
+    return userT >= clipR.fBottom || userB <= clipR.fTop;
+}
+
+bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
+    const SkRegion& clip = *fMCRec->fRegion;
+    if (clip.isEmpty()) {
+        if (bounds) {
+            bounds->setEmpty();
+        }
+        return false;
+    }
+
+    if (NULL != bounds) {
+        SkMatrix inverse;
+        SkRect   r;
+
+        fMCRec->fMatrix->invert(&inverse);
+        
+        // get the clip's bounds
+        const SkIRect& ibounds = clip.getBounds();
+        // adjust it outwards if we are antialiasing
+        int inset = (kAA_EdgeType == et);
+        r.iset(ibounds.fLeft - inset,  ibounds.fTop - inset,
+               ibounds.fRight + inset, ibounds.fBottom + inset);
+        
+        // invert into local coordinates
+        inverse.mapRect(bounds, r);
+    }
+    return true;
+}
+
+const SkMatrix& SkCanvas::getTotalMatrix() const {
+    return *fMCRec->fMatrix;
+}
+
+const SkRegion& SkCanvas::getTotalClip() const {
+    return *fMCRec->fRegion;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width,
+                                 int height, bool isOpaque, bool isForLayer) {
+    SkBitmap bitmap;
+    
+    bitmap.setConfig(config, width, height);
+    bitmap.setIsOpaque(isOpaque);
+
+    // should this happen in the device subclass?
+    bitmap.allocPixels();   
+    if (!bitmap.isOpaque()) {
+        bitmap.eraseARGB(0, 0, 0, 0);
+    }
+
+    return SkNEW_ARGS(SkDevice, (bitmap));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//  These are the virtual drawing methods
+//////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::drawPaint(const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawPaint(iter, paint);
+    }
+
+    ITER_END
+}
+
+void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                          const SkPaint& paint) {
+    if ((long)count <= 0) {
+        return;
+    }
+
+    SkASSERT(pts != NULL);
+
+    ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawPoints(iter, mode, count, pts, paint);
+    }
+    
+    ITER_END
+}
+
+void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
+    if (paint.canComputeFastBounds()) {
+        SkRect storage;
+        if (this->quickReject(paint.computeFastBounds(r, &storage),
+                              paint2EdgeType(&paint))) {
+            return;
+        }
+    }
+        
+    ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawRect(iter, r, paint);
+    }
+
+    ITER_END
+}
+
+void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    if (paint.canComputeFastBounds()) {
+        SkRect r;
+        path.computeBounds(&r, SkPath::kFast_BoundsType);
+        if (this->quickReject(paint.computeFastBounds(r, &r),
+                              paint2EdgeType(&paint))) {
+            return;
+        }
+    }
+
+    ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawPath(iter, path, paint);
+    }
+
+    ITER_END
+}
+
+void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+                          const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+
+    if (NULL == paint || (paint->getMaskFilter() == NULL)) {
+        SkRect fastBounds;
+        fastBounds.set(x, y,
+                       x + SkIntToScalar(bitmap.width()),
+                       y + SkIntToScalar(bitmap.height()));
+        if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
+            return;
+        }
+    }
+        
+    SkMatrix matrix;
+    matrix.setTranslate(x, y);
+    this->internalDrawBitmap(bitmap, matrix, paint);
+}
+
+void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                              const SkRect& dst, const SkPaint* paint) {
+    if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
+        return;
+    }
+    
+    // do this now, to avoid the cost of calling extract for RLE bitmaps
+    if (this->quickReject(dst, paint2EdgeType(paint))) {
+        return;
+    }
+    
+    SkBitmap        tmp;    // storage if we need a subset of bitmap
+    const SkBitmap* bitmapPtr = &bitmap;
+
+    if (NULL != src) {
+        if (!bitmap.extractSubset(&tmp, *src)) {
+            return;     // extraction failed
+        }
+        bitmapPtr = &tmp;
+    }
+    
+    SkScalar width = SkIntToScalar(bitmapPtr->width());
+    SkScalar height = SkIntToScalar(bitmapPtr->height());    
+    SkMatrix matrix;
+
+    if (dst.width() == width && dst.height() == height) {
+        matrix.setTranslate(dst.fLeft, dst.fTop);
+    } else {
+        SkRect tmpSrc;
+        tmpSrc.set(0, 0, width, height);
+        matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+    }
+    this->internalDrawBitmap(*bitmapPtr, matrix, paint);
+}
+
+void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
+                                const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+    this->internalDrawBitmap(bitmap, matrix, paint);
+}
+
+void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+                                const SkPaint& paint) {
+    SkDEBUGCODE(bitmap.validate();)
+    
+    ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawBitmap(iter, bitmap, matrix, paint);
+    }
+    
+    ITER_END
+}
+
+void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+                          const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+    
+    if (reject_bitmap(bitmap)) {
+        return;
+    }
+    
+    SkPaint tmp;
+    if (NULL == paint) {
+        paint = &tmp;
+    }
+    
+    ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
+                                 *paint);
+    }
+    ITER_END
+}
+
+void SkCanvas::drawText(const void* text, size_t byteLength,
+                        SkScalar x, SkScalar y, const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
+    }
+
+    ITER_END
+}
+
+void SkCanvas::drawPosText(const void* text, size_t byteLength,
+                           const SkPoint pos[], const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
+                                  paint);
+    }
+    
+    ITER_END
+}
+
+void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
+                            const SkScalar xpos[], SkScalar constY,
+                            const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
+                                  paint);
+    }
+    
+    ITER_END
+}
+
+void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
+                              const SkPath& path, const SkMatrix* matrix,
+                              const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
+                                     matrix, paint);
+    }
+
+    ITER_END
+}
+
+void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
+                            const SkPoint verts[], const SkPoint texs[],
+                            const SkColor colors[], SkXfermode* xmode,
+                            const uint16_t indices[], int indexCount,
+                            const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
+                                   colors, xmode, indices, indexCount, paint);
+    }
+    
+    ITER_END
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// These methods are NOT virtual, and therefore must call back into virtual
+// methods, rather than actually drawing themselves.
+//////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
+                        SkPorterDuff::Mode mode) {
+    SkPaint paint;
+
+    paint.setARGB(a, r, g, b);
+    if (SkPorterDuff::kSrcOver_Mode != mode) {
+        paint.setPorterDuffXfermode(mode);
+    }
+    this->drawPaint(paint);
+}
+
+void SkCanvas::drawColor(SkColor c, SkPorterDuff::Mode mode) {
+    SkPaint paint;
+
+    paint.setColor(c);
+    if (SkPorterDuff::kSrcOver_Mode != mode) {
+        paint.setPorterDuffXfermode(mode);
+    }
+    this->drawPaint(paint);
+}
+
+void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
+    SkPoint pt;
+    
+    pt.set(x, y);
+    this->drawPoints(kPoints_PointMode, 1, &pt, paint);
+}
+
+void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
+    SkPoint pt;
+    SkPaint paint;
+    
+    pt.set(x, y);
+    paint.setColor(color);
+    this->drawPoints(kPoints_PointMode, 1, &pt, paint);
+}
+
+void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
+                        const SkPaint& paint) {
+    SkPoint pts[2];
+    
+    pts[0].set(x0, y0);
+    pts[1].set(x1, y1);
+    this->drawPoints(kLines_PointMode, 2, pts, paint);
+}
+
+void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
+                              SkScalar right, SkScalar bottom,
+                              const SkPaint& paint) {
+    SkRect  r;
+
+    r.set(left, top, right, bottom);
+    this->drawRect(r, paint);
+}
+
+void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
+                          const SkPaint& paint) {
+    if (radius < 0) {
+        radius = 0;
+    }
+
+    SkRect  r;
+    r.set(cx - radius, cy - radius, cx + radius, cy + radius);
+    
+    if (paint.canComputeFastBounds()) {
+        SkRect storage;
+        if (this->quickReject(paint.computeFastBounds(r, &storage),
+                              paint2EdgeType(&paint))) {
+            return;
+        }
+    }
+    
+    SkPath  path;
+    path.addOval(r);
+    this->drawPath(path, paint);
+}
+
+void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
+                             const SkPaint& paint) {
+    if (rx > 0 && ry > 0) {
+        if (paint.canComputeFastBounds()) {
+            SkRect storage;
+            if (this->quickReject(paint.computeFastBounds(r, &storage),
+                                  paint2EdgeType(&paint))) {
+                return;
+            }
+        }
+
+        SkPath  path;
+        path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
+        this->drawPath(path, paint);
+    } else {
+        this->drawRect(r, paint);
+    }
+}
+
+void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
+    if (paint.canComputeFastBounds()) {
+        SkRect storage;
+        if (this->quickReject(paint.computeFastBounds(oval, &storage),
+                              paint2EdgeType(&paint))) {
+            return;
+        }
+    }
+
+    SkPath  path;
+    path.addOval(oval);
+    this->drawPath(path, paint);
+}
+
+void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
+                       SkScalar sweepAngle, bool useCenter,
+                       const SkPaint& paint) {
+    if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
+        this->drawOval(oval, paint);
+    } else {
+        SkPath  path;
+        if (useCenter) {
+            path.moveTo(oval.centerX(), oval.centerY());
+        }
+        path.arcTo(oval, startAngle, sweepAngle, !useCenter);
+        if (useCenter) {
+            path.close();
+        }
+        this->drawPath(path, paint);
+    }
+}
+
+void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
+                                const SkPath& path, SkScalar hOffset,
+                                SkScalar vOffset, const SkPaint& paint) {
+    SkMatrix    matrix;
+    
+    matrix.setTranslate(hOffset, vOffset);
+    this->drawTextOnPath(text, byteLength, path, &matrix, paint);
+}
+
+void SkCanvas::drawPicture(SkPicture& picture) {
+    int saveCount = save();
+    picture.draw(this);
+    restoreToCount(saveCount);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
+    // need COMPILE_TIME_ASSERT
+    SkASSERT(sizeof(fStorage) >= sizeof(SkDrawIter));
+
+    SkASSERT(canvas);
+
+    fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
+    fDone = !fImpl->next();
+}
+
+SkCanvas::LayerIter::~LayerIter() {
+    fImpl->~SkDrawIter();
+}
+
+void SkCanvas::LayerIter::next() {
+    fDone = !fImpl->next();
+}
+
+SkDevice* SkCanvas::LayerIter::device() const {
+    return fImpl->getDevice();
+}
+
+const SkMatrix& SkCanvas::LayerIter::matrix() const {
+    return fImpl->getMatrix();
+}
+
+const SkPaint& SkCanvas::LayerIter::paint() const {
+    const SkPaint* paint = fImpl->getPaint();
+    if (NULL == paint) {
+        paint = &fDefaultPaint;
+    }
+    return *paint;
+}
+
+const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
+int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
+int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
+
diff --git a/src/core/SkChunkAlloc.cpp b/src/core/SkChunkAlloc.cpp
new file mode 100644
index 0000000..ae37ec0
--- /dev/null
+++ b/src/core/SkChunkAlloc.cpp
@@ -0,0 +1,120 @@
+/* libs/corecg/SkChunkAlloc.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 "SkChunkAlloc.h"
+
+struct SkChunkAlloc::Block {
+    Block*  fNext;
+    size_t  fFreeSize;
+    char*   fFreePtr;
+    // data[] follows
+    
+    void freeChain() {    // this can be null
+        Block* block = this;
+        while (block) {
+            Block* next = block->fNext;
+            sk_free(block);
+            block = next;
+        }
+    };
+    
+    Block* tail() {
+        Block* block = this;
+        if (block) {
+            for (;;) {
+                Block* next = block->fNext;
+                if (NULL == next) {
+                    break;
+                }
+                block = next;
+            }
+        }
+        return block;
+    }
+};
+
+SkChunkAlloc::SkChunkAlloc(size_t minSize)
+    : fBlock(NULL), fMinSize(SkAlign4(minSize)), fPool(NULL), fTotalCapacity(0)
+{
+}
+
+SkChunkAlloc::~SkChunkAlloc() {
+    this->reset();
+}
+
+void SkChunkAlloc::reset() {
+    fBlock->freeChain();
+    fBlock = NULL;
+    fPool->freeChain();
+    fPool = NULL;
+    fTotalCapacity = 0;
+}
+
+void SkChunkAlloc::reuse() {
+    if (fPool && fBlock) {
+        fPool->tail()->fNext = fBlock;
+    }
+    fPool = fBlock;
+    fBlock = NULL;
+    fTotalCapacity = 0;
+}
+
+SkChunkAlloc::Block* SkChunkAlloc::newBlock(size_t bytes, AllocFailType ftype) {
+    Block* block = fPool;
+
+    if (block && bytes <= block->fFreeSize) {
+        fPool = block->fNext;
+        return block;
+    }
+
+    size_t  size = SkMax32((int32_t)bytes, (int32_t)fMinSize);
+
+    block = (Block*)sk_malloc_flags(sizeof(Block) + size,
+                        ftype == kThrow_AllocFailType ? SK_MALLOC_THROW : 0);
+
+    if (block) {
+        //    block->fNext = fBlock;
+        block->fFreeSize = size;
+        block->fFreePtr = (char*)block + sizeof(Block);
+        
+        fTotalCapacity += size;
+    }
+    return block;
+}
+
+void* SkChunkAlloc::alloc(size_t bytes, AllocFailType ftype) {
+    bytes = SkAlign4(bytes);
+
+    Block* block = fBlock;
+
+    if (block == NULL || bytes > block->fFreeSize) {
+        block = this->newBlock(bytes, ftype);
+        if (NULL == block) {
+            return NULL;
+        }
+        block->fNext = fBlock;
+        fBlock = block;
+    }
+
+    SkASSERT(block && bytes <= block->fFreeSize);
+    void* ptr = block->fFreePtr;
+
+    block->fFreeSize -= bytes;
+    block->fFreePtr += bytes;
+    return ptr;
+}
+
diff --git a/src/core/SkColor.cpp b/src/core/SkColor.cpp
new file mode 100644
index 0000000..4256179
--- /dev/null
+++ b/src/core/SkColor.cpp
@@ -0,0 +1,138 @@
+/* libs/graphics/sgl/SkColor.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 "SkColor.h"
+#include "SkColorPriv.h"
+
+SkPMColor SkPreMultiplyARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
+    if (a != 255) {
+#if 0
+        unsigned scale = SkAlpha255To256(a);
+        r = SkAlphaMul(r, scale);
+        g = SkAlphaMul(g, scale);
+        b = SkAlphaMul(b, scale);
+#else
+        r = SkMulDiv255Round(r, a);
+        g = SkMulDiv255Round(g, a);
+        b = SkMulDiv255Round(b, a);
+#endif
+    }
+    return SkPackARGB32(a, r, g, b);
+}
+
+SkPMColor SkPreMultiplyColor(SkColor c) {
+    unsigned a = SkColorGetA(c);
+    unsigned r = SkColorGetR(c);
+    unsigned g = SkColorGetG(c);
+    unsigned b = SkColorGetB(c);
+
+    return SkPreMultiplyARGB(a, r, g, b);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+static inline SkScalar ByteToScalar(U8CPU x) {
+    SkASSERT(x <= 255);
+    return SkIntToScalar(x) / 255;
+}
+
+static inline SkScalar ByteDivToScalar(int numer, U8CPU denom) {
+    // cast to keep the answer signed
+    return SkIntToScalar(numer) / (int)denom;
+}
+
+void SkRGBToHSV(U8CPU r, U8CPU g, U8CPU b, SkScalar hsv[3]) {
+    SkASSERT(hsv);
+
+    unsigned min = SkMin32(r, SkMin32(g, b));
+    unsigned max = SkMax32(r, SkMax32(g, b));
+    unsigned delta = max - min;
+
+    SkScalar v = ByteToScalar(max);
+    SkASSERT(v >= 0 && v <= SK_Scalar1);
+
+    if (0 == delta) { // we're a shade of gray
+        hsv[0] = 0;
+        hsv[1] = 0;
+        hsv[2] = v;
+        return;
+    }
+
+    SkScalar s = ByteDivToScalar(delta, max);
+    SkASSERT(s >= 0 && s <= SK_Scalar1);
+
+    SkScalar h;    
+    if (r == max) {
+        h = ByteDivToScalar(g - b, delta);
+    } else if (g == max) {
+        h = SkIntToScalar(2) + ByteDivToScalar(b - r, delta);
+    } else { // b == max
+        h = SkIntToScalar(4) + ByteDivToScalar(r - g, delta);
+    }
+
+    h *= 60;
+    if (h < 0) {
+        h += SkIntToScalar(360);
+    }
+    SkASSERT(h >= 0 && h < SkIntToScalar(360));
+
+    hsv[0] = h;
+    hsv[1] = s;
+    hsv[2] = v;
+}
+
+static inline U8CPU UnitScalarToByte(SkScalar x) {
+    if (x < 0) {
+        return 0;
+    }
+    if (x >= SK_Scalar1) {
+        return 255;
+    }
+    return SkScalarToFixed(x) >> 8;
+}
+
+SkColor SkHSVToColor(U8CPU a, const SkScalar hsv[3]) {
+    SkASSERT(hsv);
+
+    U8CPU s = UnitScalarToByte(hsv[1]);
+    U8CPU v = UnitScalarToByte(hsv[2]);
+
+    if (0 == s) { // shade of gray
+        return SkColorSetARGB(a, v, v, v);
+    }
+    SkFixed hx = (hsv[0] < 0 || hsv[0] >= SkIntToScalar(360)) ? 0 : SkScalarToFixed(hsv[0]/60);
+    SkFixed f = hx & 0xFFFF;
+    
+    unsigned v_scale = SkAlpha255To256(v);
+    unsigned p = SkAlphaMul(255 - s, v_scale);
+    unsigned q = SkAlphaMul(255 - (s * f >> 16), v_scale);
+    unsigned t = SkAlphaMul(255 - (s * (SK_Fixed1 - f) >> 16), v_scale);
+    
+    unsigned r, g, b;
+
+    SkASSERT((unsigned)(hx >> 16) < 6);
+    switch (hx >> 16) {
+        case 0: r = v; g = t; b = p; break;
+        case 1: r = q; g = v; b = p; break;
+        case 2: r = p; g = v; b = t; break;
+        case 3: r = p; g = q; b = v; break;
+        case 4: r = t;  g = p; b = v; break;
+        default: r = v; g = p; b = q; break;
+    }
+    return SkColorSetARGB(a, r, g, b);
+}
+
diff --git a/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
new file mode 100644
index 0000000..bb4be48
--- /dev/null
+++ b/src/core/SkColorFilter.cpp
@@ -0,0 +1,108 @@
+/* libs/graphics/sgl/SkColorFilter.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 "SkColorFilter.h"
+#include "SkShader.h"
+
+void SkColorFilter::filterSpan16(const uint16_t s[], int count, uint16_t d[])
+{
+    SkASSERT(this->getFlags() & SkColorFilter::kHasFilter16_Flag);
+    SkASSERT(!"missing implementation of SkColorFilter::filterSpan16");
+
+    if (d != s)
+        memcpy(d, s, count * sizeof(uint16_t));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SkFilterShader::SkFilterShader(SkShader* shader, SkColorFilter* filter)
+{
+    fShader = shader;   shader->ref();
+    fFilter = filter;   filter->ref();
+}
+
+SkFilterShader::SkFilterShader(SkFlattenableReadBuffer& buffer) :
+    INHERITED(buffer)
+{
+    fShader = static_cast<SkShader*>(buffer.readFlattenable());
+    fFilter = static_cast<SkColorFilter*>(buffer.readFlattenable());
+}
+
+SkFilterShader::~SkFilterShader()
+{
+    fFilter->unref();
+    fShader->unref();
+}
+
+void SkFilterShader::beginSession()
+{
+    this->INHERITED::beginSession();
+    fShader->beginSession();
+}
+
+void SkFilterShader::endSession()
+{
+    fShader->endSession();
+    this->INHERITED::endSession();
+}
+
+void SkFilterShader::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    this->INHERITED::flatten(buffer);
+    buffer.writeFlattenable(fShader);
+    buffer.writeFlattenable(fFilter);
+}
+
+uint32_t SkFilterShader::getFlags()
+{
+    uint32_t shaderF = fShader->getFlags();
+    uint32_t filterF = fFilter->getFlags();
+    
+    // if the filter doesn't support 16bit, clear the matching bit in the shader
+    if (!(filterF & SkColorFilter::kHasFilter16_Flag))
+        shaderF &= ~SkShader::kHasSpan16_Flag;
+    
+    // if the filter might change alpha, clear the opaque flag in the shader
+    if (!(filterF & SkColorFilter::kAlphaUnchanged_Flag))
+        shaderF &= ~(SkShader::kOpaqueAlpha_Flag | SkShader::kHasSpan16_Flag);
+    
+    return shaderF;
+}
+
+bool SkFilterShader::setContext(const SkBitmap& device,
+                                const SkPaint& paint,
+                                const SkMatrix& matrix)
+{
+    return  this->INHERITED::setContext(device, paint, matrix) &&
+            fShader->setContext(device, paint, matrix);
+}
+
+void SkFilterShader::shadeSpan(int x, int y, SkPMColor result[], int count)
+{
+    fShader->shadeSpan(x, y, result, count);
+    fFilter->filterSpan(result, count, result);
+}
+
+void SkFilterShader::shadeSpan16(int x, int y, uint16_t result[], int count)
+{
+    SkASSERT(fShader->getFlags() & SkShader::kHasSpan16_Flag);
+    SkASSERT(fFilter->getFlags() & SkColorFilter::kHasFilter16_Flag);
+
+    fShader->shadeSpan16(x, y, result, count);
+    fFilter->filterSpan16(result, count, result);
+}
+
diff --git a/src/core/SkColorTable.cpp b/src/core/SkColorTable.cpp
new file mode 100644
index 0000000..f991da3
--- /dev/null
+++ b/src/core/SkColorTable.cpp
@@ -0,0 +1,143 @@
+/* libs/graphics/sgl/SkColorTable.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 "SkBitmap.h"
+#include "SkFlattenable.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+SkColorTable::SkColorTable(int count)
+    : f16BitCache(NULL), fFlags(0)
+{
+    if (count < 0)
+        count = 0;
+    else if (count > 256)
+        count = 256;
+
+    fCount = SkToU16(count);
+    fColors = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor));
+    memset(fColors, 0, count * sizeof(SkPMColor));
+    
+    SkDEBUGCODE(fColorLockCount = 0;)
+    SkDEBUGCODE(f16BitCacheLockCount = 0;)
+}
+
+SkColorTable::SkColorTable(const SkPMColor colors[], int count)
+    : f16BitCache(NULL), fFlags(0)
+{
+    if (count < 0)
+        count = 0;
+    else if (count > 256)
+        count = 256;
+    
+    fCount = SkToU16(count);
+    fColors = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor));
+    
+    if (colors)
+        memcpy(fColors, colors, count * sizeof(SkPMColor));
+    
+    SkDEBUGCODE(fColorLockCount = 0;)
+    SkDEBUGCODE(f16BitCacheLockCount = 0;)
+}
+
+SkColorTable::~SkColorTable()
+{
+    SkASSERT(fColorLockCount == 0);
+    SkASSERT(f16BitCacheLockCount == 0);
+
+    sk_free(fColors);
+    sk_free(f16BitCache);
+}
+
+void SkColorTable::setFlags(unsigned flags)
+{
+    fFlags = SkToU8(flags);
+}
+
+void SkColorTable::unlockColors(bool changed)
+{
+    SkASSERT(fColorLockCount != 0);
+    SkDEBUGCODE(fColorLockCount -= 1;)
+    if (changed)
+        this->inval16BitCache();
+}
+
+void SkColorTable::inval16BitCache()
+{
+    SkASSERT(f16BitCacheLockCount == 0);
+    if (f16BitCache)
+    {
+        sk_free(f16BitCache);
+        f16BitCache = NULL;
+    }
+}
+
+#include "SkColorPriv.h"
+
+static inline void build_16bitcache(uint16_t dst[], const SkPMColor src[], int count)
+{
+    while (--count >= 0)
+        *dst++ = SkPixel32ToPixel16_ToU16(*src++);
+}
+
+const uint16_t* SkColorTable::lock16BitCache()
+{
+    if (fFlags & kColorsAreOpaque_Flag)
+    {
+        if (f16BitCache == NULL) // build the cache
+        {
+            f16BitCache = (uint16_t*)sk_malloc_throw(fCount * sizeof(uint16_t));
+            build_16bitcache(f16BitCache, fColors, fCount);
+        }
+    }
+    else    // our colors have alpha, so no cache
+    {
+        this->inval16BitCache();
+        if (f16BitCache)
+        {
+            sk_free(f16BitCache);
+            f16BitCache = NULL;
+        }
+    }
+
+    SkDEBUGCODE(f16BitCacheLockCount += 1);
+    return f16BitCache;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkColorTable::SkColorTable(SkFlattenableReadBuffer& buffer) {
+    f16BitCache = NULL;
+    SkDEBUGCODE(fColorLockCount = 0;)
+    SkDEBUGCODE(f16BitCacheLockCount = 0;)
+
+    fCount = buffer.readU16();
+    SkASSERT((unsigned)fCount <= 256);
+
+    fFlags = buffer.readU8();
+
+    fColors = (SkPMColor*)sk_malloc_throw(fCount * sizeof(SkPMColor));
+    buffer.read(fColors, fCount * sizeof(SkPMColor));
+}
+
+void SkColorTable::flatten(SkFlattenableWriteBuffer& buffer) const {
+    int count = this->count();
+    buffer.write16(count);
+    buffer.write8(this->getFlags());
+    buffer.writeMul4(fColors, count * sizeof(SkPMColor));
+}
+
diff --git a/src/core/SkComposeShader.cpp b/src/core/SkComposeShader.cpp
new file mode 100644
index 0000000..1e5e202
--- /dev/null
+++ b/src/core/SkComposeShader.cpp
@@ -0,0 +1,174 @@
+/* libs/graphics/effects/SkShaderExtras.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 "SkComposeShader.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkXfermode.h"
+
+//////////////////////////////////////////////////////////////////////////////////////
+    
+SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode)
+{
+    fShaderA = sA;  sA->ref();
+    fShaderB = sB;  sB->ref();
+    // mode may be null
+    fMode = mode;   mode->safeRef();
+}
+
+SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
+    INHERITED(buffer)
+{
+    fShaderA = static_cast<SkShader*>(buffer.readFlattenable());
+    fShaderB = static_cast<SkShader*>(buffer.readFlattenable());
+    fMode = static_cast<SkXfermode*>(buffer.readFlattenable());
+}
+
+SkComposeShader::~SkComposeShader()
+{
+    fMode->safeUnref(); // may be null
+    fShaderB->unref();
+    fShaderA->unref();
+}
+
+void SkComposeShader::beginSession()
+{
+    this->INHERITED::beginSession();
+    fShaderA->beginSession();
+    fShaderB->beginSession();
+}
+
+void SkComposeShader::endSession()
+{
+    fShaderA->endSession();
+    fShaderB->endSession();
+    this->INHERITED::endSession();
+}
+
+class SkAutoAlphaRestore {
+public:
+    SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha)
+    {
+        fAlpha = paint->getAlpha();
+        fPaint = paint;
+        paint->setAlpha(newAlpha);
+    }
+    ~SkAutoAlphaRestore()
+    {
+        fPaint->setAlpha(fAlpha);
+    }
+private:
+    SkPaint*    fPaint;
+    uint8_t     fAlpha;
+};
+
+void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    this->INHERITED::flatten(buffer);
+    buffer.writeFlattenable(fShaderA);
+    buffer.writeFlattenable(fShaderB);
+    buffer.writeFlattenable(fMode);
+}
+
+/*  We call setContext on our two worker shaders. However, we
+    always let them see opaque alpha, and if the paint really
+    is translucent, then we apply that after the fact.
+*/
+bool SkComposeShader::setContext(const SkBitmap& device,
+                                 const SkPaint& paint,
+                                 const SkMatrix& matrix)
+{
+    if (!this->INHERITED::setContext(device, paint, matrix))
+        return false;
+
+    // we preconcat our localMatrix (if any) with the device matrix
+    // before calling our sub-shaders
+
+    SkMatrix tmpM;
+    
+    (void)this->getLocalMatrix(&tmpM);
+    tmpM.setConcat(matrix, tmpM);
+    
+    SkAutoAlphaRestore  restore(const_cast<SkPaint*>(&paint), 0xFF);
+
+    return  fShaderA->setContext(device, paint, tmpM) &&
+            fShaderB->setContext(device, paint, tmpM);
+}
+
+// larger is better (fewer times we have to loop), but we shouldn't
+// take up too much stack-space (each element is 4 bytes)
+#define TMP_COLOR_COUNT     64
+
+void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count)
+{
+    SkShader*   shaderA = fShaderA;
+    SkShader*   shaderB = fShaderB;
+    SkXfermode* mode = fMode;
+    unsigned    scale = SkAlpha255To256(this->getPaintAlpha());
+    
+    SkPMColor   tmp[TMP_COLOR_COUNT];
+
+    if (NULL == mode)   // implied SRC_OVER
+    {
+        do {
+            int n = count;
+            if (n > TMP_COLOR_COUNT)
+                n = TMP_COLOR_COUNT;
+            
+            shaderA->shadeSpan(x, y, result, n);
+            shaderB->shadeSpan(x, y, tmp, n);
+
+            if (256 == scale)
+            {
+                for (int i = 0; i < n; i++)
+                    result[i] = SkPMSrcOver(tmp[i], result[i]);
+            }
+            else
+            {
+                for (int i = 0; i < n; i++)
+                    result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), scale);
+            }
+            
+            result += n;
+            x += n;
+            count -= n;
+        } while (count > 0);
+    }
+    else    // use mode for the composition
+    {
+        do {
+            int n = count;
+            if (n > TMP_COLOR_COUNT)
+                n = TMP_COLOR_COUNT;
+            
+            shaderA->shadeSpan(x, y, result, n);
+            shaderB->shadeSpan(x, y, tmp, n);
+            mode->xfer32(result, tmp, n, NULL);
+
+            if (256 == scale)
+            {
+                for (int i = 0; i < n; i++)
+                    result[i] = SkAlphaMulQ(result[i], scale);
+            }
+
+            result += n;
+            x += n;
+            count -= n;
+        } while (count > 0);
+    }
+}
+    
diff --git a/src/core/SkCordic.cpp b/src/core/SkCordic.cpp
new file mode 100644
index 0000000..539bc9b
--- /dev/null
+++ b/src/core/SkCordic.cpp
@@ -0,0 +1,301 @@
+/* libs/corecg/SkCordic.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 "SkCordic.h"
+#include "SkMath.h"
+#include "Sk64.h"
+
+// 0x20000000 equals pi / 4
+const int32_t kATanDegrees[] = { 0x20000000,
+    0x12E4051D, 0x9FB385B, 0x51111D4, 0x28B0D43, 0x145D7E1, 0xA2F61E, 0x517C55,
+    0x28BE53, 0x145F2E, 0xA2F98, 0x517CC, 0x28BE6, 0x145F3, 0xA2F9, 0x517C,
+    0x28BE, 0x145F, 0xA2F, 0x517, 0x28B, 0x145, 0xA2, 0x51, 0x28, 0x14,
+    0xA, 0x5, 0x2, 0x1 };
+
+const int32_t kFixedInvGain1 = 0x18bde0bb;  // 0.607252935
+
+static void SkCircularRotation(int32_t* x0, int32_t* y0, int32_t* z0) 
+{
+    int32_t t = 0;
+    int32_t x = *x0;
+    int32_t y = *y0;
+    int32_t z = *z0;
+    const int32_t* tanPtr = kATanDegrees;
+   do {
+        int32_t x1 = y >> t;
+        int32_t y1 = x >> t;
+        int32_t tan = *tanPtr++;    
+        if (z >= 0) {
+            x -= x1;
+            y += y1;
+            z -= tan;
+        } else {
+            x += x1;
+            y -= y1;
+            z += tan;
+        }
+   } while (++t < 16); // 30);
+    *x0 = x;
+    *y0 = y;
+    *z0 = z;
+}
+
+SkFixed SkCordicSinCos(SkFixed radians, SkFixed* cosp)
+{
+    int32_t scaledRadians = radians * 0x28be;   // scale radians to 65536 / PI()
+    int quadrant = scaledRadians >> 30;
+    quadrant += 1;
+    if (quadrant & 2) 
+        scaledRadians = -scaledRadians + 0x80000000;
+    /* |a| <= 90 degrees as a 1.31 number */
+    SkFixed sin = 0;
+    SkFixed cos = kFixedInvGain1;
+    SkCircularRotation(&cos, &sin, &scaledRadians);
+    Sk64 scaled;
+    scaled.setMul(sin, 0x6488d);
+    sin = scaled.fHi;
+    scaled.setMul(cos, 0x6488d);
+    if (quadrant & 2)
+        scaled.fHi = - scaled.fHi;
+    *cosp = scaled.fHi;
+    return sin;
+}
+
+SkFixed SkCordicTan(SkFixed a) 
+{
+    int32_t cos;
+    int32_t sin = SkCordicSinCos(a, &cos);
+    return SkFixedDiv(sin, cos);
+}
+
+static int32_t SkCircularVector(int32_t* y0, int32_t* x0, int32_t vecMode) 
+{
+    int32_t x = *x0;
+    int32_t y = *y0;
+    int32_t z = 0;
+    int32_t t = 0;
+    const int32_t* tanPtr = kATanDegrees;
+   do {
+        int32_t x1 = y >> t;
+        int32_t y1 = x >> t;
+        int32_t tan = *tanPtr++;    
+        if (y < vecMode) {
+            x -= x1;
+            y += y1;
+            z -= tan;
+        } else {
+            x += x1;
+            y -= y1;
+            z += tan;
+        }
+   } while (++t < 16);  // 30
+    Sk64 scaled;
+    scaled.setMul(z, 0x6488d); // scale back into the SkScalar space (0x100000000/0x28be)
+   return scaled.fHi;
+}
+
+SkFixed SkCordicASin(SkFixed a) {
+    int32_t sign = SkExtractSign(a);
+    int32_t z = SkFixedAbs(a);
+    if (z >= SK_Fixed1)
+        return SkApplySign(SK_FixedPI>>1, sign);
+    int32_t x = kFixedInvGain1;
+    int32_t y = 0;
+    z *= 0x28be;
+    z = SkCircularVector(&y, &x, z);
+    z = SkApplySign(z, ~sign);
+    return z;
+}
+
+SkFixed SkCordicACos(SkFixed a) {
+    int32_t z = SkCordicASin(a);
+    z = (SK_FixedPI>>1) - z;
+    return z;
+}
+
+SkFixed SkCordicATan2(SkFixed y, SkFixed x) {
+    if ((x | y) == 0)
+        return 0;
+    int32_t xsign = SkExtractSign(x);
+    x = SkFixedAbs(x);
+    int32_t result = SkCircularVector(&y, &x, 0);
+    if (xsign) {
+        int32_t rsign = SkExtractSign(result);
+        if (y == 0)
+            rsign = 0;
+        SkFixed pi = SkApplySign(SK_FixedPI, rsign);
+        result = pi - result;
+    }
+    return result;
+}
+
+const int32_t kATanHDegrees[] = { 
+    0x1661788D, 0xA680D61, 0x51EA6FC, 0x28CBFDD, 0x1460E34,
+    0xA2FCE8, 0x517D2E, 0x28BE6E, 0x145F32,
+    0xA2F98, 0x517CC, 0x28BE6, 0x145F3, 0xA2F9, 0x517C,
+    0x28BE, 0x145F, 0xA2F, 0x517, 0x28B, 0x145, 0xA2, 0x51, 0x28, 0x14,
+    0xA, 0x5, 0x2, 0x1 };
+
+const int32_t kFixedInvGain2 = 0x31330AAA;  // 1.207534495
+
+static void SkHyperbolic(int32_t* x0, int32_t* y0, int32_t* z0, int mode) 
+{
+    int32_t t = 1;
+    int32_t x = *x0;
+    int32_t y = *y0;
+    int32_t z = *z0;
+    const int32_t* tanPtr = kATanHDegrees;
+    int k = -3;
+    do {
+        int32_t x1 = y >> t;
+        int32_t y1 = x >> t;
+        int32_t tan = *tanPtr++;    
+        int count = 2 + (k >> 31);
+        if (++k == 1)
+            k = -2;
+        do {
+            if (((y >> 31) & mode) | ~((z >> 31) | mode)) {
+                x += x1;
+                y += y1;
+                z -= tan;
+            } else {
+                x -= x1;
+                y -= y1;
+                z += tan;
+            }
+        } while (--count);
+    } while (++t < 30);
+    *x0 = x;
+    *y0 = y;
+    *z0 = z;
+}
+
+SkFixed SkCordicLog(SkFixed a) {
+    a *= 0x28be;
+    int32_t x = a + 0x28BE60DB; // 1.0
+    int32_t y = a - 0x28BE60DB;
+    int32_t z = 0;
+    SkHyperbolic(&x, &y, &z, -1);
+    Sk64 scaled;
+    scaled.setMul(z, 0x6488d);
+    z = scaled.fHi;
+    return z << 1;
+}
+
+SkFixed SkCordicExp(SkFixed a) {
+    int32_t cosh = kFixedInvGain2;
+    int32_t sinh = 0;
+    SkHyperbolic(&cosh, &sinh, &a, 0);
+    return cosh + sinh;
+}
+
+#ifdef SK_DEBUG
+
+#ifdef SK_CAN_USE_FLOAT
+    #include "SkFloatingPoint.h"
+#endif
+
+void SkCordic_UnitTest()
+{
+#if defined(SK_SUPPORT_UNITTEST) && defined(SK_CAN_USE_FLOAT)
+    float val;
+    for (float angle = -720; angle < 720; angle += 30) {
+        float radian = angle * 3.1415925358f / 180.0f;
+        SkFixed f_angle = (int) (radian * 65536.0f);
+    // sincos
+        float sine = sinf(radian);
+        float cosine = cosf(radian);
+        SkFixed f_cosine;
+        SkFixed f_sine = SkCordicSinCos(f_angle, &f_cosine);
+        float sine2 = (float) f_sine / 65536.0f;
+        float cosine2 = (float) f_cosine / 65536.0f;
+        float error = fabsf(sine - sine2);
+        if (error > 0.001)
+            SkDebugf("sin error : angle = %g ; sin = %g ; cordic = %g\n", angle, sine, sine2);
+        error = fabsf(cosine - cosine2);
+        if (error > 0.001)
+            SkDebugf("cos error : angle = %g ; cos = %g ; cordic = %g\n", angle, cosine, cosine2);
+    // tan
+        float _tan = tanf(radian);
+        SkFixed f_tan = SkCordicTan(f_angle);
+        float tan2 = (float) f_tan / 65536.0f;
+        error = fabsf(_tan - tan2);
+        if (error > 0.05 && fabsf(_tan) < 1e6)
+            SkDebugf("tan error : angle = %g ; tan = %g ; cordic = %g\n", angle, _tan, tan2);
+    }
+    for (val = -1; val <= 1; val += .1f) {
+        SkFixed f_val = (int) (val * 65536.0f);
+    // asin
+        float arcsine = asinf(val);
+        SkFixed f_arcsine = SkCordicASin(f_val);
+        float arcsine2 = (float) f_arcsine / 65536.0f;
+        float error = fabsf(arcsine - arcsine2);
+        if (error > 0.001)
+            SkDebugf("asin error : val = %g ; asin = %g ; cordic = %g\n", val, arcsine, arcsine2);
+    }
+#if 1
+    for (val = -1; val <= 1; val += .1f) {
+#else
+    val = .5; {
+#endif
+        SkFixed f_val = (int) (val * 65536.0f);
+    // acos
+        float arccos = acosf(val);
+        SkFixed f_arccos = SkCordicACos(f_val);
+        float arccos2 = (float) f_arccos / 65536.0f;
+        float error = fabsf(arccos - arccos2);
+        if (error > 0.001)
+            SkDebugf("acos error : val = %g ; acos = %g ; cordic = %g\n", val, arccos, arccos2);
+    }
+    // atan2
+#if 1
+    for (val = -1000; val <= 1000; val += 500.f) {
+        for (float val2 = -1000; val2 <= 1000; val2 += 500.f) {
+#else
+            val = 0; {
+            float val2 = -1000; {
+#endif
+            SkFixed f_val = (int) (val * 65536.0f);
+            SkFixed f_val2 = (int) (val2 * 65536.0f);
+            float arctan = atan2f(val, val2);
+            SkFixed f_arctan = SkCordicATan2(f_val, f_val2);
+            float arctan2 = (float) f_arctan / 65536.0f;
+            float error = fabsf(arctan - arctan2);
+            if (error > 0.001)
+                SkDebugf("atan2 error : val = %g ; val2 = %g ; atan2 = %g ; cordic = %g\n", val, val2, arctan, arctan2);
+        }
+    }
+    // log
+#if 1
+    for (val = 0.125f; val <= 8.f; val *= 2.0f) {
+#else
+    val = .5; {
+#endif
+        SkFixed f_val = (int) (val * 65536.0f);
+    // acos
+        float log = logf(val);
+        SkFixed f_log = SkCordicLog(f_val);
+        float log2 = (float) f_log / 65536.0f;
+        float error = fabsf(log - log2);
+        if (error > 0.001)
+            SkDebugf("log error : val = %g ; log = %g ; cordic = %g\n", val, log, log2);
+    }
+    // exp
+#endif
+}
+
+#endif
diff --git a/src/core/SkCordic.h b/src/core/SkCordic.h
new file mode 100644
index 0000000..9f45a81
--- /dev/null
+++ b/src/core/SkCordic.h
@@ -0,0 +1,37 @@
+/* libs/corecg/SkCordic.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 SkCordic_DEFINED
+#define SkCordic_DEFINED
+
+#include "SkTypes.h"
+#include "SkFixed.h"
+
+SkFixed SkCordicACos(SkFixed a);
+SkFixed SkCordicASin(SkFixed a);
+SkFixed SkCordicATan2(SkFixed y, SkFixed x);
+SkFixed SkCordicExp(SkFixed a);
+SkFixed SkCordicLog(SkFixed a);
+SkFixed SkCordicSinCos(SkFixed radians, SkFixed* cosp);
+SkFixed SkCordicTan(SkFixed a);
+
+#ifdef SK_DEBUG
+    void SkCordic_UnitTest();
+#endif
+
+#endif // SkCordic 
+
diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h
new file mode 100644
index 0000000..5b3497e
--- /dev/null
+++ b/src/core/SkCoreBlitters.h
@@ -0,0 +1,259 @@
+/* libs/graphics/sgl/SkCoreBlitters.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 SkCoreBlitters_DEFINED
+#define SkCoreBlitters_DEFINED
+
+#include "SkBlitter.h"
+#include "SkBlitRow.h"
+
+class SkRasterBlitter : public SkBlitter {
+public:
+    SkRasterBlitter(const SkBitmap& device) : fDevice(device) {}
+
+protected:
+    const SkBitmap& fDevice;
+
+private:
+    typedef SkBlitter INHERITED;
+};
+
+class SkShaderBlitter : public SkRasterBlitter {
+public:
+    SkShaderBlitter(const SkBitmap& device, const SkPaint& paint);
+    virtual ~SkShaderBlitter();
+
+protected:
+    SkShader* fShader;
+
+private:
+    // illegal
+    SkShaderBlitter& operator=(const SkShaderBlitter&);
+
+    typedef SkRasterBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkA8_Blitter : public SkRasterBlitter {
+public:
+    SkA8_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+    virtual void blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&, const SkIRect&);
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+
+private:
+    unsigned fSrcA;
+
+    // illegal
+    SkA8_Blitter& operator=(const SkA8_Blitter&);
+
+    typedef SkRasterBlitter INHERITED;
+};
+
+class SkA8_Shader_Blitter : public SkShaderBlitter {
+public:
+    SkA8_Shader_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual ~SkA8_Shader_Blitter();
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+    virtual void blitMask(const SkMask&, const SkIRect&);
+
+private:
+    SkXfermode* fXfermode;
+    SkPMColor*  fBuffer;
+    uint8_t*    fAAExpand;
+
+    // illegal
+    SkA8_Shader_Blitter& operator=(const SkA8_Shader_Blitter&);
+
+    typedef SkShaderBlitter INHERITED;
+};
+
+////////////////////////////////////////////////////////////////
+
+class SkARGB32_Blitter : public SkRasterBlitter {
+public:
+    SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+    virtual void blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&, const SkIRect&);
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+
+protected:
+    SkColor fPMColor;
+
+private:
+    unsigned fSrcA, fSrcR, fSrcG, fSrcB;
+
+    // illegal
+    SkARGB32_Blitter& operator=(const SkARGB32_Blitter&);
+
+    typedef SkRasterBlitter INHERITED;
+};
+
+class SkARGB32_Black_Blitter : public SkARGB32_Blitter {
+public:
+    SkARGB32_Black_Blitter(const SkBitmap& device, const SkPaint& paint)
+        : SkARGB32_Blitter(device, paint) {}
+    virtual void blitMask(const SkMask&, const SkIRect&);
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+    
+private:
+    typedef SkARGB32_Blitter INHERITED;
+};
+
+class SkARGB32_Opaque_Blitter : public SkARGB32_Blitter {
+public:
+    SkARGB32_Opaque_Blitter(const SkBitmap& device, const SkPaint& paint)
+        : SkARGB32_Blitter(device, paint) { SkASSERT(paint.getAlpha() == 0xFF); }
+    virtual void blitMask(const SkMask&, const SkIRect&);
+
+private:
+    typedef SkARGB32_Blitter INHERITED;
+};
+
+class SkARGB32_Shader_Blitter : public SkShaderBlitter {
+public:
+    SkARGB32_Shader_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual ~SkARGB32_Shader_Blitter();
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+
+private:
+    SkXfermode* fXfermode;
+    SkPMColor*  fBuffer;
+
+    // illegal
+    SkARGB32_Shader_Blitter& operator=(const SkARGB32_Shader_Blitter&);
+
+    typedef SkShaderBlitter INHERITED;
+};
+
+////////////////////////////////////////////////////////////////
+
+class SkRGB16_Blitter : public SkRasterBlitter {
+public:
+    SkRGB16_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+    virtual void blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void blitRect(int x, int y, int width, int height);
+    virtual void blitMask(const SkMask&, const SkIRect&);
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+
+private:
+    SkPMColor   fSrcColor32;
+    unsigned    fScale;
+    uint16_t    fColor16;       // already scaled by fScale
+    uint16_t    fRawColor16;    // unscaled
+    uint16_t    fRawDither16;   // unscaled
+    SkBool8     fDoDither;
+
+    // illegal
+    SkRGB16_Blitter& operator=(const SkRGB16_Blitter&);
+
+    typedef SkRasterBlitter INHERITED;
+};
+
+class SkRGB16_Black_Blitter : public SkRGB16_Blitter {
+public:
+    SkRGB16_Black_Blitter(const SkBitmap& device, const SkPaint& paint);
+
+    virtual void blitMask(const SkMask&, const SkIRect&);
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+
+private:
+    typedef SkRGB16_Blitter INHERITED;
+};
+
+class SkRGB16_Shader_Blitter : public SkShaderBlitter {
+public:
+    SkRGB16_Shader_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual ~SkRGB16_Shader_Blitter();
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+    
+protected:
+    SkPMColor*      fBuffer;
+    SkBlitRow::Proc fOpaqueProc;
+    SkBlitRow::Proc fAlphaProc;
+    
+private:
+    // illegal
+    SkRGB16_Shader_Blitter& operator=(const SkRGB16_Shader_Blitter&);
+
+    typedef SkShaderBlitter INHERITED;
+};
+
+// used only if the shader can perform shadSpan16
+class SkRGB16_Shader16_Blitter : public SkRGB16_Shader_Blitter {
+public:
+    SkRGB16_Shader16_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+    
+private:
+    typedef SkRGB16_Shader_Blitter INHERITED;
+};
+
+class SkRGB16_Shader_Xfermode_Blitter : public SkShaderBlitter {
+public:
+    SkRGB16_Shader_Xfermode_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual ~SkRGB16_Shader_Xfermode_Blitter();
+    virtual void blitH(int x, int y, int width);
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+
+private:
+    SkXfermode* fXfermode;
+    SkPMColor*  fBuffer;
+    uint8_t*    fAAExpand;
+
+    // illegal
+    SkRGB16_Shader_Xfermode_Blitter& operator=(const SkRGB16_Shader_Xfermode_Blitter&);
+
+    typedef SkShaderBlitter INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class SkA1_Blitter : public SkRasterBlitter {
+public:
+    SkA1_Blitter(const SkBitmap& device, const SkPaint& paint);
+    virtual void blitH(int x, int y, int width);
+
+private:
+    uint8_t fSrcA;
+
+    // illegal
+    SkA1_Blitter& operator=(const SkA1_Blitter&);
+    
+    typedef SkRasterBlitter INHERITED;
+};
+
+
+extern SkBlitter* SkBlitter_ChooseD4444(const SkBitmap& device,
+                                        const SkPaint& paint,
+                                        void* storage, size_t storageSize);
+
+#endif
+
diff --git a/src/core/SkDebug.cpp b/src/core/SkDebug.cpp
new file mode 100644
index 0000000..64ea8b4
--- /dev/null
+++ b/src/core/SkDebug.cpp
@@ -0,0 +1,59 @@
+/* libs/corecg/SkDebug.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 "SkTypes.h"
+
+#ifdef SK_DEBUG
+
+int8_t SkToS8(long x)
+{
+    SkASSERT((int8_t)x == x);
+    return (int8_t)x;
+}
+
+uint8_t SkToU8(size_t x)
+{
+    SkASSERT((uint8_t)x == x);
+    return (uint8_t)x;
+}
+
+int16_t SkToS16(long x)
+{
+    SkASSERT((int16_t)x == x);
+    return (int16_t)x;
+}
+
+uint16_t SkToU16(size_t x)
+{
+    SkASSERT((uint16_t)x == x);
+    return (uint16_t)x;
+}
+
+int32_t SkToS32(long x)
+{
+    SkASSERT((int32_t)x == x);
+    return (int32_t)x;
+}
+
+uint32_t SkToU32(size_t x)
+{
+    SkASSERT((uint32_t)x == x);
+    return (uint32_t)x;
+}
+
+#endif
+
diff --git a/src/core/SkDebug_stdio.cpp b/src/core/SkDebug_stdio.cpp
new file mode 100644
index 0000000..c8a0d81
--- /dev/null
+++ b/src/core/SkDebug_stdio.cpp
@@ -0,0 +1,61 @@
+/* libs/corecg/SkDebug_stdio.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 "SkTypes.h"
+
+static const size_t kBufferSize = 256;
+
+#ifdef ANDROID
+
+#define LOG_TAG "skia"
+#include <utils/Log.h>
+
+void Android_SkDebugf(const char* file, int line, const char* function, 
+    const char* format, ...)
+{
+    if (format[0] == '\n' && format[1] == '\0')
+        return;
+    va_list args;
+    va_start(args, format);
+#ifdef HAVE_ANDROID_OS
+    char buffer[kBufferSize + 1];
+    vsnprintf(buffer, kBufferSize, format, args);
+    if (buffer[0] != 0)
+        __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "%s", buffer);
+#else
+    android_vprintLog(ANDROID_LOG_DEBUG, NULL, LOG_TAG, format, args);
+#endif
+    va_end(args);
+}
+
+#else
+
+#include <stdarg.h>
+#include <stdio.h>
+
+void SkDebugf(const char format[], ...)
+{
+    char    buffer[kBufferSize + 1];
+    va_list args;
+    va_start(args, format);
+    vsnprintf(buffer, kBufferSize, format, args);
+    va_end(args);
+    fprintf(stderr, buffer);
+}
+
+#endif
+
diff --git a/src/core/SkDeque.cpp b/src/core/SkDeque.cpp
new file mode 100644
index 0000000..4f15051
--- /dev/null
+++ b/src/core/SkDeque.cpp
@@ -0,0 +1,252 @@
+/* libs/graphics/sgl/SkDeque.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 "SkDeque.h"
+
+#define INIT_ELEM_COUNT 1  // should we let the caller set this?
+
+struct SkDeque::Head {
+    Head*   fNext;
+    Head*   fPrev;
+    char*   fBegin; // start of used section in this chunk
+    char*   fEnd;   // end of used section in this chunk
+    char*   fStop;  // end of the allocated chunk
+    
+    char*       start() { return (char*)(this + 1); }
+    const char* start() const { return (const char*)(this + 1); }
+    
+    void init(size_t size) {
+        fNext   = fPrev = NULL;
+        fBegin  = fEnd = NULL;
+        fStop   = (char*)this + size;
+    }
+};
+
+SkDeque::SkDeque(size_t elemSize)
+        : fElemSize(elemSize), fInitialStorage(NULL), fCount(0) {
+    fFront = fBack = NULL;
+}
+
+SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize)
+        : fElemSize(elemSize), fInitialStorage(storage), fCount(0) {
+    SkASSERT(storageSize == 0 || storage != NULL);
+    
+    if (storageSize >= sizeof(Head) + elemSize) {
+        fFront = (Head*)storage;
+        fFront->init(storageSize);
+    } else {
+        fFront = NULL;
+    }
+    fBack = fFront;
+}
+
+SkDeque::~SkDeque() {
+    Head* head = fFront;
+    Head* initialHead = (Head*)fInitialStorage;
+
+    while (head) {
+        Head* next = head->fNext;
+        if (head != initialHead) {
+            sk_free(head);
+        }
+        head = next;
+    }
+}
+
+const void* SkDeque::front() const {
+    Head* front = fFront;
+    
+    if (NULL == front) {
+        return NULL;
+    }
+    if (NULL == front->fBegin) {
+        front = front->fNext;
+        if (NULL == front) {
+            return NULL;
+        }
+    }
+    SkASSERT(front->fBegin);
+    return front->fBegin;
+}
+
+const void* SkDeque::back() const {
+    Head* back = fBack;
+
+    if (NULL == back) {
+        return NULL;
+    }
+    if (NULL == back->fEnd) {  // marked as deleted
+        back = back->fPrev;
+        if (NULL == back) {
+            return NULL;
+        }
+    }
+    SkASSERT(back->fEnd);
+    return back->fEnd - fElemSize;
+}
+
+void* SkDeque::push_front() {
+    fCount += 1;
+
+    if (NULL == fFront) {
+        fFront = (Head*)sk_malloc_throw(sizeof(Head) +
+                                        INIT_ELEM_COUNT * fElemSize);
+        fFront->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize);
+        fBack = fFront;     // update our linklist
+    }
+    
+    Head*   first = fFront;
+    char*   begin;
+
+    if (NULL == first->fBegin) {
+    INIT_CHUNK:
+        first->fEnd = first->fStop;
+        begin = first->fStop - fElemSize;
+    } else {
+        begin = first->fBegin - fElemSize;
+        if (begin < first->start()) {    // no more room in this chunk
+            // should we alloc more as we accumulate more elements?
+            size_t  size = sizeof(Head) + INIT_ELEM_COUNT * fElemSize;
+
+            first = (Head*)sk_malloc_throw(size);
+            first->init(size);
+            first->fNext = fFront;
+            fFront->fPrev = first;
+            fFront = first;
+            goto INIT_CHUNK;
+        }
+    }
+
+    first->fBegin = begin;
+    return begin;
+}
+
+void* SkDeque::push_back() {
+    fCount += 1;
+
+    if (NULL == fBack) {
+        fBack = (Head*)sk_malloc_throw(sizeof(Head) +
+                                       INIT_ELEM_COUNT * fElemSize);
+        fBack->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize);
+        fFront = fBack; // update our linklist
+    }
+    
+    Head*   last = fBack;
+    char*   end;
+
+    if (NULL == last->fBegin) {
+    INIT_CHUNK:
+        last->fBegin = last->start();
+        end = last->fBegin + fElemSize;
+    } else {
+        end = last->fEnd + fElemSize;
+        if (end > last->fStop) {  // no more room in this chunk
+            // should we alloc more as we accumulate more elements?
+            size_t  size = sizeof(Head) + INIT_ELEM_COUNT * fElemSize;
+
+            last = (Head*)sk_malloc_throw(size);
+            last->init(size);
+            last->fPrev = fBack;
+            fBack->fNext = last;
+            fBack = last;
+            goto INIT_CHUNK;
+        }
+    }
+
+    last->fEnd = end;
+    return end - fElemSize;
+}
+
+void SkDeque::pop_front() {
+    SkASSERT(fCount > 0);
+    fCount -= 1;
+
+    Head*   first = fFront;
+
+    SkASSERT(first != NULL);
+    
+    if (first->fBegin == NULL) {  // we were marked empty from before
+        first = first->fNext;
+        first->fPrev = NULL;
+        sk_free(fFront);
+        fFront = first;
+        SkASSERT(first != NULL);    // else we popped too far
+    }
+
+    char* begin = first->fBegin + fElemSize;
+    SkASSERT(begin <= first->fEnd);
+
+    if (begin < fFront->fEnd) {
+        first->fBegin = begin;
+    } else {
+        first->fBegin = first->fEnd = NULL;  // mark as empty
+    }
+}
+
+void SkDeque::pop_back() {
+    SkASSERT(fCount > 0);
+    fCount -= 1;
+
+    Head* last = fBack;
+    
+    SkASSERT(last != NULL);
+    
+    if (last->fEnd == NULL) {  // we were marked empty from before
+        last = last->fPrev;
+        last->fNext = NULL;
+        sk_free(fBack);
+        fBack = last;
+        SkASSERT(last != NULL);  // else we popped too far
+    }
+    
+    char* end = last->fEnd - fElemSize;
+    SkASSERT(end >= last->fBegin);
+
+    if (end > last->fBegin) {
+        last->fEnd = end;
+    } else {
+        last->fBegin = last->fEnd = NULL;    // mark as empty
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDeque::Iter::Iter(const SkDeque& d) : fElemSize(d.fElemSize) {
+    fHead = d.fFront;
+    while (fHead != NULL && fHead->fBegin == NULL) {
+        fHead = fHead->fNext;
+    }
+    fPos = fHead ? fHead->fBegin : NULL;
+}
+
+void* SkDeque::Iter::next() {
+    char* pos = fPos;
+    
+    if (pos) {   // if we were valid, try to move to the next setting
+        char* next = pos + fElemSize;
+        SkASSERT(next <= fHead->fEnd);
+        if (next == fHead->fEnd) { // exhausted this chunk, move to next
+            do {
+                fHead = fHead->fNext;
+            } while (fHead != NULL && fHead->fBegin == NULL);
+            next = fHead ? fHead->fBegin : NULL;
+        }
+        fPos = next;
+    }
+    return pos;
+}
+
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
new file mode 100644
index 0000000..139174d
--- /dev/null
+++ b/src/core/SkDevice.cpp
@@ -0,0 +1,110 @@
+#include "SkDevice.h"
+#include "SkDraw.h"
+#include "SkRect.h"
+
+SkDevice::SkDevice() {}
+
+SkDevice::SkDevice(const SkBitmap& bitmap) : fBitmap(bitmap) {}
+
+void SkDevice::lockPixels() {
+    fBitmap.lockPixels();
+}
+
+void SkDevice::unlockPixels() {
+    fBitmap.unlockPixels();
+}
+
+const SkBitmap& SkDevice::accessBitmap(bool changePixels) {
+    this->onAccessBitmap(&fBitmap);
+    if (changePixels) {
+        fBitmap.notifyPixelsChanged();
+    }
+    return fBitmap;
+}
+
+void SkDevice::getBounds(SkIRect* bounds) const {
+    if (bounds) {
+        bounds->set(0, 0, fBitmap.width(), fBitmap.height());
+    }
+}
+
+bool SkDevice::intersects(const SkIRect& r, SkIRect* sect) const {
+    SkIRect bounds;
+    
+    this->getBounds(&bounds);
+    return sect ? sect->intersect(r, bounds) : SkIRect::Intersects(r, bounds);
+}
+
+void SkDevice::eraseColor(SkColor eraseColor) {
+    fBitmap.eraseColor(eraseColor);
+}
+
+void SkDevice::onAccessBitmap(SkBitmap* bitmap) {}
+
+void SkDevice::setMatrixClip(const SkMatrix&, const SkRegion&) {}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+    draw.drawPaint(paint);
+}
+
+void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
+                              const SkPoint pts[], const SkPaint& paint) {
+    draw.drawPoints(mode, count, pts, paint);
+}
+
+void SkDevice::drawRect(const SkDraw& draw, const SkRect& r,
+                            const SkPaint& paint) {
+    draw.drawRect(r, paint);
+}
+
+void SkDevice::drawPath(const SkDraw& draw, const SkPath& path,
+                            const SkPaint& paint) {
+    draw.drawPath(path, paint);
+}
+
+void SkDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+                              const SkMatrix& matrix, const SkPaint& paint) {
+    draw.drawBitmap(bitmap, matrix, paint);
+}
+
+void SkDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+                              int x, int y, const SkPaint& paint) {
+    draw.drawSprite(bitmap, x, y, paint);
+}
+
+void SkDevice::drawText(const SkDraw& draw, const void* text, size_t len,
+                            SkScalar x, SkScalar y, const SkPaint& paint) {
+    draw.drawText((const char*)text, len, x, y, paint);
+}
+
+void SkDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
+                               const SkScalar xpos[], SkScalar y,
+                               int scalarsPerPos, const SkPaint& paint) {
+    draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint);
+}
+
+void SkDevice::drawTextOnPath(const SkDraw& draw, const void* text,
+                                  size_t len, const SkPath& path,
+                                  const SkMatrix* matrix,
+                                  const SkPaint& paint) {
+    draw.drawTextOnPath((const char*)text, len, path, matrix, paint);
+}
+
+void SkDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+                                int vertexCount,
+                                const SkPoint verts[], const SkPoint textures[],
+                                const SkColor colors[], SkXfermode* xmode,
+                                const uint16_t indices[], int indexCount,
+                                const SkPaint& paint) {
+    draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode,
+                      indices, indexCount, paint);
+}
+
+void SkDevice::drawDevice(const SkDraw& draw, SkDevice* device,
+                              int x, int y, const SkPaint& paint) {
+    draw.drawSprite(device->accessBitmap(false), x, y, paint);
+}
+
+
diff --git a/src/core/SkDither.cpp b/src/core/SkDither.cpp
new file mode 100644
index 0000000..53a8573
--- /dev/null
+++ b/src/core/SkDither.cpp
@@ -0,0 +1,49 @@
+#include "SkDither.h"
+
+/*  The base dither matrix we use to derive optimized ones for 565 and 4444
+ 
+    { 0,  32, 8,  40, 2,  34, 10, 42 },
+    { 48, 16, 56, 24, 50, 18, 58, 26 },
+    { 12, 44, 4,  36, 14, 46, 6,  38 },
+    { 60, 28, 52, 20, 62, 30, 54, 22 },
+    { 3,  35, 11, 43, 1,  33, 9,  41 },
+    { 51, 19, 59, 27, 49, 17, 57, 25 },
+    { 15, 47, 7,  39, 13, 45, 5,  37 },
+    { 63, 31, 55, 23, 61, 29, 53, 21 }
+
+    The 4444 version only needs 4 bits, and given that we can reduce its size
+    since the other 4x4 sub pieces all look the same once we truncate the bits.
+
+    The 565 version only needs 3 bits for red/blue, and only 2 bits for green.
+    For simplicity, we store 3 bits, and have the dither macros for green know
+    this, and they shift the dither value down by 1 to make it 2 bits.
+ */
+
+#ifdef ENABLE_DITHER_MATRIX_4X4
+
+const uint8_t gDitherMatrix_4Bit_4X4[4][4] = {
+    {  0,  8,  2, 10 },
+    { 12,  4, 14,  6 },
+    {  3, 11,  1,  9 },
+    { 15,  7, 13,  5 }
+};
+
+const uint8_t gDitherMatrix_3Bit_4X4[4][4] = {
+    {  0,  4,  1,  5 },
+    {  6,  2,  7,  3 },
+    {  1,  5,  0,  4 },
+    {  7,  3,  6,  2 }
+};
+
+#else   // used packed shorts for a scanlines worth of dither values
+
+const uint16_t gDitherMatrix_4Bit_16[4] = {
+    0xA280, 0x6E4C, 0x91B3, 0x5D7F
+};
+
+const uint16_t gDitherMatrix_3Bit_16[4] = {
+    0x5140, 0x3726, 0x4051, 0x2637
+};
+
+#endif
+
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
new file mode 100644
index 0000000..f85c5ae
--- /dev/null
+++ b/src/core/SkDraw.cpp
@@ -0,0 +1,2348 @@
+/* libs/graphics/sgl/SkDraw.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 "SkDraw.h"
+#include "SkBlitter.h"
+#include "SkBounder.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkDevice.h"
+#include "SkMaskFilter.h"
+#include "SkPaint.h"
+#include "SkPathEffect.h"
+#include "SkRasterizer.h"
+#include "SkScan.h"
+#include "SkShader.h"
+#include "SkStroke.h"
+#include "SkTemplatesPriv.h"
+#include "SkUtils.h"
+
+#include "SkAutoKern.h"
+#include "SkBitmapProcShader.h"
+#include "SkDrawProcs.h"
+
+//#define TRACE_BITMAP_DRAWS
+
+class SkAutoRestoreBounder : SkNoncopyable {
+public:
+    // note: initializing fBounder is done only to fix a warning
+    SkAutoRestoreBounder() : fDraw(NULL), fBounder(NULL) {}
+    ~SkAutoRestoreBounder() {
+        if (fDraw) {
+            fDraw->fBounder = fBounder;
+        }
+    }
+    
+    void clearBounder(const SkDraw* draw) {
+        fDraw = const_cast<SkDraw*>(draw);
+        fBounder = draw->fBounder;
+        fDraw->fBounder = NULL;
+    }
+    
+private:
+    SkDraw*     fDraw;
+    SkBounder*  fBounder;
+};
+
+static SkPoint* rect_points(SkRect& r, int index) {
+    SkASSERT((unsigned)index < 2);
+    return &((SkPoint*)(void*)&r)[index];
+}
+
+/** Helper for allocating small blitters on the stack.
+*/
+
+#define kBlitterStorageLongCount    (sizeof(SkBitmapProcShader) >> 2)
+
+class SkAutoBlitterChoose {
+public:
+    SkAutoBlitterChoose(const SkBitmap& device, const SkMatrix& matrix,
+                        const SkPaint& paint) {
+        fBlitter = SkBlitter::Choose(device, matrix, paint,
+                                     fStorage, sizeof(fStorage));
+    }
+    ~SkAutoBlitterChoose();
+
+    SkBlitter*  operator->() { return fBlitter; }
+    SkBlitter*  get() const { return fBlitter; }
+
+private:
+    SkBlitter*  fBlitter;
+    uint32_t    fStorage[kBlitterStorageLongCount];
+};
+
+SkAutoBlitterChoose::~SkAutoBlitterChoose() {
+    if ((void*)fBlitter == (void*)fStorage) {
+        fBlitter->~SkBlitter();
+    } else {
+        SkDELETE(fBlitter);
+    }
+}
+
+class SkAutoBitmapShaderInstall {
+public:
+    SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint* paint)
+            : fPaint((SkPaint*)paint) {
+        fPrevShader = paint->getShader();
+        fPrevShader->safeRef();
+        fPaint->setShader(SkShader::CreateBitmapShader( src,
+                           SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
+                           fStorage, sizeof(fStorage)));
+    }
+    ~SkAutoBitmapShaderInstall() {
+        SkShader* shader = fPaint->getShader();
+
+        fPaint->setShader(fPrevShader);
+        fPrevShader->safeUnref();
+
+        if ((void*)shader == (void*)fStorage) {
+            shader->~SkShader();
+        } else {
+            SkDELETE(shader);
+        }
+    }
+private:
+    SkPaint*    fPaint;
+    SkShader*   fPrevShader;
+    uint32_t    fStorage[kBlitterStorageLongCount];
+};
+
+class SkAutoPaintStyleRestore {
+public:
+    SkAutoPaintStyleRestore(const SkPaint& paint, SkPaint::Style style)
+            : fPaint((SkPaint&)paint) {
+        fStyle = paint.getStyle();  // record the old
+        fPaint.setStyle(style);     // change it to the specified style
+    }
+    ~SkAutoPaintStyleRestore() {
+        fPaint.setStyle(fStyle);    // restore the old
+    }
+private:
+    SkPaint&        fPaint;
+    SkPaint::Style  fStyle;
+
+    // illegal
+    SkAutoPaintStyleRestore(const SkAutoPaintStyleRestore&);
+    SkAutoPaintStyleRestore& operator=(const SkAutoPaintStyleRestore&);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDraw::SkDraw(const SkDraw& src) {
+    memcpy(this, &src, sizeof(*this));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
+
+static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) {
+    bzero(pixels, bytes);
+}
+
+static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {}
+
+static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
+    sk_memset32((uint32_t*)pixels, data, bytes >> 2);
+}
+
+static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
+    sk_memset16((uint16_t*)pixels, data, bytes >> 1);
+}
+
+static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
+    memset(pixels, data, bytes);
+}
+
+static BitmapXferProc ChooseBitmapXferProc(const SkBitmap& bitmap,
+                                           const SkPaint& paint,
+                                           uint32_t* data) {
+    // todo: we can apply colorfilter up front if no shader, so we wouldn't
+    // need to abort this fastpath
+    if (paint.getShader() || paint.getColorFilter()) {
+        return NULL;
+    }
+
+    SkPorterDuff::Mode  mode;
+    if (!SkPorterDuff::IsMode(paint.getXfermode(), &mode)) {
+        return NULL;
+    }
+    
+    SkColor color = paint.getColor();
+    
+    // collaps modes based on color...
+    if (SkPorterDuff::kSrcOver_Mode == mode) {
+        unsigned alpha = SkColorGetA(color);
+        if (0 == alpha) {
+            mode = SkPorterDuff::kDst_Mode;
+        } else if (0xFF == alpha) {
+            mode = SkPorterDuff::kSrc_Mode;
+        }
+    }
+        
+    switch (mode) {
+        case SkPorterDuff::kClear_Mode:
+//            SkDebugf("--- D_Clear_BitmapXferProc\n");
+            return D_Clear_BitmapXferProc;  // ignore data
+        case SkPorterDuff::kDst_Mode:
+//            SkDebugf("--- D_Dst_BitmapXferProc\n");
+            return D_Dst_BitmapXferProc;    // ignore data
+        case SkPorterDuff::kSrc_Mode: {
+            /*
+                should I worry about dithering for the lower depths? 
+            */
+            SkPMColor pmc = SkPreMultiplyColor(color);
+            switch (bitmap.config()) {
+                case SkBitmap::kARGB_8888_Config:
+                    if (data) {
+                        *data = pmc;
+                    }
+//                    SkDebugf("--- D32_Src_BitmapXferProc\n");
+                    return D32_Src_BitmapXferProc;
+                case SkBitmap::kARGB_4444_Config:
+                    if (data) {
+                        *data = SkPixel32ToPixel4444(pmc);
+                    }
+//                    SkDebugf("--- D16_Src_BitmapXferProc\n");
+                    return D16_Src_BitmapXferProc;
+                case SkBitmap::kRGB_565_Config:
+                    if (data) {
+                        *data = SkPixel32ToPixel16(pmc);
+                    }
+//                    SkDebugf("--- D16_Src_BitmapXferProc\n");
+                    return D16_Src_BitmapXferProc;
+                case SkBitmap::kA8_Config:
+                    if (data) {
+                        *data = SkGetPackedA32(pmc);
+                    }
+//                    SkDebugf("--- DA8_Src_BitmapXferProc\n");
+                    return DA8_Src_BitmapXferProc;
+                default:
+                    break;
+            }
+            break;
+        }
+        default:
+            break;
+    }
+    return NULL;
+}
+
+static void CallBitmapXferProc(const SkBitmap& bitmap, const SkIRect& rect,
+                               BitmapXferProc proc, uint32_t procData) {
+    int shiftPerPixel;
+    switch (bitmap.config()) {
+        case SkBitmap::kARGB_8888_Config:
+            shiftPerPixel = 2;
+            break;
+        case SkBitmap::kARGB_4444_Config:
+        case SkBitmap::kRGB_565_Config:
+            shiftPerPixel = 1;
+            break;
+        case SkBitmap::kA8_Config:
+            shiftPerPixel = 0;
+            break;
+        default:
+            SkASSERT(!"Can't use xferproc on this config");
+            return;
+    }
+
+    uint8_t* pixels = (uint8_t*)bitmap.getPixels();
+    SkASSERT(pixels);
+    const size_t rowBytes = bitmap.rowBytes();
+    const int widthBytes = rect.width() << shiftPerPixel;
+
+    // skip down to the first scanline and X position
+    pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel);
+    for (int scans = rect.height() - 1; scans >= 0; --scans) {
+        proc(pixels, widthBytes, procData);
+        pixels += rowBytes;
+    }
+}
+
+void SkDraw::drawPaint(const SkPaint& paint) const {
+    SkDEBUGCODE(this->validate();)
+
+    if (fClip->isEmpty()) {
+        return;
+    }
+
+    SkIRect    devRect;
+    devRect.set(0, 0, fBitmap->width(), fBitmap->height());
+    if (fBounder && !fBounder->doIRect(devRect)) {
+        return;
+    }
+    
+    /*  If we don't have a shader (i.e. we're just a solid color) we may
+        be faster to operate directly on the device bitmap, rather than invoking
+        a blitter. Esp. true for xfermodes, which require a colorshader to be
+        present, which is just redundant work. Since we're drawing everywhere
+        in the clip, we don't have to worry about antialiasing.
+    */
+    uint32_t procData = 0;  // to avoid the warning
+    BitmapXferProc proc = ChooseBitmapXferProc(*fBitmap, paint, &procData);
+    if (proc) {
+        if (D_Dst_BitmapXferProc == proc) { // nothing to do
+            return;
+        }
+        
+        SkRegion::Iterator iter(*fClip);
+        while (!iter.done()) {
+            CallBitmapXferProc(*fBitmap, iter.rect(), proc, procData);
+            iter.next();
+        }
+    } else {
+        // normal case: use a blitter
+        SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+        SkScan::FillIRect(devRect, fClip, blitter.get());
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct PtProcRec {
+    SkCanvas::PointMode fMode;
+    const SkPaint*  fPaint;
+    const SkRegion* fClip;
+    
+    // computed values
+    SkFixed fRadius;
+    
+    typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
+                         SkBlitter*);
+
+    bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
+              const SkRegion* clip);    
+    Proc chooseProc(SkBlitter* blitter);
+};
+
+static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                                 int count, SkBlitter* blitter) {
+    SkASSERT(rec.fClip->isRect());
+    const SkIRect& r = rec.fClip->getBounds();
+    
+    for (int i = 0; i < count; i++) {
+        int x = SkScalarFloor(devPts[i].fX);
+        int y = SkScalarFloor(devPts[i].fY);
+        if (r.contains(x, y)) {
+            blitter->blitH(x, y, 1);
+        }
+    }
+}
+
+static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
+                                    const SkPoint devPts[], int count,
+                                    SkBlitter* blitter) {
+    SkASSERT(rec.fClip->isRect());
+    const SkIRect& r = rec.fClip->getBounds();
+    uint32_t value;
+    const SkBitmap* bitmap = blitter->justAnOpaqueColor(&value);
+    SkASSERT(bitmap);
+    
+    uint16_t* addr = bitmap->getAddr16(0, 0);
+    int rb = bitmap->rowBytes();
+
+    for (int i = 0; i < count; i++) {
+        int x = SkScalarFloor(devPts[i].fX);
+        int y = SkScalarFloor(devPts[i].fY);
+        if (r.contains(x, y)) {
+//            *bitmap->getAddr16(x, y) = SkToU16(value);
+            ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
+        }
+    }
+}
+
+static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                            int count, SkBlitter* blitter) {
+    for (int i = 0; i < count; i++) {
+        int x = SkScalarFloor(devPts[i].fX);
+        int y = SkScalarFloor(devPts[i].fY);
+        if (rec.fClip->contains(x, y)) {
+            blitter->blitH(x, y, 1);
+        }
+    }
+}
+
+static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                              int count, SkBlitter* blitter) {
+    for (int i = 0; i < count; i += 2) {
+        SkScan::HairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
+    }
+}
+
+static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                              int count, SkBlitter* blitter) {
+    for (int i = 0; i < count - 1; i++) {
+        SkScan::HairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
+    }
+}
+
+// aa versions
+
+static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                              int count, SkBlitter* blitter) {
+    for (int i = 0; i < count; i += 2) {
+        SkScan::AntiHairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
+    }
+}
+
+static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
+                              int count, SkBlitter* blitter) {
+    for (int i = 0; i < count - 1; i++) {
+        SkScan::AntiHairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
+    }
+}
+
+// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
+
+static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
+                           int count, SkBlitter* blitter) {
+    const SkFixed radius = rec.fRadius;
+    for (int i = 0; i < count; i++) {
+        SkFixed x = SkScalarToFixed(devPts[i].fX);
+        SkFixed y = SkScalarToFixed(devPts[i].fY);
+        
+        SkXRect r;
+        r.fLeft = x - radius;
+        r.fTop = y - radius;
+        r.fRight = x + radius;
+        r.fBottom = y + radius;
+        
+        SkScan::FillXRect(r, rec.fClip, blitter);
+    }
+}
+
+static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
+                           int count, SkBlitter* blitter) {
+    const SkFixed radius = rec.fRadius;
+    for (int i = 0; i < count; i++) {
+        SkFixed x = SkScalarToFixed(devPts[i].fX);
+        SkFixed y = SkScalarToFixed(devPts[i].fY);
+        
+        SkXRect r;
+        r.fLeft = x - radius;
+        r.fTop = y - radius;
+        r.fRight = x + radius;
+        r.fBottom = y + radius;
+        
+        SkScan::AntiFillXRect(r, rec.fClip, blitter);
+    }
+}
+
+bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
+                     const SkMatrix* matrix, const SkRegion* clip) {
+    if (paint.getPathEffect()) {
+        return false;
+    }
+    SkScalar width = paint.getStrokeWidth();
+    if (0 == width) {
+        fMode = mode;
+        fPaint = &paint;
+        fClip = clip;
+        fRadius = SK_Fixed1 >> 1;
+        return true;
+    }
+    if (matrix->rectStaysRect() && SkCanvas::kPoints_PointMode == mode) {
+        SkScalar sx = matrix->get(SkMatrix::kMScaleX);
+        SkScalar sy = matrix->get(SkMatrix::kMScaleY);
+        if (SkScalarNearlyZero(sx - sy)) {
+            if (sx < 0) {
+                sx = -sx;
+            }
+
+            fMode = mode;
+            fPaint = &paint;
+            fClip = clip;
+            fRadius = SkScalarToFixed(SkScalarMul(width, sx)) >> 1;
+            return true;
+        }
+    }
+    return false;
+}
+
+PtProcRec::Proc PtProcRec::chooseProc(SkBlitter* blitter) {
+    Proc proc;
+    
+    // for our arrays
+    SkASSERT(0 == SkCanvas::kPoints_PointMode);
+    SkASSERT(1 == SkCanvas::kLines_PointMode);
+    SkASSERT(2 == SkCanvas::kPolygon_PointMode);
+    SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
+
+    // first check for hairlines
+    if (0 == fPaint->getStrokeWidth()) {
+        if (fPaint->isAntiAlias()) {
+            static const Proc gAAProcs[] = {
+                aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
+            };
+            proc = gAAProcs[fMode];
+        } else {
+            if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
+                uint32_t value;
+                const SkBitmap* bm = blitter->justAnOpaqueColor(&value);
+                if (bm && bm->config() == SkBitmap::kRGB_565_Config) {
+                    proc = bw_pt_rect_16_hair_proc;
+                } else {
+                    proc = bw_pt_rect_hair_proc;
+                }
+            } else {
+                static Proc gBWProcs[] = {
+                    bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
+                };
+                proc = gBWProcs[fMode];
+            }
+        }
+    } else {
+        SkASSERT(SkCanvas::kPoints_PointMode == fMode);
+        if (fPaint->isAntiAlias()) {
+            proc = aa_square_proc;
+        } else {
+            proc = bw_square_proc;
+        }
+    }
+    return proc;
+}
+
+static bool bounder_points(SkBounder* bounder, SkCanvas::PointMode mode,
+                           size_t count, const SkPoint pts[],
+                           const SkPaint& paint, const SkMatrix& matrix) {
+    SkIRect ibounds;
+    SkRect bounds;
+    SkScalar inset = paint.getStrokeWidth();
+
+    bounds.set(pts, count);
+    bounds.inset(-inset, -inset);
+    matrix.mapRect(&bounds);
+
+    bounds.roundOut(&ibounds);
+    return bounder->doIRect(ibounds);
+}
+
+// each of these costs 8-bytes of stack space, so don't make it too large
+// must be even for lines/polygon to work
+#define MAX_DEV_PTS     32
+
+void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
+                        const SkPoint pts[], const SkPaint& paint) const {
+    // if we're in lines mode, force count to be even
+    if (SkCanvas::kLines_PointMode == mode) {
+        count &= ~(size_t)1;
+    }
+
+    if ((long)count <= 0) {
+        return;
+    }
+    
+    SkAutoRestoreBounder arb;
+
+    if (fBounder) {
+        if (!bounder_points(fBounder, mode, count, pts, paint, *fMatrix)) {
+            return;
+        }
+        // clear the bounder for the rest of this function, so we don't call it
+        // again later if we happen to call ourselves for drawRect, drawPath,
+        // etc.
+        arb.clearBounder(this);
+    }
+
+    SkASSERT(pts != NULL);
+    SkDEBUGCODE(this->validate();)
+    
+     // nothing to draw
+    if (fClip->isEmpty() ||
+        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+        return;
+    }
+
+    PtProcRec rec;
+    if (rec.init(mode, paint, fMatrix, fClip)) {
+        SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+
+        SkPoint             devPts[MAX_DEV_PTS];
+        const SkMatrix*     matrix = fMatrix;
+        SkBlitter*          bltr = blitter.get();
+        PtProcRec::Proc     proc = rec.chooseProc(bltr);
+        // we have to back up subsequent passes if we're in polygon mode
+        const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
+        
+        do {
+            size_t n = count;
+            if (n > MAX_DEV_PTS) {
+                n = MAX_DEV_PTS;
+            }
+            matrix->mapPoints(devPts, pts, n);
+            proc(rec, devPts, n, bltr);
+            pts += n - backup;
+            SkASSERT(count >= n);
+            count -= n;
+            if (count > 0) {
+                count += backup;
+            }
+        } while (count != 0);
+    } else {
+        switch (mode) {
+            case SkCanvas::kPoints_PointMode: {
+                // temporarily mark the paint as filling.
+                SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style);
+
+                SkScalar width = paint.getStrokeWidth();
+                SkScalar radius = SkScalarHalf(width);
+                
+                if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
+                    SkPath      path;
+                    SkMatrix    preMatrix;
+                    
+                    path.addCircle(0, 0, radius);
+                    for (size_t i = 0; i < count; i++) {
+                        preMatrix.setTranslate(pts[i].fX, pts[i].fY);
+                        // pass true for the last point, since we can modify
+                        // then path then
+                        this->drawPath(path, paint, &preMatrix, (count-1) == i);
+                    }
+                } else {
+                    SkRect  r;
+                    
+                    for (size_t i = 0; i < count; i++) {
+                        r.fLeft = pts[i].fX - radius;
+                        r.fTop = pts[i].fY - radius;
+                        r.fRight = r.fLeft + width;
+                        r.fBottom = r.fTop + width;
+                        this->drawRect(r, paint);
+                    }
+                }
+                break;
+            }
+            case SkCanvas::kLines_PointMode:
+            case SkCanvas::kPolygon_PointMode: {
+                count -= 1;
+                SkPath path;
+                SkPaint p(paint);
+                p.setStyle(SkPaint::kStroke_Style);
+                size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
+                for (size_t i = 0; i < count; i += inc) {
+                    path.moveTo(pts[i]);
+                    path.lineTo(pts[i+1]);
+                    this->drawPath(path, p, NULL, true);
+                    path.rewind();
+                }
+                break;
+            }
+        }
+    }
+}
+
+static inline SkPoint* as_lefttop(SkRect* r) {
+    return (SkPoint*)(void*)r;
+}
+
+static inline SkPoint* as_rightbottom(SkRect* r) {
+    return ((SkPoint*)(void*)r) + 1;
+}
+
+void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
+    SkDEBUGCODE(this->validate();)
+
+    // nothing to draw
+    if (fClip->isEmpty() ||
+        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+        return;
+    }
+
+    // complex enough to draw as a path
+    if (paint.getPathEffect() || paint.getMaskFilter() ||
+            paint.getRasterizer() || !fMatrix->rectStaysRect() ||
+            (paint.getStyle() != SkPaint::kFill_Style &&
+             SkScalarHalf(paint.getStrokeWidth()) > 0)) {
+        SkPath  tmp;
+        tmp.addRect(rect);
+        tmp.setFillType(SkPath::kWinding_FillType);
+        this->drawPath(tmp, paint);
+        return;
+    }
+
+    const SkMatrix& matrix = *fMatrix;
+    SkRect          devRect;
+
+    // transform rect into devRect
+    {
+        matrix.mapXY(rect.fLeft, rect.fTop, rect_points(devRect, 0));
+        matrix.mapXY(rect.fRight, rect.fBottom, rect_points(devRect, 1));
+        devRect.sort();
+    }
+
+    if (fBounder && !fBounder->doRect(devRect, paint)) {
+        return;
+    }
+
+    // look for the quick exit, before we build a blitter
+    {
+        SkIRect ir;
+        devRect.roundOut(&ir);
+        if (fClip->quickReject(ir))
+            return;
+    }
+
+    SkAutoBlitterChoose blitterStorage(*fBitmap, matrix, paint);
+    SkBlitter*          blitter = blitterStorage.get();
+    const SkRegion*     clip = fClip;
+
+    if (paint.getStyle() == SkPaint::kFill_Style) {
+        if (paint.isAntiAlias()) {
+            SkScan::AntiFillRect(devRect, clip, blitter);
+        } else {
+            SkScan::FillRect(devRect, clip, blitter);
+        }
+    } else {
+        if (paint.isAntiAlias()) {
+            SkScan::AntiHairRect(devRect, clip, blitter);
+        } else {
+            SkScan::HairRect(devRect, clip, blitter);
+        }
+    }
+}
+
+void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
+    if (srcM.fBounds.isEmpty()) {
+        return;
+    }
+
+    SkMask          dstM;
+    const SkMask*   mask = &srcM;
+
+    dstM.fImage = NULL;
+    SkAutoMaskImage ami(&dstM, false);
+
+    if (paint.getMaskFilter() &&
+            paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, NULL)) {
+        mask = &dstM;
+    }
+
+    if (fBounder && !fBounder->doIRect(mask->fBounds)) {
+        return;
+    }
+
+    SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+
+    blitter->blitMaskRegion(*mask, *fClip);
+}
+
+class SkAutoPaintRestoreColorStrokeWidth {
+public:
+    SkAutoPaintRestoreColorStrokeWidth(const SkPaint& paint) {
+        fPaint = (SkPaint*)&paint;
+        fColor = paint.getColor();
+        fWidth = paint.getStrokeWidth();
+    }
+    ~SkAutoPaintRestoreColorStrokeWidth() {
+        fPaint->setColor(fColor);
+        fPaint->setStrokeWidth(fWidth);
+    }
+    
+private:
+    SkPaint*    fPaint;
+    SkColor     fColor;
+    SkScalar    fWidth;
+};
+
+void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& paint,
+                      const SkMatrix* prePathMatrix, bool pathIsMutable) const {
+    SkDEBUGCODE(this->validate();)
+
+    // nothing to draw
+    if (fClip->isEmpty() ||
+        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+        return;
+    }
+
+    SkPath*         pathPtr = (SkPath*)&origSrcPath;
+    bool            doFill = true;
+    SkPath          tmpPath;
+    SkMatrix        tmpMatrix;
+    const SkMatrix* matrix = fMatrix;
+
+    if (prePathMatrix) {
+        if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style ||
+                paint.getRasterizer()) {
+            SkPath* result = pathPtr;
+    
+            if (!pathIsMutable) {
+                result = &tmpPath;
+                pathIsMutable = true;
+            }
+            pathPtr->transform(*prePathMatrix, result);
+            pathPtr = result;
+        } else {
+            if (!tmpMatrix.setConcat(*matrix, *prePathMatrix)) {
+                // overflow
+                return;
+            }
+            matrix = &tmpMatrix;
+        }
+    }
+    // at this point we're done with prePathMatrix
+    SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
+        
+    /*
+        If the device thickness < 1.0, then make it a hairline, and
+        modulate alpha if the thickness is even smaller (e.g. thickness == 0.5
+        should modulate the alpha by 1/2)
+    */
+
+    SkAutoPaintRestoreColorStrokeWidth aprc(paint);
+    
+    if (paint.getStyle() == SkPaint::kStroke_Style &&
+            paint.getXfermode() == NULL &&
+            (matrix->getType() & SkMatrix::kPerspective_Mask) == 0) {
+        SkScalar width = paint.getStrokeWidth();
+        if (width > 0) {
+            width = matrix->mapRadius(paint.getStrokeWidth());
+            if (width < SK_Scalar1) {
+                int scale = (int)SkScalarMul(width, 256);
+                int alpha = paint.getAlpha() * scale >> 8;
+                
+                // pretend to be a hairline, with a modulated alpha
+                ((SkPaint*)&paint)->setAlpha(alpha);
+                ((SkPaint*)&paint)->setStrokeWidth(0);
+                
+//                SkDebugf("------ convert to hairline %d\n", scale);
+            }
+        }
+    }
+    
+    if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
+        doFill = paint.getFillPath(*pathPtr, &tmpPath);
+        pathPtr = &tmpPath;
+    }
+    
+    if (paint.getRasterizer()) {
+        SkMask  mask;
+        if (paint.getRasterizer()->rasterize(*pathPtr, *matrix,
+                            &fClip->getBounds(), paint.getMaskFilter(), &mask,
+                            SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
+            this->drawDevMask(mask, paint);
+            SkMask::FreeImage(mask.fImage);
+        }
+        return;
+    }
+
+    // avoid possibly allocating a new path in transform if we can
+    SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
+
+    // transform the path into device space
+    pathPtr->transform(*matrix, devPathPtr);
+
+    SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+
+    // how does filterPath() know to fill or hairline the path??? <mrr>
+    if (paint.getMaskFilter() &&
+            paint.getMaskFilter()->filterPath(*devPathPtr, *fMatrix, *fClip,
+                                              fBounder, blitter.get())) {
+        return; // filterPath() called the blitter, so we're done
+    }
+
+    if (fBounder && !fBounder->doPath(*devPathPtr, paint, doFill)) {
+        return;
+    }
+
+    if (doFill) {
+        if (paint.isAntiAlias()) {
+            SkScan::AntiFillPath(*devPathPtr, *fClip, blitter.get());
+        } else {
+            SkScan::FillPath(*devPathPtr, *fClip, blitter.get());
+        }
+    } else {    // hairline
+        if (paint.isAntiAlias()) {
+            SkScan::AntiHairPath(*devPathPtr, fClip, blitter.get());
+        } else {
+            SkScan::HairPath(*devPathPtr, fClip, blitter.get());
+        }
+    }
+}
+
+static inline bool just_translate(const SkMatrix& m) {
+    return (m.getType() & ~SkMatrix::kTranslate_Mask) == 0;
+}
+
+void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap,
+                              const SkPaint& paint) const {
+    SkASSERT(bitmap.getConfig() == SkBitmap::kA8_Config);
+
+    if (just_translate(*fMatrix)) {        
+        int ix = SkScalarRound(fMatrix->getTranslateX());
+        int iy = SkScalarRound(fMatrix->getTranslateY());
+
+        SkMask  mask;
+        mask.fBounds.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
+        mask.fFormat = SkMask::kA8_Format;
+        mask.fRowBytes = bitmap.rowBytes();
+        mask.fImage = bitmap.getAddr8(0, 0);
+        
+        this->drawDevMask(mask, paint);
+    } else {    // need to xform the bitmap first
+        SkRect  r;
+        SkMask  mask;
+        
+        r.set(0, 0,
+              SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
+        fMatrix->mapRect(&r);
+        r.round(&mask.fBounds);
+        
+        // set the mask's bounds to the transformed bitmap-bounds,
+        // clipped to the actual device
+        {
+            SkIRect    devBounds;
+            devBounds.set(0, 0, fBitmap->width(), fBitmap->height());
+            // need intersect(l, t, r, b) on irect
+            if (!mask.fBounds.intersect(devBounds)) {
+                return;
+            }
+        }
+        mask.fFormat = SkMask::kA8_Format;
+        mask.fRowBytes = SkAlign4(mask.fBounds.width());
+
+        // allocate (and clear) our temp buffer to hold the transformed bitmap
+        size_t size = mask.computeImageSize();
+        SkAutoMalloc    storage(size);
+        mask.fImage = (uint8_t*)storage.get();
+        memset(mask.fImage, 0, size);
+        
+        // now draw our bitmap(src) into mask(dst), transformed by the matrix
+        {
+            SkBitmap    device;
+            device.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(),
+                             mask.fBounds.height(), mask.fRowBytes);
+            device.setPixels(mask.fImage);
+            
+            SkCanvas c(device);
+            // need the unclipped top/left for the translate
+            c.translate(-SkIntToScalar(mask.fBounds.fLeft),
+                        -SkIntToScalar(mask.fBounds.fTop));
+            c.concat(*fMatrix);
+            c.drawBitmap(bitmap, 0, 0, NULL);
+        }
+        this->drawDevMask(mask, paint);
+    }
+}
+
+static bool clipped_out(const SkMatrix& m, const SkRegion& c,
+                        const SkRect& srcR) {
+    SkRect  dstR;
+    SkIRect devIR;
+    
+    m.mapRect(&dstR, srcR);
+    dstR.roundOut(&devIR);    
+    return c.quickReject(devIR);
+}
+
+static bool clipped_out(const SkMatrix& matrix, const SkRegion& clip,
+                        int width, int height) {
+    SkRect  r;
+    r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
+    return clipped_out(matrix, clip, r);
+}
+
+void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
+                        const SkPaint& paint) const {
+    SkDEBUGCODE(this->validate();)
+
+    // nothing to draw
+    if (fClip->isEmpty() ||
+            bitmap.width() == 0 || bitmap.height() == 0 ||
+            bitmap.getConfig() == SkBitmap::kNo_Config ||
+            (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+        return;
+    }
+    
+    // run away on too-big bitmaps for now (exceed 16.16)
+    if (bitmap.width() > 32767 || bitmap.height() > 32767) {
+        return;
+    }
+    
+    SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style);
+    
+    SkMatrix matrix;
+    if (!matrix.setConcat(*fMatrix, prematrix)) {
+        return;
+    }
+
+    // do I need to call the bounder first??? 
+    if (clipped_out(matrix, *fClip, bitmap.width(), bitmap.height())) {
+        return;
+    }
+
+    // only lock the pixels if we passed the clip test
+    SkAutoLockPixels alp(bitmap);
+    // after the lock, check if we are valid
+    if (!bitmap.readyToDraw()) {
+        return;
+    }
+
+    if (bitmap.getConfig() != SkBitmap::kA8_Config && just_translate(matrix)) {
+        int         ix = SkScalarRound(matrix.getTranslateX());
+        int         iy = SkScalarRound(matrix.getTranslateY());
+        uint32_t    storage[kBlitterStorageLongCount];
+        SkBlitter*  blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
+                                            ix, iy, storage, sizeof(storage));
+        if (blitter) {
+            SkAutoTPlacementDelete<SkBlitter>   ad(blitter, storage);
+
+            SkIRect    ir;
+            ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
+            
+            if (fBounder && !fBounder->doIRect(ir)) {
+                return;
+            }
+
+            SkRegion::Cliperator iter(*fClip, ir);
+            const SkIRect&       cr = iter.rect();
+
+            for (; !iter.done(); iter.next()) {
+                SkASSERT(!cr.isEmpty());
+                blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
+            }
+            return;
+        }
+#if 0
+        SkDebugf("---- MISSING sprite case: config=%d [%d %d], device=%d, xfer=%p, alpha=0x%X colorFilter=%p\n",
+                bitmap.config(), bitmap.width(), bitmap.height(), fBitmap->config(),
+                paint.getXfermode(), paint.getAlpha(), paint.getColorFilter());
+#endif
+    }
+    
+    // now make a temp draw on the stack, and use it
+    //
+    SkDraw draw(*this);
+    draw.fMatrix = &matrix;
+    
+    if (bitmap.getConfig() == SkBitmap::kA8_Config) {
+        draw.drawBitmapAsMask(bitmap, paint);
+    } else {
+        SkAutoBitmapShaderInstall   install(bitmap, &paint);
+
+        SkRect  r;
+        r.set(0, 0, SkIntToScalar(bitmap.width()),
+              SkIntToScalar(bitmap.height()));
+        // is this ok if paint has a rasterizer? 
+        draw.drawRect(r, paint);
+    }
+}
+
+void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y,
+                        const SkPaint& paint) const {
+    SkDEBUGCODE(this->validate();)
+    
+    // nothing to draw
+    if (fClip->isEmpty() ||
+            bitmap.width() == 0 || bitmap.height() == 0 ||
+            bitmap.getConfig() == SkBitmap::kNo_Config ||
+            (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+        return;
+    }
+
+    SkIRect    bounds;
+    bounds.set(x, y, x + bitmap.width(), y + bitmap.height());
+
+    if (fClip->quickReject(bounds)) {
+        return; // nothing to draw
+    }
+
+    SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style);
+
+    if (NULL == paint.getColorFilter()) {
+        uint32_t    storage[kBlitterStorageLongCount];
+        SkBlitter*  blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
+                                                x, y, storage, sizeof(storage));
+
+        if (blitter) {
+            SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage);
+
+            if (fBounder && !fBounder->doIRect(bounds)) {
+                return;
+            }
+
+            SkRegion::Cliperator iter(*fClip, bounds);
+            const SkIRect&       cr = iter.rect();
+
+            for (; !iter.done(); iter.next()) {
+                SkASSERT(!cr.isEmpty());
+                blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
+            }
+            return;
+        }
+    }
+
+    SkAutoBitmapShaderInstall   install(bitmap, &paint);
+
+    SkMatrix        matrix;
+    SkRect          r;
+
+    // get a scalar version of our rect
+    r.set(bounds);
+
+    // tell the shader our offset
+    matrix.setTranslate(r.fLeft, r.fTop);
+    paint.getShader()->setLocalMatrix(matrix);
+    
+    SkDraw draw(*this);
+    matrix.reset();
+    draw.fMatrix = &matrix;
+    // call ourself with a rect
+    // is this OK if paint has a rasterizer? 
+    draw.drawRect(r, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkScalerContext.h"
+#include "SkGlyphCache.h"
+#include "SkUtils.h"
+
+static void measure_text(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
+                const char text[], size_t byteLength, SkVector* stopVector) {
+    SkFixed     x = 0, y = 0;
+    const char* stop = text + byteLength;
+
+    SkAutoKern  autokern;
+    
+    while (text < stop) {
+        // don't need x, y here, since all subpixel variants will have the
+        // same advance
+        const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+        x += autokern.adjust(glyph) + glyph.fAdvanceX;
+        y += glyph.fAdvanceY;
+    }
+    stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
+
+    SkASSERT(text == stop);
+}
+
+void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
+                              SkScalar x, SkScalar y,
+                              const SkPaint& paint) const {
+    SkDEBUGCODE(this->validate();)
+
+    SkTextToPathIter iter(text, byteLength, paint, true, true);
+
+    SkMatrix    matrix;
+    matrix.setScale(iter.getPathScale(), iter.getPathScale());
+    matrix.postTranslate(x, y);
+
+    const SkPath* iterPath;
+    SkScalar xpos, prevXPos = 0;
+
+    while ((iterPath = iter.next(&xpos)) != NULL) {
+        matrix.postTranslate(xpos - prevXPos, 0);
+        this->drawPath(*iterPath, iter.getPaint(), &matrix, false);
+        prevXPos = xpos;
+    }
+}
+
+#define kStdStrikeThru_Offset       (-SK_Scalar1 * 6 / 21)
+#define kStdUnderline_Offset        (SK_Scalar1 / 9)
+#define kStdUnderline_Thickness     (SK_Scalar1 / 18)
+
+static void draw_paint_rect(const SkDraw* draw, const SkPaint& paint,
+                            const SkRect& r, SkScalar textSize) {
+    if (paint.getStyle() == SkPaint::kFill_Style) {
+        draw->drawRect(r, paint);
+    } else {
+        SkPaint p(paint);
+        p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
+        draw->drawRect(r, p);
+    }
+}
+
+static void handle_aftertext(const SkDraw* draw, const SkPaint& paint,
+                             SkScalar width, const SkPoint& start) {
+    uint32_t flags = paint.getFlags();
+
+    if (flags & (SkPaint::kUnderlineText_Flag |
+                 SkPaint::kStrikeThruText_Flag)) {
+        SkScalar textSize = paint.getTextSize();
+        SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
+        SkRect   r;
+
+        r.fLeft = start.fX;
+        r.fRight = start.fX + width;
+
+        if (flags & SkPaint::kUnderlineText_Flag) {
+            SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
+                                             start.fY);
+            r.fTop = offset;
+            r.fBottom = offset + height;
+            draw_paint_rect(draw, paint, r, textSize);
+        }
+        if (flags & SkPaint::kStrikeThruText_Flag) {
+            SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
+                                             start.fY);
+            r.fTop = offset;
+            r.fBottom = offset + height;
+            draw_paint_rect(draw, paint, r, textSize);
+        }
+    }
+}
+
+// disable warning : local variable used without having been initialized
+#if defined _WIN32 && _MSC_VER >= 1300 
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void D1G_NoBounder_RectClip(const SkDraw1Glyph& state,
+								   const SkGlyph& glyph, int left, int top) {
+    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+	SkASSERT(state.fClip->isRect());
+	SkASSERT(NULL == state.fBounder);
+	SkASSERT(state.fClipBounds == state.fClip->getBounds());
+
+    left += glyph.fLeft;
+    top  += glyph.fTop;
+
+    int right   = left + glyph.fWidth;
+    int bottom  = top + glyph.fHeight;
+
+	SkMask		mask;
+	SkIRect		storage;
+	SkIRect*	bounds = &mask.fBounds;
+
+	mask.fBounds.set(left, top, right, bottom);
+
+	// this extra test is worth it, assuming that most of the time it succeeds
+	// since we can avoid writing to storage
+	if (!state.fClipBounds.containsNoEmptyCheck(left, top, right, bottom)) {
+		if (!storage.intersectNoEmptyCheck(mask.fBounds, state.fClipBounds))
+			return;
+		bounds = &storage;
+	}
+	
+	uint8_t* aa = (uint8_t*)glyph.fImage;               
+	if (NULL == aa) {
+		aa = (uint8_t*)state.fCache->findImage(glyph);
+		if (NULL == aa) {
+			return; // can't rasterize glyph
+        }
+	}
+
+	mask.fRowBytes = glyph.rowBytes();
+	mask.fFormat = glyph.fMaskFormat;
+	mask.fImage = aa;
+	state.fBlitter->blitMask(mask, *bounds);
+}
+
+static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state,
+								  const SkGlyph& glyph, int left, int top) {
+    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+	SkASSERT(!state.fClip->isRect());
+	SkASSERT(NULL == state.fBounder);
+
+    SkMask  mask;
+
+    left += glyph.fLeft;
+    top  += glyph.fTop;
+
+    mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
+	SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
+
+	if (!clipper.done()) {
+		const SkIRect&  cr = clipper.rect();
+		const uint8_t*  aa = (const uint8_t*)glyph.fImage;
+		if (NULL == aa) {
+			aa = (uint8_t*)state.fCache->findImage(glyph);
+			if (NULL == aa) {
+				return;
+            }
+		}
+		
+		mask.fRowBytes = glyph.rowBytes();
+		mask.fFormat = glyph.fMaskFormat;
+		mask.fImage = (uint8_t*)aa;
+		do {
+			state.fBlitter->blitMask(mask, cr);
+			clipper.next();
+		} while (!clipper.done());
+	}
+}
+
+static void D1G_Bounder(const SkDraw1Glyph& state,
+						const SkGlyph& glyph, int left, int top) {
+    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+
+    SkMask  mask;
+
+    left += glyph.fLeft;
+    top  += glyph.fTop;
+
+    mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
+    SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
+
+	if (!clipper.done()) {
+		const SkIRect&  cr = clipper.rect();
+		const uint8_t*  aa = (const uint8_t*)glyph.fImage;
+		if (NULL == aa) {
+			aa = (uint8_t*)state.fCache->findImage(glyph);
+			if (NULL == aa) {
+				return;
+            }
+		}
+		
+		if (state.fBounder->doIRect(cr)) {
+			mask.fRowBytes = glyph.rowBytes();
+			mask.fFormat = glyph.fMaskFormat;
+			mask.fImage = (uint8_t*)aa;
+			do {
+				state.fBlitter->blitMask(mask, cr);
+				clipper.next();
+			} while (!clipper.done());
+		}
+	}
+}
+
+SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter,
+                                      SkGlyphCache* cache) {
+    fDraw = draw;
+	fBounder = draw->fBounder;
+	fClip = draw->fClip;
+    fClipBounds = fClip->getBounds();
+	fBlitter = blitter;
+	fCache = cache;
+
+    if (draw->fProcs && draw->fProcs->fD1GProc) {
+        return draw->fProcs->fD1GProc;
+    }
+
+    if (NULL == fBounder) {
+        if (fClip->isRect()) {
+            return D1G_NoBounder_RectClip;
+        } else {
+            return D1G_NoBounder_RgnClip;
+        }
+    } else {
+        return D1G_Bounder;
+    }
+}
+
+enum RoundBaseline {
+    kDont_Round_Baseline,
+    kRound_X_Baseline,
+    kRound_Y_Baseline
+};
+
+static RoundBaseline computeRoundBaseline(const SkMatrix& mat) {
+    if (mat[1] == 0 && mat[3] == 0) {
+        // we're 0 or 180 degrees, round the y coordinate of the baseline
+        return kRound_Y_Baseline;
+    } else if (mat[0] == 0 && mat[4] == 0) {
+        // we're 90 or 270 degrees, round the x coordinate of the baseline
+        return kRound_X_Baseline;
+    } else {
+        return kDont_Round_Baseline;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkDraw::drawText(const char text[], size_t byteLength,
+                      SkScalar x, SkScalar y, const SkPaint& paint) const {
+    SkASSERT(byteLength == 0 || text != NULL);
+
+    SkDEBUGCODE(this->validate();)
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0 ||
+        fClip->isEmpty() ||
+        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+        return;
+    }
+
+    SkScalar    underlineWidth = 0;
+    SkPoint     underlineStart;
+
+    underlineStart.set(0, 0);    // to avoid warning
+    if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
+                            SkPaint::kStrikeThruText_Flag)) {
+        underlineWidth = paint.measureText(text, byteLength);
+
+        SkScalar offsetX = 0;
+        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+            offsetX = SkScalarHalf(underlineWidth);
+        } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
+            offsetX = underlineWidth;
+        }
+        underlineStart.set(x - offsetX, y);
+    }
+
+    if (/*paint.isLinearText() ||*/
+        (fMatrix->getType() & SkMatrix::kPerspective_Mask)) {
+        this->drawText_asPaths(text, byteLength, x, y, paint);
+        handle_aftertext(this, paint, underlineWidth, underlineStart);
+        return;
+    }
+
+    SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
+
+    SkAutoGlyphCache    autoCache(paint, fMatrix);
+    SkGlyphCache*       cache = autoCache.getCache();
+    SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+    
+    // transform our starting point
+    {
+        SkPoint loc;
+        fMatrix->mapXY(x, y, &loc);
+        x = loc.fX;
+        y = loc.fY;
+    }
+
+    // need to measure first
+    if (paint.getTextAlign() != SkPaint::kLeft_Align) {
+        SkVector    stop;
+
+        measure_text(cache, glyphCacheProc, text, byteLength, &stop);
+
+        SkScalar    stopX = stop.fX;
+        SkScalar    stopY = stop.fY;
+
+        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+            stopX = SkScalarHalf(stopX);
+            stopY = SkScalarHalf(stopY);
+        }
+        x -= stopX;
+        y -= stopY;
+    }
+    
+    SkFixed fx = SkScalarToFixed(x);
+    SkFixed fy = SkScalarToFixed(y);
+    const char* stop = text + byteLength;
+
+    if (paint.isSubpixelText()) {
+        RoundBaseline roundBaseline = computeRoundBaseline(*fMatrix);
+        if (kRound_Y_Baseline == roundBaseline) {
+            fy = (fy + 0x8000) & ~0xFFFF;
+        } else if (kRound_X_Baseline == roundBaseline) {
+            fx = (fx + 0x8000) & ~0xFFFF;
+        }
+    } else {
+        // apply the bias here, so we don't have to add 1/2 in the loop
+        fx += SK_Fixed1/2;
+        fy += SK_Fixed1/2;
+    }
+
+    SkAutoKern          autokern;
+	SkDraw1Glyph        d1g;
+	SkDraw1Glyph::Proc  proc = d1g.init(this, blitter.get(), cache);
+
+    while (text < stop) {
+        const SkGlyph& glyph  = glyphCacheProc(cache, &text, fx, fy);
+
+        fx += autokern.adjust(glyph);
+
+        if (glyph.fWidth) {
+			proc(d1g, glyph, SkFixedFloor(fx), SkFixedFloor(fy));
+        }
+        fx += glyph.fAdvanceX;
+        fy += glyph.fAdvanceY;
+    }
+
+    if (underlineWidth) {
+        autoCache.release();    // release this now to free up the RAM
+        handle_aftertext(this, paint, underlineWidth, underlineStart);
+    }
+}
+
+// last parameter is interpreted as SkFixed [x, y]
+// return the fixed position, which may be rounded or not by the caller
+//   e.g. subpixel doesn't round
+typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
+
+static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph,
+                          SkIPoint* dst) {
+    dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
+}
+
+static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph,
+                            SkIPoint* dst) {
+    dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
+             SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
+}
+
+static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph,
+                           SkIPoint* dst) {
+    dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
+             SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
+}
+
+static AlignProc pick_align_proc(SkPaint::Align align) {
+    static const AlignProc gProcs[] = {
+        leftAlignProc, centerAlignProc, rightAlignProc
+    };
+    
+    SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
+
+    return gProcs[align];
+}
+
+class TextMapState {
+public:
+    mutable SkPoint fLoc;
+    
+    TextMapState(const SkMatrix& matrix, SkScalar y)
+        : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
+
+    typedef void (*Proc)(const TextMapState&, const SkScalar pos[]);
+    
+    Proc pickProc(int scalarsPerPosition);
+    
+private:
+    const SkMatrix&     fMatrix;
+    SkMatrix::MapXYProc fProc;
+    SkScalar            fY; // ignored by MapXYProc
+    // these are only used by Only... procs
+    SkScalar            fScaleX, fTransX, fTransformedY;
+
+    static void MapXProc(const TextMapState& state, const SkScalar pos[]) {
+        state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
+    }
+    
+    static void MapXYProc(const TextMapState& state, const SkScalar pos[]) {
+        state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
+    }
+    
+    static void MapOnlyScaleXProc(const TextMapState& state,
+                                  const SkScalar pos[]) {
+        state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
+                       state.fTransformedY);
+    }
+    
+    static void MapOnlyTransXProc(const TextMapState& state,
+                                  const SkScalar pos[]) {
+        state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
+    }
+};
+
+TextMapState::Proc TextMapState::pickProc(int scalarsPerPosition) {
+    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+    
+    if (1 == scalarsPerPosition) {
+        unsigned mtype = fMatrix.getType();
+        if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
+            return MapXProc;
+        } else {
+            fScaleX = fMatrix.getScaleX();
+            fTransX = fMatrix.getTranslateX();
+            fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
+                            fMatrix.getTranslateY();
+            return (mtype & SkMatrix::kScale_Mask) ?
+                        MapOnlyScaleXProc : MapOnlyTransXProc;
+        }
+    } else {
+        return MapXYProc;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void SkDraw::drawPosText(const char text[], size_t byteLength,
+                         const SkScalar pos[], SkScalar constY,
+                         int scalarsPerPosition, const SkPaint& paint) const {
+    SkASSERT(byteLength == 0 || text != NULL);
+    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+    SkDEBUGCODE(this->validate();)
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0 ||
+        fClip->isEmpty() ||
+        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+        return;
+    }
+
+    if (/*paint.isLinearText() ||*/
+        (fMatrix->getType() & SkMatrix::kPerspective_Mask)) {
+        // TODO !!!!
+//      this->drawText_asPaths(text, byteLength, x, y, paint);
+        return;
+    }
+
+    SkDrawCacheProc     glyphCacheProc = paint.getDrawCacheProc();
+    SkAutoGlyphCache    autoCache(paint, fMatrix);
+    SkGlyphCache*       cache = autoCache.getCache();
+    SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+    
+    const char*        stop = text + byteLength;
+    AlignProc          alignProc = pick_align_proc(paint.getTextAlign());
+	SkDraw1Glyph	   d1g;
+	SkDraw1Glyph::Proc  proc = d1g.init(this, blitter.get(), cache);
+    TextMapState       tms(*fMatrix, constY);
+    TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
+
+    if (paint.isSubpixelText()) {
+        // maybe we should skip the rounding if linearText is set
+        RoundBaseline roundBaseline = computeRoundBaseline(*fMatrix);
+
+        if (SkPaint::kLeft_Align == paint.getTextAlign()) {
+            while (text < stop) {
+                tmsProc(tms, pos);
+                
+                SkFixed fx = SkScalarToFixed(tms.fLoc.fX);
+                SkFixed fy = SkScalarToFixed(tms.fLoc.fY);
+
+                if (kRound_Y_Baseline == roundBaseline) {
+                    fy = (fy + 0x8000) & ~0xFFFF;
+                } else if (kRound_X_Baseline == roundBaseline) {
+                    fx = (fx + 0x8000) & ~0xFFFF;
+                }
+                
+                const SkGlyph& glyph = glyphCacheProc(cache, &text, fx, fy);
+                
+                if (glyph.fWidth) {
+                    proc(d1g, glyph, SkFixedFloor(fx), SkFixedFloor(fy));
+                }
+                pos += scalarsPerPosition;
+            }
+        } else {
+            while (text < stop) {
+                const SkGlyph* glyph = &glyphCacheProc(cache, &text, 0, 0);
+                
+                if (glyph->fWidth) {
+                    SkDEBUGCODE(SkFixed prevAdvX = glyph->fAdvanceX;)
+                    SkDEBUGCODE(SkFixed prevAdvY = glyph->fAdvanceY;)
+
+                    SkFixed fx, fy;
+                    tmsProc(tms, pos);
+                    
+                    {
+                        SkIPoint fixedLoc;
+                        alignProc(tms.fLoc, *glyph, &fixedLoc);
+                        fx = fixedLoc.fX;
+                        fy = fixedLoc.fY;
+
+                        if (kRound_Y_Baseline == roundBaseline) {
+                            fy = (fy + 0x8000) & ~0xFFFF;
+                        } else if (kRound_X_Baseline == roundBaseline) {
+                            fx = (fx + 0x8000) & ~0xFFFF;
+                        }
+                    }
+                    
+                    // have to call again, now that we've been "aligned"
+                    glyph = &glyphCacheProc(cache, &text, fx, fy);
+                    // the assumption is that the advance hasn't changed
+                    SkASSERT(prevAdvX == glyph->fAdvanceX);
+                    SkASSERT(prevAdvY == glyph->fAdvanceY);
+                    
+                    proc(d1g, *glyph, SkFixedFloor(fx), SkFixedFloor(fy));
+                }
+                pos += scalarsPerPosition;
+            }
+        }
+    } else {    // not subpixel
+        while (text < stop) {
+            // the last 2 parameters are ignored
+            const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+            
+            if (glyph.fWidth) {
+                tmsProc(tms, pos);
+                
+                SkIPoint fixedLoc;
+                alignProc(tms.fLoc, glyph, &fixedLoc);
+                
+                proc(d1g, glyph,
+                     SkFixedRound(fixedLoc.fX), SkFixedRound(fixedLoc.fY));
+            }
+            pos += scalarsPerPosition;
+        }
+    }
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPathMeasure.h"
+
+static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
+                        SkPathMeasure& meas, const SkMatrix& matrix) {
+    SkMatrix::MapXYProc proc = matrix.getMapXYProc();
+
+    for (int i = 0; i < count; i++) {
+        SkPoint pos;
+        SkVector tangent;
+
+        proc(matrix, src[i].fX, src[i].fY, &pos);
+        SkScalar sx = pos.fX;
+        SkScalar sy = pos.fY;
+
+        meas.getPosTan(sx, &pos, &tangent);
+
+        /*  This is the old way (that explains our approach but is way too slow
+            SkMatrix    matrix;
+            SkPoint     pt;
+
+            pt.set(sx, sy);
+            matrix.setSinCos(tangent.fY, tangent.fX);
+            matrix.preTranslate(-sx, 0);
+            matrix.postTranslate(pos.fX, pos.fY);
+            matrix.mapPoints(&dst[i], &pt, 1);
+        */
+        dst[i].set(pos.fX - SkScalarMul(tangent.fY, sy),
+                   pos.fY + SkScalarMul(tangent.fX, sy));
+    }
+}
+
+/*  TODO
+
+    Need differentially more subdivisions when the follow-path is curvy. Not sure how to
+    determine that, but we need it. I guess a cheap answer is let the caller tell us,
+    but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
+*/
+static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
+                      const SkMatrix& matrix) {
+    SkPath::Iter    iter(src, false);
+    SkPoint         srcP[4], dstP[3];
+    SkPath::Verb    verb;
+
+    while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                morphpoints(dstP, srcP, 1, meas, matrix);
+                dst->moveTo(dstP[0]);
+                break;
+            case SkPath::kLine_Verb:
+                // turn lines into quads to look bendy
+                srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX);
+                srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY);
+                morphpoints(dstP, srcP, 2, meas, matrix);
+                dst->quadTo(dstP[0], dstP[1]);
+                break;
+            case SkPath::kQuad_Verb:
+                morphpoints(dstP, &srcP[1], 2, meas, matrix);
+                dst->quadTo(dstP[0], dstP[1]);
+                break;
+            case SkPath::kCubic_Verb:
+                morphpoints(dstP, &srcP[1], 3, meas, matrix);
+                dst->cubicTo(dstP[0], dstP[1], dstP[2]);
+                break;
+            case SkPath::kClose_Verb:
+                dst->close();
+                break;
+            default:
+                SkASSERT(!"unknown verb");
+                break;
+        }
+    }
+}
+
+void SkDraw::drawTextOnPath(const char text[], size_t byteLength,
+                            const SkPath& follow, const SkMatrix* matrix,
+                            const SkPaint& paint) const {
+    SkASSERT(byteLength == 0 || text != NULL);
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0 ||
+        fClip->isEmpty() ||
+        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+        return;
+    }
+
+    SkTextToPathIter    iter(text, byteLength, paint, true, true);
+    SkPathMeasure       meas(follow, false);
+    SkScalar            hOffset = 0;
+
+    // need to measure first
+    if (paint.getTextAlign() != SkPaint::kLeft_Align) {
+        SkScalar pathLen = meas.getLength();
+        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+            pathLen = SkScalarHalf(pathLen);
+        }
+        hOffset += pathLen;
+    }
+
+    const SkPath*   iterPath;
+    SkScalar        xpos;
+    SkMatrix        scaledMatrix;
+    SkScalar        scale = iter.getPathScale();
+
+    scaledMatrix.setScale(scale, scale);
+        
+    while ((iterPath = iter.next(&xpos)) != NULL) {
+        SkPath      tmp;
+        SkMatrix    m(scaledMatrix);
+
+        m.postTranslate(xpos + hOffset, 0);
+        if (matrix) {
+            m.postConcat(*matrix);
+        }
+        morphpath(&tmp, *iterPath, meas, m);
+        this->drawPath(tmp, iter.getPaint());
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct VertState {
+    int f0, f1, f2;
+
+    VertState(int vCount, const uint16_t indices[], int indexCount)
+            : fIndices(indices) {
+        fCurrIndex = 0;
+        if (indices) {
+            fCount = indexCount;
+        } else {
+            fCount = vCount;
+        }
+    }
+    
+    typedef bool (*Proc)(VertState*);    
+    Proc chooseProc(SkCanvas::VertexMode mode);
+
+private:
+    int             fCount;
+    int             fCurrIndex;
+    const uint16_t* fIndices;
+    
+    static bool Triangles(VertState*);
+    static bool TrianglesX(VertState*);
+    static bool TriangleStrip(VertState*);
+    static bool TriangleStripX(VertState*);
+    static bool TriangleFan(VertState*);
+    static bool TriangleFanX(VertState*);
+};
+
+bool VertState::Triangles(VertState* state) {
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f0 = index + 0;
+    state->f1 = index + 1;
+    state->f2 = index + 2;
+    state->fCurrIndex = index + 3;
+    return true;
+}
+
+bool VertState::TrianglesX(VertState* state) {
+    const uint16_t* indices = state->fIndices;
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f0 = indices[index + 0];
+    state->f1 = indices[index + 1];
+    state->f2 = indices[index + 2];
+    state->fCurrIndex = index + 3;
+    return true;
+}
+
+bool VertState::TriangleStrip(VertState* state) {
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f2 = index + 2;
+    if (index & 1) {
+        state->f0 = index + 1;
+        state->f1 = index + 0;
+    } else {
+        state->f0 = index + 0;
+        state->f1 = index + 1;
+    }
+    state->fCurrIndex = index + 1;
+    return true;
+}
+
+bool VertState::TriangleStripX(VertState* state) {
+    const uint16_t* indices = state->fIndices;
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f2 = indices[index + 2];
+    if (index & 1) {
+        state->f0 = indices[index + 1];
+        state->f1 = indices[index + 0];
+    } else {
+        state->f0 = indices[index + 0];
+        state->f1 = indices[index + 1];
+    }
+    state->fCurrIndex = index + 1;
+    return true;
+}
+
+bool VertState::TriangleFan(VertState* state) {
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f0 = 0;
+    state->f1 = index + 1;
+    state->f2 = index + 2;
+    state->fCurrIndex = index + 1;
+    return true;
+}
+
+bool VertState::TriangleFanX(VertState* state) {
+    const uint16_t* indices = state->fIndices;
+    int index = state->fCurrIndex;
+    if (index + 3 > state->fCount) {
+        return false;
+    }
+    state->f0 = indices[0];
+    state->f1 = indices[index + 1];
+    state->f2 = indices[index + 2];
+    state->fCurrIndex = index + 1;
+    return true;
+}
+
+VertState::Proc VertState::chooseProc(SkCanvas::VertexMode mode) {
+    switch (mode) {
+        case SkCanvas::kTriangles_VertexMode:
+            return fIndices ? TrianglesX : Triangles;
+        case SkCanvas::kTriangleStrip_VertexMode:
+            return fIndices ? TriangleStripX : TriangleStrip;
+        case SkCanvas::kTriangleFan_VertexMode:
+            return fIndices ? TriangleFanX : TriangleFan;
+        default:
+            return NULL;
+    }
+}
+
+typedef void (*HairProc)(const SkPoint&, const SkPoint&, const SkRegion*,
+                         SkBlitter*);
+
+static HairProc ChooseHairProc(bool doAntiAlias) {
+    return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
+}
+
+static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
+                              const SkPoint texs[], SkMatrix* matrix) {
+    SkPoint src[3], dst[3];
+    
+    src[0] = texs[state.f0];
+    src[1] = texs[state.f1];
+    src[2] = texs[state.f2];
+    dst[0] = verts[state.f0];
+    dst[1] = verts[state.f1];
+    dst[2] = verts[state.f2];
+    return matrix->setPolyToPoly(src, dst, 3);
+}
+
+class SkTriColorShader : public SkShader {
+public:
+    SkTriColorShader() {}
+
+    bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
+    
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+    
+protected:
+    SkTriColorShader(SkFlattenableReadBuffer& buffer) : SkShader(buffer) {}
+    
+    virtual Factory getFactory() { return CreateProc; }
+    
+private:
+    SkMatrix    fDstToUnit;
+    SkPMColor   fColors[3];
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkTriColorShader, (buffer));
+    }
+    typedef SkShader INHERITED;
+};
+
+bool SkTriColorShader::setup(const SkPoint pts[], const SkColor colors[],
+                             int index0, int index1, int index2) {
+    
+    fColors[0] = SkPreMultiplyColor(colors[index0]);
+    fColors[1] = SkPreMultiplyColor(colors[index1]);
+    fColors[2] = SkPreMultiplyColor(colors[index2]);
+    
+    SkMatrix m, im;
+    m.reset();
+    m.set(0, pts[index1].fX - pts[index0].fX);
+    m.set(1, pts[index2].fX - pts[index0].fX);
+    m.set(2, pts[index0].fX);
+    m.set(3, pts[index1].fY - pts[index0].fY);
+    m.set(4, pts[index2].fY - pts[index0].fY);
+    m.set(5, pts[index0].fY);
+    if (!m.invert(&im)) {
+        return false;
+    }
+    return fDstToUnit.setConcat(im, this->getTotalInverse());
+}
+
+#include "SkColorPriv.h"
+#include "SkPorterDuff.h"
+#include "SkComposShader.h"
+#include "SkXfermode.h"
+
+static int ScalarTo256(SkScalar v) {
+    int scale = SkScalarToFixed(v) >> 8;
+    if (scale < 0) {
+        scale = 0;
+    }
+    if (scale > 255) {
+        scale = 255;
+    }
+    return SkAlpha255To256(scale);
+}
+
+void SkTriColorShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
+    SkPoint src;
+    
+    for (int i = 0; i < count; i++) {
+        fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src);
+        x += 1;
+        
+        int scale1 = ScalarTo256(src.fX);
+        int scale2 = ScalarTo256(src.fY);
+        int scale0 = 256 - scale1 - scale2;
+        if (scale0 < 0) {
+            if (scale1 > scale2) {
+                scale2 = 256 - scale1;
+            } else {
+                scale1 = 256 - scale2;
+            }
+            scale0 = 0;
+        }
+        
+        dstC[i] = SkAlphaMulQ(fColors[0], scale0) +
+        SkAlphaMulQ(fColors[1], scale1) +
+        SkAlphaMulQ(fColors[2], scale2);
+    }
+}
+
+void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
+                          const SkPoint vertices[], const SkPoint textures[],
+                          const SkColor colors[], SkXfermode* xmode,
+                          const uint16_t indices[], int indexCount,
+                          const SkPaint& paint) const {
+    SkASSERT(0 == count || NULL != vertices);
+    
+    // abort early if there is nothing to draw
+    if (count < 3 || (indices && indexCount < 3) || fClip->isEmpty() ||
+            (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+        return;
+    }
+    
+    // transform out vertices into device coordinates
+    SkAutoSTMalloc<16, SkPoint> storage(count);
+    SkPoint* devVerts = storage.get();
+    fMatrix->mapPoints(devVerts, vertices, count);
+    
+    if (fBounder) {
+        SkRect bounds;
+        bounds.set(devVerts, count);
+        if (!fBounder->doRect(bounds, paint)) {
+            return;
+        }
+    }
+    
+    /*
+        We can draw the vertices in 1 of 4 ways:
+
+        - solid color (no shader/texture[], no colors[])
+        - just colors (no shader/texture[], has colors[])
+        - just texture (has shader/texture[], no colors[])
+        - colors * texture (has shader/texture[], has colors[])
+     
+        Thus for texture drawing, we need both texture[] and a shader.
+    */
+
+    SkTriColorShader triShader; // must be above declaration of p
+    SkPaint p(paint);
+
+    SkShader* shader = p.getShader();
+    if (NULL == shader) {
+        // if we have no shader, we ignore the texture coordinates
+        textures = NULL;
+    } else if (NULL == textures) {
+        // if we don't have texture coordinates, ignore the shader
+        p.setShader(NULL);
+        shader = NULL;
+    }
+
+    // setup the custom shader (if needed)
+    if (NULL != colors) {
+        if (NULL == textures) {
+            // just colors (no texture)
+            p.setShader(&triShader);
+        } else {
+            // colors * texture
+            SkASSERT(shader);
+            bool releaseMode = false;
+            if (NULL == xmode) {
+                xmode = SkPorterDuff::CreateXfermode(
+                                                  SkPorterDuff::kMultiply_Mode);
+                releaseMode = true;
+            }
+            SkShader* compose = SkNEW_ARGS(SkComposeShader,
+                                           (&triShader, shader, xmode));
+            p.setShader(compose)->unref();
+            if (releaseMode) {
+                xmode->unref();
+            }
+        }
+    }
+
+    SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, p);
+    // setup our state and function pointer for iterating triangles
+    VertState       state(count, indices, indexCount);
+    VertState::Proc vertProc = state.chooseProc(vmode);
+        
+    if (NULL != textures || NULL != colors) {
+        SkMatrix  localM, tempM;
+        bool      hasLocalM = shader && shader->getLocalMatrix(&localM);
+        
+        if (NULL != colors) {
+            if (!triShader.setContext(*fBitmap, p, *fMatrix)) {
+                colors = NULL;
+            }
+        }
+    
+        while (vertProc(&state)) {
+            if (NULL != textures) {
+                if (texture_to_matrix(state, vertices, textures, &tempM)) {
+                    if (hasLocalM) {
+                        tempM.postConcat(localM);
+                    }
+                    shader->setLocalMatrix(tempM);
+                    // need to recal setContext since we changed the local matrix
+                    if (!shader->setContext(*fBitmap, p, *fMatrix)) {
+                        continue;
+                    }
+                }
+            }
+            if (NULL != colors) {
+                if (!triShader.setup(vertices, colors,
+                                     state.f0, state.f1, state.f2)) {
+                    continue;
+                }
+            }
+            SkScan::FillTriangle(devVerts[state.f0], devVerts[state.f1],
+                                 devVerts[state.f2], fClip, blitter.get());
+        }
+        // now restore the shader's original local matrix
+        if (NULL != shader) {
+            if (hasLocalM) {
+                shader->setLocalMatrix(localM);
+            } else {
+                shader->resetLocalMatrix();
+            }
+        }
+    } else {
+        // no colors[] and no texture
+        HairProc hairProc = ChooseHairProc(paint.isAntiAlias());
+        while (vertProc(&state)) {
+            hairProc(devVerts[state.f0], devVerts[state.f1], fClip, blitter.get());
+            hairProc(devVerts[state.f1], devVerts[state.f2], fClip, blitter.get());
+            hairProc(devVerts[state.f2], devVerts[state.f0], fClip, blitter.get());
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkDraw::validate() const {
+    SkASSERT(fBitmap != NULL);
+    SkASSERT(fMatrix != NULL);
+    SkASSERT(fClip != NULL);
+
+    const SkIRect&  cr = fClip->getBounds();
+    SkIRect         br;
+
+    br.set(0, 0, fBitmap->width(), fBitmap->height());
+    SkASSERT(cr.isEmpty() || br.contains(cr));
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkBounder::doIRect(const SkIRect& r) {
+    SkIRect    rr;
+    return rr.intersect(fClip->getBounds(), r) && this->onIRect(rr);
+}
+
+bool SkBounder::doHairline(const SkPoint& pt0, const SkPoint& pt1,
+                           const SkPaint& paint) {
+    SkIRect     r;
+    SkScalar    v0, v1;
+
+    v0 = pt0.fX;
+    v1 = pt1.fX;
+    if (v0 > v1) {
+        SkTSwap<SkScalar>(v0, v1);
+    }
+    r.fLeft     = SkScalarFloor(v0);
+    r.fRight    = SkScalarCeil(v1);
+
+    v0 = pt0.fY;
+    v1 = pt1.fY;
+    if (v0 > v1) {
+        SkTSwap<SkScalar>(v0, v1);
+    }
+    r.fTop      = SkScalarFloor(v0);
+    r.fBottom   = SkScalarCeil(v1);
+
+    if (paint.isAntiAlias()) {
+        r.inset(-1, -1);
+    }
+    return this->doIRect(r);
+}
+
+bool SkBounder::doRect(const SkRect& rect, const SkPaint& paint) {
+    SkIRect    r;
+
+    if (paint.getStyle() == SkPaint::kFill_Style) {
+        rect.round(&r);
+    } else {
+        int rad = -1;
+        rect.roundOut(&r);
+        if (paint.isAntiAlias()) {
+            rad = -2;
+        }
+        r.inset(rad, rad);
+    }
+    return this->doIRect(r);
+}
+
+bool SkBounder::doPath(const SkPath& path, const SkPaint& paint, bool doFill) {
+    SkRect      bounds;
+    SkIRect     r;
+
+    path.computeBounds(&bounds, SkPath::kFast_BoundsType);
+
+    if (doFill) {
+        bounds.round(&r);
+    } else {    // hairline
+        bounds.roundOut(&r);
+    }
+
+    if (paint.isAntiAlias()) {
+        r.inset(-1, -1);
+    }
+    return this->doIRect(r);
+}
+
+void SkBounder::commit() {
+    // override in subclass
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkPath.h"
+#include "SkDraw.h"
+#include "SkRegion.h"
+#include "SkBlitter.h"
+
+static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds,
+                           SkMaskFilter* filter, const SkMatrix* filterMatrix,
+                           SkIRect* bounds) {
+    if (devPath.isEmpty()) {
+        return false;
+    }
+
+    SkIPoint   margin;
+    margin.set(0, 0);
+
+    //  init our bounds from the path
+    {
+        SkRect      pathBounds;
+        devPath.computeBounds(&pathBounds, SkPath::kExact_BoundsType);
+        pathBounds.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+        pathBounds.roundOut(bounds);
+    }
+    
+    if (filter) {
+        SkASSERT(filterMatrix);
+        
+        SkMask  srcM, dstM;
+        
+        srcM.fBounds = *bounds;
+        srcM.fFormat = SkMask::kA8_Format;
+        srcM.fImage = NULL;
+        if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
+            return false;
+        }
+        *bounds = dstM.fBounds;
+    }
+
+    if (clipBounds && !SkIRect::Intersects(*clipBounds, *bounds)) {
+        return false;
+    }
+    
+    // (possibly) trim the srcM bounds to reflect the clip
+    // (plus whatever slop the filter needs)
+    if (clipBounds && !clipBounds->contains(*bounds)) {
+        SkIRect tmp = *bounds;
+        (void)tmp.intersect(*clipBounds);
+        tmp.inset(-margin.fX, -margin.fY);
+        (void)bounds->intersect(tmp);
+    }
+
+    return true;
+}
+
+static void draw_into_mask(const SkMask& mask, const SkPath& devPath) {
+    SkBitmap    bm;
+    SkDraw      draw;
+    SkRegion    clipRgn;
+    SkMatrix    matrix;
+    SkPaint     paint;
+
+    bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height(), mask.fRowBytes);
+    bm.setPixels(mask.fImage);
+
+    clipRgn.setRect(0, 0, mask.fBounds.width(), mask.fBounds.height());
+    matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
+                        -SkIntToScalar(mask.fBounds.fTop));
+
+    draw.fBitmap    = &bm;
+    draw.fClip      = &clipRgn;
+    draw.fMatrix    = &matrix;
+    draw.fBounder   = NULL;
+    paint.setAntiAlias(true);
+    draw.drawPath(devPath, paint);
+}
+
+bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
+                        SkMaskFilter* filter, const SkMatrix* filterMatrix,
+                        SkMask* mask, SkMask::CreateMode mode) {
+    if (SkMask::kJustRenderImage_CreateMode != mode) {
+        if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds))
+            return false;
+    }
+    
+    if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
+        mask->fFormat = SkMask::kA8_Format;
+        mask->fRowBytes = mask->fBounds.width();
+        mask->fImage = SkMask::AllocImage(mask->computeImageSize());
+        memset(mask->fImage, 0, mask->computeImageSize());
+    }
+
+    if (SkMask::kJustComputeBounds_CreateMode != mode) {
+        draw_into_mask(*mask, devPath);
+    }
+    
+    return true;
+}
diff --git a/src/core/SkDrawProcs.h b/src/core/SkDrawProcs.h
new file mode 100644
index 0000000..d64c088
--- /dev/null
+++ b/src/core/SkDrawProcs.h
@@ -0,0 +1,26 @@
+#ifndef SkDrawProcs_DEFINED
+#define SkDrawProcs_DEFINED
+
+#include "SkDraw.h"
+
+class SkBlitter;
+
+struct SkDraw1Glyph {
+    const SkDraw*   fDraw;
+	SkBounder*		fBounder;
+	const SkRegion*	fClip;
+	SkBlitter*		fBlitter;
+	SkGlyphCache*	fCache;
+	SkIRect			fClipBounds;
+	
+	typedef void (*Proc)(const SkDraw1Glyph&, const SkGlyph&, int x, int y);
+	
+	Proc init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache);
+};
+
+struct SkDrawProcs {
+    SkDraw1Glyph::Proc  fD1GProc;
+};
+
+#endif
+
diff --git a/src/core/SkEdge.cpp b/src/core/SkEdge.cpp
new file mode 100644
index 0000000..6efe1ba
--- /dev/null
+++ b/src/core/SkEdge.cpp
@@ -0,0 +1,484 @@
+/* libs/graphics/sgl/SkEdge.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 "SkEdge.h"
+#include "SkFDot6.h"
+
+/*
+    In setLine, setQuadratic, setCubic, the first thing we do is to convert
+    the points into FDot6. This is modulated by the shift parameter, which
+    will either be 0, or something like 2 for antialiasing.
+
+    In the float case, we want to turn the float into .6 by saying pt * 64,
+    or pt * 256 for antialiasing. This is implemented as 1 << (shift + 6).
+
+    In the fixed case, we want to turn the fixed into .6 by saying pt >> 10,
+    or pt >> 8 for antialiasing. This is implemented as pt >> (10 - shift).
+*/
+
+/////////////////////////////////////////////////////////////////////////
+
+int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip,
+                    int shift) {
+    SkFDot6 x0, y0, x1, y1;
+
+    {
+#ifdef SK_SCALAR_IS_FLOAT
+        float scale = float(1 << (shift + 6));
+        x0 = int(p0.fX * scale);
+        y0 = int(p0.fY * scale);
+        x1 = int(p1.fX * scale);
+        y1 = int(p1.fY * scale);
+#else
+        shift = 10 - shift;
+        x0 = p0.fX >> shift;
+        y0 = p0.fY >> shift;
+        x1 = p1.fX >> shift;
+        y1 = p1.fY >> shift;
+#endif
+    }
+
+    int winding = 1;
+
+    if (y0 > y1) {
+        SkTSwap(x0, x1);
+        SkTSwap(y0, y1);
+        winding = -1;
+    }
+
+    int top = SkFDot6Round(y0);
+    int bot = SkFDot6Round(y1);
+
+    // are we a zero-height line?
+    if (top == bot) {
+        return 0;
+    }
+    // are we completely above or below the clip?
+    if (NULL != clip && (top >= clip->fBottom || bot <= clip->fTop)) {
+        return 0;
+    }
+
+    SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0);
+
+    fX          = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63));   // + SK_Fixed1/2
+    fDX         = slope;
+    fFirstY     = top;
+    fLastY      = bot - 1;
+    fCurveCount = 0;
+    fWinding    = SkToS8(winding);
+    fCurveShift = 0;
+
+    if (clip) {
+        this->chopLineWithClip(*clip);
+    }
+    return 1;
+}
+
+// called from a curve subclass
+int SkEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1)
+{
+    SkASSERT(fWinding == 1 || fWinding == -1);
+    SkASSERT(fCurveCount != 0);
+//    SkASSERT(fCurveShift != 0);
+
+    y0 >>= 10;
+    y1 >>= 10;
+
+    SkASSERT(y0 <= y1);
+
+    int top = SkFDot6Round(y0);
+    int bot = SkFDot6Round(y1);
+
+//  SkASSERT(top >= fFirstY);
+
+    // are we a zero-height line?
+    if (top == bot)
+        return 0;
+
+    x0 >>= 10;
+    x1 >>= 10;
+
+    SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0);
+
+    fX          = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63));   // + SK_Fixed1/2
+    fDX         = slope;
+    fFirstY     = top;
+    fLastY      = bot - 1;
+
+    return 1;
+}
+
+void SkEdge::chopLineWithClip(const SkIRect& clip)
+{
+    int top = fFirstY;
+
+    SkASSERT(top < clip.fBottom);
+
+    // clip the line to the top
+    if (top < clip.fTop)
+    {
+        SkASSERT(fLastY >= clip.fTop);
+        fX += fDX * (clip.fTop - top);
+        fFirstY = clip.fTop;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  We store 1<<shift in a (signed) byte, so its maximum value is 1<<6 == 64.
+    Note that this limits the number of lines we use to approximate a curve.
+    If we need to increase this, we need to store fCurveCount in something
+    larger than int8_t.
+*/
+#define MAX_COEFF_SHIFT     6
+
+static inline SkFDot6 cheap_distance(SkFDot6 dx, SkFDot6 dy)
+{
+    dx = SkAbs32(dx);
+    dy = SkAbs32(dy);
+    // return max + min/2
+    if (dx > dy)
+        dx += dy >> 1;
+    else
+        dx = dy + (dx >> 1);
+    return dx;
+}
+
+static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy)
+{
+    // cheap calc of distance from center of p0-p2 to the center of the curve
+    SkFDot6 dist = cheap_distance(dx, dy);
+
+    // shift down dist (it is currently in dot6)
+    // down by 5 should give us 1/2 pixel accuracy (assuming our dist is accurate...)
+    // this is chosen by heuristic: make it as big as possible (to minimize segments)
+    // ... but small enough so that our curves still look smooth
+    dist = (dist + (1 << 4)) >> 5;
+
+    // each subdivision (shift value) cuts this dist (error) by 1/4
+    return (32 - SkCLZ(dist)) >> 1;
+}
+
+int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], const SkIRect* clip, int shift)
+{
+    SkFDot6 x0, y0, x1, y1, x2, y2;
+
+    {
+#ifdef SK_SCALAR_IS_FLOAT
+        float scale = float(1 << (shift + 6));
+        x0 = int(pts[0].fX * scale);
+        y0 = int(pts[0].fY * scale);
+        x1 = int(pts[1].fX * scale);
+        y1 = int(pts[1].fY * scale);
+        x2 = int(pts[2].fX * scale);
+        y2 = int(pts[2].fY * scale);
+#else
+        shift = 10 - shift;
+        x0 = pts[0].fX >> shift;
+        y0 = pts[0].fY >> shift;
+        x1 = pts[1].fX >> shift;
+        y1 = pts[1].fY >> shift;
+        x2 = pts[2].fX >> shift;
+        y2 = pts[2].fY >> shift;
+#endif
+    }
+
+    int winding = 1;
+    if (y0 > y2)
+    {
+        SkTSwap(x0, x2);
+        SkTSwap(y0, y2);
+        winding = -1;
+    }
+    SkASSERT(y0 <= y1 && y1 <= y2);
+
+    int top = SkFDot6Round(y0);
+    int bot = SkFDot6Round(y2);
+
+    // are we a zero-height quad (line)?
+    if (top == bot)
+        return 0;
+    // are we completely above or below the clip?
+    if (clip && (top >= clip->fBottom || bot <= clip->fTop))
+        return 0;
+
+    // compute number of steps needed (1 << shift)
+    {
+        SkFDot6 dx = ((x1 << 1) - x0 - x2) >> 2;
+        SkFDot6 dy = ((y1 << 1) - y0 - y2) >> 2;
+        shift = diff_to_shift(dx, dy);
+        SkASSERT(shift >= 0);
+    }
+    // need at least 1 subdivision for our bias trick
+    if (shift == 0) {
+        shift = 1;
+    } else if (shift > MAX_COEFF_SHIFT) {
+        shift = MAX_COEFF_SHIFT;
+    }
+    
+    fWinding    = SkToS8(winding);
+    fCurveShift = SkToU8(shift);
+    //fCubicDShift only set for cubics
+    fCurveCount = SkToS8(1 << shift);
+
+    SkFixed A = SkFDot6ToFixed(x0 - x1 - x1 + x2);
+    SkFixed B = SkFDot6ToFixed(x1 - x0 + x1 - x0);
+
+    fQx     = SkFDot6ToFixed(x0);
+    fQDx    = B + (A >> shift);     // biased by shift
+    fQDDx   = A >> (shift - 1);     // biased by shift
+
+    A = SkFDot6ToFixed(y0 - y1 - y1 + y2);
+    B = SkFDot6ToFixed(y1 - y0 + y1 - y0);
+
+    fQy     = SkFDot6ToFixed(y0);
+    fQDy    = B + (A >> shift);     // biased by shift
+    fQDDy   = A >> (shift - 1);     // biased by shift
+
+    fQLastX = SkFDot6ToFixed(x2);
+    fQLastY = SkFDot6ToFixed(y2);
+
+    if (clip)
+    {
+        do {
+            for (;!this->updateQuadratic();)
+                ;
+        } while (!this->intersectsClip(*clip));
+        this->chopLineWithClip(*clip);
+        return 1;
+    }
+    return this->updateQuadratic();
+}
+
+int SkQuadraticEdge::updateQuadratic()
+{
+    int     success;
+    int     count = fCurveCount;
+    SkFixed oldx = fQx;
+    SkFixed oldy = fQy;
+    SkFixed dx = fQDx;
+    SkFixed dy = fQDy;
+    SkFixed newx, newy;
+    int     shift = fCurveShift;
+
+    SkASSERT(count > 0);
+
+    do {
+        if (--count > 0)
+        {
+            newx    = oldx + (dx >> shift);
+            dx    += fQDDx;
+            newy    = oldy + (dy >> shift);
+            dy    += fQDDy;
+        }
+        else    // last segment
+        {
+            newx    = fQLastX;
+            newy    = fQLastY;
+        }
+        success = this->updateLine(oldx, oldy, newx, newy);
+        oldx = newx;
+        oldy = newy;
+    } while (count > 0 && !success);
+
+    fQx         = newx;
+    fQy         = newy;
+    fQDx        = dx;
+    fQDy        = dy;
+    fCurveCount = SkToS16(count);
+    return success;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+static inline int SkFDot6UpShift(SkFDot6 x, int upShift) {
+    SkASSERT((x << upShift >> upShift) == x);
+    return x << upShift;
+}
+
+/*  f(1/3) = (8a + 12b + 6c + d) / 27
+    f(2/3) = (a + 6b + 12c + 8d) / 27
+
+    f(1/3)-b = (8a - 15b + 6c + d) / 27
+    f(2/3)-c = (a + 6b - 15c + 8d) / 27
+
+    use 16/512 to approximate 1/27
+*/
+static SkFDot6 cubic_delta_from_line(SkFDot6 a, SkFDot6 b, SkFDot6 c, SkFDot6 d)
+{
+    SkFDot6 oneThird = ((a << 3) - ((b << 4) - b) + 6*c + d) * 19 >> 9;
+    SkFDot6 twoThird = (a + 6*b - ((c << 4) - c) + (d << 3)) * 19 >> 9;
+
+    return SkMax32(SkAbs32(oneThird), SkAbs32(twoThird));
+}
+
+int SkCubicEdge::setCubic(const SkPoint pts[4], const SkIRect* clip, int shift)
+{
+    SkFDot6 x0, y0, x1, y1, x2, y2, x3, y3;
+
+    {
+#ifdef SK_SCALAR_IS_FLOAT
+        float scale = float(1 << (shift + 6));
+        x0 = int(pts[0].fX * scale);
+        y0 = int(pts[0].fY * scale);
+        x1 = int(pts[1].fX * scale);
+        y1 = int(pts[1].fY * scale);
+        x2 = int(pts[2].fX * scale);
+        y2 = int(pts[2].fY * scale);
+        x3 = int(pts[3].fX * scale);
+        y3 = int(pts[3].fY * scale);
+#else
+        shift = 10 - shift;
+        x0 = pts[0].fX >> shift;
+        y0 = pts[0].fY >> shift;
+        x1 = pts[1].fX >> shift;
+        y1 = pts[1].fY >> shift;
+        x2 = pts[2].fX >> shift;
+        y2 = pts[2].fY >> shift;
+        x3 = pts[3].fX >> shift;
+        y3 = pts[3].fY >> shift;
+#endif
+    }
+
+    int winding = 1;
+    if (y0 > y3)
+    {
+        SkTSwap(x0, x3);
+        SkTSwap(x1, x2);
+        SkTSwap(y0, y3);
+        SkTSwap(y1, y2);
+        winding = -1;
+    }
+
+    int top = SkFDot6Round(y0);
+    int bot = SkFDot6Round(y3);
+
+    // are we a zero-height cubic (line)?
+    if (top == bot)
+        return 0;
+
+    // are we completely above or below the clip?
+    if (clip && (top >= clip->fBottom || bot <= clip->fTop))
+        return 0;
+
+    // compute number of steps needed (1 << shift)
+    {
+        // Can't use (center of curve - center of baseline), since center-of-curve
+        // need not be the max delta from the baseline (it could even be coincident)
+        // so we try just looking at the two off-curve points
+        SkFDot6 dx = cubic_delta_from_line(x0, x1, x2, x3);
+        SkFDot6 dy = cubic_delta_from_line(y0, y1, y2, y3);
+        // add 1 (by observation)
+        shift = diff_to_shift(dx, dy) + 1;
+    }
+    // need at least 1 subdivision for our bias trick
+    SkASSERT(shift > 0);
+    if (shift > MAX_COEFF_SHIFT) {
+        shift = MAX_COEFF_SHIFT;
+    }
+
+    /*  Since our in coming data is initially shifted down by 10 (or 8 in
+        antialias). That means the most we can shift up is 8. However, we
+        compute coefficients with a 3*, so the safest upshift is really 6
+    */
+    int upShift = 6;    // largest safe value
+    int downShift = shift + upShift - 10;
+    if (downShift < 0) {
+        downShift = 0;
+        upShift = 10 - shift;
+    }
+
+    fWinding    = SkToS8(winding);
+    fCurveCount = SkToS8(-1 << shift);
+    fCurveShift = SkToU8(shift);
+    fCubicDShift = SkToU8(downShift);
+
+    SkFixed B = SkFDot6UpShift(3 * (x1 - x0), upShift);
+    SkFixed C = SkFDot6UpShift(3 * (x0 - x1 - x1 + x2), upShift);
+    SkFixed D = SkFDot6UpShift(x3 + 3 * (x1 - x2) - x0, upShift);
+
+    fCx     = SkFDot6ToFixed(x0);
+    fCDx    = B + (C >> shift) + (D >> 2*shift);    // biased by shift
+    fCDDx   = 2*C + (3*D >> (shift - 1));           // biased by 2*shift
+    fCDDDx  = 3*D >> (shift - 1);                   // biased by 2*shift
+
+    B = SkFDot6UpShift(3 * (y1 - y0), upShift);
+    C = SkFDot6UpShift(3 * (y0 - y1 - y1 + y2), upShift);
+    D = SkFDot6UpShift(y3 + 3 * (y1 - y2) - y0, upShift);
+
+    fCy     = SkFDot6ToFixed(y0);
+    fCDy    = B + (C >> shift) + (D >> 2*shift);    // biased by shift
+    fCDDy   = 2*C + (3*D >> (shift - 1));           // biased by 2*shift
+    fCDDDy  = 3*D >> (shift - 1);                   // biased by 2*shift
+
+    fCLastX = SkFDot6ToFixed(x3);
+    fCLastY = SkFDot6ToFixed(y3);
+
+    if (clip)
+    {
+        do {
+            for (;!this->updateCubic();)
+                ;
+        } while (!this->intersectsClip(*clip));
+        this->chopLineWithClip(*clip);
+        return 1;
+    }
+    return this->updateCubic();
+}
+
+int SkCubicEdge::updateCubic()
+{
+    int     success;
+    int     count = fCurveCount;
+    SkFixed oldx = fCx;
+    SkFixed oldy = fCy;
+    SkFixed newx, newy;
+    const int ddshift = fCurveShift;
+    const int dshift = fCubicDShift;
+
+    SkASSERT(count < 0);
+
+    do {
+        if (++count < 0)
+        {
+            newx    = oldx + (fCDx >> dshift);
+            fCDx    += fCDDx >> ddshift;
+            fCDDx   += fCDDDx;
+
+            newy    = oldy + (fCDy >> dshift);
+            fCDy    += fCDDy >> ddshift;
+            fCDDy   += fCDDDy;
+        }
+        else    // last segment
+        {
+        //  SkDebugf("LastX err=%d, LastY err=%d\n", (oldx + (fCDx >> shift) - fLastX), (oldy + (fCDy >> shift) - fLastY));
+            newx    = fCLastX;
+            newy    = fCLastY;
+        }
+        success = this->updateLine(oldx, oldy, newx, newy);
+        oldx = newx;
+        oldy = newy;
+    } while (count < 0 && !success);
+
+    fCx         = newx;
+    fCy         = newy;
+    fCurveCount = SkToS16(count);
+    return success;
+}
+
+
+
diff --git a/src/core/SkEdge.h b/src/core/SkEdge.h
new file mode 100644
index 0000000..5b0cc75
--- /dev/null
+++ b/src/core/SkEdge.h
@@ -0,0 +1,93 @@
+/* libs/graphics/sgl/SkEdge.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 SkEdge_DEFINED
+#define SkEdge_DEFINED
+
+#include "SkRect.h"
+
+struct SkEdge {
+    enum Type {
+        kLine_Type,
+        kQuad_Type,
+        kCubic_Type
+    };
+
+    SkEdge* fNext;
+    SkEdge* fPrev;
+
+    SkFixed fX;
+    SkFixed fDX;
+    int32_t fFirstY;
+    int32_t fLastY;
+    int8_t fCurveCount;    // only used by kQuad(+) and kCubic(-)
+    uint8_t fCurveShift;    // appled to all Dx/DDx/DDDx except for fCubicDShift exception
+    uint8_t fCubicDShift;   // applied to fCDx and fCDy only in cubic
+    int8_t  fWinding;       // 1 or -1
+
+    int setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip,
+                int shiftUp);
+    inline int updateLine(SkFixed ax, SkFixed ay, SkFixed bx, SkFixed by);
+    void chopLineWithClip(const SkIRect& clip);
+
+    inline bool intersectsClip(const SkIRect& clip) const {
+        SkASSERT(fFirstY < clip.fBottom);
+        return fLastY >= clip.fTop;
+    }
+
+#ifdef SK_DEBUG
+    void dump() const {
+    #ifdef SK_CAN_USE_FLOAT
+        SkDebugf("edge: firstY:%d lastY:%d x:%g dx:%g w:%d\n", fFirstY, fLastY, SkFixedToFloat(fX), SkFixedToFloat(fDX), fWinding);
+    #else
+        SkDebugf("edge: firstY:%d lastY:%d x:%x dx:%x w:%d\n", fFirstY, fLastY, fX, fDX, fWinding);
+    #endif
+    }
+
+    void validate() const {
+        SkASSERT(fPrev && fNext);
+        SkASSERT(fPrev->fNext == this);
+        SkASSERT(fNext->fPrev == this);
+
+        SkASSERT(fFirstY <= fLastY);
+        SkASSERT(SkAbs32(fWinding) == 1);
+    }
+#endif
+};
+
+struct SkQuadraticEdge : public SkEdge {
+    SkFixed fQx, fQy;
+    SkFixed fQDx, fQDy;
+    SkFixed fQDDx, fQDDy;
+    SkFixed fQLastX, fQLastY;
+
+    int setQuadratic(const SkPoint pts[3], const SkIRect* clip, int shiftUp);
+    int updateQuadratic();
+};
+
+struct SkCubicEdge : public SkEdge {
+    SkFixed fCx, fCy;
+    SkFixed fCDx, fCDy;
+    SkFixed fCDDx, fCDDy;
+    SkFixed fCDDDx, fCDDDy;
+    SkFixed fCLastX, fCLastY;
+
+    int setCubic(const SkPoint pts[4], const SkIRect* clip, int shiftUp);
+    int updateCubic();
+};
+
+#endif
diff --git a/src/core/SkFP.h b/src/core/SkFP.h
new file mode 100644
index 0000000..6c0c526
--- /dev/null
+++ b/src/core/SkFP.h
@@ -0,0 +1,87 @@
+/* libs/graphics/sgl/SkFP.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 SkFP_DEFINED
+#define SkFP_DEFINED
+
+#include "SkMath.h"
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+    typedef float SkFP;
+
+    #define SkScalarToFP(n)         (n)
+    #define SkFPToScalar(n)         (n)
+    #define SkIntToFP(n)            SkIntToScalar(n)
+    #define SkFPRound(x)            SkScalarRound(n)
+    #define SkFPCeil(x)             SkScalarCeil(n)
+    #define SkFPFloor(x)            SkScalarFloor(n)
+
+    #define SkFPNeg(x)              (-(x))
+    #define SkFPAbs(x)              SkScalarAbs(x)
+    #define SkFPAdd(a, b)           ((a) + (b))
+    #define SkFPSub(a, b)           ((a) - (b))
+    #define SkFPMul(a, b)           ((a) * (b))
+    #define SkFPMulInt(a, n)        ((a) * (n))
+    #define SkFPDiv(a, b)           ((a) / (b))
+    #define SkFPDivInt(a, n)        ((a) / (n))
+    #define SkFPInvert(x)           SkScalarInvert(x)
+    #define SkFPSqrt(x)             SkScalarSqrt(x)
+    #define SkFPCubeRoot(x)         pow(x, 1.0f/3)
+
+    #define SkFPLT(a, b)            ((a) < (b))
+    #define SkFPLE(a, b)            ((a) <= (b))
+    #define SkFPGT(a, b)            ((a) > (b))
+    #define SkFPGE(a, b)            ((a) >= (b))
+
+#else   // scalar is fixed
+
+    #include "SkFloat.h"
+
+    typedef int32_t SkFP;
+
+    #define SkScalarToFP(n)         SkFloat::SetShift(n, -16)
+    #define SkFPToScalar(n)         SkFloat::GetShift(n, -16)
+    #define SkIntToFP(n)            SkFloat::SetShift(n, 0)
+    #define SkFPRound(x)            SkFloat::Round(x);
+    #define SkFPCeil(x)             SkFloat::Ceil();
+    #define SkFPFloor(x)            SkFloat::Floor();
+
+    #define SkFPNeg(x)              SkFloat::Neg(x)
+    #define SkFPAbs(x)              SkFloat::Abs(x)
+    #define SkFPAdd(a, b)           SkFloat::Add(a, b)
+    #define SkFPSub(a, b)           SkFloat::Add(a, SkFloat::Neg(b))
+    #define SkFPMul(a, b)           SkFloat::Mul(a, b)
+    #define SkFPMulInt(a, n)        SkFloat::MulInt(a, n)
+    #define SkFPDiv(a, b)           SkFloat::Div(a, b)
+    #define SkFPDivInt(a, n)        SkFloat::DivInt(a, n)
+    #define SkFPInvert(x)           SkFloat::Invert(x)
+    #define SkFPSqrt(x)             SkFloat::Sqrt(x)
+    #define SkFPCubeRoot(x)         SkFloat::CubeRoot(x)
+
+    #define SkFPLT(a, b)            (SkFloat::Cmp(a, b) < 0)
+    #define SkFPLE(a, b)            (SkFloat::Cmp(a, b) <= 0)
+    #define SkFPGT(a, b)            (SkFloat::Cmp(a, b) > 0)
+    #define SkFPGE(a, b)            (SkFloat::Cmp(a, b) >= 0)
+
+#endif
+
+#ifdef SK_DEBUG
+    void SkFP_UnitTest();
+#endif
+
+#endif
diff --git a/src/core/SkFilterProc.cpp b/src/core/SkFilterProc.cpp
new file mode 100644
index 0000000..814bafe
--- /dev/null
+++ b/src/core/SkFilterProc.cpp
@@ -0,0 +1,303 @@
+/* libs/graphics/sgl/SkFilterProc.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 "SkFilterProc.h"
+
+/*  [1-x 1-y] [x 1-y]
+    [1-x   y] [x   y]
+*/
+
+static unsigned bilerp00(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return a00; }
+static unsigned bilerp01(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * a00 + a01) >> 2; }
+static unsigned bilerp02(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a01) >> 1; }
+static unsigned bilerp03(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + 3 * a01) >> 2; }
+
+static unsigned bilerp10(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * a00 + a10) >> 2; }
+static unsigned bilerp11(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a00 + 3 * (a01 + a10) + a11) >> 4; }
+static unsigned bilerp12(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a00 + a01) + a10 + a11) >> 3; }
+static unsigned bilerp13(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a01 + 3 * (a00 + a11) + a10) >> 4; }
+
+static unsigned bilerp20(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a10) >> 1; }
+static unsigned bilerp21(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a00 + a10) + a01 + a11) >> 3; }
+static unsigned bilerp22(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + a01 + a10 + a11) >> 2; }
+static unsigned bilerp23(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a01 + a11) + a00 + a10) >> 3; }
+
+static unsigned bilerp30(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (a00 + 3 * a10) >> 2; }
+static unsigned bilerp31(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a10 + 3 * (a00 + a11) + a01) >> 4; }
+static unsigned bilerp32(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (3 * (a10 + a11) + a00 + a01) >> 3; }
+static unsigned bilerp33(unsigned a00, unsigned a01, unsigned a10, unsigned a11) { return (9 * a11 + 3 * (a01 + a10) + a00) >> 4; }
+
+static const SkFilterProc gBilerpProcs[4 * 4] = {
+    bilerp00, bilerp01, bilerp02, bilerp03,
+    bilerp10, bilerp11, bilerp12, bilerp13,
+    bilerp20, bilerp21, bilerp22, bilerp23,
+    bilerp30, bilerp31, bilerp32, bilerp33
+};
+
+const SkFilterProc* SkGetBilinearFilterProcTable()
+{
+    return gBilerpProcs;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#define MASK            0xFF00FF
+#define LO_PAIR(x)      ((x) & MASK)
+#define HI_PAIR(x)      (((x) >> 8) & MASK)
+#define COMBINE(lo, hi) (((lo) & ~0xFF00) | (((hi) & ~0xFF00) << 8))
+
+///////////////////////////////////////////////////////////////////////////////
+
+static unsigned bilerp4_00(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    return c00;
+}
+static unsigned bilerp4_01(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c01)) >> 2;
+    uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c01)) >> 2;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_02(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01)) >> 1;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01)) >> 1;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_03(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c01)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c01)) >> 2;
+    return COMBINE(lo, hi);
+}
+
+static unsigned bilerp4_10(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c10)) >> 2;
+    uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c10)) >> 2;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_11(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (9 * LO_PAIR(c00) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c11)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c00) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c11)) >> 4;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_12(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c01)) + LO_PAIR(c10) + LO_PAIR(c11)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c01)) + HI_PAIR(c10) + HI_PAIR(c11)) >> 3;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_13(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (9 * LO_PAIR(c01) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c10)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c01) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c10)) >> 4;
+    return COMBINE(lo, hi);
+}
+
+static unsigned bilerp4_20(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c10)) >> 1;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c10)) >> 1;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_21(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c10)) + LO_PAIR(c01) + LO_PAIR(c11)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c10)) + HI_PAIR(c01) + HI_PAIR(c11)) >> 3;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_22(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01) + LO_PAIR(c10) + LO_PAIR(c11)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01) + HI_PAIR(c10) + HI_PAIR(c11)) >> 2;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_23(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (3 * (LO_PAIR(c01) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c10)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c01) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c10)) >> 3;
+    return COMBINE(lo, hi);
+}
+
+static unsigned bilerp4_30(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c10)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c10)) >> 2;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_31(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (9 * LO_PAIR(c10) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c01)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c10) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c01)) >> 4;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_32(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (3 * (LO_PAIR(c10) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c01)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c10) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c01)) >> 3;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerp4_33(uint32_t c00, uint32_t c01, uint32_t c10, uint32_t c11) {
+    uint32_t lo = (9 * LO_PAIR(c11) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c00)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c11) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c00)) >> 4;
+    return COMBINE(lo, hi);
+}
+
+static const SkFilter32Proc gBilerp32Procs[4 * 4] = {
+    bilerp4_00, bilerp4_01, bilerp4_02, bilerp4_03,
+    bilerp4_10, bilerp4_11, bilerp4_12, bilerp4_13,
+    bilerp4_20, bilerp4_21, bilerp4_22, bilerp4_23,
+    bilerp4_30, bilerp4_31, bilerp4_32, bilerp4_33
+};
+
+const SkFilter32Proc* SkGetFilter32ProcTable()
+{
+    return gBilerp32Procs;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static unsigned bilerptr00(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    return *a00;
+}
+static unsigned bilerptr01(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;   
+    uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c01)) >> 2;
+    uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c01)) >> 2;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerptr02(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;   
+    uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01)) >> 1;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01)) >> 1;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerptr03(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;
+    uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c01)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c01)) >> 2;
+    return COMBINE(lo, hi);
+}
+
+static unsigned bilerptr10(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c10 = *a10;
+    uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c10)) >> 2;
+    uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c10)) >> 2;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerptr11(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;
+    uint32_t c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (9 * LO_PAIR(c00) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c11)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c00) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c11)) >> 4;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerptr12(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;
+    uint32_t c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c01)) + LO_PAIR(c10) + LO_PAIR(c11)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c01)) + HI_PAIR(c10) + HI_PAIR(c11)) >> 3;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerptr13(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;
+    uint32_t c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (9 * LO_PAIR(c01) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c10)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c01) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c10)) >> 4;
+    return COMBINE(lo, hi);
+}
+
+static unsigned bilerptr20(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c10 = *a10;
+    uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c10)) >> 1;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c10)) >> 1;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerptr21(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;
+    uint32_t c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (3 * (LO_PAIR(c00) + LO_PAIR(c10)) + LO_PAIR(c01) + LO_PAIR(c11)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c00) + HI_PAIR(c10)) + HI_PAIR(c01) + HI_PAIR(c11)) >> 3;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerptr22(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;
+    uint32_t c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01) + LO_PAIR(c10) + LO_PAIR(c11)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01) + HI_PAIR(c10) + HI_PAIR(c11)) >> 2;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerptr23(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;
+    uint32_t c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (3 * (LO_PAIR(c01) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c10)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c01) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c10)) >> 3;
+    return COMBINE(lo, hi);
+}
+
+static unsigned bilerptr30(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c10 = *a10;
+    uint32_t lo = (LO_PAIR(c00) + 3 * LO_PAIR(c10)) >> 2;
+    uint32_t hi = (HI_PAIR(c00) + 3 * HI_PAIR(c10)) >> 2;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerptr31(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;
+    uint32_t c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (9 * LO_PAIR(c10) + 3 * (LO_PAIR(c00) + LO_PAIR(c11)) + LO_PAIR(c01)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c10) + 3 * (HI_PAIR(c00) + HI_PAIR(c11)) + HI_PAIR(c01)) >> 4;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerptr32(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;
+    uint32_t c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (3 * (LO_PAIR(c10) + LO_PAIR(c11)) + LO_PAIR(c00) + LO_PAIR(c01)) >> 3;
+    uint32_t hi = (3 * (HI_PAIR(c10) + HI_PAIR(c11)) + HI_PAIR(c00) + HI_PAIR(c01)) >> 3;
+    return COMBINE(lo, hi);
+}
+static unsigned bilerptr33(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
+    uint32_t c00 = *a00;
+    uint32_t c01 = *a01;
+    uint32_t c10 = *a10;
+    uint32_t c11 = *a11;
+    uint32_t lo = (9 * LO_PAIR(c11) + 3 * (LO_PAIR(c01) + LO_PAIR(c10)) + LO_PAIR(c00)) >> 4;
+    uint32_t hi = (9 * HI_PAIR(c11) + 3 * (HI_PAIR(c01) + HI_PAIR(c10)) + HI_PAIR(c00)) >> 4;
+    return COMBINE(lo, hi);
+}
+
+static const SkFilterPtrProc gBilerpPtrProcs[4 * 4] = {
+    bilerptr00, bilerptr01, bilerptr02, bilerptr03,
+    bilerptr10, bilerptr11, bilerptr12, bilerptr13,
+    bilerptr20, bilerptr21, bilerptr22, bilerptr23,
+    bilerptr30, bilerptr31, bilerptr32, bilerptr33
+};
+
+const SkFilterPtrProc* SkGetBilinearFilterPtrProcTable()
+{
+    return gBilerpPtrProcs;
+}
+
diff --git a/src/core/SkFilterProc.h b/src/core/SkFilterProc.h
new file mode 100644
index 0000000..9af4ed5
--- /dev/null
+++ b/src/core/SkFilterProc.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkFilter_DEFINED
+#define SkFilter_DEFINED
+
+#include "SkMath.h"
+#include "SkFixed.h"
+
+typedef unsigned (*SkFilterProc)(unsigned x00, unsigned x01,
+                                 unsigned x10, unsigned x11);
+
+const SkFilterProc* SkGetBilinearFilterProcTable();
+
+inline SkFilterProc SkGetBilinearFilterProc(const SkFilterProc* table,
+                                            SkFixed x, SkFixed y)
+{
+    SkASSERT(table);
+    
+    // convert to dot 2
+    x = (unsigned)(x << 16) >> 30;
+    y = (unsigned)(y << 16) >> 30;
+    return table[(y << 2) | x];
+}
+
+inline SkFilterProc SkGetBilinearFilterProc22(const SkFilterProc* table,
+                                              unsigned x, unsigned y)
+{
+    SkASSERT(table);
+    
+    // extract low 2 bits
+    x = x << 30 >> 30;
+    y = y << 30 >> 30;
+    return table[(y << 2) | x];
+}
+
+inline const SkFilterProc* SkGetBilinearFilterProc22Row(const SkFilterProc* table,
+                                                        unsigned y)
+{
+    SkASSERT(table);
+    // extract low 2 bits and shift up 2
+    return &table[y << 30 >> 28];
+}
+
+inline SkFilterProc SkGetBilinearFilterProc22RowProc(const SkFilterProc* row,
+                                                     unsigned x)
+{
+    SkASSERT(row);    
+    // extract low 2 bits
+    return row[x << 30 >> 30];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef unsigned (*SkFilter32Proc)(uint32_t x00, uint32_t x01,
+                                   uint32_t x10, uint32_t x11);
+
+const SkFilter32Proc* SkGetFilter32ProcTable();
+
+inline SkFilter32Proc SkGetFilter32Proc22(const SkFilter32Proc* table,
+                                          unsigned x, unsigned y)
+{
+    SkASSERT(table);
+    
+    // extract low 2 bits
+    x = x << 30 >> 30;
+    y = y << 30 >> 30;
+    return table[(y << 2) | x];
+}
+
+inline const SkFilter32Proc* SkGetFilter32Proc22Row(const SkFilter32Proc* table,
+                                                    unsigned y)
+{
+    SkASSERT(table);
+    // extract low 2 bits and shift up 2
+    return &table[y << 30 >> 28];
+}
+
+inline SkFilter32Proc SkGetFilter32Proc22RowProc(const SkFilter32Proc* row,
+                                                 unsigned x)
+{
+    SkASSERT(row);
+    // extract low 2 bits
+    return row[x << 30 >> 30];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Special version of SkFilterProc. This takes the address of 4 ints, and combines them a byte at a
+    time. AABBCCDD.
+*/
+typedef uint32_t (*SkFilterPtrProc)(const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
+
+const SkFilterPtrProc* SkGetBilinearFilterPtrProcTable();
+inline SkFilterPtrProc SkGetBilinearFilterPtrProc(const SkFilterPtrProc* table, SkFixed x, SkFixed y)
+{
+    SkASSERT(table);
+
+    // convert to dot 2
+    x = (unsigned)(x << 16) >> 30;
+    y = (unsigned)(y << 16) >> 30;
+    return table[(y << 2) | x];
+}
+
+/** Given a Y value, return a subset of the proc table for that value.
+    Pass this to SkGetBilinearFilterPtrXProc with the corresponding X value to get the
+    correct proc.
+*/
+inline const SkFilterPtrProc* SkGetBilinearFilterPtrProcYTable(const SkFilterPtrProc* table, SkFixed y)
+{
+    SkASSERT(table);
+
+    y = (unsigned)(y << 16) >> 30;
+    return table + (y << 2);
+}
+
+/** Given a subtable returned by SkGetBilinearFilterPtrProcYTable(), return the proc for the
+    specified X value.
+*/
+inline SkFilterPtrProc SkGetBilinearFilterPtrXProc(const SkFilterPtrProc* table, SkFixed x)
+{
+    SkASSERT(table);
+
+    // convert to dot 2
+    x = (unsigned)(x << 16) >> 30;
+    return table[x];
+}
+
+#endif
+
+
diff --git a/src/core/SkFlattenable.cpp b/src/core/SkFlattenable.cpp
new file mode 100644
index 0000000..3558519
--- /dev/null
+++ b/src/core/SkFlattenable.cpp
@@ -0,0 +1,263 @@
+#include "SkFlattenable.h"
+#include "SkTypeface.h"
+
+void SkFlattenable::flatten(SkFlattenableWriteBuffer&)
+{
+    /*  we don't write anything at the moment, but this allows our subclasses
+        to not know that, since we want them to always call INHERITED::flatten()
+        in their code.
+    */
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkFlattenableReadBuffer::SkFlattenableReadBuffer() {
+    fRCArray = NULL;
+    fRCCount = 0;
+    
+    fTFArray = NULL;
+    fTFCount = 0;
+    
+    fFactoryArray = NULL;
+    fFactoryCount = 0;
+}
+
+SkFlattenableReadBuffer::SkFlattenableReadBuffer(const void* data) :
+        INHERITED(data, 1024 * 1024) {
+    fRCArray = NULL;
+    fRCCount = 0;
+    
+    fTFArray = NULL;
+    fTFCount = 0;
+    
+    fFactoryArray = NULL;
+    fFactoryCount = 0;
+}
+
+SkFlattenableReadBuffer::SkFlattenableReadBuffer(const void* data, size_t size)
+        : INHERITED(data, size) {
+    fRCArray = NULL;
+    fRCCount = 0;
+    
+    fTFArray = NULL;
+    fTFCount = 0;
+    
+    fFactoryArray = NULL;
+    fFactoryCount = 0;
+}
+
+SkTypeface* SkFlattenableReadBuffer::readTypeface() {
+    uint32_t index = this->readU32();
+    if (0 == index || index > (unsigned)fTFCount) {
+        if (index) {
+            SkDebugf("====== typeface index %d\n", index);
+        }
+        return NULL;
+    } else {
+        SkASSERT(fTFArray);
+        return fTFArray[index - 1];
+    }
+}
+
+SkRefCnt* SkFlattenableReadBuffer::readRefCnt() {
+    uint32_t index = this->readU32();
+    if (0 == index || index > (unsigned)fRCCount) {
+        return NULL;
+    } else {
+        SkASSERT(fRCArray);
+        return fRCArray[index - 1];
+    }
+}
+
+SkFlattenable* SkFlattenableReadBuffer::readFlattenable() {
+    SkFlattenable::Factory factory = NULL;
+    
+    if (fFactoryCount > 0) {
+        uint32_t index = this->readU32();
+        if (index > 0) {
+            index -= 1;
+            SkASSERT(index < (unsigned)fFactoryCount);
+            factory = fFactoryArray[index];
+            // if we recorded an index, but failed to get a factory, we need
+            // to skip the flattened data in the buffer
+            if (NULL == factory) {
+                uint32_t size = this->readU32();
+                this->skip(size);
+                // fall through and return NULL for the object
+            }
+        }
+    } else {
+        factory = (SkFlattenable::Factory)readFunctionPtr();
+    }
+
+    SkFlattenable* obj = NULL;
+    if (factory) {
+        uint32_t sizeRecorded = this->readU32();
+        uint32_t offset = this->offset();
+        obj = (*factory)(*this);
+        // check that we read the amount we expected
+        uint32_t sizeRead = this->offset() - offset;
+        if (sizeRecorded != sizeRead) {
+            // we could try to fix up the offset...
+            sk_throw();
+        }
+    }
+    return obj;
+}
+
+void* SkFlattenableReadBuffer::readFunctionPtr() {
+    void* proc;
+    this->read(&proc, sizeof(proc));
+    return proc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFlattenableWriteBuffer::SkFlattenableWriteBuffer(size_t minSize) :
+        INHERITED(minSize) {
+    fFlags = (Flags)0;
+    fRCRecorder = NULL;
+    fTFRecorder = NULL;
+    fFactoryRecorder = NULL;
+}
+
+SkFlattenableWriteBuffer::~SkFlattenableWriteBuffer() {
+    fRCRecorder->safeUnref();
+    fTFRecorder->safeUnref();
+    fFactoryRecorder->safeUnref();
+}
+
+SkRefCntRecorder* SkFlattenableWriteBuffer::setRefCntRecorder(
+                                                    SkRefCntRecorder* rec) {
+    SkRefCnt_SafeAssign(fRCRecorder, rec);
+    return rec;
+}
+
+SkRefCntRecorder* SkFlattenableWriteBuffer::setTypefaceRecorder(
+                                                    SkRefCntRecorder* rec) {
+    SkRefCnt_SafeAssign(fTFRecorder, rec);
+    return rec;
+}
+
+SkFactoryRecorder* SkFlattenableWriteBuffer::setFactoryRecorder(
+                                                    SkFactoryRecorder* rec) {
+    SkRefCnt_SafeAssign(fFactoryRecorder, rec);
+    return rec;
+}
+
+void SkFlattenableWriteBuffer::writeTypeface(SkTypeface* obj) {
+    if (NULL == obj || NULL == fTFRecorder) {
+        this->write32(0);
+    } else {
+        this->write32(fTFRecorder->record(obj));
+    }
+}
+
+void SkFlattenableWriteBuffer::writeRefCnt(SkRefCnt* obj) {
+    if (NULL == obj || NULL == fRCRecorder) {
+        this->write32(0);
+    } else {
+        this->write32(fRCRecorder->record(obj));
+    }
+}
+
+void SkFlattenableWriteBuffer::writeFlattenable(SkFlattenable* flattenable) {
+    SkFlattenable::Factory factory = NULL;
+    if (flattenable) {
+        factory = flattenable->getFactory();
+    }
+
+    if (fFactoryRecorder) {
+        this->write32(fFactoryRecorder->record(factory));
+    } else {
+        this->writeFunctionPtr((void*)factory);
+    }
+    
+    if (factory) {
+        // make room for the size of the flatttened object
+        (void)this->reserve(sizeof(uint32_t));
+        // record the current size, so we can subtract after the object writes.
+        uint32_t offset = this->size();
+        // now flatten the object
+        flattenable->flatten(*this);
+        uint32_t objSize = this->size() - offset;
+        // record the obj's size
+        *this->peek32(offset - sizeof(uint32_t)) = objSize;
+    }
+}
+
+void SkFlattenableWriteBuffer::writeFunctionPtr(void* proc) {
+    *(void**)this->reserve(sizeof(void*)) = proc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkRefCntRecorder::~SkRefCntRecorder() {
+    // call this now, while our decPtr() is sill in scope
+    this->reset();
+}
+
+void SkRefCntRecorder::incPtr(void* ptr) {
+    ((SkRefCnt*)ptr)->ref();
+}
+
+void SkRefCntRecorder::decPtr(void* ptr) {
+    ((SkRefCnt*)ptr)->unref();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#define MAX_PAIR_COUNT  64
+
+struct Pair {
+    const char*             fName;
+    SkFlattenable::Factory  fFactory;
+};
+
+static int gCount;
+static Pair gPairs[MAX_PAIR_COUNT];
+
+void SkFlattenable::Register(const char name[], Factory factory) {
+    SkASSERT(name);
+    SkASSERT(factory);
+    
+    static bool gOnce;
+    if (!gOnce) {
+        gCount = 0;
+        gOnce = true;
+    }
+    
+    SkASSERT(gCount < MAX_PAIR_COUNT);
+    
+    gPairs[gCount].fName = name;
+    gPairs[gCount].fFactory = factory;
+    gCount += 1;
+}
+
+SkFlattenable::Factory SkFlattenable::NameToFactory(const char name[]) {
+    const Pair* pairs = gPairs;
+    for (int i = gCount - 1; i >= 0; --i) {
+        if (strcmp(pairs[i].fName, name) == 0) {
+            return pairs[i].fFactory;
+        }
+    }
+    return NULL;
+}
+
+const char* SkFlattenable::FactoryToName(Factory fact) {
+    const Pair* pairs = gPairs;
+    for (int i = gCount - 1; i >= 0; --i) {
+        if (pairs[i].fFactory == fact) {
+            return pairs[i].fName;
+        }
+    }
+    return NULL;
+}
+
+bool SkFlattenable::toDumpString(SkString* str) const {
+    return false;
+}
+
diff --git a/src/core/SkFloat.cpp b/src/core/SkFloat.cpp
new file mode 100644
index 0000000..504c1d3
--- /dev/null
+++ b/src/core/SkFloat.cpp
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkFloat.h"
+#include "SkMath.h"
+
+#define EXP_BIAS    (127+23)
+
+static int get_unsigned_exp(uint32_t packed)
+{
+    return (packed << 1 >> 24);
+}
+
+static unsigned get_unsigned_value(uint32_t packed)
+{
+    return (packed << 9 >> 9) | (1 << 23);
+}
+
+static int get_signed_value(int32_t packed)
+{
+    return SkApplySign(get_unsigned_value(packed), SkExtractSign(packed));
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+int SkFloat::GetShift(int32_t packed, int shift)
+{
+    if (packed == 0)
+        return 0;
+
+    int exp = get_unsigned_exp(packed) - EXP_BIAS - shift;
+    int value = get_unsigned_value(packed);
+
+    if (exp >= 0)
+    {
+        if (exp > 8)    // overflow
+            value = SK_MaxS32;
+        else
+            value <<= exp;
+    }
+    else
+    {
+        exp = -exp;
+        if (exp > 23)   // underflow
+            value = 0;
+        else
+            value >>= exp;
+    }
+    return SkApplySign(value, SkExtractSign(packed));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+int32_t SkFloat::SetShift(int value, int shift)
+{
+    if (value == 0)
+        return 0;
+
+    // record the sign and make value positive
+    int sign = SkExtractSign(value);
+    value = SkApplySign(value, sign);
+
+    if (value >> 24)    // value is too big (has more than 24 bits set)
+    {
+        int bias = 8 - SkCLZ(value);
+        SkASSERT(bias > 0 && bias < 8);
+        value >>= bias;
+        shift += bias;
+    }
+    else
+    {
+        int zeros = SkCLZ(value << 8);
+        SkASSERT(zeros >= 0 && zeros <= 23);
+        value <<= zeros;
+        shift -= zeros;
+    }
+    // now value is left-aligned to 24 bits
+    SkASSERT((value >> 23) == 1);
+
+    shift += EXP_BIAS;
+    if (shift < 0)  // underflow
+        return 0;
+    else
+    {
+        if (shift > 255)    // overflow
+        {
+            shift = 255;
+            value = 0x00FFFFFF;
+        }
+        int32_t packed = sign << 31;        // set the sign-bit
+        packed |= shift << 23;          // store the packed exponent
+        packed |= ((unsigned)(value << 9) >> 9);    // clear 24th bit of value (its implied)
+
+#ifdef SK_DEBUG
+        {
+            int n;
+
+            n = SkExtractSign(packed);
+            SkASSERT(n == sign);
+            n = get_unsigned_exp(packed);
+            SkASSERT(n == shift);
+            n = get_unsigned_value(packed);
+            SkASSERT(n == value);
+        }
+#endif
+        return packed;
+    }
+}
+
+int32_t SkFloat::Neg(int32_t packed)
+{
+    if (packed)
+        packed = packed ^ (1 << 31);
+    return packed;
+}
+
+int32_t SkFloat::Add(int32_t packed_a, int32_t packed_b)
+{
+    if (packed_a == 0)
+        return packed_b;
+    if (packed_b == 0)
+        return packed_a;
+
+    int exp_a = get_unsigned_exp(packed_a);
+    int exp_b = get_unsigned_exp(packed_b);
+    int exp_diff = exp_a - exp_b;
+
+    int shift_a = 0, shift_b = 0;
+    int exp;
+
+    if (exp_diff >= 0)
+    {
+        if (exp_diff > 24)  // B is too small to contribute
+            return packed_a;
+        shift_b = exp_diff;
+        exp = exp_a;
+    }
+    else
+    {
+        exp_diff = -exp_diff;
+        if (exp_diff > 24)  // A is too small to contribute
+            return packed_b;
+        shift_a = exp_diff;
+        exp = exp_b;
+    }
+
+    int value_a = get_signed_value(packed_a) >> shift_a;
+    int value_b = get_signed_value(packed_b) >> shift_b;
+
+    return SkFloat::SetShift(value_a + value_b, exp - EXP_BIAS);
+}
+
+#include "Sk64.h"
+
+static inline int32_t mul24(int32_t a, int32_t b)
+{
+    Sk64 tmp;
+
+    tmp.setMul(a, b);
+    tmp.roundRight(24);
+    return tmp.get32();
+}
+
+int32_t SkFloat::Mul(int32_t packed_a, int32_t packed_b)
+{
+    if (packed_a == 0 || packed_b == 0)
+        return 0;
+
+    int exp_a = get_unsigned_exp(packed_a);
+    int exp_b = get_unsigned_exp(packed_b);
+
+    int value_a = get_signed_value(packed_a);
+    int value_b = get_signed_value(packed_b);
+
+    return SkFloat::SetShift(mul24(value_a, value_b), exp_a + exp_b - 2*EXP_BIAS + 24);
+}
+
+int32_t SkFloat::MulInt(int32_t packed, int n)
+{
+    return Mul(packed, SetShift(n, 0));
+}
+
+int32_t SkFloat::Div(int32_t packed_n, int32_t packed_d)
+{
+    SkASSERT(packed_d != 0);
+
+    if (packed_n == 0)
+        return 0;
+
+    int exp_n = get_unsigned_exp(packed_n);
+    int exp_d = get_unsigned_exp(packed_d);
+
+    int value_n = get_signed_value(packed_n);
+    int value_d = get_signed_value(packed_d);
+
+    return SkFloat::SetShift(SkDivBits(value_n, value_d, 24), exp_n - exp_d - 24);
+}
+
+int32_t SkFloat::DivInt(int32_t packed, int n)
+{
+    return Div(packed, SetShift(n, 0));
+}
+
+int32_t SkFloat::Invert(int32_t packed)
+{
+    return Div(packed, SetShift(1, 0));
+}
+
+int32_t SkFloat::Sqrt(int32_t packed)
+{
+    if (packed < 0)
+    {
+        SkASSERT(!"can't sqrt a negative number");
+        return 0;
+    }
+
+    int exp = get_unsigned_exp(packed);
+    int value = get_unsigned_value(packed);
+
+    int nexp = exp - EXP_BIAS;
+    int root = SkSqrtBits(value << (nexp & 1), 26);
+    nexp >>= 1;
+    return SkFloat::SetShift(root, nexp - 11);
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable warning : unreachable code
+#pragma warning ( push )
+#pragma warning ( disable : 4702 )
+#endif
+
+int32_t SkFloat::CubeRoot(int32_t packed)
+{
+    sk_throw();
+    return 0;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+static inline int32_t clear_high_bit(int32_t n)
+{
+    return ((uint32_t)(n << 1)) >> 1;
+}
+
+static inline int int_sign(int32_t a, int32_t b)
+{
+    return a > b ? 1 : (a < b ? -1 : 0);
+}
+
+int SkFloat::Cmp(int32_t packed_a, int32_t packed_b)
+{
+    packed_a = SkApplySign(clear_high_bit(packed_a), SkExtractSign(packed_a));
+    packed_b = SkApplySign(clear_high_bit(packed_b), SkExtractSign(packed_b));
+
+    return int_sign(packed_a, packed_b);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#include "SkRandom.h"
+#ifdef SK_CAN_USE_FLOAT
+    #include "SkFloatingPoint.h"
+#endif
+
+void SkFloat::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    SkFloat a, b, c, d;
+    int     n;
+
+    a.setZero();
+    n = a.getInt();
+    SkASSERT(n == 0);
+
+    b.setInt(5);
+    n = b.getInt();
+    SkASSERT(n == 5);
+
+    c.setInt(-3);
+    n = c.getInt();
+    SkASSERT(n == -3);
+
+    d.setAdd(c, b);
+    SkDebugf("SkFloat: %d + %d = %d\n", c.getInt(), b.getInt(), d.getInt());    
+
+    SkRandom    rand;
+
+#ifdef SK_CAN_USE_FLOAT
+    int i;
+    for (i = 0; i < 1000; i++)
+    {
+        float fa, fb;
+        int aa = rand.nextS() >> 14;
+        int bb = rand.nextS() >> 14;
+        a.setInt(aa);
+        b.setInt(bb);
+        SkASSERT(a.getInt() == aa);
+        SkASSERT(b.getInt() == bb);
+
+        c.setAdd(a, b);
+        int cc = c.getInt();
+        SkASSERT(cc == aa + bb);
+
+        c.setSub(a, b);
+        cc = c.getInt();
+        SkASSERT(cc == aa - bb);
+
+        aa >>= 5;
+        bb >>= 5;
+        a.setInt(aa);
+        b.setInt(bb);
+        c.setMul(a, b);
+        cc = c.getInt();
+        SkASSERT(cc == aa * bb);
+        /////////////////////////////////////
+
+        aa = rand.nextS() >> 11;
+        a.setFixed(aa);
+        cc = a.getFixed();
+        SkASSERT(aa == cc);
+
+        bb = rand.nextS() >> 11;
+        b.setFixed(bb);
+        cc = b.getFixed();
+        SkASSERT(bb == cc);
+
+        cc = SkFixedMul(aa, bb);
+        c.setMul(a, b);
+        SkFixed dd = c.getFixed();
+        int diff = cc - dd;
+        SkASSERT(SkAbs32(diff) <= 1);
+
+        fa = (float)aa / 65536.0f;
+        fb = (float)bb / 65536.0f;
+        a.assertEquals(fa);
+        b.assertEquals(fb);
+        fa = a.getFloat();
+        fb = b.getFloat();
+
+        c.assertEquals(fa * fb, 1);
+
+        c.setDiv(a, b);
+        cc = SkFixedDiv(aa, bb);
+        dd = c.getFixed();
+        diff = cc - dd;
+        SkASSERT(SkAbs32(diff) <= 3);
+
+        c.assertEquals(fa / fb, 1);
+
+        SkASSERT((aa == bb) == (a == b));
+        SkASSERT((aa != bb) == (a != b));
+        SkASSERT((aa < bb) == (a < b));
+        SkASSERT((aa <= bb) == (a <= b));
+        SkASSERT((aa > bb) == (a > b));
+        SkASSERT((aa >= bb) == (a >= b));
+
+        if (aa < 0)
+        {
+            aa = -aa;
+            fa = -fa;
+        }
+        a.setFixed(aa);
+        c.setSqrt(a);
+        cc = SkFixedSqrt(aa);
+        dd = c.getFixed();
+        SkASSERT(dd == cc);
+
+        c.assertEquals(sk_float_sqrt(fa), 2);
+
+        // cuberoot
+#if 0
+        a.setInt(1);
+        a.cubeRoot();
+        a.assertEquals(1.0f, 0);
+        a.setInt(8);
+        a.cubeRoot();
+        a.assertEquals(2.0f, 0);
+        a.setInt(27);
+        a.cubeRoot();
+        a.assertEquals(3.0f, 0);
+#endif
+    }
+#endif
+#endif
+}
+
+#endif
diff --git a/src/core/SkFloat.h b/src/core/SkFloat.h
new file mode 100644
index 0000000..31aaeed
--- /dev/null
+++ b/src/core/SkFloat.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkFloat_DEFINED
+#define SkFloat_DEFINED
+
+#include "SkFixed.h"
+
+class SkFloat {
+public:
+    SkFloat() {}
+
+    void    setZero() { fPacked = 0; }
+//  void    setShift(int value, int shift) { fPacked = SetShift(value, shift); }
+    void    setInt(int value) { fPacked = SetShift(value, 0); }
+    void    setFixed(SkFixed value) { fPacked = SetShift(value, -16); }
+    void    setFract(SkFract value) { fPacked = SetShift(value, -30); }
+
+//  int     getShift(int shift) const { return GetShift(fPacked, shift); }
+    int     getInt() const { return GetShift(fPacked, 0); }
+    SkFixed getFixed() const { return GetShift(fPacked, -16); }
+    SkFract getFract() const { return GetShift(fPacked, -30); }
+
+    void    abs() { fPacked = Abs(fPacked); }
+    void    negate() { fPacked = Neg(fPacked); }
+
+    void    shiftLeft(int bits) { fPacked = Shift(fPacked, bits); }
+    void    setShiftLeft(const SkFloat& a, int bits) { fPacked = Shift(a.fPacked, bits); }
+
+    void    shiftRight(int bits) { fPacked = Shift(fPacked, -bits); }
+    void    setShiftRight(const SkFloat& a, int bits) { fPacked = Shift(a.fPacked, -bits); }
+
+    void    add(const SkFloat& a) { fPacked = Add(fPacked, a.fPacked); }
+    void    setAdd(const SkFloat& a, const SkFloat& b) { fPacked = Add(a.fPacked, b.fPacked); }
+
+    void    sub(const SkFloat& a) { fPacked = Add(fPacked, Neg(a.fPacked)); }
+    void    setSub(const SkFloat& a, const SkFloat& b) { fPacked = Add(a.fPacked, Neg(b.fPacked)); }
+
+    void    mul(const SkFloat& a) { fPacked = Mul(fPacked, a.fPacked); }
+    void    setMul(const SkFloat& a, const SkFloat& b) { fPacked = Mul(a.fPacked, b.fPacked); }
+
+    void    div(const SkFloat& a) { fPacked = Div(fPacked, a.fPacked); }
+    void    setDiv(const SkFloat& a, const SkFloat& b) { fPacked = Div(a.fPacked, b.fPacked); }
+
+    void    sqrt() { fPacked = Sqrt(fPacked); }
+    void    setSqrt(const SkFloat& a) { fPacked = Sqrt(a.fPacked); }
+    void    cubeRoot() { fPacked = CubeRoot(fPacked); }
+    void    setCubeRoot(const SkFloat& a) { fPacked = CubeRoot(a.fPacked); }
+
+    friend bool operator==(const SkFloat& a, const SkFloat& b) { return a.fPacked == b.fPacked; }
+    friend bool operator!=(const SkFloat& a, const SkFloat& b) { return a.fPacked != b.fPacked; }
+    friend bool operator<(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) < 0; }
+    friend bool operator<=(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) <= 0; }
+    friend bool operator>(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) > 0; }
+    friend bool operator>=(const SkFloat& a, const SkFloat& b) { return Cmp(a.fPacked, b.fPacked) >= 0; }
+
+#ifdef SK_DEBUG
+    static void UnitTest();
+
+    void assertEquals(float f, int tolerance = 0)
+    {
+        union {
+            float   fFloat;
+            int32_t fPacked;
+        } tmp;
+        
+        tmp.fFloat = f;
+        int d = tmp.fPacked - fPacked;
+        SkASSERT(SkAbs32(d) <= tolerance);
+    }
+    float getFloat() const
+    {
+        union {
+            float   fFloat;
+            int32_t fPacked;
+        } tmp;
+        
+        tmp.fPacked = fPacked;
+        return tmp.fFloat;
+    }
+#endif
+
+private:
+    int32_t fPacked;
+
+    SkFloat(int32_t packed) : fPacked(fPacked) {}
+
+public:
+    static int GetShift(int32_t packed, int shift);
+    static int32_t SetShift(int value, int shift);
+    static int32_t Neg(int32_t);
+    static int32_t Abs(int32_t packed) { return (uint32_t)(packed << 1) >> 1; }
+    static int32_t Shift(int32_t, int bits);
+    static int32_t Add(int32_t, int32_t);
+    static int32_t Mul(int32_t, int32_t);
+    static int32_t MulInt(int32_t, int);
+    static int32_t Div(int32_t, int32_t);
+    static int32_t DivInt(int32_t, int);
+    static int32_t Invert(int32_t);
+    static int32_t Sqrt(int32_t);
+    static int32_t CubeRoot(int32_t);
+    static int Cmp(int32_t, int32_t);
+};
+
+#endif
diff --git a/src/core/SkFloatBits.cpp b/src/core/SkFloatBits.cpp
new file mode 100644
index 0000000..1f15df2
--- /dev/null
+++ b/src/core/SkFloatBits.cpp
@@ -0,0 +1,205 @@
+#include "SkFloatBits.h"
+#include "SkMath.h"
+
+/******************************************************************************
+    SkFloatBits_toInt[Floor, Round, Ceil] are identical except for what they
+    do right before they return ... >> exp;
+    Floor - adds nothing
+    Round - adds 1 << (exp - 1)
+    Ceil - adds (1 << exp) - 1
+
+    Floor and Cast are very similar, but Cast applies its sign after all other
+    computations on value. Also, Cast does not need to check for negative zero,
+    as that value (0x80000000) "does the right thing" for Ceil. Note that it
+    doesn't for Floor/Round/Ceil, hence the explicit check.
+******************************************************************************/
+
+#define EXP_BIAS            (127+23)
+#define MATISSA_MAGIC_BIG   (1 << 23)
+
+static inline int unpack_exp(uint32_t packed) {
+    return (packed << 1 >> 24);
+}
+
+#if 0
+// the ARM compiler generates an extra BIC, so I use the dirty version instead
+static inline int unpack_matissa(uint32_t packed) {
+    // we could mask with 0x7FFFFF, but that is harder for ARM to encode
+    return (packed & ~0xFF000000) | MATISSA_MAGIC_BIG;
+}
+#endif
+
+// returns the low 24-bits, so we need to OR in the magic_bit afterwards
+static inline int unpack_matissa_dirty(uint32_t packed) {
+    return packed & ~0xFF000000;
+}
+
+// same as (int)float
+int32_t SkFloatBits_toIntCast(int32_t packed) {
+    int exp = unpack_exp(packed) - EXP_BIAS;
+    int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+    
+    if (exp >= 0) {
+        if (exp > 7) {    // overflow
+            value = SK_MaxS32;
+        } else {
+            value <<= exp;
+        }
+    } else {
+        exp = -exp;
+        if (exp > 25) {   // underflow
+            exp = 25;
+        }
+        value >>= exp;
+    }
+    return SkApplySign(value, SkExtractSign(packed));
+}
+
+// same as (int)floor(float)
+int32_t SkFloatBits_toIntFloor(int32_t packed) {
+    // curse you negative 0
+    if ((packed << 1) == 0) {
+        return 0;
+    }
+    
+    int exp = unpack_exp(packed) - EXP_BIAS;
+    int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+
+    if (exp >= 0) {
+        if (exp > 7) {    // overflow
+            value = SK_MaxS32;
+        } else {
+            value <<= exp;
+        }
+        // apply the sign after we check for overflow
+        return SkApplySign(value, SkExtractSign(packed));
+    } else {
+        // apply the sign before we right-shift
+        value = SkApplySign(value, SkExtractSign(packed));
+        exp = -exp;
+        if (exp > 25) {   // underflow
+            exp = 25;
+        }
+        // int add = 0;
+        return value >> exp;
+    }
+}
+
+// same as (int)floor(float + 0.5)
+int32_t SkFloatBits_toIntRound(int32_t packed) {
+    // curse you negative 0
+    if ((packed << 1) == 0) {
+        return 0;
+    }
+    
+    int exp = unpack_exp(packed) - EXP_BIAS;
+    int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+    
+    if (exp >= 0) {
+        if (exp > 7) {    // overflow
+            value = SK_MaxS32;
+        } else {
+            value <<= exp;
+        }
+        // apply the sign after we check for overflow
+        return SkApplySign(value, SkExtractSign(packed));
+    } else {
+        // apply the sign before we right-shift
+        value = SkApplySign(value, SkExtractSign(packed));
+        exp = -exp;
+        if (exp > 25) {   // underflow
+            exp = 25;
+        }
+        int add = 1 << (exp - 1);
+        return (value + add) >> exp;
+    }
+}
+
+// same as (int)ceil(float)
+int32_t SkFloatBits_toIntCeil(int32_t packed) {
+    // curse you negative 0
+    if ((packed << 1) == 0) {
+        return 0;
+    }
+    
+    int exp = unpack_exp(packed) - EXP_BIAS;
+    int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
+    
+    if (exp >= 0) {
+        if (exp > 7) {    // overflow
+            value = SK_MaxS32;
+        } else {
+            value <<= exp;
+        }
+        // apply the sign after we check for overflow
+        return SkApplySign(value, SkExtractSign(packed));
+    } else {
+        // apply the sign before we right-shift
+        value = SkApplySign(value, SkExtractSign(packed));
+        exp = -exp;
+        if (exp > 25) {   // underflow
+            exp = 25;
+        }
+        int add = (1 << exp) - 1;
+        return (value + add) >> exp;
+    }
+}
+
+float SkFloatBits_intToFloatNative(int x);
+float SkFloatBits_intToFloatNative(int x) {
+    return x;
+}
+
+float SkIntToFloatCast(int32_t value) {
+    if (0 == value) {
+        return 0;
+    }
+
+    int shift = EXP_BIAS;
+    
+    // record the sign and make value positive
+    int sign = SkExtractSign(value);
+    value = SkApplySign(value, sign);
+    
+    if (value >> 24) {    // value is too big (has more than 24 bits set)
+        int bias = 8 - SkCLZ(value);
+        SkDebugf("value = %d, bias = %d\n", value, bias);
+        SkASSERT(bias > 0 && bias < 8);
+        value >>= bias; // need to round?
+        shift += bias;
+    } else {
+        int zeros = SkCLZ(value << 8);
+        SkASSERT(zeros >= 0 && zeros <= 23);
+        value <<= zeros;
+        shift -= zeros;
+    }
+    
+    // now value is left-aligned to 24 bits
+    SkASSERT((value >> 23) == 1);
+    SkASSERT(shift >= 0 && shift <= 255);
+    
+    SkFloatIntUnion data;
+    data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG);
+    return data.fFloat;
+}
+
+float SkIntToFloatCast_NoOverflowCheck(int32_t value) {
+    if (0 == value) {
+        return 0;
+    }
+
+    int shift = EXP_BIAS;
+    
+    // record the sign and make value positive
+    int sign = SkExtractSign(value);
+    value = SkApplySign(value, sign);
+    
+    int zeros = SkCLZ(value << 8);
+    value <<= zeros;
+    shift -= zeros;
+    
+    SkFloatIntUnion data;
+    data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG);
+    return data.fFloat;
+}
+
diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
new file mode 100644
index 0000000..4f22e92
--- /dev/null
+++ b/src/core/SkGeometry.cpp
@@ -0,0 +1,1072 @@
+/* libs/graphics/sgl/SkGeometry.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 "SkGeometry.h"
+#include "Sk64.h"
+#include "SkMatrix.h"
+
+/** If defined, this makes eval_quad and eval_cubic do more setup (sometimes
+    involving integer multiplies by 2 or 3, but fewer calls to SkScalarMul.
+    May also introduce overflow of fixed when we compute our setup.
+*/
+#ifdef SK_SCALAR_IS_FIXED
+    #define DIRECT_EVAL_OF_POLYNOMIALS
+#endif
+
+////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FIXED
+    static int is_not_monotonic(int a, int b, int c, int d)
+    {
+        return (((a - b) | (b - c) | (c - d)) & ((b - a) | (c - b) | (d - c))) >> 31;
+    }
+
+    static int is_not_monotonic(int a, int b, int c)
+    {
+        return (((a - b) | (b - c)) & ((b - a) | (c - b))) >> 31;
+    }
+#else
+    static int is_not_monotonic(float a, float b, float c)
+    {
+        float ab = a - b;
+        float bc = b - c;
+        if (ab < 0)
+            bc = -bc;
+        return ab == 0 || bc < 0;
+    }
+#endif
+
+////////////////////////////////////////////////////////////////////////
+
+static bool is_unit_interval(SkScalar x)
+{
+    return x > 0 && x < SK_Scalar1;
+}
+
+static int valid_unit_divide(SkScalar numer, SkScalar denom, SkScalar* ratio)
+{
+    SkASSERT(ratio);
+
+    if (numer < 0)
+    {
+        numer = -numer;
+        denom = -denom;
+    }
+
+    if (denom == 0 || numer == 0 || numer >= denom)
+        return 0;
+
+    SkScalar r = SkScalarDiv(numer, denom);
+    SkASSERT(r >= 0 && r < SK_Scalar1);
+    if (r == 0) // catch underflow if numer <<<< denom
+        return 0;
+    *ratio = r;
+    return 1;
+}
+
+/** From Numerical Recipes in C.
+
+    Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C])
+    x1 = Q / A
+    x2 = C / Q
+*/
+int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2])
+{
+    SkASSERT(roots);
+
+    if (A == 0)
+        return valid_unit_divide(-C, B, roots);
+
+    SkScalar* r = roots;
+
+#ifdef SK_SCALAR_IS_FLOAT
+    float R = B*B - 4*A*C;
+    if (R < 0)  // complex roots
+        return 0;
+    R = sk_float_sqrt(R);
+#else
+    Sk64    RR, tmp;
+
+    RR.setMul(B,B);
+    tmp.setMul(A,C);
+    tmp.shiftLeft(2);
+    RR.sub(tmp);
+    if (RR.isNeg())
+        return 0;
+    SkFixed R = RR.getSqrt();
+#endif
+
+    SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
+    r += valid_unit_divide(Q, A, r);
+    r += valid_unit_divide(C, Q, r);
+    if (r - roots == 2)
+    {
+        if (roots[0] > roots[1])
+            SkTSwap<SkScalar>(roots[0], roots[1]);
+        else if (roots[0] == roots[1])  // nearly-equal?
+            r -= 1; // skip the double root
+    }
+    return (int)(r - roots);
+}
+
+#ifdef SK_SCALAR_IS_FIXED
+/** Trim A/B/C down so that they are all <= 32bits
+    and then call SkFindUnitQuadRoots()
+*/
+static int Sk64FindFixedQuadRoots(const Sk64& A, const Sk64& B, const Sk64& C, SkFixed roots[2])
+{
+    int na = A.shiftToMake32();
+    int nb = B.shiftToMake32();
+    int nc = C.shiftToMake32();
+
+    int shift = SkMax32(na, SkMax32(nb, nc));
+    SkASSERT(shift >= 0);
+
+    return SkFindUnitQuadRoots(A.getShiftRight(shift), B.getShiftRight(shift), C.getShiftRight(shift), roots);
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+static SkScalar eval_quad(const SkScalar src[], SkScalar t)
+{
+    SkASSERT(src);
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+
+#ifdef DIRECT_EVAL_OF_POLYNOMIALS
+    SkScalar    C = src[0];
+    SkScalar    A = src[4] - 2 * src[2] + C;
+    SkScalar    B = 2 * (src[2] - C);
+    return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
+#else
+    SkScalar    ab = SkScalarInterp(src[0], src[2], t);
+    SkScalar    bc = SkScalarInterp(src[2], src[4], t); 
+    return SkScalarInterp(ab, bc, t);
+#endif
+}
+
+static SkScalar eval_quad_derivative(const SkScalar src[], SkScalar t)
+{
+    SkScalar A = src[4] - 2 * src[2] + src[0];
+    SkScalar B = src[2] - src[0];
+
+    return 2 * SkScalarMulAdd(A, t, B);
+}
+
+static SkScalar eval_quad_derivative_at_half(const SkScalar src[])
+{
+    SkScalar A = src[4] - 2 * src[2] + src[0];
+    SkScalar B = src[2] - src[0];
+    return A + 2 * B;
+}
+
+void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent)
+{
+    SkASSERT(src);
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+
+    if (pt)
+        pt->set(eval_quad(&src[0].fX, t), eval_quad(&src[0].fY, t));
+    if (tangent)
+        tangent->set(eval_quad_derivative(&src[0].fX, t),
+                     eval_quad_derivative(&src[0].fY, t));
+}
+
+void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent)
+{
+    SkASSERT(src);
+
+    if (pt)
+    {
+        SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
+        SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
+        SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
+        SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
+        pt->set(SkScalarAve(x01, x12), SkScalarAve(y01, y12));
+    }
+    if (tangent)
+        tangent->set(eval_quad_derivative_at_half(&src[0].fX),
+                     eval_quad_derivative_at_half(&src[0].fY));
+}
+
+static void interp_quad_coords(const SkScalar* src, SkScalar* dst, SkScalar t)
+{
+    SkScalar    ab = SkScalarInterp(src[0], src[2], t);
+    SkScalar    bc = SkScalarInterp(src[2], src[4], t);
+
+    dst[0] = src[0];
+    dst[2] = ab;
+    dst[4] = SkScalarInterp(ab, bc, t);
+    dst[6] = bc;
+    dst[8] = src[4];
+}
+
+void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t)
+{
+    SkASSERT(t > 0 && t < SK_Scalar1);
+
+    interp_quad_coords(&src[0].fX, &dst[0].fX, t);
+    interp_quad_coords(&src[0].fY, &dst[0].fY, t);
+}
+
+void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5])
+{
+    SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
+    SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
+    SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
+    SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
+
+    dst[0] = src[0];
+    dst[1].set(x01, y01);
+    dst[2].set(SkScalarAve(x01, x12), SkScalarAve(y01, y12));
+    dst[3].set(x12, y12);
+    dst[4] = src[2];
+}
+
+/** Quad'(t) = At + B, where
+    A = 2(a - 2b + c)
+    B = 2(b - a)
+    Solve for t, only if it fits between 0 < t < 1
+*/
+int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1])
+{
+    /*  At + B == 0
+        t = -B / A
+    */
+#ifdef SK_SCALAR_IS_FIXED
+    return is_not_monotonic(a, b, c) && valid_unit_divide(a - b, a - b - b + c, tValue);
+#else
+    return valid_unit_divide(a - b, a - b - b + c, tValue);
+#endif
+}
+
+static void flatten_double_quad_extrema(SkScalar coords[14])
+{
+    coords[2] = coords[6] = coords[4];
+}
+
+static void force_quad_monotonic_in_y(SkPoint pts[3])
+{
+    // zap pts[1].fY to the nearest value
+    SkScalar ab = SkScalarAbs(pts[0].fY - pts[1].fY);
+    SkScalar bc = SkScalarAbs(pts[1].fY - pts[2].fY);
+    pts[1].fY = ab < bc ? pts[0].fY : pts[2].fY;
+}
+
+/*  Returns 0 for 1 quad, and 1 for two quads, either way the answer is
+    stored in dst[]. Guarantees that the 1/2 quads will be monotonic.
+*/
+int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5])
+{
+    SkASSERT(src);
+    SkASSERT(dst);
+
+#if 0
+    static bool once = true;
+    if (once)
+    {
+        once = false;
+        SkPoint s[3] = { 0, 26398, 0, 26331, 0, 20621428 };
+        SkPoint d[6];
+        
+        int n = SkChopQuadAtYExtrema(s, d);
+        SkDebugf("chop=%d, Y=[%x %x %x %x %x %x]\n", n, d[0].fY, d[1].fY, d[2].fY, d[3].fY, d[4].fY, d[5].fY);
+    }
+#endif
+
+    SkScalar a = src[0].fY;
+    SkScalar b = src[1].fY;
+    SkScalar c = src[2].fY;
+
+    if (is_not_monotonic(a, b, c))
+    {
+        SkScalar    tValue;
+        if (valid_unit_divide(a - b, a - b - b + c, &tValue))
+        {
+            SkChopQuadAt(src, dst, tValue);
+            flatten_double_quad_extrema(&dst[0].fY);
+            return 1;
+        }
+        // if we get here, we need to force dst to be monotonic, even though
+        // we couldn't compute a unit_divide value (probably underflow).
+        b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
+    }
+    dst[0].set(src[0].fX, a);
+    dst[1].set(src[1].fX, b);
+    dst[2].set(src[2].fX, c);
+    return 0;
+}
+
+//  F(t)    = a (1 - t) ^ 2 + 2 b t (1 - t) + c t ^ 2
+//  F'(t)   = 2 (b - a) + 2 (a - 2b + c) t
+//  F''(t)  = 2 (a - 2b + c)
+//
+//  A = 2 (b - a)
+//  B = 2 (a - 2b + c)
+//
+//  Maximum curvature for a quadratic means solving
+//  Fx' Fx'' + Fy' Fy'' = 0
+//
+//  t = - (Ax Bx + Ay By) / (Bx ^ 2 + By ^ 2)
+//
+int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5])
+{
+    SkScalar    Ax = src[1].fX - src[0].fX;
+    SkScalar    Ay = src[1].fY - src[0].fY;
+    SkScalar    Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX;
+    SkScalar    By = src[0].fY - src[1].fY - src[1].fY + src[2].fY;
+    SkScalar    t = 0;  // 0 means don't chop
+
+#ifdef SK_SCALAR_IS_FLOAT
+    (void)valid_unit_divide(-(Ax * Bx + Ay * By), Bx * Bx + By * By, &t);
+#else
+    // !!! should I use SkFloat here? seems like it
+    Sk64    numer, denom, tmp;
+
+    numer.setMul(Ax, -Bx);
+    tmp.setMul(Ay, -By);
+    numer.add(tmp);
+
+    if (numer.isPos())  // do nothing if numer <= 0
+    {
+        denom.setMul(Bx, Bx);
+        tmp.setMul(By, By);
+        denom.add(tmp);
+        SkASSERT(!denom.isNeg());
+        if (numer < denom)
+        {
+            t = numer.getFixedDiv(denom);
+            SkASSERT(t >= 0 && t <= SK_Fixed1);     // assert that we're numerically stable (ha!)
+            if ((unsigned)t >= SK_Fixed1)           // runtime check for numerical stability
+                t = 0;  // ignore the chop
+        }
+    }
+#endif
+
+    if (t == 0)
+    {
+        memcpy(dst, src, 3 * sizeof(SkPoint));
+        return 1;
+    }
+    else
+    {
+        SkChopQuadAt(src, dst, t);
+        return 2;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS /////
+////////////////////////////////////////////////////////////////////////////////////////
+
+static void get_cubic_coeff(const SkScalar pt[], SkScalar coeff[4])
+{
+    coeff[0] = pt[6] + 3*(pt[2] - pt[4]) - pt[0];
+    coeff[1] = 3*(pt[4] - pt[2] - pt[2] + pt[0]);
+    coeff[2] = 3*(pt[2] - pt[0]);
+    coeff[3] = pt[0];
+}
+
+void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4])
+{
+    SkASSERT(pts);
+
+    if (cx)
+        get_cubic_coeff(&pts[0].fX, cx);
+    if (cy)
+        get_cubic_coeff(&pts[0].fY, cy);
+}
+
+static SkScalar eval_cubic(const SkScalar src[], SkScalar t)
+{
+    SkASSERT(src);
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+
+    if (t == 0)
+        return src[0];
+
+#ifdef DIRECT_EVAL_OF_POLYNOMIALS
+    SkScalar D = src[0];
+    SkScalar A = src[6] + 3*(src[2] - src[4]) - D;
+    SkScalar B = 3*(src[4] - src[2] - src[2] + D);
+    SkScalar C = 3*(src[2] - D);
+
+    return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
+#else
+    SkScalar    ab = SkScalarInterp(src[0], src[2], t);
+    SkScalar    bc = SkScalarInterp(src[2], src[4], t);
+    SkScalar    cd = SkScalarInterp(src[4], src[6], t);
+    SkScalar    abc = SkScalarInterp(ab, bc, t);
+    SkScalar    bcd = SkScalarInterp(bc, cd, t);
+    return SkScalarInterp(abc, bcd, t);
+#endif
+}
+
+/** return At^2 + Bt + C
+*/
+static SkScalar eval_quadratic(SkScalar A, SkScalar B, SkScalar C, SkScalar t)
+{
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+
+    return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
+}
+
+static SkScalar eval_cubic_derivative(const SkScalar src[], SkScalar t)
+{
+    SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0];
+    SkScalar B = 2*(src[4] - 2 * src[2] + src[0]);
+    SkScalar C = src[2] - src[0];
+
+    return eval_quadratic(A, B, C, t);
+}
+
+static SkScalar eval_cubic_2ndDerivative(const SkScalar src[], SkScalar t)
+{
+    SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0];
+    SkScalar B = src[4] - 2 * src[2] + src[0];
+
+    return SkScalarMulAdd(A, t, B);
+}
+
+void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc, SkVector* tangent, SkVector* curvature)
+{
+    SkASSERT(src);
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
+
+    if (loc)
+        loc->set(eval_cubic(&src[0].fX, t), eval_cubic(&src[0].fY, t));
+    if (tangent)
+        tangent->set(eval_cubic_derivative(&src[0].fX, t),
+                     eval_cubic_derivative(&src[0].fY, t));
+    if (curvature)
+        curvature->set(eval_cubic_2ndDerivative(&src[0].fX, t),
+                       eval_cubic_2ndDerivative(&src[0].fY, t));
+}
+
+/** Cubic'(t) = At^2 + Bt + C, where
+    A = 3(-a + 3(b - c) + d)
+    B = 6(a - 2b + c)
+    C = 3(b - a)
+    Solve for t, keeping only those that fit betwee 0 < t < 1
+*/
+int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2])
+{
+#ifdef SK_SCALAR_IS_FIXED
+    if (!is_not_monotonic(a, b, c, d))
+        return 0;
+#endif
+
+    // we divide A,B,C by 3 to simplify
+    SkScalar A = d - a + 3*(b - c);
+    SkScalar B = 2*(a - b - b + c);
+    SkScalar C = b - a;
+
+    return SkFindUnitQuadRoots(A, B, C, tValues);
+}
+
+static void interp_cubic_coords(const SkScalar* src, SkScalar* dst, SkScalar t)
+{
+    SkScalar    ab = SkScalarInterp(src[0], src[2], t);
+    SkScalar    bc = SkScalarInterp(src[2], src[4], t);
+    SkScalar    cd = SkScalarInterp(src[4], src[6], t);
+    SkScalar    abc = SkScalarInterp(ab, bc, t);
+    SkScalar    bcd = SkScalarInterp(bc, cd, t);
+    SkScalar    abcd = SkScalarInterp(abc, bcd, t);
+
+    dst[0] = src[0];
+    dst[2] = ab;
+    dst[4] = abc;
+    dst[6] = abcd;
+    dst[8] = bcd;
+    dst[10] = cd;
+    dst[12] = src[6];
+}
+
+void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t)
+{
+    SkASSERT(t > 0 && t < SK_Scalar1);
+
+    interp_cubic_coords(&src[0].fX, &dst[0].fX, t);
+    interp_cubic_coords(&src[0].fY, &dst[0].fY, t);
+}
+
+void SkChopCubicAt(const SkPoint src[4], SkPoint dst[], const SkScalar tValues[], int roots)
+{
+#ifdef SK_DEBUG
+    {
+        for (int i = 0; i < roots - 1; i++)
+        {
+            SkASSERT(is_unit_interval(tValues[i]));
+            SkASSERT(is_unit_interval(tValues[i+1]));
+            SkASSERT(tValues[i] < tValues[i+1]);
+        }
+    }
+#endif
+
+    if (dst)
+    {
+        if (roots == 0) // nothing to chop
+            memcpy(dst, src, 4*sizeof(SkPoint));
+        else
+        {
+            SkScalar    t = tValues[0];
+            SkPoint     tmp[4];
+
+            for (int i = 0; i < roots; i++)
+            {
+                SkChopCubicAt(src, dst, t);
+                if (i == roots - 1)
+                    break;
+
+                SkDEBUGCODE(int valid =) valid_unit_divide(tValues[i+1] - tValues[i], SK_Scalar1 - tValues[i], &t);
+                SkASSERT(valid);
+
+                dst += 3;
+                memcpy(tmp, dst, 4 * sizeof(SkPoint));
+                src = tmp;
+            }
+        }
+    }
+}
+
+void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7])
+{
+    SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
+    SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
+    SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
+    SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
+    SkScalar x23 = SkScalarAve(src[2].fX, src[3].fX);
+    SkScalar y23 = SkScalarAve(src[2].fY, src[3].fY);
+
+    SkScalar x012 = SkScalarAve(x01, x12);
+    SkScalar y012 = SkScalarAve(y01, y12);
+    SkScalar x123 = SkScalarAve(x12, x23);
+    SkScalar y123 = SkScalarAve(y12, y23);
+
+    dst[0] = src[0];
+    dst[1].set(x01, y01);
+    dst[2].set(x012, y012);
+    dst[3].set(SkScalarAve(x012, x123), SkScalarAve(y012, y123));
+    dst[4].set(x123, y123);
+    dst[5].set(x23, y23);
+    dst[6] = src[3];
+}
+
+static void flatten_double_cubic_extrema(SkScalar coords[14])
+{
+    coords[4] = coords[8] = coords[6];
+}
+
+/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that
+    the resulting beziers are monotonic in Y. This is called by the scan converter.
+    Depending on what is returned, dst[] is treated as follows
+    0   dst[0..3] is the original cubic
+    1   dst[0..3] and dst[3..6] are the two new cubics
+    2   dst[0..3], dst[3..6], dst[6..9] are the three new cubics
+    If dst == null, it is ignored and only the count is returned.
+*/
+int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10])
+{
+    SkScalar    tValues[2];
+    int         roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, tValues);
+
+    SkChopCubicAt(src, dst, tValues, roots);
+    if (dst && roots > 0)
+    {
+        // we do some cleanup to ensure our Y extrema are flat
+        flatten_double_cubic_extrema(&dst[0].fY);
+        if (roots == 2)
+            flatten_double_cubic_extrema(&dst[3].fY);
+    }
+    return roots;
+}
+
+/** http://www.faculty.idc.ac.il/arik/quality/appendixA.html
+
+    Inflection means that curvature is zero.
+    Curvature is [F' x F''] / [F'^3]
+    So we solve F'x X F''y - F'y X F''y == 0
+    After some canceling of the cubic term, we get
+    A = b - a
+    B = c - 2b + a
+    C = d - 3c + 3b - a
+    (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0
+*/
+int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[])
+{
+    SkScalar    Ax = src[1].fX - src[0].fX;
+    SkScalar    Ay = src[1].fY - src[0].fY;
+    SkScalar    Bx = src[2].fX - 2 * src[1].fX + src[0].fX;
+    SkScalar    By = src[2].fY - 2 * src[1].fY + src[0].fY;
+    SkScalar    Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX;
+    SkScalar    Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY;
+    int         count;
+
+#ifdef SK_SCALAR_IS_FLOAT
+    count = SkFindUnitQuadRoots(Bx*Cy - By*Cx, Ax*Cy - Ay*Cx, Ax*By - Ay*Bx, tValues);
+#else
+    Sk64    A, B, C, tmp;
+
+    A.setMul(Bx, Cy);
+    tmp.setMul(By, Cx);
+    A.sub(tmp);
+
+    B.setMul(Ax, Cy);
+    tmp.setMul(Ay, Cx);
+    B.sub(tmp);
+
+    C.setMul(Ax, By);
+    tmp.setMul(Ay, Bx);
+    C.sub(tmp);
+
+    count = Sk64FindFixedQuadRoots(A, B, C, tValues);
+#endif
+
+    return count;
+}
+
+int SkChopCubicAtInflections(const SkPoint src[], SkPoint dst[10])
+{
+    SkScalar    tValues[2];
+    int         count = SkFindCubicInflections(src, tValues);
+
+    if (dst)
+    {
+        if (count == 0)
+            memcpy(dst, src, 4 * sizeof(SkPoint));
+        else
+            SkChopCubicAt(src, dst, tValues, count);
+    }
+    return count + 1;
+}
+
+template <typename T> void bubble_sort(T array[], int count)
+{
+    for (int i = count - 1; i > 0; --i)
+        for (int j = i; j > 0; --j)
+            if (array[j] < array[j-1])
+            {
+                T   tmp(array[j]);
+                array[j] = array[j-1];
+                array[j-1] = tmp;
+            }
+}
+
+#include "SkFP.h"
+
+// newton refinement
+#if 0
+static SkScalar refine_cubic_root(const SkFP coeff[4], SkScalar root)
+{
+    //  x1 = x0 - f(t) / f'(t)
+
+    SkFP    T = SkScalarToFloat(root);
+    SkFP    N, D;
+
+    // f' = 3*coeff[0]*T^2 + 2*coeff[1]*T + coeff[2]
+    D = SkFPMul(SkFPMul(coeff[0], SkFPMul(T,T)), 3);
+    D = SkFPAdd(D, SkFPMulInt(SkFPMul(coeff[1], T), 2));
+    D = SkFPAdd(D, coeff[2]);
+
+    if (D == 0)
+        return root;
+
+    // f = coeff[0]*T^3 + coeff[1]*T^2 + coeff[2]*T + coeff[3]
+    N = SkFPMul(SkFPMul(SkFPMul(T, T), T), coeff[0]);
+    N = SkFPAdd(N, SkFPMul(SkFPMul(T, T), coeff[1]));
+    N = SkFPAdd(N, SkFPMul(T, coeff[2]));
+    N = SkFPAdd(N, coeff[3]);
+
+    if (N)
+    {
+        SkScalar delta = SkFPToScalar(SkFPDiv(N, D));
+
+        if (delta)
+            root -= delta;
+    }
+    return root;
+}
+#endif
+
+#if defined _WIN32 && _MSC_VER >= 1300  && defined SK_SCALAR_IS_FIXED // disable warning : unreachable code if building fixed point for windows desktop
+#pragma warning ( disable : 4702 )
+#endif
+
+/*  Solve coeff(t) == 0, returning the number of roots that
+    lie withing 0 < t < 1.
+    coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3]
+*/
+static int solve_cubic_polynomial(const SkFP coeff[4], SkScalar tValues[3])
+{
+#ifndef SK_SCALAR_IS_FLOAT
+    return 0;   // this is not yet implemented for software float
+#endif
+
+    if (SkScalarNearlyZero(coeff[0]))   // we're just a quadratic
+    {
+        return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues);
+    }
+
+    SkFP    a, b, c, Q, R;
+
+    {
+        SkASSERT(coeff[0] != 0);
+
+        SkFP inva = SkFPInvert(coeff[0]);
+        a = SkFPMul(coeff[1], inva);
+        b = SkFPMul(coeff[2], inva);
+        c = SkFPMul(coeff[3], inva);
+    }
+    Q = SkFPDivInt(SkFPSub(SkFPMul(a,a), SkFPMulInt(b, 3)), 9);
+//  R = (2*a*a*a - 9*a*b + 27*c) / 54;
+    R = SkFPMulInt(SkFPMul(SkFPMul(a, a), a), 2);
+    R = SkFPSub(R, SkFPMulInt(SkFPMul(a, b), 9));
+    R = SkFPAdd(R, SkFPMulInt(c, 27));
+    R = SkFPDivInt(R, 54);
+
+    SkFP Q3 = SkFPMul(SkFPMul(Q, Q), Q);
+    SkFP R2MinusQ3 = SkFPSub(SkFPMul(R,R), Q3);
+    SkFP adiv3 = SkFPDivInt(a, 3);
+
+    SkScalar*   roots = tValues;
+    SkScalar    r;
+
+    if (SkFPLT(R2MinusQ3, 0))   // we have 3 real roots
+    {
+#ifdef SK_SCALAR_IS_FLOAT
+        float theta = sk_float_acos(R / sk_float_sqrt(Q3));
+        float neg2RootQ = -2 * sk_float_sqrt(Q);
+
+        r = neg2RootQ * sk_float_cos(theta/3) - adiv3;
+        if (is_unit_interval(r))
+            *roots++ = r;
+
+        r = neg2RootQ * sk_float_cos((theta + 2*SK_ScalarPI)/3) - adiv3;
+        if (is_unit_interval(r))
+            *roots++ = r;
+
+        r = neg2RootQ * sk_float_cos((theta - 2*SK_ScalarPI)/3) - adiv3;
+        if (is_unit_interval(r))
+            *roots++ = r;
+
+        // now sort the roots
+        bubble_sort(tValues, (int)(roots - tValues));
+#endif
+    }
+    else                // we have 1 real root
+    {
+        SkFP A = SkFPAdd(SkFPAbs(R), SkFPSqrt(R2MinusQ3));
+        A = SkFPCubeRoot(A);
+        if (SkFPGT(R, 0))
+            A = SkFPNeg(A);
+
+        if (A != 0)
+            A = SkFPAdd(A, SkFPDiv(Q, A));
+        r = SkFPToScalar(SkFPSub(A, adiv3));
+        if (is_unit_interval(r))
+            *roots++ = r;
+    }
+
+    return (int)(roots - tValues);
+}
+
+/*  Looking for F' dot F'' == 0
+    
+    A = b - a
+    B = c - 2b + a
+    C = d - 3c + 3b - a
+
+    F' = 3Ct^2 + 6Bt + 3A
+    F'' = 6Ct + 6B
+
+    F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
+*/
+static void formulate_F1DotF2(const SkScalar src[], SkFP coeff[4])
+{
+    SkScalar    a = src[2] - src[0];
+    SkScalar    b = src[4] - 2 * src[2] + src[0];
+    SkScalar    c = src[6] + 3 * (src[2] - src[4]) - src[0];
+
+    SkFP    A = SkScalarToFP(a);
+    SkFP    B = SkScalarToFP(b);
+    SkFP    C = SkScalarToFP(c);
+
+    coeff[0] = SkFPMul(C, C);
+    coeff[1] = SkFPMulInt(SkFPMul(B, C), 3);
+    coeff[2] = SkFPMulInt(SkFPMul(B, B), 2);
+    coeff[2] = SkFPAdd(coeff[2], SkFPMul(C, A));
+    coeff[3] = SkFPMul(A, B);
+}
+
+// EXPERIMENTAL: can set this to zero to accept all t-values 0 < t < 1
+//#define kMinTValueForChopping (SK_Scalar1 / 256)
+#define kMinTValueForChopping   0
+
+/*  Looking for F' dot F'' == 0
+    
+    A = b - a
+    B = c - 2b + a
+    C = d - 3c + 3b - a
+
+    F' = 3Ct^2 + 6Bt + 3A
+    F'' = 6Ct + 6B
+
+    F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
+*/
+int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3])
+{
+    SkFP    coeffX[4], coeffY[4];
+    int     i;
+
+    formulate_F1DotF2(&src[0].fX, coeffX);
+    formulate_F1DotF2(&src[0].fY, coeffY);
+
+    for (i = 0; i < 4; i++)
+        coeffX[i] = SkFPAdd(coeffX[i],coeffY[i]);
+
+    SkScalar    t[3];
+    int         count = solve_cubic_polynomial(coeffX, t);
+    int         maxCount = 0;
+
+    // now remove extrema where the curvature is zero (mins)
+    // !!!! need a test for this !!!!
+    for (i = 0; i < count; i++)
+    {
+        // if (not_min_curvature())
+        if (t[i] > kMinTValueForChopping && t[i] < SK_Scalar1 - kMinTValueForChopping)
+            tValues[maxCount++] = t[i];
+    }
+    return maxCount;
+}
+
+int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3])
+{
+    SkScalar    t_storage[3];
+
+    if (tValues == NULL)
+        tValues = t_storage;
+
+    int count = SkFindCubicMaxCurvature(src, tValues);
+
+    if (dst)
+    {
+        if (count == 0)
+            memcpy(dst, src, 4 * sizeof(SkPoint));
+        else
+            SkChopCubicAt(src, dst, tValues, count);
+    }
+    return count + 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+/*  Find t value for quadratic [a, b, c] = d.
+    Return 0 if there is no solution within [0, 1)
+*/
+static SkScalar quad_solve(SkScalar a, SkScalar b, SkScalar c, SkScalar d)
+{
+    // At^2 + Bt + C = d
+    SkScalar A = a - 2 * b + c;
+    SkScalar B = 2 * (b - a);
+    SkScalar C = a - d;
+
+    SkScalar    roots[2];
+    int         count = SkFindUnitQuadRoots(A, B, C, roots);
+
+    SkASSERT(count <= 1);
+    return count == 1 ? roots[0] : 0;
+}
+
+/*  given a quad-curve and a point (x,y), chop the quad at that point and return
+    the new quad's offCurve point. Should only return false if the computed pos
+    is the start of the curve (i.e. root == 0)
+*/
+static bool quad_pt2OffCurve(const SkPoint quad[3], SkScalar x, SkScalar y, SkPoint* offCurve)
+{
+    const SkScalar* base;
+    SkScalar        value;
+
+    if (SkScalarAbs(x) < SkScalarAbs(y)) {
+        base = &quad[0].fX;
+        value = x;
+    } else {
+        base = &quad[0].fY;
+        value = y;
+    }
+
+    // note: this returns 0 if it thinks value is out of range, meaning the
+    // root might return something outside of [0, 1)
+    SkScalar t = quad_solve(base[0], base[2], base[4], value);
+
+    if (t > 0)
+    {
+        SkPoint tmp[5];
+        SkChopQuadAt(quad, tmp, t);
+        *offCurve = tmp[1];
+        return true;
+    } else {
+        /*  t == 0 means either the value triggered a root outside of [0, 1)
+            For our purposes, we can ignore the <= 0 roots, but we want to
+            catch the >= 1 roots (which given our caller, will basically mean
+            a root of 1, give-or-take numerical instability). If we are in the
+            >= 1 case, return the existing offCurve point.
+        
+            The test below checks to see if we are close to the "end" of the
+            curve (near base[4]). Rather than specifying a tolerance, I just
+            check to see if value is on to the right/left of the middle point
+            (depending on the direction/sign of the end points).
+        */
+        if ((base[0] < base[4] && value > base[2]) ||
+            (base[0] > base[4] && value < base[2]))   // should root have been 1
+        {
+            *offCurve = quad[1];
+            return true;
+        }
+    }
+    return false;
+}
+
+static const SkPoint gQuadCirclePts[kSkBuildQuadArcStorage] = {
+    { SK_Scalar1,           0               },
+    { SK_Scalar1,           SK_ScalarTanPIOver8 },
+    { SK_ScalarRoot2Over2,  SK_ScalarRoot2Over2 },
+    { SK_ScalarTanPIOver8,  SK_Scalar1          },
+
+    { 0,                    SK_Scalar1      },
+    { -SK_ScalarTanPIOver8, SK_Scalar1  },
+    { -SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 },
+    { -SK_Scalar1,          SK_ScalarTanPIOver8 },
+
+    { -SK_Scalar1,          0               },
+    { -SK_Scalar1,          -SK_ScalarTanPIOver8    },
+    { -SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2    },
+    { -SK_ScalarTanPIOver8, -SK_Scalar1     },
+
+    { 0,                    -SK_Scalar1     },
+    { SK_ScalarTanPIOver8,  -SK_Scalar1     },
+    { SK_ScalarRoot2Over2,  -SK_ScalarRoot2Over2    },
+    { SK_Scalar1,           -SK_ScalarTanPIOver8    },
+
+    { SK_Scalar1,           0   }
+};
+
+int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop,
+                   SkRotationDirection dir, const SkMatrix* userMatrix,
+                   SkPoint quadPoints[])
+{
+    // rotate by x,y so that uStart is (1.0)
+    SkScalar x = SkPoint::DotProduct(uStart, uStop);
+    SkScalar y = SkPoint::CrossProduct(uStart, uStop);
+
+    SkScalar absX = SkScalarAbs(x);
+    SkScalar absY = SkScalarAbs(y);
+
+    int pointCount;
+
+    // check for (effectively) coincident vectors
+    // this can happen if our angle is nearly 0 or nearly 180 (y == 0)
+    // ... we use the dot-prod to distinguish between 0 and 180 (x > 0)
+    if (absY <= SK_ScalarNearlyZero && x > 0 &&
+        ((y >= 0 && kCW_SkRotationDirection == dir) ||
+         (y <= 0 && kCCW_SkRotationDirection == dir))) {
+            
+        // just return the start-point
+        quadPoints[0].set(SK_Scalar1, 0);
+        pointCount = 1;
+    } else {
+        if (dir == kCCW_SkRotationDirection)
+            y = -y;
+
+        // what octant (quadratic curve) is [xy] in?
+        int oct = 0;
+        bool sameSign = true;
+
+        if (0 == y)
+        {
+            oct = 4;        // 180
+            SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero);
+        }
+        else if (0 == x)
+        {
+            SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero);
+            if (y > 0)
+                oct = 2;    // 90
+            else
+                oct = 6;    // 270
+        }
+        else
+        {
+            if (y < 0)
+                oct += 4;
+            if ((x < 0) != (y < 0))
+            {
+                oct += 2;
+                sameSign = false;
+            }
+            if ((absX < absY) == sameSign)
+                oct += 1;
+        }
+
+        int wholeCount = oct << 1;
+        memcpy(quadPoints, gQuadCirclePts, (wholeCount + 1) * sizeof(SkPoint));
+
+        const SkPoint* arc = &gQuadCirclePts[wholeCount];
+        if (quad_pt2OffCurve(arc, x, y, &quadPoints[wholeCount + 1]))
+        {
+            quadPoints[wholeCount + 2].set(x, y);
+            wholeCount += 2;
+        }
+        pointCount = wholeCount + 1;
+    }
+
+    // now handle counter-clockwise and the initial unitStart rotation
+    SkMatrix    matrix;
+    matrix.setSinCos(uStart.fY, uStart.fX);
+    if (dir == kCCW_SkRotationDirection) {
+        matrix.preScale(SK_Scalar1, -SK_Scalar1);
+    }
+    if (userMatrix) {
+        matrix.postConcat(*userMatrix);
+    }
+    matrix.mapPoints(quadPoints, pointCount);
+    return pointCount;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkGeometry::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    SkPoint pts[3], dst[5];
+
+    pts[0].set(0, 0);
+    pts[1].set(100, 50);
+    pts[2].set(0, 100);
+
+    int count = SkChopQuadAtMaxCurvature(pts, dst);
+    SkASSERT(count == 1 || count == 2);
+#endif
+}
+
+#endif
+
+
diff --git a/src/core/SkGeometry.h b/src/core/SkGeometry.h
new file mode 100644
index 0000000..571159f
--- /dev/null
+++ b/src/core/SkGeometry.h
@@ -0,0 +1,163 @@
+/* libs/graphics/sgl/SkGeometry.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 SkGeometry_DEFINED
+#define SkGeometry_DEFINED
+
+#include "SkMatrix.h"
+
+/** Given a quadratic equation Ax^2 + Bx + C = 0, return 0, 1, 2 roots for the
+    equation.
+*/
+int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]);
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Set pt to the point on the src quadratic specified by t. t must be
+    0 <= t <= 1.0
+*/
+void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent = NULL);
+void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent = NULL);
+
+/** Given a src quadratic bezier, chop it at the specified t value,
+    where 0 < t < 1, and return the two new quadratics in dst:
+    dst[0..2] and dst[2..4]
+*/
+void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t);
+
+/** Given a src quadratic bezier, chop it at the specified t == 1/2,
+    The new quads are returned in dst[0..2] and dst[2..4]
+*/
+void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]);
+
+/** Given the 3 coefficients for a quadratic bezier (either X or Y values), look
+    for extrema, and return the number of t-values that are found that represent
+    these extrema. If the quadratic has no extrema betwee (0..1) exclusive, the
+    function returns 0.
+    Returned count      tValues[]
+    0                   ignored
+    1                   0 < tValues[0] < 1
+*/
+int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValues[1]);
+
+/** Given 3 points on a quadratic bezier, chop it into 1, 2 beziers such that
+    the resulting beziers are monotonic in Y. This is called by the scan converter.
+    Depending on what is returned, dst[] is treated as follows
+    1   dst[0..2] is the original quad
+    2   dst[0..2] and dst[2..4] are the two new quads
+    If dst == null, it is ignored and only the count is returned.
+*/
+int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]);
+
+/** Given 3 points on a quadratic bezier, divide it into 2 quadratics
+    if the point of maximum curvature exists on the quad segment.
+    Depending on what is returned, dst[] is treated as follows
+    1   dst[0..2] is the original quad
+    2   dst[0..2] and dst[2..4] are the two new quads
+    If dst == null, it is ignored and only the count is returned.
+*/
+int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5]);
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+/** Convert from parametric from (pts) to polynomial coefficients
+    coeff[0]*T^3 + coeff[1]*T^2 + coeff[2]*T + coeff[3]
+*/
+void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4]);
+
+/** Set pt to the point on the src cubic specified by t. t must be
+    0 <= t <= 1.0
+*/
+void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* locOrNull, SkVector* tangentOrNull, SkVector* curvatureOrNull);
+
+/** Given a src cubic bezier, chop it at the specified t value,
+    where 0 < t < 1, and return the two new cubics in dst:
+    dst[0..3] and dst[3..6]
+*/
+void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t);
+void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], const SkScalar t[], int t_count);
+
+/** Given a src cubic bezier, chop it at the specified t == 1/2,
+    The new cubics are returned in dst[0..3] and dst[3..6]
+*/
+void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7]);
+
+/** Given the 4 coefficients for a cubic bezier (either X or Y values), look
+    for extrema, and return the number of t-values that are found that represent
+    these extrema. If the cubic has no extrema betwee (0..1) exclusive, the
+    function returns 0.
+    Returned count      tValues[]
+    0                   ignored
+    1                   0 < tValues[0] < 1
+    2                   0 < tValues[0] < tValues[1] < 1
+*/
+int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2]);
+
+/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that
+    the resulting beziers are monotonic in Y. This is called by the scan converter.
+    Depending on what is returned, dst[] is treated as follows
+    1   dst[0..3] is the original cubic
+    2   dst[0..3] and dst[3..6] are the two new cubics
+    3   dst[0..3], dst[3..6], dst[6..9] are the three new cubics
+    If dst == null, it is ignored and only the count is returned.
+*/
+int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]);
+
+/** Given a cubic bezier, return 0, 1, or 2 t-values that represent the
+    inflection points.
+*/
+int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[2]);
+
+/** Return 1 for no chop, or 2 for having chopped the cubic at its
+    inflection point.
+*/
+int SkChopCubicAtInflections(const SkPoint src[4], SkPoint dst[10]);
+
+int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3]);
+int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3] = NULL);
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+enum SkRotationDirection {
+    kCW_SkRotationDirection,
+    kCCW_SkRotationDirection
+};
+
+/** Maximum number of points needed in the quadPoints[] parameter for
+    SkBuildQuadArc()
+*/
+#define kSkBuildQuadArcStorage  17
+
+/** Given 2 unit vectors and a rotation direction, fill out the specified
+    array of points with quadratic segments. Return is the number of points
+    written to, which will be { 0, 3, 5, 7, ... kSkBuildQuadArcStorage }
+
+    matrix, if not null, is appled to the points before they are returned.
+*/
+int SkBuildQuadArc(const SkVector& unitStart, const SkVector& unitStop, SkRotationDirection,
+                   const SkMatrix* matrix, SkPoint quadPoints[]);
+
+//////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+    class SkGeometry {
+    public:
+        static void UnitTest();
+    };
+#endif
+
+#endif
diff --git a/src/core/SkGlobals.cpp b/src/core/SkGlobals.cpp
new file mode 100644
index 0000000..bc72b97
--- /dev/null
+++ b/src/core/SkGlobals.cpp
@@ -0,0 +1,92 @@
+/* libs/graphics/sgl/SkGlobals.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 "SkGlobals.h"
+#include "SkThread.h"
+
+SkGlobals::Rec::~Rec()
+{
+}
+
+SkGlobals::Rec* SkGlobals::Find(uint32_t tag, Rec* (*create_proc)())
+{
+    SkGlobals::BootStrap&   bootstrap = SkGlobals::GetBootStrap();
+
+    Rec* rec = bootstrap.fHead;
+    while (rec)
+    {
+        if (rec->fTag == tag)
+            return rec;
+        rec = rec->fNext;
+    }
+
+    if (create_proc == NULL) // no create proc, just return not found
+        return NULL;
+
+    // if we get here, we may need to create one. First grab the mutex, and
+    // search again, creating one if its not found the 2nd time.
+
+    bootstrap.fMutex.acquire();
+
+    // search again, now that we have the mutex. Odds are it won't be there, but we check again
+    // just in case it was added by another thread before we grabbed the mutex
+
+    Rec*& head = bootstrap.fHead;
+    rec = head;
+    while (rec)
+    {
+        if (rec->fTag == tag)
+            break;
+        rec = rec->fNext;
+    }
+
+    if (rec == NULL && (rec = create_proc()) != NULL)
+    {
+        rec->fTag = tag;
+        rec->fNext = head;
+        bootstrap.fHead = rec;
+    }
+
+    bootstrap.fMutex.release();
+    return rec;
+}
+
+void SkGlobals::Init()
+{
+}
+
+void SkGlobals::Term()
+{
+    SkGlobals::BootStrap&   bootstrap = SkGlobals::GetBootStrap();
+
+    bootstrap.fMutex.acquire();
+
+    Rec*&   head = bootstrap.fHead;
+    Rec*    rec = head;
+
+    while (rec)
+    {
+        Rec* next = rec->fNext;
+        SkDELETE(rec);
+        rec = next;
+    }
+
+    bootstrap.fHead = NULL;
+    bootstrap.fMutex.release();
+}
+
+
diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp
new file mode 100644
index 0000000..6b214df
--- /dev/null
+++ b/src/core/SkGlyphCache.cpp
@@ -0,0 +1,662 @@
+/* libs/graphics/sgl/SkGlyphCache.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 "SkGlyphCache.h"
+#include "SkFontHost.h"
+#include "SkPaint.h"
+#include "SkTemplates.h"
+
+#define SPEW_PURGE_STATUS
+//#define USE_CACHE_HASH
+//#define RECORD_HASH_EFFICIENCY
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef RECORD_HASH_EFFICIENCY
+    static uint32_t gHashSuccess;
+    static uint32_t gHashCollision;
+
+    static void RecordHashSuccess() {
+        gHashSuccess += 1;
+    }
+
+    static void RecordHashCollisionIf(bool pred) {
+        if (pred) {
+            gHashCollision += 1;
+            
+            uint32_t total = gHashSuccess + gHashCollision;
+            SkDebugf("Font Cache Hash success rate: %d%%\n",
+                     100 * gHashSuccess / total);
+        }
+    }
+#else
+    #define RecordHashSuccess() (void)0
+    #define RecordHashCollisionIf(pred) (void)0
+#endif
+#define RecordHashCollision() RecordHashCollisionIf(true)
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define kMinGlphAlloc       (sizeof(SkGlyph) * 64)
+#define kMinImageAlloc      (24 * 64)   // should be pointsize-dependent
+
+#define METRICS_RESERVE_COUNT  128  // so we don't grow this array a lot
+
+SkGlyphCache::SkGlyphCache(const SkDescriptor* desc)
+        : fGlyphAlloc(kMinGlphAlloc), fImageAlloc(kMinImageAlloc) {
+    fPrev = fNext = NULL;
+
+    fDesc = desc->copy();
+    fScalerContext = SkScalerContext::Create(desc);
+    fScalerContext->getFontMetrics(NULL, &fFontMetricsY);
+
+    // init to 0 so that all of the pointers will be null
+    memset(fGlyphHash, 0, sizeof(fGlyphHash));
+    // init with 0xFF so that the charCode field will be -1, which is invalid
+    memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
+    
+    fMemoryUsed = sizeof(*this) + kMinGlphAlloc + kMinImageAlloc;
+    
+    fGlyphArray.setReserve(METRICS_RESERVE_COUNT);
+
+    fMetricsCount = 0;
+    fAdvanceCount = 0;
+    fAuxProcList = NULL;
+}
+
+SkGlyphCache::~SkGlyphCache() {
+    SkGlyph**   gptr = fGlyphArray.begin();
+    SkGlyph**   stop = fGlyphArray.end();
+    while (gptr < stop) {
+        SkPath* path = (*gptr)->fPath;
+        if (path) {
+            SkDELETE(path);
+        }
+        gptr += 1;
+    }
+    SkDescriptor::Free(fDesc);
+    SkDELETE(fScalerContext);
+    this->invokeAndRemoveAuxProcs();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+class AutoCheckForNull {
+public:
+    AutoCheckForNull(const SkTDArray<SkGlyph*>& array) : fArray(array) {
+        for (int i = 0; i < array.count(); i++)
+            SkASSERT(array[i]);
+    }
+    ~AutoCheckForNull() {
+        const SkTDArray<SkGlyph*>& array = fArray;
+        for (int i = 0; i < array.count(); i++) {
+            SkASSERT(array[i]);
+        }
+    }
+private:
+    const SkTDArray<SkGlyph*>& fArray;
+};
+#define VALIDATE()  AutoCheckForNull acfn(fGlyphArray)
+#else
+#define VALIDATE()
+#endif
+
+uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(charCode);
+    const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
+    
+    if (rec.fID == id) {
+        return rec.fGlyph->getGlyphID();
+    } else {
+        return fScalerContext->charToGlyphID(charCode);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(charCode);
+    CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
+    
+    if (rec->fID != id) {
+        // this ID is based on the UniChar
+        rec->fID = id;
+        // this ID is based on the glyph index
+        id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
+        rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
+    }
+    return *rec->fGlyph;
+}
+
+const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(glyphID);
+    unsigned index = ID2HashIndex(id);
+    SkGlyph* glyph = fGlyphHash[index];
+
+    if (NULL == glyph || glyph->fID != id) {
+        glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
+        fGlyphHash[index] = glyph;
+    }
+    return *glyph;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(charCode);
+    CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
+    
+    if (rec->fID != id) {
+        RecordHashCollisionIf(rec->fGlyph != NULL);
+        // this ID is based on the UniChar
+        rec->fID = id;
+        // this ID is based on the glyph index
+        id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
+        rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
+    } else {
+        RecordHashSuccess();
+        if (rec->fGlyph->isJustAdvance()) {
+            fScalerContext->getMetrics(rec->fGlyph);
+        }
+    }
+    SkASSERT(rec->fGlyph->isFullMetrics());
+    return *rec->fGlyph;
+}
+
+const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
+                                               SkFixed x, SkFixed y) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(charCode, x, y);
+    CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
+    
+    if (rec->fID != id) {
+        RecordHashCollisionIf(rec->fGlyph != NULL);
+        // this ID is based on the UniChar
+        rec->fID = id;
+        // this ID is based on the glyph index
+        id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
+        rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
+    } else {
+        RecordHashSuccess();
+        if (rec->fGlyph->isJustAdvance()) {
+            fScalerContext->getMetrics(rec->fGlyph);
+        }
+    }
+    SkASSERT(rec->fGlyph->isFullMetrics());
+    return *rec->fGlyph;
+}
+
+const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(glyphID);
+    unsigned index = ID2HashIndex(id);
+    SkGlyph* glyph = fGlyphHash[index];
+    
+    if (NULL == glyph || glyph->fID != id) {
+        RecordHashCollisionIf(glyph != NULL);
+        glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
+        fGlyphHash[index] = glyph;
+    } else {
+        RecordHashSuccess();
+        if (glyph->isJustAdvance()) {
+            fScalerContext->getMetrics(glyph);
+        }
+    }
+    SkASSERT(glyph->isFullMetrics());
+    return *glyph;
+}
+
+const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
+                                               SkFixed x, SkFixed y) {
+    VALIDATE();
+    uint32_t id = SkGlyph::MakeID(glyphID, x, y);
+    unsigned index = ID2HashIndex(id);
+    SkGlyph* glyph = fGlyphHash[index];
+
+    if (NULL == glyph || glyph->fID != id) {
+        RecordHashCollisionIf(glyph != NULL);
+        glyph = this->lookupMetrics(id, kFull_MetricsType);
+        fGlyphHash[index] = glyph;
+    } else {
+        RecordHashSuccess();
+        if (glyph->isJustAdvance()) {
+            fScalerContext->getMetrics(glyph);
+        }
+    }
+    SkASSERT(glyph->isFullMetrics());
+    return *glyph;
+}
+
+SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
+    SkGlyph* glyph;
+
+    int     hi = 0;
+    int     count = fGlyphArray.count();
+
+    if (count) {
+        SkGlyph**   gptr = fGlyphArray.begin();
+        int     lo = 0;
+
+        hi = count - 1;
+        while (lo < hi) {
+            int mid = (hi + lo) >> 1;
+            if (gptr[mid]->fID < id) {
+                lo = mid + 1;
+            } else {
+                hi = mid;
+            }
+        }
+        glyph = gptr[hi];
+        if (glyph->fID == id) {
+            if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
+                fScalerContext->getMetrics(glyph);
+            }
+            return glyph;
+        }
+
+        // check if we need to bump hi before falling though to the allocator
+        if (glyph->fID < id) {
+            hi += 1;
+        }
+    }
+
+    // not found, but hi tells us where to inser the new glyph
+    fMemoryUsed += sizeof(SkGlyph);
+
+    glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
+                                        SkChunkAlloc::kThrow_AllocFailType);
+    glyph->fID = id;
+    glyph->fImage = NULL;
+    glyph->fPath = NULL;
+    *fGlyphArray.insert(hi) = glyph;
+    
+    if (kJustAdvance_MetricsType == mtype) {
+        fScalerContext->getAdvance(glyph);
+        fAdvanceCount += 1;
+    } else {
+        SkASSERT(kFull_MetricsType == mtype);
+        fScalerContext->getMetrics(glyph);
+        fMetricsCount += 1;
+    }
+
+    return glyph;
+}
+
+const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
+    if (glyph.fWidth) {
+        if (glyph.fImage == NULL) {
+            size_t  size = glyph.computeImageSize();
+            const_cast<SkGlyph&>(glyph).fImage = fImageAlloc.alloc(size,
+                                        SkChunkAlloc::kReturnNil_AllocFailType);
+            fScalerContext->getImage(glyph);
+            fMemoryUsed += size;
+        }
+    }
+    return glyph.fImage;
+}
+
+const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
+    if (glyph.fWidth) {
+        if (glyph.fPath == NULL) {
+            const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
+            fScalerContext->getPath(glyph, glyph.fPath);
+            fMemoryUsed += sizeof(SkPath) +
+                    glyph.fPath->getPoints(NULL, 0x7FFFFFFF) * sizeof(SkPoint);
+        }
+    }
+    return glyph.fPath;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
+    const AuxProcRec* rec = fAuxProcList;
+    while (rec) {
+        if (rec->fProc == proc) {
+            if (dataPtr) {
+                *dataPtr = rec->fData;
+            }
+            return true;
+        }
+        rec = rec->fNext;
+    }
+    return false;
+}
+
+void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
+    if (proc == NULL) {
+        return;
+    }
+
+    AuxProcRec* rec = fAuxProcList;
+    while (rec) {
+        if (rec->fProc == proc) {
+            rec->fData = data;
+            return;
+        }
+        rec = rec->fNext;
+    }
+    // not found, create a new rec
+    rec = SkNEW(AuxProcRec);
+    rec->fProc = proc;
+    rec->fData = data;
+    rec->fNext = fAuxProcList;
+    fAuxProcList = rec;
+}
+
+void SkGlyphCache::removeAuxProc(void (*proc)(void*)) {
+    AuxProcRec* rec = fAuxProcList;
+    AuxProcRec* prev = NULL;
+    while (rec) {
+        AuxProcRec* next = rec->fNext;
+        if (rec->fProc == proc) {
+            if (prev) {
+                prev->fNext = next;
+            } else {
+                fAuxProcList = next;
+            }
+            SkDELETE(rec);
+            return;
+        }
+        prev = rec;
+        rec = next;
+    }
+}
+
+void SkGlyphCache::invokeAndRemoveAuxProcs() {
+    AuxProcRec* rec = fAuxProcList;
+    while (rec) {
+        rec->fProc(rec->fData);
+        AuxProcRec* next = rec->fNext;
+        SkDELETE(rec);
+        rec = next;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGlobals.h"
+#include "SkThread.h"
+
+#define SkGlyphCache_GlobalsTag     SkSetFourByteTag('g', 'l', 'f', 'c')
+
+#ifdef USE_CACHE_HASH
+    #define HASH_BITCOUNT   6
+    #define HASH_COUNT      (1 << HASH_BITCOUNT)
+    #define HASH_MASK       (HASH_COUNT - 1)
+    
+    static unsigned desc_to_hashindex(const SkDescriptor* desc)
+    {
+        SkASSERT(HASH_MASK < 256);  // since our munging reduces to 8 bits
+
+        uint32_t n = *(const uint32_t*)desc;    //desc->getChecksum();
+        SkASSERT(n == desc->getChecksum());
+
+        // don't trust that the low bits of checksum vary enough, so...
+        n ^= (n >> 24) ^ (n >> 16) ^ (n >> 8) ^ (n >> 30);
+
+        return n & HASH_MASK;
+    }
+#endif
+
+class SkGlyphCache_Globals : public SkGlobals::Rec {
+public:
+    SkMutex         fMutex;
+    SkGlyphCache*   fHead;
+    size_t          fTotalMemoryUsed;
+#ifdef USE_CACHE_HASH
+    SkGlyphCache*   fHash[HASH_COUNT];
+#endif
+
+#ifdef SK_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+};
+
+#ifdef SK_USE_RUNTIME_GLOBALS
+    static SkGlobals::Rec* create_globals() {
+        SkGlyphCache_Globals* rec = SkNEW(SkGlyphCache_Globals);
+        rec->fHead = NULL;
+        rec->fTotalMemoryUsed = 0;
+#ifdef USE_CACHE_HASH
+        memset(rec->fHash, 0, sizeof(rec->fHash));
+#endif
+        return rec;
+    }
+
+    #define FIND_GC_GLOBALS()   *(SkGlyphCache_Globals*)SkGlobals::Find(SkGlyphCache_GlobalsTag, create_globals)
+    #define GET_GC_GLOBALS()    *(SkGlyphCache_Globals*)SkGlobals::Get(SkGlyphCache_GlobalsTag)
+#else
+    static SkGlyphCache_Globals gGCGlobals;
+    #define FIND_GC_GLOBALS()   gGCGlobals
+    #define GET_GC_GLOBALS()    gGCGlobals
+#endif
+
+void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
+                                  void* context) {
+    SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
+    SkAutoMutexAcquire    ac(globals.fMutex);
+    SkGlyphCache*         cache;
+    
+    globals.validate();
+    
+    for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
+        if (proc(cache, context)) {
+            break;
+        }
+    }
+
+    globals.validate();
+}
+
+/*  This guy calls the visitor from within the mutext lock, so the visitor
+    cannot:
+    - take too much time
+    - try to acquire the mutext again
+    - call a fontscaler (which might call into the cache)
+*/
+SkGlyphCache* SkGlyphCache::VisitCache(const SkDescriptor* desc,
+                              bool (*proc)(const SkGlyphCache*, void*),
+                              void* context) {
+    SkASSERT(desc);
+
+    SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
+    SkAutoMutexAcquire    ac(globals.fMutex);
+    SkGlyphCache*         cache;
+    bool                  insideMutex = true;
+
+    globals.validate();
+
+#ifdef USE_CACHE_HASH
+    SkGlyphCache** hash = globals.fHash;
+    unsigned index = desc_to_hashindex(desc);
+    cache = hash[index];
+    if (cache && *cache->fDesc == *desc) {
+        cache->detach(&globals.fHead);
+        goto FOUND_IT;
+    }
+#endif
+
+    for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
+        if (cache->fDesc->equals(*desc)) {
+            cache->detach(&globals.fHead);
+            goto FOUND_IT;
+        }
+    }
+
+    /* Release the mutex now, before we create a new entry (which might have
+        side-effects like trying to access the cache/mutex (yikes!)
+    */
+    ac.release();           // release the mutex now
+    insideMutex = false;    // can't use globals anymore
+
+    cache = SkNEW_ARGS(SkGlyphCache, (desc));
+
+FOUND_IT:
+    if (proc(cache, context)) {   // stay detached
+        if (insideMutex) {
+            SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed);
+            globals.fTotalMemoryUsed -= cache->fMemoryUsed;
+#ifdef USE_CACHE_HASH
+            hash[index] = NULL;
+#endif
+        }
+    } else {                        // reattach
+        if (insideMutex) {
+            cache->attachToHead(&globals.fHead);
+#ifdef USE_CACHE_HASH
+            hash[index] = cache;
+#endif
+        } else {
+            AttachCache(cache);
+        }
+        cache = NULL;
+    }
+    return cache;
+}
+
+void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
+    SkASSERT(cache);
+    SkASSERT(cache->fNext == NULL);
+
+    SkGlyphCache_Globals& globals = GET_GC_GLOBALS();
+    SkAutoMutexAcquire    ac(globals.fMutex);
+
+    globals.validate();
+
+    // if we have a fixed budget for our cache, do a purge here
+    {
+        size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed;
+        size_t amountToFree = SkFontHost::ShouldPurgeFontCache(allocated);
+        if (amountToFree)
+            (void)InternalFreeCache(&globals, amountToFree);
+    }
+
+    cache->attachToHead(&globals.fHead);
+    globals.fTotalMemoryUsed += cache->fMemoryUsed;
+
+#ifdef USE_CACHE_HASH
+    unsigned index = desc_to_hashindex(cache->fDesc);
+    SkASSERT(globals.fHash[index] != cache);
+    globals.fHash[index] = cache;
+#endif
+
+    globals.validate();
+}
+
+size_t SkGlyphCache::GetCacheUsed() {
+    SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
+    SkAutoMutexAcquire  ac(globals.fMutex);
+    
+    return SkGlyphCache::ComputeMemoryUsed(globals.fHead);
+}
+
+bool SkGlyphCache::SetCacheUsed(size_t bytesUsed) {
+    size_t curr = SkGlyphCache::GetCacheUsed();
+
+    if (curr > bytesUsed) {
+        SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
+        SkAutoMutexAcquire  ac(globals.fMutex);
+    
+        return InternalFreeCache(&globals, curr - bytesUsed) > 0;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) {
+    if (cache) {
+        while (cache->fNext) {
+            cache = cache->fNext;
+        }
+    }
+    return cache;
+}
+
+size_t SkGlyphCache::ComputeMemoryUsed(const SkGlyphCache* head) {
+    size_t size = 0;
+    
+    while (head != NULL) {
+        size += head->fMemoryUsed;
+        head = head->fNext;
+    }
+    return size;
+}
+
+#ifdef SK_DEBUG
+void SkGlyphCache_Globals::validate() const {
+    size_t computed = SkGlyphCache::ComputeMemoryUsed(fHead);
+    if (fTotalMemoryUsed != computed) {
+        printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed);
+    }
+    SkASSERT(fTotalMemoryUsed == computed);
+}
+#endif
+
+size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals,
+                                       size_t bytesNeeded) {
+    globals->validate();
+
+    size_t  bytesFreed = 0;
+    int     count = 0;
+
+    // don't do any "small" purges
+    size_t minToPurge = globals->fTotalMemoryUsed >> 2;
+    if (bytesNeeded < minToPurge)
+        bytesNeeded = minToPurge;
+
+    SkGlyphCache* cache = FindTail(globals->fHead);
+    while (cache != NULL && bytesFreed < bytesNeeded) {
+        SkGlyphCache* prev = cache->fPrev;
+        bytesFreed += cache->fMemoryUsed;
+
+#ifdef USE_CACHE_HASH
+        unsigned index = desc_to_hashindex(cache->fDesc);
+        if (cache == globals->fHash[index]) {
+            globals->fHash[index] = NULL;
+        }
+#endif
+
+        cache->detach(&globals->fHead);
+        SkDELETE(cache);
+        cache = prev;
+        count += 1;
+    }
+
+    SkASSERT(bytesFreed <= globals->fTotalMemoryUsed);
+    globals->fTotalMemoryUsed -= bytesFreed;
+    globals->validate();
+
+#ifdef SPEW_PURGE_STATUS
+    if (count) {
+        SkDebugf("purging %dK from font cache [%d entries]\n",
+                 (int)(bytesFreed >> 10), count);
+    }
+#endif
+
+    return bytesFreed;
+}
+
diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h
new file mode 100644
index 0000000..2462ea5
--- /dev/null
+++ b/src/core/SkGlyphCache.h
@@ -0,0 +1,274 @@
+/* libs/graphics/sgl/SkGlyphCache.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 SkGlyphCache_DEFINED
+#define SkGlyphCache_DEFINED
+
+#include "SkBitmap.h"
+#include "SkChunkAlloc.h"
+#include "SkDescriptor.h"
+#include "SkScalerContext.h"
+#include "SkTemplates.h"
+
+class SkPaint;
+
+class SkGlyphCache_Globals;
+
+/** \class SkGlyphCache
+
+    This class represents a strike: a specific combination of typeface, size,
+    matrix, etc., and holds the glyphs for that strike. Calling any of the
+    getUnichar.../getGlyphID... methods will return the requested glyph,
+    either instantly if it is already cahced, or by first generating it and then
+    adding it to the strike.
+
+    The strikes are held in a global list, available to all threads. To interact
+    with one, call either VisitCache() or DetachCache().
+*/
+class SkGlyphCache {
+public:
+    /** Returns a glyph with valid fAdvance and fDevKern fields.
+        The remaining fields may be valid, but that is not guaranteed. If you
+        require those, call getUnicharMetrics or getGlyphIDMetrics instead.
+    */
+    const SkGlyph& getUnicharAdvance(SkUnichar);
+    const SkGlyph& getGlyphIDAdvance(uint16_t);
+    
+    /** Returns a glyph with all fields valid except fImage and fPath, which
+        may be null. If they are null, call findImage or findPath for those.
+        If they are not null, then they are valid.
+        
+        This call is potentially slower than the matching ...Advance call. If
+        you only need the fAdvance/fDevKern fields, call those instead.
+    */
+    const SkGlyph& getUnicharMetrics(SkUnichar);
+    const SkGlyph& getGlyphIDMetrics(uint16_t);
+    
+    /** These are variants that take the device position of the glyph. Call
+        these only if you are drawing in subpixel mode. Passing 0, 0 is
+        effectively the same as calling the variants w/o the extra params, tho
+        a tiny bit slower.
+    */
+    const SkGlyph& getUnicharMetrics(SkUnichar, SkFixed x, SkFixed y);
+    const SkGlyph& getGlyphIDMetrics(uint16_t, SkFixed x, SkFixed y);
+    
+    /** Return the glyphID for the specified Unichar. If the char has already
+        been seen, use the existing cache entry. If not, ask the scalercontext
+        to compute it for us.
+    */
+    uint16_t unicharToGlyph(SkUnichar);
+    
+    /** Return the image associated with the glyph. If it has not been generated
+        this will trigger that.
+    */
+    const void* findImage(const SkGlyph&);
+    /** Return the Path associated with the glyph. If it has not been generated
+        this will trigger that.
+    */
+    const SkPath* findPath(const SkGlyph&);
+
+    /** Return the vertical metrics for this strike.
+    */
+    const SkPaint::FontMetrics& getFontMetricsY() const {
+        return fFontMetricsY;
+    }
+    
+    /*  AuxProc/Data allow a client to associate data with this cache entry.
+        Multiple clients can use this, as their data is keyed with a function
+        pointer. In addition to serving as a key, the function pointer is called
+        with the data when the glyphcache object is deleted, so the client can
+        cleanup their data as well. NOTE: the auxProc must not try to access
+        this glyphcache in any way, since it may be in the process of being
+        deleted.
+    */
+    
+    //! If the proc is found, return true and set *dataPtr to its data
+    bool getAuxProcData(void (*auxProc)(void*), void** dataPtr) const;
+    //! Add a proc/data pair to the glyphcache. proc should be non-null
+    void setAuxProc(void (*auxProc)(void*), void* auxData);
+    //! If found, remove the proc/data pair from the glyphcache (does not
+    //  call the proc)
+    void removeAuxProc(void (*auxProc)(void*));
+
+    /** Call proc on all cache entries, stopping early if proc returns true.
+        The proc should not create or delete caches, since it could produce
+        deadlock.
+    */
+    static void VisitAllCaches(bool (*proc)(SkGlyphCache*, void*), void* ctx);
+    
+    /** Find a matching cache entry, and call proc() with it. If none is found
+        create a new one. If the proc() returns true, detach the cache and
+        return it, otherwise leave it and return NULL.
+    */
+    static SkGlyphCache* VisitCache(const SkDescriptor* desc,
+                                    bool (*proc)(const SkGlyphCache*, void*),
+                                    void* context);
+    
+    /** Given a strike that was returned by either VisitCache() or DetachCache()
+        add it back into the global cache list (after which the caller should
+        not reference it anymore.
+    */
+    static void AttachCache(SkGlyphCache*);
+
+    /** Detach a strike from the global cache matching the specified descriptor.
+        Once detached, it can be queried/modified by the current thread, and
+        when finished, be reattached to the global cache with AttachCache().
+        While detached, if another request is made with the same descriptor,
+        a different strike will be generated. This is fine. It does mean we
+        can have more than 1 strike for the same descriptor, but that will
+        eventually get purged, and the win is that different thread will never
+        block each other while a strike is being used.
+    */
+    static SkGlyphCache* DetachCache(const SkDescriptor* desc) {
+        return VisitCache(desc, DetachProc, NULL);
+    }
+
+    /** Return the approximate number of bytes used by the font cache
+    */
+    static size_t GetCacheUsed();
+
+    /** This can be called to purge old font data, in an attempt to free
+        enough bytes such that the font cache is not using more than the
+        specified number of bytes. It is thread-safe, and may be called at
+        any time.
+        Return true if some amount of the cache was purged.
+    */
+    static bool SetCacheUsed(size_t bytesUsed);
+
+private:
+    SkGlyphCache(const SkDescriptor*);
+    ~SkGlyphCache();
+    
+    enum MetricsType {
+        kJustAdvance_MetricsType,
+        kFull_MetricsType
+    };
+
+    SkGlyph* lookupMetrics(uint32_t id, MetricsType);
+    static bool DetachProc(const SkGlyphCache*, void*) { return true; }
+
+    void detach(SkGlyphCache** head) {
+        if (fPrev) {
+            fPrev->fNext = fNext;
+        } else {
+            *head = fNext;
+        }
+        if (fNext) {
+            fNext->fPrev = fPrev;
+        }
+        fPrev = fNext = NULL;
+    }
+    
+    void attachToHead(SkGlyphCache** head) {
+        SkASSERT(NULL == fPrev && NULL == fNext);
+        if (*head) {
+            (*head)->fPrev = this;
+            fNext = *head;
+        }
+        *head = this;
+    }
+
+    SkGlyphCache*       fNext, *fPrev;
+    SkDescriptor*       fDesc;
+    SkScalerContext*    fScalerContext;
+    SkPaint::FontMetrics fFontMetricsY;
+
+    enum {
+        kHashBits   = 8,
+        kHashCount  = 1 << kHashBits,
+        kHashMask   = kHashCount - 1
+    };
+    SkGlyph*            fGlyphHash[kHashCount];
+    SkTDArray<SkGlyph*> fGlyphArray;
+    SkChunkAlloc        fGlyphAlloc;
+    SkChunkAlloc        fImageAlloc;
+    
+    int fMetricsCount, fAdvanceCount;
+
+    struct CharGlyphRec {
+        uint32_t    fID;    // unichar + subpixel
+        SkGlyph*    fGlyph;
+    };
+    // no reason to use the same kHashCount as fGlyphHash, but we do for now
+    CharGlyphRec    fCharToGlyphHash[kHashCount];
+    
+    enum {
+        // shift so that the top bits fall into kHashBits region
+        kShiftForHashIndex = SkGlyph::kSubShift +
+                             SkGlyph::kSubBits*2 -
+                             kHashBits
+    };
+
+    static inline unsigned ID2HashIndex(uint32_t id) {
+        return (id ^ (id >> kShiftForHashIndex)) & kHashMask;
+    }
+    
+    // used to track (approx) how much ram is tied-up in this cache
+    size_t  fMemoryUsed;
+
+    struct AuxProcRec {
+        AuxProcRec* fNext;
+        void (*fProc)(void*);
+        void* fData;
+    };
+    AuxProcRec* fAuxProcList;
+    void invokeAndRemoveAuxProcs();
+
+    // This relies on the caller to have already acquired the mutex to access the global cache
+    static size_t InternalFreeCache(SkGlyphCache_Globals*, size_t bytesNeeded);
+
+    inline static SkGlyphCache* FindTail(SkGlyphCache* head);
+    static size_t ComputeMemoryUsed(const SkGlyphCache* head);
+
+    friend class SkGlyphCache_Globals;
+};
+
+class SkAutoGlyphCache {
+public:
+    SkAutoGlyphCache(SkGlyphCache* cache) : fCache(cache) {}
+    SkAutoGlyphCache(const SkDescriptor* desc)
+    {
+        fCache = SkGlyphCache::DetachCache(desc);
+    }
+    SkAutoGlyphCache(const SkPaint& paint, const SkMatrix* matrix)
+    {
+        fCache = paint.detachCache(matrix);
+    }
+    ~SkAutoGlyphCache()
+    {
+        if (fCache)
+            SkGlyphCache::AttachCache(fCache);
+    }
+
+    SkGlyphCache*   getCache() const { return fCache; }
+
+    void release()
+    {
+        if (fCache)
+        {
+            SkGlyphCache::AttachCache(fCache);
+            fCache = NULL;
+        }
+    }
+private:
+    SkGlyphCache*   fCache;
+    
+    static bool DetachProc(const SkGlyphCache*, void*);
+};
+
+#endif
+
diff --git a/src/core/SkGraphics.cpp b/src/core/SkGraphics.cpp
new file mode 100644
index 0000000..64fbab9
--- /dev/null
+++ b/src/core/SkGraphics.cpp
@@ -0,0 +1,526 @@
+/* libs/graphics/sgl/SkGraphics.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 "SkGraphics.h"
+
+#include "Sk64.h"
+#include "SkBlitter.h"
+#include "SkCanvas.h"
+#include "SkFloat.h"
+#include "SkGeometry.h"
+#include "SkGlobals.h"
+#include "SkMath.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkRefCnt.h"
+#include "SkScalerContext.h"
+#include "SkShader.h"
+#include "SkStream.h"
+#include "SkTSearch.h"
+#include "SkTime.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+#if 0
+
+#define SK_SORT_TEMPLATE_TYPE       int
+#define SK_SORT_TEMPLATE_NAME       sort_int
+#define SK_SORT_TEMPLATE_CMP(a, b)   ((a) - (b))
+#include "SkSortTemplate.h"
+
+#define SK_SORT_TEMPLATE_TYPE       int*
+#define SK_SORT_TEMPLATE_NAME       sort_intptr
+#define SK_SORT_TEMPLATE_CMP(a, b)   (*(a) - *(b))
+#include "SkSortTemplate.h"
+
+static void test_sort()
+{
+    int array[] = { 4, 3, 7, 5, 2, 5, 1, 2, 9, 6, 7, 4, 5, 3, 1, 0 };
+    int* ptr[SK_ARRAY_COUNT(array)];
+    int i, N = SK_ARRAY_COUNT(array) - 1;
+
+    for (i = 0; i < N; i++)
+        printf(" %d", array[i]);
+    printf("\n");
+    
+    for (i = 0; i < N; i++)
+        ptr[i] = &array[i];
+    sort_intptr(ptr, N);
+    for (i = 0; i < N; i++)
+        printf(" %d", *ptr[i]);
+    printf("\n");
+
+    sort_int(array, N);
+    for (i = 0; i < N; i++)
+        printf(" %d", array[i]);
+    printf("\n");
+
+}
+#endif
+
+#define SPEED_TESTx
+
+#define typesizeline(type)  { #type , sizeof(type) }
+#define unittestline(type)  { #type , type::UnitTest }
+
+
+#ifdef BUILD_EMBOSS_TABLE
+    extern void SkEmbossMask_BuildTable();
+#endif
+
+#ifdef BUILD_RADIALGRADIENT_TABLE
+    extern void SkRadialGradient_BuildTable();
+#endif
+
+#define BIG_LOOP_COUNT  1000000
+#define TEXT_LOOP_COUNT 1000
+
+#ifdef SPEED_TEST
+static int test_s64(int i)
+{
+    Sk64    a, b, c;
+    
+    c.set(0);
+    a.set(i);
+    b.setMul(i, i);
+    a.add(b);
+    a.add(c);
+    return c.getFixed();
+}
+
+static int test_native_64(int i)
+{
+    int16_t    a, b, c;
+    
+    c = 0;
+    a = i;
+    b = (int64_t)i * i;
+    a += b;
+    a += c;
+    return (int)(c >> 16);
+}
+
+static void test_drawText(SkBitmap::Config config, SkColor color)
+{
+    SkBitmap    bm;
+    
+    bm.setConfig(config, 320, 240);
+    bm.allocPixels();
+    
+    SkCanvas canvas(bm);
+    SkPaint  paint;
+    
+    paint.setAntiAlias(true);
+    paint.setTextSize(SkIntToScalar(12));
+    paint.setColor(color);
+    
+    SkScalar x = SkIntToScalar(20);
+    SkScalar y = SkIntToScalar(100);
+    const char* text = "Hamburgefons";
+    size_t      len = strlen(text);
+
+    // draw once to populate the cache
+    canvas.drawText(text, len, x, y, paint);
+    
+    SkMSec now = SkTime::GetMSecs();
+    for (int i = 0; i < TEXT_LOOP_COUNT; i++)
+        canvas.drawText(text, len, x, y, paint);
+    printf("----------- Config: %d, Color=%x, CPS = %g\n", config, color,
+           len * TEXT_LOOP_COUNT * 1000.0 / (SkTime::GetMSecs() - now));
+}
+
+#endif
+
+#include "SkFloatBits.h"
+
+static inline float fast_inc(float x) {
+    SkFloatIntUnion data;
+    data.fFloat = x;
+    data.fSignBitInt += 1;
+    return data.fFloat;
+}
+
+extern float dummy();
+int time_math() {
+    SkMSec now;
+    int i;
+    int sum = 0;
+    const int repeat = 1000000;
+    float f;
+
+    f = dummy();
+    now = SkTime::GetMSecs();
+    for (i = repeat - 1; i >= 0; --i) {
+        sum += (int)f; f = fast_inc(f);
+        sum += (int)f; f = fast_inc(f);
+        sum += (int)f; f = fast_inc(f);
+        sum += (int)f; f = fast_inc(f);
+    }
+    SkDebugf("---- native cast %d\n", SkTime::GetMSecs() - now);
+
+    f = dummy();
+    now = SkTime::GetMSecs();
+    for (i = repeat - 1; i >= 0; --i) {
+        sum += SkFloatToIntCast(f); f = fast_inc(f);
+        sum += SkFloatToIntCast(f); f = fast_inc(f);
+        sum += SkFloatToIntCast(f); f = fast_inc(f);
+        sum += SkFloatToIntCast(f); f = fast_inc(f);
+    }
+    SkDebugf("---- hack cast %d\n", SkTime::GetMSecs() - now);
+
+    f = dummy();
+    now = SkTime::GetMSecs();
+    for (i = repeat - 1; i >= 0; --i) {
+        sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+        sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+        sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+        sum += (int)floorf(f + 0.5f); f = fast_inc(f);
+    }
+    SkDebugf("---- native round %d\n", SkTime::GetMSecs() - now);
+    
+    f = dummy();
+    now = SkTime::GetMSecs();
+    for (i = repeat - 1; i >= 0; --i) {
+        sum += SkFloatToIntRound(f); f = fast_inc(f);
+        sum += SkFloatToIntRound(f); f = fast_inc(f);
+        sum += SkFloatToIntRound(f); f = fast_inc(f);
+        sum += SkFloatToIntRound(f); f = fast_inc(f);
+    }
+    SkDebugf("---- hack round %d\n", SkTime::GetMSecs() - now);
+    
+    f = dummy();
+    now = SkTime::GetMSecs();
+    for (i = repeat - 1; i >= 0; --i) {
+        sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+        sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+        sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+        sum += SkFloat2Bits(floorf(f)); f = fast_inc(f);
+    }
+    SkDebugf("---- native floor %d\n", SkTime::GetMSecs() - now);
+    
+    f = dummy();
+    now = SkTime::GetMSecs();
+    for (i = repeat - 1; i >= 0; --i) {
+        sum += SkFloatToIntFloor(f); f = fast_inc(f);
+        sum += SkFloatToIntFloor(f); f = fast_inc(f);
+        sum += SkFloatToIntFloor(f); f = fast_inc(f);
+        sum += SkFloatToIntFloor(f); f = fast_inc(f);
+    }
+    SkDebugf("---- hack floor %d\n", SkTime::GetMSecs() - now);
+    
+    return sum;
+}
+
+static float time_intToFloat() {
+    const int repeat = 1000000;
+    int i, n;
+    SkMSec now;
+    float sum = 0;
+    
+    n = (int)dummy();
+    now = SkTime::GetMSecs();
+    for (i = repeat - 1; i >= 0; --i) {
+        sum += (float)n; n += 1;
+        sum += (float)n; n += 1;
+        sum += (float)n; n += 1;
+        sum += (float)n; n += 1;
+    }
+    SkDebugf("---- native i2f %d\n", SkTime::GetMSecs() - now);
+    
+    n = (int)dummy();
+    now = SkTime::GetMSecs();
+    for (i = repeat - 1; i >= 0; --i) {
+        sum += SkIntToFloatCast(n); n += 1;
+        sum += SkIntToFloatCast(n); n += 1;
+        sum += SkIntToFloatCast(n); n += 1;
+        sum += SkIntToFloatCast(n); n += 1;
+    }
+    SkDebugf("---- check i2f %d\n", SkTime::GetMSecs() - now);
+
+    n = (int)dummy();
+    now = SkTime::GetMSecs();
+    for (i = repeat - 1; i >= 0; --i) {
+        sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+        sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+        sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+        sum += SkIntToFloatCast_NoOverflowCheck(n); n += 1;
+    }
+    SkDebugf("---- nocheck i2f %d\n", SkTime::GetMSecs() - now);
+
+    return sum;
+}
+
+void SkGraphics::Init(bool runUnitTests)
+{
+    SkGlobals::Init();
+
+//    time_math();
+//    time_intToFloat();
+    
+#ifdef BUILD_EMBOSS_TABLE
+    SkEmbossMask_BuildTable();
+#endif
+#ifdef BUILD_RADIALGRADIENT_TABLE
+    SkRadialGradient_BuildTable();
+#endif
+
+#ifdef SK_SUPPORT_UNITTEST
+    if (runUnitTests == false)
+        return;
+    int i;
+
+    static const struct {
+        const char* fTypeName;
+        size_t      fSizeOf;
+    } gTypeSize[] = {
+        typesizeline(char),
+        typesizeline(short),
+        typesizeline(int),
+        typesizeline(long),
+        typesizeline(size_t),
+        typesizeline(void*),
+
+        typesizeline(S8CPU),
+        typesizeline(U8CPU),
+        typesizeline(S16CPU),
+        typesizeline(U16CPU),
+
+        typesizeline(SkPoint),
+        typesizeline(SkRect),
+        typesizeline(SkMatrix),
+        typesizeline(SkPath),
+        typesizeline(SkGlyph),
+        typesizeline(SkRefCnt),
+
+        typesizeline(SkPaint),
+        typesizeline(SkCanvas),
+        typesizeline(SkBlitter),
+        typesizeline(SkShader),
+        typesizeline(SkXfermode),
+        typesizeline(SkPathEffect)
+    };
+
+#ifdef SK_CPU_BENDIAN
+    SkDebugf("SkGraphics: big-endian\n");
+#else
+    SkDebugf("SkGraphics: little-endian\n");
+#endif
+
+    {
+        char    test = 0xFF;
+        int     itest = test;   // promote to int, see if it sign-extended
+        if (itest < 0)
+            SkDebugf("SkGraphics: char is signed\n");
+        else
+            SkDebugf("SkGraphics: char is unsigned\n");
+    }
+    for (i = 0; i < (int)SK_ARRAY_COUNT(gTypeSize); i++)
+        SkDebugf("SkGraphics: sizeof(%s) = %d\n", gTypeSize[i].fTypeName, gTypeSize[i].fSizeOf);
+
+    static const struct {
+        const char* fTypeName;
+        void (*fUnitTest)();
+    } gUnitTests[] = {
+        unittestline(Sk64),
+        unittestline(SkMath),
+        unittestline(SkUtils),
+        unittestline(SkString),
+        unittestline(SkMatrix),
+        unittestline(SkGeometry),
+        unittestline(SkPath),
+        unittestline(SkPathMeasure),
+        unittestline(SkStream),
+        unittestline(SkWStream),
+    };
+
+    for (i = 0; i < (int)SK_ARRAY_COUNT(gUnitTests); i++)
+    {
+        SkDebugf("SkGraphics: Running UnitTest for %s\n", gUnitTests[i].fTypeName);
+        gUnitTests[i].fUnitTest();
+        SkDebugf("SkGraphics: End UnitTest for %s\n", gUnitTests[i].fTypeName);
+    }
+    SkQSort_UnitTest();
+
+#endif
+
+    if (false)  // test asm fixmul
+    {
+        int j;
+        SkMSec now = SkTime::GetMSecs();
+        for (j = 0; j < BIG_LOOP_COUNT; j++) {
+            (void)SkFixedMul_portable(0x8000, 0x150000);
+        }
+        SkMSec now2 = SkTime::GetMSecs();
+        printf("-------- SkFixedMul_portable = %d\n", now2 - now);
+
+        for (j = 0; j < BIG_LOOP_COUNT; j++) {
+            (void)SkFixedMul(0x8000, 0x150000);
+        }
+        printf("-------- SkFixedMul = %d\n", SkTime::GetMSecs() - now2);
+
+        SkRandom rand;
+        for (j = 0; j < 10000; j++) {
+            SkFixed a = rand.nextS() >> 8;
+            SkFixed b = rand.nextS() >> 8;
+            SkFixed c1 = SkFixedMul_portable(a, b);
+            SkFixed c2 = SkFixedMul(a, b);
+            if (SkAbs32(c1 - c2) > 1)
+                printf("------ FixMul disagreement: (%x %x) slow=%x fast=%x\n", a, b, c1, c2);
+        }
+    }
+    
+    if (false)  // test asm fractmul
+    {
+        int j;
+        SkMSec now = SkTime::GetMSecs();
+        for (j = 0; j < BIG_LOOP_COUNT; j++) {
+            (void)SkFractMul_portable(0x800000, 0x1500000);
+        }
+        SkMSec now2 = SkTime::GetMSecs();
+        printf("-------- SkFractMul_portable = %d\n", now2 - now);
+
+        for (j = 0; j < BIG_LOOP_COUNT; j++) {
+            (void)SkFractMul(0x800000, 0x1500000);
+        }
+        printf("-------- SkFractMul = %d\n", SkTime::GetMSecs() - now2);
+
+        SkRandom rand;
+        for (j = 0; j < 10000; j++) {
+            SkFixed a = rand.nextS() >> 1;
+            SkFixed b = rand.nextS() >> 1;
+            SkFixed c1 = SkFractMul_portable(a, b);
+            SkFixed c2 = SkFractMul(a, b);
+            if (SkAbs32(c1 - c2) > 1)
+                printf("------ FractMul disagreement: (%x %x) slow=%x fast=%x\n", a, b, c1, c2);
+        }
+    }
+    
+    if (false)   // test asm clz
+    {
+        int j;
+        SkMSec now = SkTime::GetMSecs();
+        for (j = 0; j < BIG_LOOP_COUNT; j++) {
+            (void)SkCLZ_portable(now);
+        }
+        SkMSec now2 = SkTime::GetMSecs();
+        printf("-------- SkCLZ_portable = %d\n", now2 - now);
+
+        for (j = 0; j < BIG_LOOP_COUNT; j++) {
+            (void)SkCLZ(now);
+        }
+        printf("-------- SkCLZ = %d\n", SkTime::GetMSecs() - now2);
+
+        SkRandom rand;
+        for (j = 0; j < 10000; j++) {
+            uint32_t a = rand.nextU();
+            int c1 = SkCLZ_portable(a);
+            int c2 = SkCLZ(a);
+            if (c1 != c2)
+                printf("------ CLZ disagreement: (%x) slow=%x fast=%x\n", a, c1, c2);
+        }
+    }
+    
+#ifdef SPEED_TEST
+    if (false) {
+        int i;
+        int (*proc)(int);
+
+        static const struct {
+            int (*proc)(int);
+            const char* name;
+        } gList[] = {
+            { test_s64, "Sk64" },
+            { test_native_64, "native" }
+        };
+
+        for (size_t j = 0; j < SK_ARRAY_COUNT(gList); j++) {
+            SkMSec now = SkTime::GetMSecs();
+            proc = gList[j].proc;
+            for (i = 0; i < BIG_LOOP_COUNT; i++) {
+                proc(i);
+            }
+            printf("-------- %s = %d\n", gList[j].name, SkTime::GetMSecs() - now);
+        }
+    }
+#endif
+
+    if (false) {
+        size_t i, size = 480;
+        char* buffer = (char*)sk_malloc_throw(size);
+        uint16_t* buffer16 = (uint16_t*)buffer;
+        uint32_t* buffer32 = (uint32_t*)buffer;
+
+        SkMSec now = SkTime::GetMSecs();
+        for (i = 0; i < 100000; i++) {
+            sk_memset16(buffer16, (uint16_t)i, size >> 1);
+        }
+        SkMSec now2 = SkTime::GetMSecs();
+        for (i = 0; i < 100000; i++) {
+            sk_memset16_portable(buffer16, (uint16_t)i, size >> 1);
+        }
+        SkMSec now3 = SkTime::GetMSecs();
+        printf("----------- memset16: native %d, portable %d\n", now2 - now, now3 - now2);
+
+        now = SkTime::GetMSecs();
+        for (i = 0; i < 100000; i++) {
+            sk_memset32(buffer32, i, size >> 2);
+        }
+        now2 = SkTime::GetMSecs();
+        for (i = 0; i < 100000; i++) {
+            sk_memset32_portable(buffer32, i, size >> 2);
+        }
+        now3 = SkTime::GetMSecs();
+        printf("----------- memset32: native %d, portable %d\n", now2 - now, now3 - now2);
+        
+        sk_free(buffer);
+    }
+    
+#ifdef SPEED_TEST
+    if (false) {
+        test_drawText(SkBitmap::kARGB_8888_Config, SK_ColorBLACK);
+        test_drawText(SkBitmap::kARGB_8888_Config, SK_ColorRED);
+        test_drawText(SkBitmap::kRGB_565_Config, SK_ColorBLACK);
+        test_drawText(SkBitmap::kRGB_565_Config, SK_ColorRED);
+    }
+#endif
+    
+//    if (true) {
+//        test_sort();
+//    }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+#include "SkGlyphCache.h"
+
+void SkGraphics::Term() {
+    SkGraphics::SetFontCacheUsed(0);
+    SkGlobals::Term();
+}
+
+size_t SkGraphics::GetFontCacheUsed() {
+    return SkGlyphCache::GetCacheUsed();
+}
+
+bool SkGraphics::SetFontCacheUsed(size_t usageInBytes) {
+    return SkGlyphCache::SetCacheUsed(usageInBytes);
+}
+
+float dummy() { return 1.25f; }
diff --git a/src/core/SkMask.cpp b/src/core/SkMask.cpp
new file mode 100644
index 0000000..b237639
--- /dev/null
+++ b/src/core/SkMask.cpp
@@ -0,0 +1,49 @@
+/* libs/graphics/sgl/SkMask.cpp
+**
+** Copyright 2007, 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 "SkMask.h"
+
+size_t SkMask::computeImageSize() const
+{
+    return fBounds.height() * fRowBytes;
+}
+
+size_t SkMask::computeTotalImageSize() const
+{
+    size_t size = this->computeImageSize();
+
+    if (fFormat == SkMask::k3D_Format)
+        size *= 3;
+    return size;
+}
+
+/** We explicitly use this allocator for SkBimap pixels, so that we can
+    freely assign memory allocated by one class to the other.
+*/
+uint8_t* SkMask::AllocImage(size_t size)
+{
+    return (uint8_t*)sk_malloc_throw(SkAlign4(size));
+}
+
+/** We explicitly use this allocator for SkBimap pixels, so that we can
+    freely assign memory allocated by one class to the other.
+*/
+void SkMask::FreeImage(void* image)
+{
+    sk_free(image);
+}
+
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
new file mode 100644
index 0000000..56fff97
--- /dev/null
+++ b/src/core/SkMaskFilter.cpp
@@ -0,0 +1,62 @@
+/* libs/graphics/sgl/SkMaskFilter.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 "SkMaskFilter.h"
+#include "SkBlitter.h"
+#include "SkBounder.h"
+#include "SkBuffer.h"
+#include "SkDraw.h"
+#include "SkRegion.h"
+
+bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&, SkIPoint*)
+{
+    return false;
+}
+
+bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
+                              const SkRegion& clip, SkBounder* bounder,
+                              SkBlitter* blitter)
+{
+    SkMask  srcM, dstM;
+
+    if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
+                            SkMask::kComputeBoundsAndRenderImage_CreateMode))
+    {
+        return false;
+    }
+
+    SkAutoMaskImage autoSrc(&srcM, false);
+
+    if (!this->filterMask(&dstM, srcM, matrix, NULL))
+        return false;
+
+    SkAutoMaskImage         autoDst(&dstM, false);
+    SkRegion::Cliperator    clipper(clip, dstM.fBounds);
+
+    if (!clipper.done() && (bounder == NULL || bounder->doIRect(dstM.fBounds)))
+    {
+        const SkIRect& cr = clipper.rect();
+        do {
+            blitter->blitMask(dstM, cr);
+            clipper.next();
+        } while (!clipper.done());
+    }
+
+    return true;
+}
+
+
diff --git a/src/core/SkMath.cpp b/src/core/SkMath.cpp
new file mode 100644
index 0000000..c627d9b
--- /dev/null
+++ b/src/core/SkMath.cpp
@@ -0,0 +1,932 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkMath.h"
+#include "SkCordic.h"
+#include "SkFloatBits.h"
+#include "SkFloatingPoint.h"
+#include "Sk64.h"
+#include "SkScalar.h"
+
+#ifdef SK_SCALAR_IS_FLOAT
+    const uint32_t gIEEENotANumber = 0x7FFFFFFF;
+    const uint32_t gIEEEInfinity = 0x7F800000;
+#endif
+
+#define sub_shift(zeros, x, n)  \
+    zeros -= n;                 \
+    x >>= n
+    
+int SkCLZ_portable(uint32_t x) {
+    if (x == 0) {
+        return 32;
+    }
+
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    int zeros = 31;
+    if (x & 0xFFFF0000) {
+        sub_shift(zeros, x, 16);
+    }
+    if (x & 0xFF00) {
+        sub_shift(zeros, x, 8);
+    }
+    if (x & 0xF0) {
+        sub_shift(zeros, x, 4);
+    }
+    if (x & 0xC) {
+        sub_shift(zeros, x, 2);
+    }
+    if (x & 0x2) {
+        sub_shift(zeros, x, 1);
+    }
+#else
+    int zeros = ((x >> 16) - 1) >> 31 << 4;
+    x <<= zeros;
+
+    int nonzero = ((x >> 24) - 1) >> 31 << 3;
+    zeros += nonzero;
+    x <<= nonzero;
+
+    nonzero = ((x >> 28) - 1) >> 31 << 2;
+    zeros += nonzero;
+    x <<= nonzero;
+
+    nonzero = ((x >> 30) - 1) >> 31 << 1;
+    zeros += nonzero;
+    x <<= nonzero;
+
+    zeros += (~x) >> 31;
+#endif
+
+    return zeros;
+}
+
+int32_t SkMulDiv(int32_t numer1, int32_t numer2, int32_t denom) {
+    SkASSERT(denom);
+
+    Sk64 tmp;
+    tmp.setMul(numer1, numer2);
+    tmp.div(denom, Sk64::kTrunc_DivOption);
+    return tmp.get32();
+}
+
+int32_t SkMulShift(int32_t a, int32_t b, unsigned shift) {
+    int sign = SkExtractSign(a ^ b);
+
+    if (shift > 63) {
+        return sign;
+    }
+
+    a = SkAbs32(a);
+    b = SkAbs32(b);
+
+    uint32_t ah = a >> 16;
+    uint32_t al = a & 0xFFFF;
+    uint32_t bh = b >> 16;
+    uint32_t bl = b & 0xFFFF;
+
+    uint32_t A = ah * bh;
+    uint32_t B = ah * bl + al * bh;
+    uint32_t C = al * bl;
+
+    /*  [  A  ]
+           [  B  ]
+              [  C  ]
+    */
+    uint32_t lo = C + (B << 16);
+    int32_t  hi = A + (B >> 16) + (lo < C);
+
+    if (sign < 0) {
+        hi = -hi - Sk32ToBool(lo);
+        lo = 0 - lo;
+    }
+
+    if (shift == 0) {
+#ifdef SK_DEBUGx
+        SkASSERT(((int32_t)lo >> 31) == hi);
+#endif
+        return lo;
+    } else if (shift >= 32) {
+        return hi >> (shift - 32);
+    } else {
+#ifdef SK_DEBUGx
+        int32_t tmp = hi >> shift;
+        SkASSERT(tmp == 0 || tmp == -1);
+#endif
+        // we want (hi << (32 - shift)) | (lo >> shift) but rounded
+        int roundBit = (lo >> (shift - 1)) & 1;
+        return ((hi << (32 - shift)) | (lo >> shift)) + roundBit;
+    }
+}
+
+SkFixed SkFixedMul_portable(SkFixed a, SkFixed b) {
+#if 0
+    Sk64    tmp;
+
+    tmp.setMul(a, b);
+    tmp.shiftRight(16);
+    return tmp.fLo;
+#elif defined(SkLONGLONG)
+    return (SkLONGLONG)a * b >> 16;
+#else
+    int sa = SkExtractSign(a);
+    int sb = SkExtractSign(b);
+    // now make them positive
+    a = SkApplySign(a, sa);
+    b = SkApplySign(b, sb);
+
+    uint32_t    ah = a >> 16;
+    uint32_t    al = a & 0xFFFF;
+    uint32_t bh = b >> 16;
+    uint32_t bl = b & 0xFFFF;
+
+    uint32_t R = ah * b + al * bh + (al * bl >> 16);
+
+    return SkApplySign(R, sa ^ sb);
+#endif
+}
+
+SkFract SkFractMul_portable(SkFract a, SkFract b) {
+#if 0
+    Sk64 tmp;
+    tmp.setMul(a, b);
+    return tmp.getFract();
+#elif defined(SkLONGLONG)
+    return (SkLONGLONG)a * b >> 30;
+#else
+    int sa = SkExtractSign(a);
+    int sb = SkExtractSign(b);
+    // now make them positive
+    a = SkApplySign(a, sa);
+    b = SkApplySign(b, sb);
+
+    uint32_t ah = a >> 16;
+    uint32_t al = a & 0xFFFF;
+    uint32_t bh = b >> 16;
+    uint32_t bl = b & 0xFFFF;
+
+    uint32_t A = ah * bh;
+    uint32_t B = ah * bl + al * bh;
+    uint32_t C = al * bl;
+
+    /*  [  A  ]
+           [  B  ]
+              [  C  ]
+    */
+    uint32_t Lo = C + (B << 16);
+    uint32_t Hi = A + (B >>16) + (Lo < C);
+
+    SkASSERT((Hi >> 29) == 0);  // else overflow
+
+    int32_t R = (Hi << 2) + (Lo >> 30);
+
+    return SkApplySign(R, sa ^ sb);
+#endif
+}
+
+int SkFixedMulCommon(SkFixed a, int b, int bias) {
+    // this function only works if b is 16bits
+    SkASSERT(b == (int16_t)b);
+    SkASSERT(b >= 0);
+
+    int sa = SkExtractSign(a);
+    a = SkApplySign(a, sa);
+    uint32_t ah = a >> 16;
+    uint32_t al = a & 0xFFFF;
+    uint32_t R = ah * b + ((al * b + bias) >> 16);
+    return SkApplySign(R, sa);
+}
+
+#ifdef SK_DEBUGx
+    #define TEST_FASTINVERT
+#endif
+
+SkFixed SkFixedFastInvert(SkFixed x) {
+/*  Adapted (stolen) from gglRecip()
+*/
+
+    if (x == SK_Fixed1) {
+        return SK_Fixed1;
+    }
+
+    int      sign = SkExtractSign(x);
+    uint32_t a = SkApplySign(x, sign);
+
+    if (a <= 2) {
+        return SkApplySign(SK_MaxS32, sign);
+    }
+
+#ifdef TEST_FASTINVERT
+    SkFixed orig = a;
+    uint32_t slow = SkFixedDiv(SK_Fixed1, a);
+#endif
+
+    // normalize a
+    int lz = SkCLZ(a);
+    a = a << lz >> 16;
+
+    // compute 1/a approximation (0.5 <= a < 1.0) 
+    uint32_t r = 0x17400 - a;      // (2.90625 (~2.914) - 2*a) >> 1
+
+    // Newton-Raphson iteration:
+    // x = r*(2 - a*r) = ((r/2)*(1 - a*r/2))*4
+    r = ( (0x10000 - ((a*r)>>16)) * r ) >> 15;
+    r = ( (0x10000 - ((a*r)>>16)) * r ) >> (30 - lz);
+
+#ifdef TEST_FASTINVERT
+    SkDebugf("SkFixedFastInvert(%x %g) = %x %g Slow[%x %g]\n",
+                orig, orig/65536.,
+                r, r/65536.,
+                slow, slow/65536.);
+#endif
+
+    return SkApplySign(r, sign);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define DIVBITS_ITER(n)                                 \
+    case n:                                             \
+        if ((numer = (numer << 1) - denom) >= 0)        \
+            result |= 1 << (n - 1); else numer += denom
+            
+int32_t SkDivBits(int32_t numer, int32_t denom, int shift_bias) {
+    SkASSERT(denom != 0);
+    if (numer == 0) {
+        return 0;
+    }
+        
+    // make numer and denom positive, and sign hold the resulting sign
+    int32_t sign = SkExtractSign(numer ^ denom);
+    numer = SkAbs32(numer);
+    denom = SkAbs32(denom);
+
+    int nbits = SkCLZ(numer) - 1;
+    int dbits = SkCLZ(denom) - 1;
+    int bits = shift_bias - nbits + dbits;
+
+    if (bits < 0) {  // answer will underflow
+        return 0;
+    }
+    if (bits > 31) {  // answer will overflow
+        return SkApplySign(SK_MaxS32, sign);
+    }
+
+    denom <<= dbits;
+    numer <<= nbits;
+    
+    SkFixed result = 0;
+    
+    // do the first one
+    if ((numer -= denom) >= 0) {
+        result = 1;
+    } else {
+        numer += denom;
+    }
+    
+    // Now fall into our switch statement if there are more bits to compute
+    if (bits > 0) {
+        // make room for the rest of the answer bits
+        result <<= bits;
+        switch (bits) {
+            DIVBITS_ITER(31); DIVBITS_ITER(30); DIVBITS_ITER(29);
+            DIVBITS_ITER(28); DIVBITS_ITER(27); DIVBITS_ITER(26);
+            DIVBITS_ITER(25); DIVBITS_ITER(24); DIVBITS_ITER(23);
+            DIVBITS_ITER(22); DIVBITS_ITER(21); DIVBITS_ITER(20);
+            DIVBITS_ITER(19); DIVBITS_ITER(18); DIVBITS_ITER(17);
+            DIVBITS_ITER(16); DIVBITS_ITER(15); DIVBITS_ITER(14);
+            DIVBITS_ITER(13); DIVBITS_ITER(12); DIVBITS_ITER(11);
+            DIVBITS_ITER(10); DIVBITS_ITER( 9); DIVBITS_ITER( 8);
+            DIVBITS_ITER( 7); DIVBITS_ITER( 6); DIVBITS_ITER( 5);
+            DIVBITS_ITER( 4); DIVBITS_ITER( 3); DIVBITS_ITER( 2);
+            // we merge these last two together, makes GCC make better ARM
+            default:
+            DIVBITS_ITER( 1);
+        }
+    }
+
+    if (result < 0) {
+        result = SK_MaxS32;
+    }
+    return SkApplySign(result, sign);
+}
+
+/*  mod(float numer, float denom) seems to always return the sign
+    of the numer, so that's what we do too
+*/
+SkFixed SkFixedMod(SkFixed numer, SkFixed denom) {
+    int sn = SkExtractSign(numer);
+    int sd = SkExtractSign(denom);
+
+    numer = SkApplySign(numer, sn);
+    denom = SkApplySign(denom, sd);
+    
+    if (numer < denom) {
+        return SkApplySign(numer, sn);
+    } else if (numer == denom) {
+        return 0;
+    } else {
+        SkFixed div = SkFixedDiv(numer, denom);
+        return SkApplySign(SkFixedMul(denom, div & 0xFFFF), sn);
+    }
+}
+
+/* www.worldserver.com/turk/computergraphics/FixedSqrt.pdf
+*/
+int32_t SkSqrtBits(int32_t x, int count) {
+    SkASSERT(x >= 0 && count > 0 && (unsigned)count <= 30);
+
+    uint32_t    root = 0;
+    uint32_t    remHi = 0;
+    uint32_t    remLo = x;
+
+    do {
+        root <<= 1;
+
+        remHi = (remHi<<2) | (remLo>>30);
+        remLo <<= 2;
+
+        uint32_t testDiv = (root << 1) + 1;
+        if (remHi >= testDiv) {
+            remHi -= testDiv;
+            root++;
+        }
+    } while (--count >= 0);
+
+    return root;
+}
+
+int32_t SkCubeRootBits(int32_t value, int bits) {
+    SkASSERT(bits > 0);
+
+    int sign = SkExtractSign(value);
+    value = SkApplySign(value, sign);
+
+    uint32_t root = 0;
+    uint32_t curr = (uint32_t)value >> 30;
+    value <<= 2;
+
+    do {
+        root <<= 1;
+        uint32_t guess = root * root + root;
+        guess = (guess << 1) + guess;   // guess *= 3
+        if (guess < curr) {
+            curr -= guess + 1;
+            root |= 1;
+        }
+        curr = (curr << 3) | ((uint32_t)value >> 29);
+        value <<= 3;
+    } while (--bits);
+
+    return SkApplySign(root, sign);
+}
+
+SkFixed SkFixedMean(SkFixed a, SkFixed b) {
+    Sk64 tmp;
+    
+    tmp.setMul(a, b);
+    return tmp.getSqrt();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FLOAT
+float SkScalarSinCos(float radians, float* cosValue) {
+    float sinValue = sk_float_sin(radians);
+
+    if (cosValue) {
+        *cosValue = sk_float_cos(radians);
+        if (SkScalarNearlyZero(*cosValue)) {
+            *cosValue = 0;
+        }
+    }
+
+    if (SkScalarNearlyZero(sinValue)) {
+        sinValue = 0;
+    }
+    return sinValue;
+}
+#endif
+
+#define INTERP_SINTABLE
+#define BUILD_TABLE_AT_RUNTIMEx
+
+#define kTableSize  256
+
+#ifdef BUILD_TABLE_AT_RUNTIME
+    static uint16_t gSkSinTable[kTableSize];
+
+    static void build_sintable(uint16_t table[]) {
+        for (int i = 0; i < kTableSize; i++) {
+            double  rad = i * 3.141592653589793 / (2*kTableSize);
+            double  val = sin(rad);
+            int     ival = (int)(val * SK_Fixed1);
+            table[i] = SkToU16(ival);
+        }
+    }
+#else
+    #include "SkSinTable.h"
+#endif
+
+#define SK_Fract1024SizeOver2PI     0x28BE60    /* floatToFract(1024 / 2PI) */
+
+#ifdef INTERP_SINTABLE
+static SkFixed interp_table(const uint16_t table[], int index, int partial255) {
+    SkASSERT((unsigned)index < kTableSize);
+    SkASSERT((unsigned)partial255 <= 255);
+
+    SkFixed lower = table[index];
+    SkFixed upper = (index == kTableSize - 1) ? SK_Fixed1 : table[index + 1];
+
+    SkASSERT(lower < upper);
+    SkASSERT(lower >= 0);
+    SkASSERT(upper <= SK_Fixed1);
+
+    partial255 += (partial255 >> 7);
+    return lower + ((upper - lower) * partial255 >> 8);
+}
+#endif
+
+SkFixed SkFixedSinCos(SkFixed radians, SkFixed* cosValuePtr) {
+    SkASSERT(SK_ARRAY_COUNT(gSkSinTable) == kTableSize);
+
+#ifdef BUILD_TABLE_AT_RUNTIME
+    static bool gFirstTime = true;
+    if (gFirstTime) {
+        build_sintable(gSinTable);
+        gFirstTime = false;
+    }
+#endif
+
+    // make radians positive
+    SkFixed sinValue, cosValue;
+    int32_t cosSign = 0;
+    int32_t sinSign = SkExtractSign(radians);
+    radians = SkApplySign(radians, sinSign);
+    // scale it to 0...1023 ...
+
+#ifdef INTERP_SINTABLE
+    radians = SkMulDiv(radians, 2 * kTableSize * 256, SK_FixedPI);
+    int findex = radians & (kTableSize * 256 - 1);
+    int index = findex >> 8;
+    int partial = findex & 255;
+    sinValue = interp_table(gSkSinTable, index, partial);
+
+    findex = kTableSize * 256 - findex - 1;
+    index = findex >> 8;
+    partial = findex & 255;
+    cosValue = interp_table(gSkSinTable, index, partial);
+
+    int quad = ((unsigned)radians / (kTableSize * 256)) & 3;
+#else
+    radians = SkMulDiv(radians, 2 * kTableSize, SK_FixedPI);
+    int     index = radians & (kTableSize - 1);
+
+    if (index == 0) {
+        sinValue = 0;
+        cosValue = SK_Fixed1;
+    } else {
+        sinValue = gSkSinTable[index];
+        cosValue = gSkSinTable[kTableSize - index];
+    }
+    int quad = ((unsigned)radians / kTableSize) & 3;
+#endif
+
+    if (quad & 1) {
+        SkTSwap<SkFixed>(sinValue, cosValue);
+    }
+    if (quad & 2) {
+        sinSign = ~sinSign;
+    }
+    if (((quad - 1) & 2) == 0) {
+        cosSign = ~cosSign;
+    }
+
+    // restore the sign for negative angles
+    sinValue = SkApplySign(sinValue, sinSign);
+    cosValue = SkApplySign(cosValue, cosSign);
+
+#ifdef SK_DEBUG
+    if (1) {
+        SkFixed sin2 = SkFixedMul(sinValue, sinValue);
+        SkFixed cos2 = SkFixedMul(cosValue, cosValue);
+        int diff = cos2 + sin2 - SK_Fixed1;
+        SkASSERT(SkAbs32(diff) <= 7);
+    }
+#endif
+
+    if (cosValuePtr) {
+        *cosValuePtr = cosValue;
+    }
+    return sinValue;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFixed SkFixedTan(SkFixed radians) { return SkCordicTan(radians); }
+SkFixed SkFixedASin(SkFixed x) { return SkCordicASin(x); }
+SkFixed SkFixedACos(SkFixed x) { return SkCordicACos(x); }
+SkFixed SkFixedATan2(SkFixed y, SkFixed x) { return SkCordicATan2(y, x); }
+SkFixed SkFixedExp(SkFixed x) { return SkCordicExp(x); }
+SkFixed SkFixedLog(SkFixed x) { return SkCordicLog(x); }
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#include "SkRandom.h"
+
+#ifdef SkLONGLONG
+static int symmetric_fixmul(int a, int b) {
+    int sa = SkExtractSign(a);
+    int sb = SkExtractSign(b);
+
+    a = SkApplySign(a, sa);
+    b = SkApplySign(b, sb);
+
+#if 1
+    int c = (int)(((SkLONGLONG)a * b) >> 16);
+    
+    return SkApplySign(c, sa ^ sb);
+#else
+    SkLONGLONG ab = (SkLONGLONG)a * b;
+    if (sa ^ sb) {
+        ab = -ab;
+    }
+    return ab >> 16;
+#endif
+}
+#endif
+
+#include "SkPoint.h"
+
+#ifdef SK_SUPPORT_UNITTEST
+static void check_length(const SkPoint& p, SkScalar targetLen) {
+    float x = SkScalarToFloat(p.fX);
+    float y = SkScalarToFloat(p.fY);
+    float len = sk_float_sqrt(x*x + y*y);
+    
+    len /= SkScalarToFloat(targetLen);
+
+    SkASSERT(len > 0.999f && len < 1.001f);
+}
+#endif
+
+#ifdef SK_CAN_USE_FLOAT
+
+static float nextFloat(SkRandom& rand) {
+    SkFloatIntUnion data;
+    data.fSignBitInt = rand.nextU();
+    return data.fFloat;
+}
+
+/*  returns true if a == b as resulting from (int)x. Since it is undefined
+    what to do if the float exceeds 2^32-1, we check for that explicitly.
+*/
+static bool equal_float_native_skia(float x, uint32_t ni, uint32_t si) {
+    if (!(x == x)) {    // NAN
+        return si == SK_MaxS32 || si == SK_MinS32;
+    }
+    // for out of range, C is undefined, but skia always should return NaN32
+    if (x > SK_MaxS32) {
+        return si == SK_MaxS32;
+    }
+    if (x < -SK_MaxS32) {
+        return si == SK_MinS32;
+    }
+    return si == ni;
+}
+
+static void assert_float_equal(const char op[], float x, uint32_t ni,
+                               uint32_t si) {
+    if (!equal_float_native_skia(x, ni, si)) {
+        SkDebugf("-- %s float %g bits %x native %x skia %x\n", op, x, ni, si);
+        SkASSERT(!"oops");
+    }
+}
+
+static void test_float_cast(float x) {
+    int ix = (int)x;
+    int iix = SkFloatToIntCast(x);
+    assert_float_equal("cast", x, ix, iix);
+}
+
+static void test_float_floor(float x) {
+    int ix = (int)floor(x);
+    int iix = SkFloatToIntFloor(x);
+    assert_float_equal("floor", x, ix, iix);
+}
+
+static void test_float_round(float x) {
+    double xx = x + 0.5;    // need intermediate double to avoid temp loss
+    int ix = (int)floor(xx);
+    int iix = SkFloatToIntRound(x);
+    assert_float_equal("round", x, ix, iix);
+}
+
+static void test_float_ceil(float x) {
+    int ix = (int)ceil(x);
+    int iix = SkFloatToIntCeil(x);
+    assert_float_equal("ceil", x, ix, iix);
+}
+
+static void test_float_conversions(float x) {
+    test_float_cast(x);
+    test_float_floor(x);
+    test_float_round(x);
+    test_float_ceil(x);
+}
+
+static void test_int2float(int ival) {
+    float x0 = (float)ival;
+    float x1 = SkIntToFloatCast(ival);
+    float x2 = SkIntToFloatCast_NoOverflowCheck(ival);
+    SkASSERT(x0 == x1);
+    SkASSERT(x0 == x2);
+}
+
+static void unittest_fastfloat() {
+    SkRandom rand;
+    size_t i;
+
+    static const float gFloats[] = {
+        0.f, 1.f, 0.5f, 0.499999f, 0.5000001f, 1.f/3,
+        0.000000001f, 1000000000.f,     // doesn't overflow
+        0.0000000001f, 10000000000.f    // does overflow
+    };
+    for (i = 0; i < SK_ARRAY_COUNT(gFloats); i++) {
+//        SkDebugf("---- test floats %g %d\n", gFloats[i], (int)gFloats[i]);
+        test_float_conversions(gFloats[i]);
+        test_float_conversions(-gFloats[i]);
+    }
+    
+    for (int outer = 0; outer < 100; outer++) {
+        rand.setSeed(outer);
+        for (i = 0; i < 100000; i++) {
+            float x = nextFloat(rand);
+            test_float_conversions(x);
+        }
+        
+        test_int2float(0);
+        test_int2float(1);
+        test_int2float(-1);
+        for (i = 0; i < 100000; i++) {
+            // for now only test ints that are 24bits or less, since we don't
+            // round (down) large ints the same as IEEE...
+            int ival = rand.nextU() & 0xFFFFFF;
+            test_int2float(ival);
+            test_int2float(-ival);
+        }
+    }
+}
+
+#endif
+
+static void test_muldiv255() {
+    for (int a = 0; a <= 255; a++) {
+        for (int b = 0; b <= 255; b++) {
+            int ab = a * b;
+            float s = ab / 255.0f;
+            int round = (int)floorf(s + 0.5f);
+            int trunc = (int)floorf(s);
+            
+            int iround = SkMulDiv255Round(a, b);
+            int itrunc = SkMulDiv255Trunc(a, b);
+            
+            SkASSERT(iround == round);
+            SkASSERT(itrunc == trunc);
+            
+            SkASSERT(itrunc <= iround);
+            SkASSERT(iround <= a);
+            SkASSERT(iround <= b);
+        }
+    }
+}
+
+void SkMath::UnitTest() {    
+#ifdef SK_SUPPORT_UNITTEST
+    int         i;
+    int32_t     x;
+    SkRandom    rand;
+
+    SkToS8(127);    SkToS8(-128);       SkToU8(255);
+    SkToS16(32767); SkToS16(-32768);    SkToU16(65535);
+    SkToS32(2*1024*1024);   SkToS32(-2*1024*1024);  SkToU32(4*1024*1024);
+
+    SkCordic_UnitTest();
+
+    // these should assert
+#if 0
+    SkToS8(128);
+    SkToS8(-129);
+    SkToU8(256);
+    SkToU8(-5);
+
+    SkToS16(32768);
+    SkToS16(-32769);
+    SkToU16(65536);
+    SkToU16(-5);
+
+    if (sizeof(size_t) > 4) {
+        SkToS32(4*1024*1024);
+        SkToS32(-4*1024*1024);
+        SkToU32(5*1024*1024);
+        SkToU32(-5);
+    }
+#endif
+
+    test_muldiv255();
+
+#ifdef SK_DEBUG
+    {
+        SkScalar x = SK_ScalarNaN;
+        SkASSERT(SkScalarIsNaN(x));
+    }
+#endif
+
+    for (i = 1; i <= 10; i++) {
+        x = SkCubeRootBits(i*i*i, 11);
+        SkASSERT(x == i);
+    }
+
+    x = SkFixedSqrt(SK_Fixed1);
+    SkASSERT(x == SK_Fixed1);
+    x = SkFixedSqrt(SK_Fixed1/4);
+    SkASSERT(x == SK_Fixed1/2);
+    x = SkFixedSqrt(SK_Fixed1*4);
+    SkASSERT(x == SK_Fixed1*2);
+
+    x = SkFractSqrt(SK_Fract1);
+    SkASSERT(x == SK_Fract1);
+    x = SkFractSqrt(SK_Fract1/4);
+    SkASSERT(x == SK_Fract1/2);
+    x = SkFractSqrt(SK_Fract1/16);
+    SkASSERT(x == SK_Fract1/4);
+
+    for (i = 1; i < 100; i++) {
+        x = SkFixedSqrt(SK_Fixed1 * i * i);
+        SkASSERT(x == SK_Fixed1 * i);
+    }
+
+    for (i = 0; i < 1000; i++) {
+        int value = rand.nextS16();
+        int max = rand.nextU16();
+
+        int clamp = SkClampMax(value, max);
+        int clamp2 = value < 0 ? 0 : (value > max ? max : value);
+        SkASSERT(clamp == clamp2);
+    }
+    
+    for (i = 0; i < 100000; i++) {
+        SkPoint p;
+        
+        p.setLength(rand.nextS(), rand.nextS(), SK_Scalar1);
+        check_length(p, SK_Scalar1);
+        p.setLength(rand.nextS() >> 13, rand.nextS() >> 13, SK_Scalar1);
+        check_length(p, SK_Scalar1);
+    }
+
+    {
+        SkFixed result = SkFixedDiv(100, 100);
+        SkASSERT(result == SK_Fixed1);
+        result = SkFixedDiv(1, SK_Fixed1);
+        SkASSERT(result == 1);
+    }
+    
+#ifdef SK_CAN_USE_FLOAT
+    unittest_fastfloat();
+#endif
+    
+#ifdef SkLONGLONG
+    for (i = 0; i < 100000; i++) {
+        SkFixed numer = rand.nextS();
+        SkFixed denom = rand.nextS();
+        SkFixed result = SkFixedDiv(numer, denom);
+        SkLONGLONG check = ((SkLONGLONG)numer << 16) / denom;
+
+        (void)SkCLZ(numer);
+        (void)SkCLZ(denom);
+
+        SkASSERT(result != (SkFixed)SK_NaN32);
+        if (check > SK_MaxS32) {
+            check = SK_MaxS32;
+        } else if (check < -SK_MaxS32) {
+            check = SK_MinS32;
+        }
+        SkASSERT(result == (int32_t)check);
+
+        result = SkFractDiv(numer, denom);
+        check = ((SkLONGLONG)numer << 30) / denom;
+
+        SkASSERT(result != (SkFixed)SK_NaN32);
+        if (check > SK_MaxS32) {
+            check = SK_MaxS32;
+        } else if (check < -SK_MaxS32) {
+            check = SK_MinS32;
+        }
+        SkASSERT(result == (int32_t)check);
+
+        // make them <= 2^24, so we don't overflow in fixmul
+        numer = numer << 8 >> 8;
+        denom = denom << 8 >> 8;
+
+        result = SkFixedMul(numer, denom);
+        SkFixed r2 = symmetric_fixmul(numer, denom);
+//        SkASSERT(result == r2);
+
+        result = SkFixedMul(numer, numer);
+        r2 = SkFixedSquare(numer);
+        SkASSERT(result == r2);
+        
+#ifdef SK_CAN_USE_FLOAT
+        if (numer >= 0 && denom >= 0) {
+            SkFixed mean = SkFixedMean(numer, denom);
+            float fm = sk_float_sqrt(sk_float_abs(SkFixedToFloat(numer) * SkFixedToFloat(denom)));
+            SkFixed mean2 = SkFloatToFixed(fm);
+            int diff = SkAbs32(mean - mean2);
+            SkASSERT(diff <= 1);
+        }
+
+        {
+            SkFixed mod = SkFixedMod(numer, denom);
+            float n = SkFixedToFloat(numer);
+            float d = SkFixedToFloat(denom);
+            float m = sk_float_mod(n, d);
+#if 0
+            SkDebugf("%g mod %g = %g [%g]\n",
+                    SkFixedToFloat(numer), SkFixedToFloat(denom),
+                    SkFixedToFloat(mod), m);
+#endif
+            SkASSERT(mod == 0 || (mod < 0) == (m < 0)); // ensure the same sign
+            int diff = SkAbs32(mod - SkFloatToFixed(m));
+            SkASSERT((diff >> 7) == 0);
+        }
+#endif
+    }
+#endif
+
+#ifdef SK_CAN_USE_FLOAT
+    for (i = 0; i < 100000; i++) {
+        SkFract x = rand.nextU() >> 1;
+        double xx = (double)x / SK_Fract1;
+        SkFract xr = SkFractSqrt(x);
+        SkFract check = SkFloatToFract(sqrt(xx));
+        SkASSERT(xr == check || xr == check-1 || xr == check+1);
+
+        xr = SkFixedSqrt(x);
+        xx = (double)x / SK_Fixed1;
+        check = SkFloatToFixed(sqrt(xx));
+        SkASSERT(xr == check || xr == check-1);
+
+        xr = SkSqrt32(x);
+        xx = (double)x;
+        check = (int32_t)sqrt(xx);
+        SkASSERT(xr == check || xr == check-1);
+    }
+#endif
+
+#if !defined(SK_SCALAR_IS_FLOAT) && defined(SK_CAN_USE_FLOAT)
+    {
+        SkFixed s, c;
+        s = SkFixedSinCos(0, &c);
+        SkASSERT(s == 0);
+        SkASSERT(c == SK_Fixed1);
+    }
+
+    int maxDiff = 0;
+    for (i = 0; i < 10000; i++) {
+        SkFixed rads = rand.nextS() >> 10;
+        double frads = SkFixedToFloat(rads);
+
+        SkFixed s, c;
+        s = SkScalarSinCos(rads, &c);
+
+        double fs = sin(frads);
+        double fc = cos(frads);
+
+        SkFixed is = SkFloatToFixed(fs);
+        SkFixed ic = SkFloatToFixed(fc);
+
+        maxDiff = SkMax32(maxDiff, SkAbs32(is - s));
+        maxDiff = SkMax32(maxDiff, SkAbs32(ic - c));
+    }
+    SkDebugf("SinCos: maximum error = %d\n", maxDiff);
+#endif
+#endif
+}
+
+#endif
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
new file mode 100644
index 0000000..893aea1
--- /dev/null
+++ b/src/core/SkMatrix.cpp
@@ -0,0 +1,1690 @@
+/* libs/corecg/SkMatrix.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 "SkMatrix.h"
+#include "Sk64.h"
+#include "SkFloatBits.h"
+#include "SkString.h"
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define kMatrix22Elem   SK_Scalar1
+#else
+    #define kMatrix22Elem   SK_Fract1
+#endif
+
+/*      [scale-x    skew-x      trans-x]   [X]   [X']
+        [skew-y     scale-y     trans-y] * [Y] = [Y']
+        [persp-0    persp-1     persp-2]   [1]   [1 ]
+*/
+
+void SkMatrix::reset() {
+    fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
+    fMat[kMSkewX]  = fMat[kMSkewY] = 
+    fMat[kMTransX] = fMat[kMTransY] =
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+    fMat[kMPersp2] = kMatrix22Elem;
+
+    this->setTypeMask(kIdentity_Mask | kRectStaysRect_Mask);
+}
+
+static inline int has_perspective(const SkMatrix& matrix) {
+    return matrix.getType() & SkMatrix::kPerspective_Mask;
+}
+
+// this guy aligns with the masks, so we can compute a mask from a varaible 0/1
+enum {
+    kTranslate_Shift,
+    kScale_Shift,
+    kAffine_Shift,
+    kPerspective_Shift,
+    kRectStaysRect_Shift
+};
+
+#ifdef SK_SCALAR_IS_FLOAT
+    static const int32_t kScalar1Int = 0x3f800000;
+    static const int32_t kPersp1Int  = 0x3f800000;
+#else
+    #define scalarAsInt(x)  (x)
+    static const int32_t kScalar1Int = (1 << 16);
+    static const int32_t kPersp1Int  = (1 << 30);
+#endif
+
+uint8_t SkMatrix::computeTypeMask() const {
+    unsigned mask = 0;
+
+    if (SkScalarAs2sCompliment(fMat[kMPersp0]) |
+            SkScalarAs2sCompliment(fMat[kMPersp1]) |
+            (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
+        mask |= kPerspective_Mask;
+    }
+    
+    if (SkScalarAs2sCompliment(fMat[kMTransX]) |
+            SkScalarAs2sCompliment(fMat[kMTransY])) {
+        mask |= kTranslate_Mask;
+    }
+
+    int m00 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleX]);
+    int m01 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewX]);
+    int m10 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewY]);
+    int m11 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleY]);
+    
+    if (m01 | m10) {
+        mask |= kAffine_Mask;
+    }
+
+    if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) {
+        mask |= kScale_Mask;
+    }
+    
+    if ((mask & kPerspective_Mask) == 0) {
+        // map non-zero to 1
+        m00 = m00 != 0;
+        m01 = m01 != 0;
+        m10 = m10 != 0;
+        m11 = m11 != 0;
+        
+        // record if the (p)rimary and (s)econdary diagonals are all 0 or
+        // all non-zero (answer is 0 or 1)
+        int dp0 = (m00 | m11) ^ 1;  // true if both are 0
+        int dp1 = m00 & m11;        // true if both are 1
+        int ds0 = (m01 | m10) ^ 1;  // true if both are 0
+        int ds1 = m01 & m10;        // true if both are 1
+        
+        // return 1 if primary is 1 and secondary is 0 or
+        // primary is 0 and secondary is 1
+        mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
+    }
+
+    return SkToU8(mask);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
+    if (SkScalarAs2sCompliment(dx) | SkScalarAs2sCompliment(dy)) {
+        fMat[kMTransX] = dx;
+        fMat[kMTransY] = dy;
+
+        fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
+        fMat[kMSkewX]  = fMat[kMSkewY] = 
+        fMat[kMPersp0] = fMat[kMPersp1] = 0;
+        fMat[kMPersp2] = kMatrix22Elem;
+
+        this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask);
+    } else {
+        this->reset();
+    }
+}
+
+bool SkMatrix::preTranslate(SkScalar dx, SkScalar dy) {
+    if (has_perspective(*this)) {
+        SkMatrix    m;
+        m.setTranslate(dx, dy);
+        return this->preConcat(m);
+    }
+    
+    if (SkScalarAs2sCompliment(dx) | SkScalarAs2sCompliment(dy)) {
+        fMat[kMTransX] += SkScalarMul(fMat[kMScaleX], dx) +
+                          SkScalarMul(fMat[kMSkewX], dy);
+        fMat[kMTransY] += SkScalarMul(fMat[kMSkewY], dx) +
+                          SkScalarMul(fMat[kMScaleY], dy);
+
+        this->setTypeMask(kUnknown_Mask);
+    }
+    return true;
+}
+
+bool SkMatrix::postTranslate(SkScalar dx, SkScalar dy) {
+    if (has_perspective(*this)) {
+        SkMatrix    m;
+        m.setTranslate(dx, dy);
+        return this->postConcat(m);
+    }
+    
+    if (SkScalarAs2sCompliment(dx) | SkScalarAs2sCompliment(dy)) {
+        fMat[kMTransX] += dx;
+        fMat[kMTransY] += dy;
+        this->setTypeMask(kUnknown_Mask);
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    fMat[kMScaleX] = sx;
+    fMat[kMScaleY] = sy;
+    fMat[kMTransX] = px - SkScalarMul(sx, px);
+    fMat[kMTransY] = py - SkScalarMul(sy, py);
+    fMat[kMPersp2] = kMatrix22Elem;
+
+    fMat[kMSkewX]  = fMat[kMSkewY] = 
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+    
+    this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
+}
+
+void SkMatrix::setScale(SkScalar sx, SkScalar sy) {
+    fMat[kMScaleX] = sx;
+    fMat[kMScaleY] = sy;
+    fMat[kMPersp2] = kMatrix22Elem;
+
+    fMat[kMTransX] = fMat[kMTransY] =
+    fMat[kMSkewX]  = fMat[kMSkewY] = 
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+
+    this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
+}
+
+bool SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    SkMatrix    m;
+    m.setScale(sx, sy, px, py);
+    return this->preConcat(m);
+}
+
+bool SkMatrix::preScale(SkScalar sx, SkScalar sy) {
+    SkMatrix    m;
+    m.setScale(sx, sy);
+    return this->preConcat(m);
+}
+
+bool SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    SkMatrix    m;
+    m.setScale(sx, sy, px, py);
+    return this->postConcat(m);
+}
+
+bool SkMatrix::postScale(SkScalar sx, SkScalar sy) {
+    SkMatrix    m;
+    m.setScale(sx, sy);
+    return this->postConcat(m);
+}
+
+#ifdef SK_SCALAR_IS_FIXED
+    static inline SkFixed roundidiv(SkFixed numer, int denom) {
+        int ns = numer >> 31;
+        int ds = denom >> 31;
+        numer = (numer ^ ns) - ns;
+        denom = (denom ^ ds) - ds;
+        
+        SkFixed answer = (numer + (denom >> 1)) / denom;
+        int as = ns ^ ds;
+        return (answer ^ as) - as;
+    }
+#endif
+
+// this guy perhaps can go away, if we have a fract/high-precision way to
+// scale matrices
+bool SkMatrix::postIDiv(int divx, int divy) {
+    if (divx == 0 || divy == 0) {
+        return false;
+    }
+
+#ifdef SK_SCALAR_IS_FIXED
+    fMat[kMScaleX] = roundidiv(fMat[kMScaleX], divx);
+    fMat[kMSkewX]  = roundidiv(fMat[kMSkewX],  divx);
+    fMat[kMTransX] = roundidiv(fMat[kMTransX], divx);
+
+    fMat[kMScaleY] = roundidiv(fMat[kMScaleY], divy);
+    fMat[kMSkewY]  = roundidiv(fMat[kMSkewY],  divy);
+    fMat[kMTransY] = roundidiv(fMat[kMTransY], divy);
+#else
+    const float invX = 1.f / divx;
+    const float invY = 1.f / divy;
+
+    fMat[kMScaleX] *= invX;
+    fMat[kMSkewX]  *= invX;
+    fMat[kMTransX] *= invX;
+    
+    fMat[kMScaleY] *= invY;
+    fMat[kMSkewY]  *= invY;
+    fMat[kMTransY] *= invY;
+#endif
+
+    this->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV,
+                         SkScalar px, SkScalar py) {
+    const SkScalar oneMinusCosV = SK_Scalar1 - cosV;
+
+    fMat[kMScaleX]  = cosV;
+    fMat[kMSkewX]   = -sinV;
+    fMat[kMTransX]  = SkScalarMul(sinV, py) + SkScalarMul(oneMinusCosV, px);
+
+    fMat[kMSkewY]   = sinV;
+    fMat[kMScaleY]  = cosV;
+    fMat[kMTransY]  = SkScalarMul(-sinV, px) + SkScalarMul(oneMinusCosV, py);
+
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+    fMat[kMPersp2] = kMatrix22Elem;
+    
+    this->setTypeMask(kUnknown_Mask);
+}
+
+void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV) {
+    fMat[kMScaleX]  = cosV;
+    fMat[kMSkewX]   = -sinV;
+    fMat[kMTransX]  = 0;
+
+    fMat[kMSkewY]   = sinV;
+    fMat[kMScaleY]  = cosV;
+    fMat[kMTransY]  = 0;
+
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+    fMat[kMPersp2] = kMatrix22Elem;
+
+    this->setTypeMask(kUnknown_Mask);
+}
+
+void SkMatrix::setRotate(SkScalar degrees, SkScalar px, SkScalar py) {
+    SkScalar sinV, cosV;
+    sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
+    this->setSinCos(sinV, cosV, px, py);
+}
+
+void SkMatrix::setRotate(SkScalar degrees) {
+    SkScalar sinV, cosV;
+    sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
+    this->setSinCos(sinV, cosV);
+}
+
+bool SkMatrix::preRotate(SkScalar degrees, SkScalar px, SkScalar py) {
+    SkMatrix    m;
+    m.setRotate(degrees, px, py);
+    return this->preConcat(m);
+}
+
+bool SkMatrix::preRotate(SkScalar degrees) {
+    SkMatrix    m;
+    m.setRotate(degrees);
+    return this->preConcat(m);
+}
+
+bool SkMatrix::postRotate(SkScalar degrees, SkScalar px, SkScalar py) {
+    SkMatrix    m;
+    m.setRotate(degrees, px, py);
+    return this->postConcat(m);
+}
+
+bool SkMatrix::postRotate(SkScalar degrees) {
+    SkMatrix    m;
+    m.setRotate(degrees);
+    return this->postConcat(m);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::setSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    fMat[kMScaleX]  = SK_Scalar1;
+    fMat[kMSkewX]   = sx;
+    fMat[kMTransX]  = SkScalarMul(-sx, py);
+
+    fMat[kMSkewY]   = sy;
+    fMat[kMScaleY]  = SK_Scalar1;
+    fMat[kMTransY]  = SkScalarMul(-sy, px);
+
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+    fMat[kMPersp2] = kMatrix22Elem;
+
+    this->setTypeMask(kUnknown_Mask);
+}
+
+void SkMatrix::setSkew(SkScalar sx, SkScalar sy) {
+    fMat[kMScaleX]  = SK_Scalar1;
+    fMat[kMSkewX]   = sx;
+    fMat[kMTransX]  = 0;
+
+    fMat[kMSkewY]   = sy;
+    fMat[kMScaleY]  = SK_Scalar1;
+    fMat[kMTransY]  = 0;
+
+    fMat[kMPersp0] = fMat[kMPersp1] = 0;
+    fMat[kMPersp2] = kMatrix22Elem;
+
+    this->setTypeMask(kUnknown_Mask);
+}
+
+bool SkMatrix::preSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    SkMatrix    m;
+    m.setSkew(sx, sy, px, py);
+    return this->preConcat(m);
+}
+
+bool SkMatrix::preSkew(SkScalar sx, SkScalar sy) {
+    SkMatrix    m;
+    m.setSkew(sx, sy);
+    return this->preConcat(m);
+}
+
+bool SkMatrix::postSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
+    SkMatrix    m;
+    m.setSkew(sx, sy, px, py);
+    return this->postConcat(m);
+}
+
+bool SkMatrix::postSkew(SkScalar sx, SkScalar sy) {
+    SkMatrix    m;
+    m.setSkew(sx, sy);
+    return this->postConcat(m);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkMatrix::setRectToRect(const SkRect& src, const SkRect& dst,
+                             ScaleToFit align)
+{
+    if (src.isEmpty()) {
+        this->reset();
+        return false;
+    }
+
+    if (dst.isEmpty()) {
+        bzero(fMat, 8 * sizeof(SkScalar));
+        this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
+    } else {
+        SkScalar    tx, sx = SkScalarDiv(dst.width(), src.width());
+        SkScalar    ty, sy = SkScalarDiv(dst.height(), src.height());
+        bool        xLarger = false;
+
+        if (align != kFill_ScaleToFit) {
+            if (sx > sy) {
+                xLarger = true;
+                sx = sy;
+            } else {
+                sy = sx;
+            }
+        }
+
+        tx = dst.fLeft - SkScalarMul(src.fLeft, sx);
+        ty = dst.fTop - SkScalarMul(src.fTop, sy);
+        if (align == kCenter_ScaleToFit || align == kEnd_ScaleToFit) {
+            SkScalar diff;
+
+            if (xLarger) {
+                diff = dst.width() - SkScalarMul(src.width(), sy);
+            } else {
+                diff = dst.height() - SkScalarMul(src.height(), sy);
+            }
+            
+            if (align == kCenter_ScaleToFit) {
+                diff = SkScalarHalf(diff);
+            }
+
+            if (xLarger) {
+                tx += diff;
+            } else {
+                ty += diff;
+            }
+        }
+
+        fMat[kMScaleX] = sx;
+        fMat[kMScaleY] = sy;
+        fMat[kMTransX] = tx;
+        fMat[kMTransY] = ty;
+        fMat[kMSkewX]  = fMat[kMSkewY] = 
+        fMat[kMPersp0] = fMat[kMPersp1] = 0;
+
+        this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
+    }
+    // shared cleanup
+    fMat[kMPersp2] = kMatrix22Elem;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FLOAT
+    static inline int fixmuladdmul(float a, float b, float c, float d,
+                                   float* result) {
+        *result = a * b + c * d;
+        return true;
+    }
+
+    static inline int fixmuladdmulshiftmul(float a, float b, float c, float d,
+                           int /*shift not used*/, float scale, float* result) {
+        *result = (a * b + c * d) * scale;
+        return true;
+    }
+
+    static inline bool rowcol3(const float row[], const float col[],
+                               float* result) {
+        *result = row[0] * col[0] + row[1] * col[3] + row[2] * col[6];
+        return true;
+    }
+
+    static inline int negifaddoverflows(float& result, float a, float b) {
+        result = a + b;
+        return 0;
+    }
+#else
+    static inline bool fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d,
+                                    SkFixed* result) {
+        Sk64    tmp1, tmp2;
+        tmp1.setMul(a, b);
+        tmp2.setMul(c, d);
+        tmp1.add(tmp2);
+        if (tmp1.isFixed()) {
+            *result = tmp1.getFixed();
+            return true;
+        }
+        return false;
+    }
+
+    static inline bool fixmuladdmulshiftmul(SkFixed a, SkFixed b, SkFixed c,
+                        SkFixed d, int shift, SkFixed scale, SkFixed* result) {
+        Sk64    tmp1, tmp2;
+        tmp1.setMul(a, b);
+        tmp2.setMul(c, d);
+        tmp1.add(tmp2);
+
+        int32_t hi = SkAbs32(tmp1.fHi);
+        int afterShift = 16;
+        if (hi >> 15) {
+            int clz = 17 - SkCLZ(hi);
+            SkASSERT(clz > 0 && clz <= 16);
+            afterShift -= clz;
+            shift += clz;
+        }
+
+        tmp1.roundRight(shift + 16);
+        SkASSERT(tmp1.is32());
+
+        tmp1.setMul(tmp1.get32(), scale);
+        tmp1.roundRight(afterShift);
+        if (tmp1.is32()) {
+            *result = tmp1.get32();
+            return true;
+        }
+        return false;
+    }
+
+    static inline SkFixed fracmuladdmul(SkFixed a, SkFract b, SkFixed c,
+                                        SkFract d) {
+        Sk64 tmp1, tmp2;
+        tmp1.setMul(a, b);
+        tmp2.setMul(c, d);
+        tmp1.add(tmp2);
+        return tmp1.getFract();
+    }
+
+    static inline bool rowcol3(const SkFixed row[], const SkFixed col[],
+                               SkFixed* result) {
+        Sk64 tmp1, tmp2;
+
+        tmp1.setMul(row[0], col[0]);    // N * fixed
+        tmp2.setMul(row[1], col[3]);    // N * fixed
+        tmp1.add(tmp2);
+
+        tmp2.setMul(row[2], col[6]);    // N * fract
+        tmp2.roundRight(14);            // make it fixed
+        tmp1.add(tmp2);
+
+        if (tmp1.isFixed()) {
+            *result = tmp1.getFixed();
+            return true;
+        }
+        return false;
+    }
+
+    static inline int negifaddoverflows(SkFixed& result, SkFixed a, SkFixed b) {
+        SkFixed c = a + b;
+        result = c;
+        return (c ^ a) & (c ^ b);
+    }
+#endif
+
+static void normalize_perspective(SkScalar mat[9]) {
+    if (SkScalarAbs(mat[SkMatrix::kMPersp2]) > kMatrix22Elem) {
+        for (int i = 0; i < 9; i++)
+            mat[i] = SkScalarHalf(mat[i]);
+    }
+}
+
+bool SkMatrix::setConcat(const SkMatrix& a, const SkMatrix& b) {
+    TypeMask aType = a.getType();
+    TypeMask bType = b.getType();
+
+    if (0 == aType) {
+        *this = b;
+    } else if (0 == bType) {
+        *this = a;
+    } else {
+        SkMatrix tmp;
+
+        if ((aType | bType) & kPerspective_Mask) {
+            if (!rowcol3(&a.fMat[0], &b.fMat[0], &tmp.fMat[kMScaleX])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[0], &b.fMat[1], &tmp.fMat[kMSkewX])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[0], &b.fMat[2], &tmp.fMat[kMTransX])) {
+                return false;
+            }
+
+            if (!rowcol3(&a.fMat[3], &b.fMat[0], &tmp.fMat[kMSkewY])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[3], &b.fMat[1], &tmp.fMat[kMScaleY])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[3], &b.fMat[2], &tmp.fMat[kMTransY])) {
+                return false;
+            }
+
+            if (!rowcol3(&a.fMat[6], &b.fMat[0], &tmp.fMat[kMPersp0])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[6], &b.fMat[1], &tmp.fMat[kMPersp1])) {
+                return false;
+            }
+            if (!rowcol3(&a.fMat[6], &b.fMat[2], &tmp.fMat[kMPersp2])) {
+                return false;
+            }
+
+            normalize_perspective(tmp.fMat);
+        } else {    // not perspective
+            if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMScaleX],
+                    a.fMat[kMSkewX], b.fMat[kMSkewY], &tmp.fMat[kMScaleX])) {
+                return false;
+            }
+            if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMSkewX],
+                      a.fMat[kMSkewX], b.fMat[kMScaleY], &tmp.fMat[kMSkewX])) {
+                return false;
+            }
+            if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMTransX],
+                      a.fMat[kMSkewX], b.fMat[kMTransY], &tmp.fMat[kMTransX])) {
+                return false;
+            }
+            if (negifaddoverflows(tmp.fMat[kMTransX], tmp.fMat[kMTransX],
+                                  a.fMat[kMTransX]) < 0) {
+                return false;
+            }
+
+            if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMScaleX],
+                      a.fMat[kMScaleY], b.fMat[kMSkewY], &tmp.fMat[kMSkewY])) {
+                return false;
+            }
+            if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMSkewX],
+                    a.fMat[kMScaleY], b.fMat[kMScaleY], &tmp.fMat[kMScaleY])) {
+                return false;
+            }
+            if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMTransX],
+                     a.fMat[kMScaleY], b.fMat[kMTransY], &tmp.fMat[kMTransY])) {
+                return false;
+            }
+            if (negifaddoverflows(tmp.fMat[kMTransY], tmp.fMat[kMTransY],
+                                  a.fMat[kMTransY]) < 0) {
+                return false;
+            }
+
+            tmp.fMat[kMPersp0] = tmp.fMat[kMPersp1] = 0;
+            tmp.fMat[kMPersp2] = kMatrix22Elem;
+        }
+        *this = tmp;
+    }
+    this->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+bool SkMatrix::preConcat(const SkMatrix& mat) {
+    // check for identity first, so we don't do a needless copy of ourselves
+    // to ourselves inside setConcat()
+    return mat.isIdentity() || this->setConcat(*this, mat);
+}
+
+bool SkMatrix::postConcat(const SkMatrix& mat) {
+    // check for identity first, so we don't do a needless copy of ourselves
+    // to ourselves inside setConcat()
+    return mat.isIdentity() || this->setConcat(mat, *this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define SkPerspMul(a, b)            SkScalarMul(a, b)
+    #define SkScalarMulShift(a, b, s)   SkScalarMul(a, b)
+    static float sk_inv_determinant(const float mat[9], int isPerspective,
+                                    int* /* (only used in Fixed case) */) {
+        double det;
+
+        if (isPerspective) {
+            det =   mat[SkMatrix::kMScaleX] * ((double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp2] - (double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp1]) +
+                    mat[SkMatrix::kMSkewX] * ((double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp0] - (double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp2]) +
+                    mat[SkMatrix::kMTransX] * ((double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp1] - (double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp0]);
+        } else {
+            det =   (double)mat[SkMatrix::kMScaleX] * mat[SkMatrix::kMScaleY] - (double)mat[SkMatrix::kMSkewX] * mat[SkMatrix::kMSkewY];
+        }
+
+        // Since the determinant is on the order of the square of the matrix members,
+        // compare to the square of the default nearly-zero constant
+        if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
+            return 0;
+        }
+        return (float)(1.0 / det);
+    }
+#else
+    #define SkPerspMul(a, b)            SkFractMul(a, b)
+    #define SkScalarMulShift(a, b, s)   SkMulShift(a, b, s)
+    static void set_muladdmul(Sk64* dst, int32_t a, int32_t b, int32_t c,
+                              int32_t d) {
+        Sk64 tmp;
+        dst->setMul(a, b);
+        tmp.setMul(c, d);
+        dst->add(tmp);
+    }
+
+    static SkFixed sk_inv_determinant(const SkFixed mat[9], int isPerspective,
+                                      int* shift) {
+        Sk64    tmp1, tmp2;
+
+        if (isPerspective) {
+            tmp1.setMul(mat[SkMatrix::kMScaleX], fracmuladdmul(mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp2], -mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp1]));
+            tmp2.setMul(mat[SkMatrix::kMSkewX], fracmuladdmul(mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp0], -mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp2]));
+            tmp1.add(tmp2);
+            tmp2.setMul(mat[SkMatrix::kMTransX], fracmuladdmul(mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp1], -mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp0]));
+            tmp1.add(tmp2);
+        } else {
+            tmp1.setMul(mat[SkMatrix::kMScaleX], mat[SkMatrix::kMScaleY]);
+            tmp2.setMul(mat[SkMatrix::kMSkewX], mat[SkMatrix::kMSkewY]);
+            tmp1.sub(tmp2);
+        }
+
+        int s = tmp1.getClzAbs();
+        *shift = s;
+
+        SkFixed denom;
+        if (s <= 32) {
+            denom = tmp1.getShiftRight(33 - s);
+        } else {
+            denom = (int32_t)tmp1.fLo << (s - 33);
+        }
+
+        if (denom == 0) {
+            return 0;
+        }
+        /** This could perhaps be a special fractdiv function, since both of its
+            arguments are known to have bit 31 clear and bit 30 set (when they
+            are made positive), thus eliminating the need for calling clz()
+        */
+        return SkFractDiv(SK_Fract1, denom);
+    }
+#endif
+
+bool SkMatrix::invert(SkMatrix* inv) const {
+    int         isPersp = has_perspective(*this);
+    int         shift;
+    SkScalar    scale = sk_inv_determinant(fMat, isPersp, &shift);
+
+    if (scale == 0) { // underflow
+        return false;
+    }
+
+    if (inv) {
+        SkMatrix tmp;
+        if (inv == this)
+            inv = &tmp;
+
+        if (isPersp) {
+            shift = 61 - shift;
+            inv->fMat[kMScaleX] = SkScalarMulShift(SkPerspMul(fMat[kMScaleY], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransY], fMat[kMPersp1]), scale, shift);
+            inv->fMat[kMSkewX]  = SkScalarMulShift(SkPerspMul(fMat[kMTransX], fMat[kMPersp1]) - SkPerspMul(fMat[kMSkewX],  fMat[kMPersp2]), scale, shift);
+            inv->fMat[kMTransX] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMTransY]) - SkScalarMul(fMat[kMTransX], fMat[kMScaleY]), scale, shift);
+
+            inv->fMat[kMSkewY]  = SkScalarMulShift(SkPerspMul(fMat[kMTransY], fMat[kMPersp0]) - SkPerspMul(fMat[kMSkewY],   fMat[kMPersp2]), scale, shift);
+            inv->fMat[kMScaleY] = SkScalarMulShift(SkPerspMul(fMat[kMScaleX], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransX],  fMat[kMPersp0]), scale, shift);
+            inv->fMat[kMTransY] = SkScalarMulShift(SkScalarMul(fMat[kMTransX], fMat[kMSkewY]) - SkScalarMul(fMat[kMScaleX], fMat[kMTransY]), scale, shift);
+
+            inv->fMat[kMPersp0] = SkScalarMulShift(SkScalarMul(fMat[kMSkewY], fMat[kMPersp1]) - SkScalarMul(fMat[kMScaleY], fMat[kMPersp0]), scale, shift);             
+            inv->fMat[kMPersp1] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMPersp0]) - SkScalarMul(fMat[kMScaleX], fMat[kMPersp1]), scale, shift);
+            inv->fMat[kMPersp2] = SkScalarMulShift(SkScalarMul(fMat[kMScaleX], fMat[kMScaleY]) - SkScalarMul(fMat[kMSkewX], fMat[kMSkewY]), scale, shift);
+#ifdef SK_SCALAR_IS_FIXED
+            if (SkAbs32(inv->fMat[kMPersp2]) > SK_Fixed1) {
+                Sk64    tmp;
+
+                tmp.set(SK_Fract1);
+                tmp.shiftLeft(16);
+                tmp.div(inv->fMat[kMPersp2], Sk64::kRound_DivOption);
+
+                SkFract scale = tmp.get32();
+
+                for (int i = 0; i < 9; i++) {
+                    inv->fMat[i] = SkFractMul(inv->fMat[i], scale);
+                }
+            }
+            inv->fMat[kMPersp2] = SkFixedToFract(inv->fMat[kMPersp2]);
+#endif
+        } else {   // not perspective
+#ifdef SK_SCALAR_IS_FIXED
+            Sk64    tx, ty;
+            int     clzNumer;
+
+            // check the 2x2 for overflow
+            {
+                int32_t value = SkAbs32(fMat[kMScaleY]);
+                value |= SkAbs32(fMat[kMSkewX]);
+                value |= SkAbs32(fMat[kMScaleX]);
+                value |= SkAbs32(fMat[kMSkewY]);
+                clzNumer = SkCLZ(value);
+                if (shift - clzNumer > 31)
+                    return false;   // overflow
+            }
+
+            set_muladdmul(&tx, fMat[kMSkewX], fMat[kMTransY], -fMat[kMScaleY], fMat[kMTransX]);
+            set_muladdmul(&ty, fMat[kMSkewY], fMat[kMTransX], -fMat[kMScaleX], fMat[kMTransY]);
+            // check tx,ty for overflow
+            clzNumer = SkCLZ(SkAbs32(tx.fHi) | SkAbs32(ty.fHi));
+            if (shift - clzNumer > 14) {
+                return false;   // overflow
+            }
+
+            int fixedShift = 61 - shift;
+            int sk64shift = 44 - shift + clzNumer;
+
+            inv->fMat[kMScaleX] = SkMulShift(fMat[kMScaleY], scale, fixedShift);
+            inv->fMat[kMSkewX]  = SkMulShift(-fMat[kMSkewX], scale, fixedShift);
+            inv->fMat[kMTransX] = SkMulShift(tx.getShiftRight(33 - clzNumer), scale, sk64shift);
+                
+            inv->fMat[kMSkewY]  = SkMulShift(-fMat[kMSkewY], scale, fixedShift);
+            inv->fMat[kMScaleY] = SkMulShift(fMat[kMScaleX], scale, fixedShift);
+            inv->fMat[kMTransY] = SkMulShift(ty.getShiftRight(33 - clzNumer), scale, sk64shift);
+#else
+            inv->fMat[kMScaleX] = SkScalarMul(fMat[kMScaleY], scale);
+            inv->fMat[kMSkewX] = SkScalarMul(-fMat[kMSkewX], scale);
+            if (!fixmuladdmulshiftmul(fMat[kMSkewX], fMat[kMTransY], -fMat[kMScaleY], fMat[kMTransX], shift, scale, &inv->fMat[kMTransX])) {
+                return false;
+            }
+                
+            inv->fMat[kMSkewY] = SkScalarMul(-fMat[kMSkewY], scale);
+            inv->fMat[kMScaleY] = SkScalarMul(fMat[kMScaleX], scale);
+            if (!fixmuladdmulshiftmul(fMat[kMSkewY], fMat[kMTransX], -fMat[kMScaleX], fMat[kMTransY], shift, scale, &inv->fMat[kMTransY])) {
+                return false;
+            }
+#endif
+            inv->fMat[kMPersp0] = 0;
+            inv->fMat[kMPersp1] = 0;
+            inv->fMat[kMPersp2] = kMatrix22Elem;
+        }
+
+        if (inv == &tmp) {
+            *(SkMatrix*)this = tmp;
+        }
+        inv->setTypeMask(kUnknown_Mask);
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[],
+                            const SkPoint src[], int count) {
+    SkASSERT(m.getType() == 0);
+
+    if (dst != src && count > 0)
+        memcpy(dst, src, count * sizeof(SkPoint));
+}
+
+void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[],
+                         const SkPoint src[], int count) {
+    SkASSERT(m.getType() == kTranslate_Mask);
+
+    if (count > 0) {
+        SkScalar tx = m.fMat[kMTransX];
+        SkScalar ty = m.fMat[kMTransY];
+        do {
+            dst->fY = src->fY + ty;
+            dst->fX = src->fX + tx;
+            src += 1;
+            dst += 1;
+        } while (--count);
+    }
+}
+
+void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[],
+                         const SkPoint src[], int count) {
+    SkASSERT(m.getType() == kScale_Mask);
+
+    if (count > 0) {
+        SkScalar mx = m.fMat[kMScaleX];
+        SkScalar my = m.fMat[kMScaleY];
+        do {
+            dst->fY = SkScalarMul(src->fY, my);
+            dst->fX = SkScalarMul(src->fX, mx);
+            src += 1;
+            dst += 1;
+        } while (--count);
+    }
+}
+
+void SkMatrix::ScaleTrans_pts(const SkMatrix& m, SkPoint dst[],
+                              const SkPoint src[], int count) {
+    SkASSERT(m.getType() == (kScale_Mask | kTranslate_Mask));
+
+    if (count > 0) {
+        SkScalar mx = m.fMat[kMScaleX];
+        SkScalar my = m.fMat[kMScaleY];
+        SkScalar tx = m.fMat[kMTransX];
+        SkScalar ty = m.fMat[kMTransY];
+        do {
+            dst->fY = SkScalarMulAdd(src->fY, my, ty);
+            dst->fX = SkScalarMulAdd(src->fX, mx, tx);
+            src += 1;
+            dst += 1;
+        } while (--count);
+    }
+}
+
+void SkMatrix::Rot_pts(const SkMatrix& m, SkPoint dst[],
+                       const SkPoint src[], int count) {
+    SkASSERT((m.getType() & (kPerspective_Mask | kTranslate_Mask)) == 0);
+
+    if (count > 0) {
+        SkScalar mx = m.fMat[kMScaleX];
+        SkScalar my = m.fMat[kMScaleY];
+        SkScalar kx = m.fMat[kMSkewX];
+        SkScalar ky = m.fMat[kMSkewY];
+        do {
+            SkScalar sy = src->fY;
+            SkScalar sx = src->fX;
+            src += 1;
+            dst->fY = SkScalarMul(sx, ky) + SkScalarMul(sy, my);
+            dst->fX = SkScalarMul(sx, mx) + SkScalarMul(sy, kx);
+            dst += 1;
+        } while (--count);
+    }
+}
+
+void SkMatrix::RotTrans_pts(const SkMatrix& m, SkPoint dst[],
+                            const SkPoint src[], int count) {
+    SkASSERT((m.getType() & kPerspective_Mask) == 0);
+
+    if (count > 0) {
+        SkScalar mx = m.fMat[kMScaleX];
+        SkScalar my = m.fMat[kMScaleY];
+        SkScalar kx = m.fMat[kMSkewX];
+        SkScalar ky = m.fMat[kMSkewY];
+        SkScalar tx = m.fMat[kMTransX];
+        SkScalar ty = m.fMat[kMTransY];
+        do {
+            SkScalar sy = src->fY;
+            SkScalar sx = src->fX;
+            src += 1;
+            dst->fY = SkScalarMul(sx, ky) + SkScalarMulAdd(sy, my, ty);
+            dst->fX = SkScalarMul(sx, mx) + SkScalarMulAdd(sy, kx, tx);
+            dst += 1;
+        } while (--count);
+    }
+}
+
+void SkMatrix::Persp_pts(const SkMatrix& m, SkPoint dst[],
+                         const SkPoint src[], int count) {
+    SkASSERT(m.getType() & kPerspective_Mask);
+
+#ifdef SK_SCALAR_IS_FIXED
+    SkFixed persp2 = SkFractToFixed(m.fMat[kMPersp2]);
+#endif
+
+    if (count > 0) {
+        do {
+            SkScalar sy = src->fY;
+            SkScalar sx = src->fX;
+            src += 1;
+
+            SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) +
+                         SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
+            SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) +
+                         SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
+#ifdef SK_SCALAR_IS_FIXED
+            SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) +
+                        SkFractMul(sy, m.fMat[kMPersp1]) + persp2;
+#else
+            float z = SkScalarMul(sx, m.fMat[kMPersp0]) +
+                      SkScalarMulAdd(sy, m.fMat[kMPersp1], m.fMat[kMPersp2]);
+#endif
+            if (z) {
+                z = SkScalarFastInvert(z);
+            }
+
+            dst->fY = SkScalarMul(y, z);
+            dst->fX = SkScalarMul(x, z);
+            dst += 1;
+        } while (--count);
+    }
+}
+
+const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
+    SkMatrix::Identity_pts, SkMatrix::Trans_pts,
+    SkMatrix::Scale_pts,    SkMatrix::ScaleTrans_pts,
+    SkMatrix::Rot_pts,      SkMatrix::RotTrans_pts,
+    SkMatrix::Rot_pts,      SkMatrix::RotTrans_pts,
+    // repeat the persp proc 8 times
+    SkMatrix::Persp_pts,    SkMatrix::Persp_pts,
+    SkMatrix::Persp_pts,    SkMatrix::Persp_pts,
+    SkMatrix::Persp_pts,    SkMatrix::Persp_pts,
+    SkMatrix::Persp_pts,    SkMatrix::Persp_pts
+};
+
+void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
+    SkASSERT((dst && src && count > 0) || count == 0);
+    // no partial overlap
+    SkASSERT(src == dst || SkAbs32((int32_t)(src - dst)) >= count);
+
+    this->getMapPtsProc()(*this, dst, src, count);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
+    if (this->getType() & kPerspective_Mask) {
+        SkPoint origin;
+
+        MapXYProc proc = this->getMapXYProc();
+        proc(*this, 0, 0, &origin);
+
+        for (int i = count - 1; i >= 0; --i) {
+            SkPoint tmp;
+
+            proc(*this, src[i].fX, src[i].fY, &tmp);
+            dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY);
+        }
+    } else {
+        SkMatrix tmp = *this;
+
+        tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0;
+        tmp.clearTypeMask(kTranslate_Mask);
+        tmp.mapPoints(dst, src, count);
+    }
+}
+
+bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const {
+    SkASSERT(dst && &src);
+
+    if (this->rectStaysRect()) {
+        this->mapPoints((SkPoint*)dst, (const SkPoint*)&src, 2);
+        dst->sort();
+        return true;
+    } else {
+        SkPoint quad[4];
+
+        src.toQuad(quad);
+        this->mapPoints(quad, quad, 4);
+        dst->set(quad, 4);
+        return false;
+    }
+}
+
+SkScalar SkMatrix::mapRadius(SkScalar radius) const {
+    SkVector    vec[2];
+
+    vec[0].set(radius, 0);
+    vec[1].set(0, radius);
+    this->mapVectors(vec, 2);
+
+    SkScalar d0 = vec[0].length();
+    SkScalar d1 = vec[1].length();
+
+    return SkScalarMean(d0, d1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::Persp_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                        SkPoint* pt) {
+    SkASSERT(m.getType() & kPerspective_Mask);
+
+    SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) +
+                 SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
+    SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) +
+                 SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
+#ifdef SK_SCALAR_IS_FIXED
+    SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) +
+                SkFractMul(sy, m.fMat[kMPersp1]) +
+                SkFractToFixed(m.fMat[kMPersp2]);
+#else
+    float z = SkScalarMul(sx, m.fMat[kMPersp0]) +
+              SkScalarMul(sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
+#endif
+    if (z) {
+        z = SkScalarFastInvert(z);
+    }
+    pt->fX = SkScalarMul(x, z);
+    pt->fY = SkScalarMul(y, z);
+}
+
+#ifdef SK_SCALAR_IS_FIXED
+static SkFixed fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d) {
+    Sk64    tmp, tmp1;
+
+    tmp.setMul(a, b);
+    tmp1.setMul(c, d);
+    return tmp.addGetFixed(tmp1);
+//  tmp.add(tmp1);
+//  return tmp.getFixed();
+}
+#endif
+
+void SkMatrix::RotTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                           SkPoint* pt) {
+    SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask)) == kAffine_Mask);
+    
+#ifdef SK_SCALAR_IS_FIXED
+    pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) +
+             m.fMat[kMTransX];
+    pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) +
+             m.fMat[kMTransY];
+#else
+    pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) +
+             SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]);
+    pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) +
+             SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
+#endif
+}
+
+void SkMatrix::Rot_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                      SkPoint* pt) {
+    SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask))== kAffine_Mask);
+    SkASSERT(0 == m.fMat[kMTransX]);
+    SkASSERT(0 == m.fMat[kMTransY]);
+
+#ifdef SK_SCALAR_IS_FIXED
+    pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]);
+    pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]);
+#else
+    pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) +
+             SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]);
+    pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) +
+             SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
+#endif
+}
+
+void SkMatrix::ScaleTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                             SkPoint* pt) {
+    SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
+             == kScale_Mask);
+    
+    pt->fX = SkScalarMulAdd(sx, m.fMat[kMScaleX], m.fMat[kMTransX]);
+    pt->fY = SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
+}
+
+void SkMatrix::Scale_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                        SkPoint* pt) {
+    SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
+             == kScale_Mask);
+    SkASSERT(0 == m.fMat[kMTransX]);
+    SkASSERT(0 == m.fMat[kMTransY]);
+
+    pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]);
+    pt->fY = SkScalarMul(sy, m.fMat[kMScaleY]);
+}
+
+void SkMatrix::Trans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                        SkPoint* pt) {
+    SkASSERT(m.getType() == kTranslate_Mask);
+
+    pt->fX = sx + m.fMat[kMTransX];
+    pt->fY = sy + m.fMat[kMTransY];
+}
+
+void SkMatrix::Identity_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
+                           SkPoint* pt) {
+    SkASSERT(0 == m.getType());
+
+    pt->fX = sx;
+    pt->fY = sy;
+}
+
+const SkMatrix::MapXYProc SkMatrix::gMapXYProcs[] = {
+    SkMatrix::Identity_xy, SkMatrix::Trans_xy,
+    SkMatrix::Scale_xy,    SkMatrix::ScaleTrans_xy,
+    SkMatrix::Rot_xy,      SkMatrix::RotTrans_xy,
+    SkMatrix::Rot_xy,      SkMatrix::RotTrans_xy,
+    // repeat the persp proc 8 times
+    SkMatrix::Persp_xy,    SkMatrix::Persp_xy,
+    SkMatrix::Persp_xy,    SkMatrix::Persp_xy,
+    SkMatrix::Persp_xy,    SkMatrix::Persp_xy,
+    SkMatrix::Persp_xy,    SkMatrix::Persp_xy
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// if its nearly zero (just made up 26, perhaps it should be bigger or smaller)
+#ifdef SK_SCALAR_IS_FIXED
+    typedef SkFract             SkPerspElemType;
+    #define PerspNearlyZero(x)  (SkAbs32(x) < (SK_Fract1 >> 26))
+#else
+    typedef float               SkPerspElemType;
+    #define PerspNearlyZero(x)  SkScalarNearlyZero(x, (1.0f / (1 << 26)))
+#endif
+
+bool SkMatrix::fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const {
+    if (PerspNearlyZero(fMat[kMPersp0])) {
+        if (stepX || stepY) {
+            if (PerspNearlyZero(fMat[kMPersp1]) &&
+                    PerspNearlyZero(fMat[kMPersp2] - kMatrix22Elem)) {
+                if (stepX) {
+                    *stepX = SkScalarToFixed(fMat[kMScaleX]);
+                }
+                if (stepY) {
+                    *stepY = SkScalarToFixed(fMat[kMSkewY]);
+                }
+            } else {
+#ifdef SK_SCALAR_IS_FIXED
+                SkFixed z = SkFractMul(y, fMat[kMPersp1]) +
+                            SkFractToFixed(fMat[kMPersp2]);
+#else
+                float z = y * fMat[kMPersp1] + fMat[kMPersp2];
+#endif
+                if (stepX) {
+                    *stepX = SkScalarToFixed(SkScalarDiv(fMat[kMScaleX], z));
+                }
+                if (stepY) {
+                    *stepY = SkScalarToFixed(SkScalarDiv(fMat[kMSkewY], z));
+                }
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPerspIter.h"
+
+SkPerspIter::SkPerspIter(const SkMatrix& m, SkScalar x0, SkScalar y0, int count)
+        : fMatrix(m), fSX(x0), fSY(y0), fCount(count) {
+    SkPoint pt;
+
+    SkMatrix::Persp_xy(m, x0, y0, &pt);
+    fX = SkScalarToFixed(pt.fX);
+    fY = SkScalarToFixed(pt.fY);
+}
+
+int SkPerspIter::next() {
+    int n = fCount;
+    
+    if (0 == n) {
+        return 0;
+    }
+    SkPoint pt;
+    SkFixed x = fX;
+    SkFixed y = fY;
+    SkFixed dx, dy;
+
+    if (n >= kCount) {
+        n = kCount;
+        fSX += SkIntToScalar(kCount);
+        SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
+        fX = SkScalarToFixed(pt.fX);
+        fY = SkScalarToFixed(pt.fY);
+        dx = (fX - x) >> kShift;
+        dy = (fY - y) >> kShift;
+    } else {
+        fSX += SkIntToScalar(n);
+        SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
+        fX = SkScalarToFixed(pt.fX);
+        fY = SkScalarToFixed(pt.fY);
+        dx = (fX - x) / n;
+        dy = (fY - y) / n;
+    }
+
+    SkFixed* p = fStorage;
+    for (int i = 0; i < n; i++) {
+        *p++ = x; x += dx;
+        *p++ = y; y += dy;
+    }
+    
+    fCount -= n;
+    return n;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FIXED
+
+static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
+    SkFixed x = SK_Fixed1, y = SK_Fixed1;
+    SkPoint pt1, pt2;
+    Sk64    w1, w2;
+
+    if (count > 1) {
+        pt1.fX = poly[1].fX - poly[0].fX;
+        pt1.fY = poly[1].fY - poly[0].fY;
+        y = SkPoint::Length(pt1.fX, pt1.fY);
+        if (y == 0) {
+            return false;
+        }
+        switch (count) {
+            case 2:
+                break;
+            case 3:
+                pt2.fX = poly[0].fY - poly[2].fY;
+                pt2.fY = poly[2].fX - poly[0].fX;
+                goto CALC_X;
+            default:
+                pt2.fX = poly[0].fY - poly[3].fY;
+                pt2.fY = poly[3].fX - poly[0].fX;
+            CALC_X:
+                w1.setMul(pt1.fX, pt2.fX);
+                w2.setMul(pt1.fY, pt2.fY);
+                w1.add(w2);
+                w1.div(y, Sk64::kRound_DivOption);
+                if (!w1.is32()) {
+                    return false;
+                }
+                x = w1.get32();
+                break;
+        }
+    }
+    pt->set(x, y);
+    return true;
+}
+
+bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scalePt) {
+    // need to check if SkFixedDiv overflows...
+
+    const SkFixed scale = scalePt.fY;
+    dst->fMat[kMScaleX] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale);
+    dst->fMat[kMSkewY]  = SkFixedDiv(srcPt[0].fX - srcPt[1].fX, scale);
+    dst->fMat[kMPersp0] = 0;
+    dst->fMat[kMSkewX]  = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale);
+    dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale);
+    dst->fMat[kMPersp1] = 0;
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = SK_Fract1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scale) {
+    // really, need to check if SkFixedDiv overflow'd
+
+    dst->fMat[kMScaleX] = SkFixedDiv(srcPt[2].fX - srcPt[0].fX, scale.fX);
+    dst->fMat[kMSkewY]  = SkFixedDiv(srcPt[2].fY - srcPt[0].fY, scale.fX);
+    dst->fMat[kMPersp0] = 0;
+    dst->fMat[kMSkewX]  = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale.fY);
+    dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale.fY);
+    dst->fMat[kMPersp1] = 0;
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = SK_Fract1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scale) {
+    SkFract a1, a2;
+    SkFixed x0, y0, x1, y1, x2, y2;
+
+    x0 = srcPt[2].fX - srcPt[0].fX;
+    y0 = srcPt[2].fY - srcPt[0].fY;
+    x1 = srcPt[2].fX - srcPt[1].fX;
+    y1 = srcPt[2].fY - srcPt[1].fY;
+    x2 = srcPt[2].fX - srcPt[3].fX;
+    y2 = srcPt[2].fY - srcPt[3].fY;
+
+    /* check if abs(x2) > abs(y2) */
+    if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
+        SkFixed denom = SkMulDiv(x1, y2, x2) - y1;
+        if (0 == denom) {
+            return false;
+        }
+        a1 = SkFractDiv(SkMulDiv(x0 - x1, y2, x2) - y0 + y1, denom);
+    } else {
+        SkFixed denom = x1 - SkMulDiv(y1, x2, y2);
+        if (0 == denom) {
+            return false;
+        }
+        a1 = SkFractDiv(x0 - x1 - SkMulDiv(y0 - y1, x2, y2), denom);
+    }
+
+    /* check if abs(x1) > abs(y1) */
+    if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
+        SkFixed denom = y2 - SkMulDiv(x2, y1, x1);
+        if (0 == denom) {
+            return false;
+        }
+        a2 = SkFractDiv(y0 - y2 - SkMulDiv(x0 - x2, y1, x1), denom);
+    } else {
+        SkFixed denom = SkMulDiv(y2, x1, y1) - x2;
+        if (0 == denom) {
+            return false;
+        }
+        a2 = SkFractDiv(SkMulDiv(y0 - y2, x1, y1) - x0 + x2, denom);
+    }
+
+    // need to check if SkFixedDiv overflows...
+    dst->fMat[kMScaleX] = SkFixedDiv(SkFractMul(a2, srcPt[3].fX) +
+                                     srcPt[3].fX - srcPt[0].fX, scale.fX);
+    dst->fMat[kMSkewY]  = SkFixedDiv(SkFractMul(a2, srcPt[3].fY) +
+                                     srcPt[3].fY - srcPt[0].fY, scale.fX);
+    dst->fMat[kMPersp0] = SkFixedDiv(a2, scale.fX);
+    dst->fMat[kMSkewX]  = SkFixedDiv(SkFractMul(a1, srcPt[1].fX) +
+                                     srcPt[1].fX - srcPt[0].fX, scale.fY);
+    dst->fMat[kMScaleY] = SkFixedDiv(SkFractMul(a1, srcPt[1].fY) +
+                                     srcPt[1].fY - srcPt[0].fY, scale.fY);
+    dst->fMat[kMPersp1] = SkFixedDiv(a1, scale.fY);
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = SK_Fract1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+#else   /* Scalar is float */
+
+static inline bool checkForZero(float x) {
+    return x*x == 0;
+}
+
+static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
+    float   x = 1, y = 1;
+    SkPoint pt1, pt2;
+
+    if (count > 1) {
+        pt1.fX = poly[1].fX - poly[0].fX;
+        pt1.fY = poly[1].fY - poly[0].fY;
+        y = SkPoint::Length(pt1.fX, pt1.fY);
+        if (checkForZero(y)) {
+            return false;
+        }
+        switch (count) {
+            case 2:
+                break;
+            case 3:
+                pt2.fX = poly[0].fY - poly[2].fY;
+                pt2.fY = poly[2].fX - poly[0].fX;
+                goto CALC_X;
+            default:
+                pt2.fX = poly[0].fY - poly[3].fY;
+                pt2.fY = poly[3].fX - poly[0].fX;
+            CALC_X:
+                x = SkScalarDiv(SkScalarMul(pt1.fX, pt2.fX) +
+                                SkScalarMul(pt1.fY, pt2.fY), y);
+                break;
+        }
+    }
+    pt->set(x, y);
+    return true;
+}
+
+bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scale) {
+    float invScale = 1 / scale.fY;
+
+    dst->fMat[kMScaleX] = (srcPt[1].fY - srcPt[0].fY) * invScale;
+    dst->fMat[kMSkewY] = (srcPt[0].fX - srcPt[1].fX) * invScale;
+    dst->fMat[kMPersp0] = 0;
+    dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
+    dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
+    dst->fMat[kMPersp1] = 0;
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = 1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scale) {
+    float invScale = 1 / scale.fX;
+    dst->fMat[kMScaleX] = (srcPt[2].fX - srcPt[0].fX) * invScale;
+    dst->fMat[kMSkewY] = (srcPt[2].fY - srcPt[0].fY) * invScale;
+    dst->fMat[kMPersp0] = 0;
+
+    invScale = 1 / scale.fY;
+    dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
+    dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
+    dst->fMat[kMPersp1] = 0;
+
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = 1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
+                         const SkPoint& scale) {
+    float   a1, a2;
+    float   x0, y0, x1, y1, x2, y2;
+
+    x0 = srcPt[2].fX - srcPt[0].fX;
+    y0 = srcPt[2].fY - srcPt[0].fY;
+    x1 = srcPt[2].fX - srcPt[1].fX;
+    y1 = srcPt[2].fY - srcPt[1].fY;
+    x2 = srcPt[2].fX - srcPt[3].fX;
+    y2 = srcPt[2].fY - srcPt[3].fY;
+
+    /* check if abs(x2) > abs(y2) */
+    if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
+        float denom = SkScalarMulDiv(x1, y2, x2) - y1;
+        if (checkForZero(denom)) {
+            return false;
+        }
+        a1 = SkScalarDiv(SkScalarMulDiv(x0 - x1, y2, x2) - y0 + y1, denom);
+    } else {
+        float denom = x1 - SkScalarMulDiv(y1, x2, y2);
+        if (checkForZero(denom)) {
+            return false;
+        }
+        a1 = SkScalarDiv(x0 - x1 - SkScalarMulDiv(y0 - y1, x2, y2), denom);
+    }
+
+    /* check if abs(x1) > abs(y1) */
+    if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
+        float denom = y2 - SkScalarMulDiv(x2, y1, x1);
+        if (checkForZero(denom)) {
+            return false;
+        }
+        a2 = SkScalarDiv(y0 - y2 - SkScalarMulDiv(x0 - x2, y1, x1), denom);
+    } else {
+        float denom = SkScalarMulDiv(y2, x1, y1) - x2;
+        if (checkForZero(denom)) {
+            return false;
+        }
+        a2 = SkScalarDiv(SkScalarMulDiv(y0 - y2, x1, y1) - x0 + x2, denom);
+    }
+
+    float invScale = 1 / scale.fX;
+    dst->fMat[kMScaleX] = SkScalarMul(SkScalarMul(a2, srcPt[3].fX) +
+                                      srcPt[3].fX - srcPt[0].fX, invScale);
+    dst->fMat[kMSkewY] = SkScalarMul(SkScalarMul(a2, srcPt[3].fY) +
+                                     srcPt[3].fY - srcPt[0].fY, invScale);
+    dst->fMat[kMPersp0] = SkScalarMul(a2, invScale);
+    invScale = 1 / scale.fY;
+    dst->fMat[kMSkewX] = SkScalarMul(SkScalarMul(a1, srcPt[1].fX) +
+                                     srcPt[1].fX - srcPt[0].fX, invScale);
+    dst->fMat[kMScaleY] = SkScalarMul(SkScalarMul(a1, srcPt[1].fY) +
+                                      srcPt[1].fY - srcPt[0].fY, invScale);
+    dst->fMat[kMPersp1] = SkScalarMul(a1, invScale);
+    dst->fMat[kMTransX] = srcPt[0].fX;
+    dst->fMat[kMTransY] = srcPt[0].fY;
+    dst->fMat[kMPersp2] = 1;
+    dst->setTypeMask(kUnknown_Mask);
+    return true;
+}
+
+#endif
+
+typedef bool (*PolyMapProc)(const SkPoint[], SkMatrix*, const SkPoint&);
+
+/*  Taken from Rob Johnson's original sample code in QuickDraw GX
+*/
+bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
+                             int count) {
+    if ((unsigned)count > 4) {
+        SkDebugf("--- SkMatrix::setPolyToPoly count out of range %d\n", count);
+        return false;
+    }
+
+    if (0 == count) {
+        this->reset();
+        return true;
+    }
+    if (1 == count) {
+        this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY);
+        return true;
+    }
+
+    SkPoint scale;
+    if (!poly_to_point(&scale, src, count) ||
+            SkScalarNearlyZero(scale.fX) ||
+            SkScalarNearlyZero(scale.fY)) {
+        return false;
+    }
+
+    static const PolyMapProc gPolyMapProcs[] = {
+        SkMatrix::Poly2Proc, SkMatrix::Poly3Proc, SkMatrix::Poly4Proc
+    };
+    PolyMapProc proc = gPolyMapProcs[count - 2];
+
+    SkMatrix tempMap, result;
+    tempMap.setTypeMask(kUnknown_Mask);
+
+    if (!proc(src, &tempMap, scale)) {
+        return false;
+    }
+    if (!tempMap.invert(&result)) {
+        return false;
+    }
+    if (!proc(dst, &tempMap, scale)) {
+        return false;
+    }
+    if (!result.setConcat(tempMap, result)) {
+        return false;
+    }
+    *this = result;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix::dump() const {
+    SkString str;
+    this->toDumpString(&str);
+    SkDebugf("%s\n", str.c_str());
+}
+
+void SkMatrix::toDumpString(SkString* str) const {
+#ifdef SK_CAN_USE_FLOAT
+    str->printf("[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
+#ifdef SK_SCALAR_IS_FLOAT
+             fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
+             fMat[6], fMat[7], fMat[8]);
+#else
+    SkFixedToFloat(fMat[0]), SkFixedToFloat(fMat[1]), SkFixedToFloat(fMat[2]),
+    SkFixedToFloat(fMat[3]), SkFixedToFloat(fMat[4]), SkFixedToFloat(fMat[5]),
+    SkFractToFloat(fMat[6]), SkFractToFloat(fMat[7]), SkFractToFloat(fMat[8]));
+#endif
+#else   // can't use float
+    str->printf("[%x %x %x][%x %x %x][%x %x %x]",
+                fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
+                fMat[6], fMat[7], fMat[8]);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkMatrix::UnitTest() {
+#ifdef SK_SUPPORT_UNITTEST
+    SkMatrix    mat, inverse, iden1, iden2;
+
+    mat.reset();
+    mat.setTranslate(SK_Scalar1, SK_Scalar1);
+    mat.invert(&inverse);
+    inverse.dump();
+    iden1.setConcat(mat, inverse);
+    iden1.dump();
+
+    mat.setScale(SkIntToScalar(2), SkIntToScalar(2));
+    mat.invert(&inverse);
+    inverse.dump();
+    iden1.setConcat(mat, inverse);
+    iden1.dump();
+
+    mat.setScale(SK_Scalar1/2, SK_Scalar1/2);
+    mat.invert(&inverse);
+    inverse.dump();
+    iden1.setConcat(mat, inverse);
+    iden1.dump();
+    SkASSERT(iden1.isIdentity());
+
+    mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
+    mat.postRotate(SkIntToScalar(25));
+
+    SkASSERT(mat.invert(NULL));
+    mat.invert(&inverse);
+
+    iden1.setConcat(mat, inverse);
+    iden2.setConcat(inverse, mat);
+
+    iden1.dump();
+//    SkASSERT(iden1.isIdentity());
+    iden2.dump();
+//    SkASSERT(iden2.isIdentity());
+    
+    // rectStaysRect test
+    {
+        static const struct {
+            SkScalar    m00, m01, m10, m11;
+            bool        mStaysRect;
+        }
+        gRectStaysRectSamples[] = {
+            {          0,          0,          0,           0, false },
+            {          0,          0,          0,  SK_Scalar1, false },
+            {          0,          0, SK_Scalar1,           0, false },
+            {          0,          0, SK_Scalar1,  SK_Scalar1, false },
+            {          0, SK_Scalar1,          0,           0, false },
+            {          0, SK_Scalar1,          0,  SK_Scalar1, false },
+            {          0, SK_Scalar1, SK_Scalar1,           0, true },
+            {          0, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false },
+            { SK_Scalar1,          0,          0,           0, false },
+            { SK_Scalar1,          0,          0,  SK_Scalar1, true },
+            { SK_Scalar1,          0, SK_Scalar1,           0, false },
+            { SK_Scalar1,          0, SK_Scalar1,  SK_Scalar1, false },
+            { SK_Scalar1, SK_Scalar1,          0,           0, false },
+            { SK_Scalar1, SK_Scalar1,          0,  SK_Scalar1, false },
+            { SK_Scalar1, SK_Scalar1, SK_Scalar1,           0, false },
+            { SK_Scalar1, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false }
+        };
+        
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
+            SkMatrix    m;
+            
+            m.reset();
+            m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
+            m.set(SkMatrix::kMSkewX,  gRectStaysRectSamples[i].m01);
+            m.set(SkMatrix::kMSkewY,  gRectStaysRectSamples[i].m10);
+            m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
+            SkASSERT(m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
+        }
+    }
+#endif
+}
+
+#endif
diff --git a/src/core/SkMemory_stdlib.cpp b/src/core/SkMemory_stdlib.cpp
new file mode 100644
index 0000000..f1ac36b
--- /dev/null
+++ b/src/core/SkMemory_stdlib.cpp
@@ -0,0 +1,287 @@
+/* libs/corecg/SkMemory_stdlib.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 "SkTypes.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef SK_DEBUG
+    #define SK_TAG_BLOCKS
+    // #define SK_TRACK_ALLOC  // enable to see a printf for every alloc/free
+    // #define SK_CHECK_TAGS   // enable to double-check debugging link list
+#endif
+
+#ifdef SK_TAG_BLOCKS
+
+#include "SkThread.h"
+
+// size this (as a multiple of 4) so that the total offset to the internal data
+// is at least a multiple of 8 (since some clients of our malloc may require
+// that.
+static const char kBlockHeaderTag[] = { 's', 'k', 'i', 'a', '1', '2', '3', '4' };
+static const char kBlockTrailerTag[] = { 'a', 'i', 'k', 's' };
+#define kByteFill 0xCD
+#define kDeleteFill 0xEF
+
+static SkMutex& get_block_mutex() {
+    static SkMutex* gBlockMutex;
+    if (NULL == gBlockMutex) {
+        gBlockMutex = new SkMutex;
+    }
+    return *gBlockMutex;
+}
+
+static struct SkBlockHeader* gHeader;
+
+struct SkBlockHeader {
+    SkBlockHeader* fNext;
+#ifdef SK_CHECK_TAGS
+    SkBlockHeader** fTop; // set to verify in debugger that block was alloc'd / freed with same gHeader
+    SkBlockHeader* fPrevious; // set to see in debugger previous block when corruption happens
+#endif
+    size_t fSize;
+    char fHeader[sizeof(kBlockHeaderTag)];
+    // data goes here. The offset to this point must be a multiple of 8
+    char fTrailer[sizeof(kBlockTrailerTag)];
+
+    void* add(size_t realSize) 
+    {
+        SkAutoMutexAcquire  ac(get_block_mutex());
+        InMutexValidate();
+        fNext = gHeader;
+#ifdef SK_CHECK_TAGS
+        fTop = &gHeader;
+        fPrevious = NULL;
+        if (fNext != NULL)
+            fNext->fPrevious = this;
+#endif
+        gHeader = this;
+        fSize = realSize;
+        memcpy(fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag));
+        void* result = fTrailer;
+        void* trailer = (char*)result + realSize;
+        memcpy(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag));
+        return result;
+    }
+    
+    static void Dump()
+    {
+        SkAutoMutexAcquire  ac(get_block_mutex());
+        InMutexValidate();
+        SkBlockHeader* header = gHeader;
+        int count = 0;
+        size_t size = 0;
+        while (header != NULL) {
+            char scratch[256];
+            char* pos = scratch;
+            size_t size = header->fSize;
+            int* data = (int*)(void*)header->fTrailer;
+            pos += sprintf(pos, "%p 0x%08zx (%7zd)  ",
+                data, size, size);
+            size >>= 2;
+            size_t ints = size > 4 ? 4 : size;
+            size_t index;
+            for (index = 0; index < ints; index++)
+                pos += sprintf(pos, "0x%08x ", data[index]);
+            pos += sprintf(pos, " (");
+            for (index = 0; index < ints; index++)
+                pos += sprintf(pos, "%g ", data[index] / 65536.0f);
+            if (ints > 0)
+                --pos;
+            pos += sprintf(pos, ") \"");
+            size_t chars = size > 16 ? 16 : size;
+            char* chPtr = (char*) data;
+            for (index = 0; index < chars; index++) {
+                char ch = chPtr[index];
+                pos += sprintf(pos, "%c", ch >= ' ' && ch < 0x7f ? ch : '?');
+            }
+            pos += sprintf(pos, "\"");
+            SkDebugf("%s\n", scratch);
+            count++;
+            size += header->fSize;
+            header = header->fNext;
+        }
+        SkDebugf("--- count %d  size 0x%08x (%zd) ---\n", count, size, size);
+    }
+
+    void remove() const
+    {
+        SkAutoMutexAcquire  ac(get_block_mutex());
+        SkBlockHeader** findPtr = &gHeader;
+        do {
+            SkBlockHeader* find = *findPtr;
+            SkASSERT(find != NULL);
+            if (find == this) {
+                *findPtr = fNext;
+                break;
+            }
+            findPtr = &find->fNext;
+        } while (true);
+        InMutexValidate();
+        SkASSERT(memcmp(fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag)) == 0);
+        const char* trailer = fTrailer + fSize;
+        SkASSERT(memcmp(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)) == 0);
+    }
+    
+    static void Validate()
+    {
+        SkAutoMutexAcquire  ac(get_block_mutex());
+        InMutexValidate();
+    }
+
+private:
+    static void InMutexValidate()
+    {
+        SkBlockHeader* header = gHeader;
+        while (header != NULL) {
+            SkASSERT(memcmp(header->fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag)) == 0);
+            char* trailer = header->fTrailer + header->fSize;
+            SkASSERT(memcmp(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)) == 0);
+            header = header->fNext;
+        }
+    }
+};
+
+void Dump()
+{
+    SkBlockHeader::Dump();
+}
+
+void ValidateHeap()
+{
+    SkBlockHeader::Validate();
+}
+#else
+void Dump() {}
+void ValidateHeap() {}
+#endif
+
+void sk_throw()
+{
+#ifdef ANDROID
+    fprintf(stderr, "throwing...\n");
+#endif
+    SkASSERT(!"sk_throw");
+    abort();
+}
+
+void sk_out_of_memory(void)
+{
+#ifdef ANDROID
+    fprintf(stderr,"- out of memory in SGL -\n");
+#endif
+    SkASSERT(!"sk_out_of_memory");
+    abort();
+}
+
+void* sk_malloc_throw(size_t size)
+{
+    return sk_malloc_flags(size, SK_MALLOC_THROW);
+}
+
+void* sk_realloc_throw(void* addr, size_t size)
+{
+#ifdef SK_TAG_BLOCKS
+    ValidateHeap();
+    if (addr != NULL) {
+        SkBlockHeader* header = (SkBlockHeader*)
+            ((char*)addr - SK_OFFSETOF(SkBlockHeader, fTrailer));
+        header->remove();
+#ifdef SK_TRACK_ALLOC
+        printf("sk_realloc_throw %p oldSize=%zd\n", addr, header->fSize);
+#endif
+        addr = header;
+    }
+    size_t realSize = size;
+    if (size) 
+        size += sizeof(SkBlockHeader);
+#endif
+
+    void* p = realloc(addr, size);
+    if (size == 0)
+    {
+        ValidateHeap();
+        return p;
+    }
+
+    if (p == NULL)
+        sk_throw();
+#ifdef SK_TAG_BLOCKS
+    else
+    {
+        SkBlockHeader* header = (SkBlockHeader*) p;
+        p = header->add(realSize);
+#ifdef SK_TRACK_ALLOC
+        printf("sk_realloc_throw %p size=%zd\n", p, realSize);
+#endif
+    }
+#endif
+    ValidateHeap();
+    return p;
+}
+
+void sk_free(void* p)
+{
+    if (p)
+    {
+        ValidateHeap();
+#ifdef SK_TAG_BLOCKS
+        SkBlockHeader* header = (SkBlockHeader*) 
+            ((char*)p - SK_OFFSETOF(SkBlockHeader, fTrailer));
+        header->remove();
+#ifdef SK_TRACK_ALLOC
+        printf("sk_free %p size=%zd\n", p, header->fSize);
+#endif
+        size_t size = header->fSize + sizeof(SkBlockHeader);
+        memset(header, kDeleteFill, size);
+        p = header;
+#endif
+        ValidateHeap();
+        free(p);
+        ValidateHeap();
+    }
+}
+
+void* sk_malloc_flags(size_t size, unsigned flags)
+{
+    ValidateHeap();
+#ifdef SK_TAG_BLOCKS
+    size_t realSize = size;
+    size += sizeof(SkBlockHeader);
+#endif
+    
+    void* p = malloc(size);
+    if (p == NULL)
+    {
+        if (flags & SK_MALLOC_THROW)
+            sk_throw();
+    }
+#ifdef SK_TAG_BLOCKS
+    else
+    {
+        SkBlockHeader* header = (SkBlockHeader*) p;
+        p = header->add(realSize);
+        memset(p, kByteFill, realSize);
+#ifdef SK_TRACK_ALLOC
+        printf("sk_malloc_flags %p size=%zd\n", p, realSize);
+#endif
+    }
+#endif
+    ValidateHeap();
+    return p;
+}
+
diff --git a/src/core/SkPackBits.cpp b/src/core/SkPackBits.cpp
new file mode 100644
index 0000000..3e92841
--- /dev/null
+++ b/src/core/SkPackBits.cpp
@@ -0,0 +1,407 @@
+#include "SkPackBits.h"
+
+#define GATHER_STATSx
+
+static inline void small_memcpy(void* SK_RESTRICT dst,
+                                const void* SK_RESTRICT src, int n) {
+    SkASSERT(n > 0 && n <= 15);
+    uint8_t* d = (uint8_t*)dst;
+    const uint8_t* s = (const uint8_t*)src;
+    switch (n) {
+        case 15: *d++ = *s++;
+        case 14: *d++ = *s++;
+        case 13: *d++ = *s++;
+        case 12: *d++ = *s++;
+        case 11: *d++ = *s++;
+        case 10: *d++ = *s++;
+        case  9: *d++ = *s++;
+        case  8: *d++ = *s++;
+        case  7: *d++ = *s++;
+        case  6: *d++ = *s++;
+        case  5: *d++ = *s++;
+        case  4: *d++ = *s++;
+        case  3: *d++ = *s++;
+        case  2: *d++ = *s++;
+        case  1: *d++ = *s++;
+        case  0: break;
+    }
+}
+
+static inline void small_memset(void* dst, uint8_t value, int n) {
+    SkASSERT(n > 0 && n <= 15);
+    uint8_t* d = (uint8_t*)dst;
+    switch (n) {
+        case 15: *d++ = value;
+        case 14: *d++ = value;
+        case 13: *d++ = value;
+        case 12: *d++ = value;
+        case 11: *d++ = value;
+        case 10: *d++ = value;
+        case  9: *d++ = value;
+        case  8: *d++ = value;
+        case  7: *d++ = value;
+        case  6: *d++ = value;
+        case  5: *d++ = value;
+        case  4: *d++ = value;
+        case  3: *d++ = value;
+        case  2: *d++ = value;
+        case  1: *d++ = value;
+        case  0: break;
+    }
+}
+
+// can we do better for small counts with our own inlined memcpy/memset?
+
+#define PB_MEMSET(addr, value, count)       \
+do {                                        \
+if ((count) > 15) {                     \
+memset(addr, value, count);         \
+} else {                                \
+small_memset(addr, value, count);   \
+}                                       \
+} while (0)
+
+#define PB_MEMCPY(dst, src, count)      \
+do {                                    \
+    if ((count) > 15) {                 \
+        memcpy(dst, src, count);        \
+    } else {                            \
+        small_memcpy(dst, src, count);  \
+    }                                   \
+} while (0)
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef GATHER_STATS
+    static int gMemSetBuckets[129];
+    static int gMemCpyBuckets[129];
+    static int gCounter;
+
+static void register_memset_count(int n) {
+    SkASSERT((unsigned)n <= 128);
+    gMemSetBuckets[n] += 1;
+    gCounter += 1;
+    
+    if ((gCounter & 0xFF) == 0) {
+        SkDebugf("----- packbits memset stats: ");
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gMemSetBuckets); i++) {
+            if (gMemSetBuckets[i]) {
+                SkDebugf(" %d:%d", i, gMemSetBuckets[i]);
+            }
+        }
+    }
+}
+static void register_memcpy_count(int n) {
+    SkASSERT((unsigned)n <= 128);
+    gMemCpyBuckets[n] += 1;
+    gCounter += 1;
+    
+    if ((gCounter & 0x1FF) == 0) {
+        SkDebugf("----- packbits memcpy stats: ");
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gMemCpyBuckets); i++) {
+            if (gMemCpyBuckets[i]) {
+                SkDebugf(" %d:%d", i, gMemCpyBuckets[i]);
+            }
+        }
+    }
+}
+#else
+#define register_memset_count(n)
+#define register_memcpy_count(n)
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+size_t SkPackBits::ComputeMaxSize16(int count) {
+    // worst case is the number of 16bit values (times 2) +
+    // 1 byte per (up to) 128 entries.
+    return ((count + 127) >> 7) + (count << 1);
+}
+
+size_t SkPackBits::ComputeMaxSize8(int count) {
+    // worst case is the number of 8bit values + 1 byte per (up to) 128 entries.
+    return ((count + 127) >> 7) + count;
+}
+
+static uint8_t* flush_same16(uint8_t dst[], uint16_t value, int count) {
+    while (count > 0) {
+        int n = count;
+        if (n > 128) {
+            n = 128;
+        }
+        *dst++ = (uint8_t)(n - 1);
+        *dst++ = (uint8_t)(value >> 8);
+        *dst++ = (uint8_t)value;
+        count -= n;
+    }
+    return dst;
+}
+
+static uint8_t* flush_same8(uint8_t dst[], uint8_t value, int count) {
+    while (count > 0) {
+        int n = count;
+        if (n > 128) {
+            n = 128;
+        }
+        *dst++ = (uint8_t)(n - 1);
+        *dst++ = (uint8_t)value;
+        count -= n;
+    }
+    return dst;
+}
+
+static uint8_t* flush_diff16(uint8_t SK_RESTRICT dst[],
+                             const uint16_t SK_RESTRICT src[], int count) {
+    while (count > 0) {
+        int n = count;
+        if (n > 128) {
+            n = 128;
+        }
+        *dst++ = (uint8_t)(n + 127);
+        PB_MEMCPY(dst, src, n * sizeof(uint16_t));
+        src += n;
+        dst += n * sizeof(uint16_t);
+        count -= n;
+    }
+    return dst;
+}
+
+static uint8_t* flush_diff8(uint8_t SK_RESTRICT dst[],
+                            const uint8_t SK_RESTRICT src[], int count) {
+    while (count > 0) {
+        int n = count;
+        if (n > 128) {
+            n = 128;
+        }
+        *dst++ = (uint8_t)(n + 127);
+        PB_MEMCPY(dst, src, n);
+        src += n;
+        dst += n;
+        count -= n;
+    }
+    return dst;
+}
+
+size_t SkPackBits::Pack16(const uint16_t SK_RESTRICT src[], int count,
+                          uint8_t SK_RESTRICT dst[]) {
+    uint8_t* origDst = dst;
+    const uint16_t* stop = src + count;
+
+    for (;;) {
+        count = stop - src;
+        SkASSERT(count >= 0);
+        if (count == 0) {
+            return dst - origDst;
+        }
+        if (1 == count) {
+            *dst++ = 0;
+            *dst++ = (uint8_t)(*src >> 8);
+            *dst++ = (uint8_t)*src;
+            return dst - origDst;
+        }
+        
+        unsigned value = *src;
+        const uint16_t* s = src + 1;
+        
+        if (*s == value) { // accumulate same values...
+            do {
+                s++;
+                if (s == stop) {
+                    break;
+                }
+            } while (*s == value);
+            dst = flush_same16(dst, value, s - src);
+        } else {    // accumulate diff values...
+            do {
+                if (++s == stop) {
+                    goto FLUSH_DIFF;
+                }
+            } while (*s != s[-1]);
+            s -= 1; // back up so we don't grab one of the "same" values that follow
+        FLUSH_DIFF:
+            dst = flush_diff16(dst, src, s - src);
+        }
+        src = s;
+    }
+}
+
+size_t SkPackBits::Pack8(const uint8_t SK_RESTRICT src[], int count,
+                         uint8_t SK_RESTRICT dst[]) {
+    uint8_t* origDst = dst;
+    const uint8_t* stop = src + count;
+
+    for (;;) {
+        count = stop - src;
+        SkASSERT(count >= 0);
+        if (count == 0) {
+            return dst - origDst;
+        }
+        if (1 == count) {
+            *dst++ = 0;
+            *dst++ = *src;
+            return dst - origDst;
+        }
+        
+        unsigned value = *src;
+        const uint8_t* s = src + 1;
+        
+        if (*s == value) { // accumulate same values...
+            do {
+                s++;
+                if (s == stop) {
+                    break;
+                }
+            } while (*s == value);
+            dst = flush_same8(dst, value, s - src);
+        } else {    // accumulate diff values...
+            do {
+                if (++s == stop) {
+                    goto FLUSH_DIFF;
+                }
+                // only stop if we hit 3 in a row,
+                // otherwise we get bigger than compuatemax
+            } while (*s != s[-1] || s[-1] != s[-2]);
+            s -= 2; // back up so we don't grab the "same" values that follow
+        FLUSH_DIFF:
+            dst = flush_diff8(dst, src, s - src);
+        }
+        src = s;
+    }
+}
+
+#include "SkUtils.h"
+
+int SkPackBits::Unpack16(const uint8_t SK_RESTRICT src[], size_t srcSize,
+                         uint16_t SK_RESTRICT dst[]) {
+    uint16_t* origDst = dst;
+    const uint8_t* stop = src + srcSize;
+    
+    while (src < stop) {
+        unsigned n = *src++;
+        if (n <= 127) {   // repeat count (n + 1)
+            n += 1;
+            sk_memset16(dst, (src[0] << 8) | src[1], n);
+            src += 2;
+        } else {    // same count (n - 127)
+            n -= 127;
+            PB_MEMCPY(dst, src, n * sizeof(uint16_t));
+            src += n * sizeof(uint16_t);
+        }
+        dst += n;
+    }
+    SkASSERT(src == stop);
+    return dst - origDst;
+}
+
+int SkPackBits::Unpack8(const uint8_t SK_RESTRICT src[], size_t srcSize,
+                        uint8_t SK_RESTRICT dst[]) {
+    uint8_t* origDst = dst;
+    const uint8_t* stop = src + srcSize;
+    
+    while (src < stop) {
+        unsigned n = *src++;
+        if (n <= 127) {   // repeat count (n + 1)
+            n += 1;
+            PB_MEMSET(dst, *src++, n);
+        } else {    // same count (n - 127)
+            n -= 127;
+            PB_MEMCPY(dst, src, n);
+            src += n;
+        }
+        dst += n;
+    }
+    SkASSERT(src == stop);
+    return dst - origDst;
+}
+
+enum UnpackState {
+    CLEAN_STATE,
+    REPEAT_BYTE_STATE,
+    COPY_SRC_STATE
+};
+
+void SkPackBits::Unpack8(uint8_t SK_RESTRICT dst[], size_t dstSkip,
+                         size_t dstWrite, const uint8_t SK_RESTRICT src[]) {
+    if (dstWrite == 0) {
+        return;
+    }
+
+    UnpackState state = CLEAN_STATE;
+    size_t      stateCount = 0;
+    
+    // state 1: do the skip-loop
+    while (dstSkip > 0) {
+        unsigned n = *src++;
+        if (n <= 127) {   // repeat count (n + 1)
+            n += 1;
+            if (n > dstSkip) {
+                state = REPEAT_BYTE_STATE;
+                stateCount = n - dstSkip;
+                n = dstSkip;
+                // we don't increment src here, since its needed in stage 2
+            } else {
+                src++;  // skip the src byte
+            }
+        } else {    // same count (n - 127)
+            n -= 127;
+            if (n > dstSkip) {
+                state = COPY_SRC_STATE;
+                stateCount = n - dstSkip;
+                n = dstSkip;
+            }
+            src += n;
+        }
+        dstSkip -= n;
+    }
+    
+    // stage 2: perform any catchup from the skip-stage
+    if (stateCount > dstWrite) {
+        stateCount = dstWrite;
+    }
+    switch (state) {
+        case REPEAT_BYTE_STATE:
+            SkASSERT(stateCount > 0);
+            register_memset_count(stateCount);
+            PB_MEMSET(dst, *src++, stateCount);
+            break;
+        case COPY_SRC_STATE:
+            SkASSERT(stateCount > 0);
+            register_memcpy_count(stateCount);
+            PB_MEMCPY(dst, src, stateCount);
+            src += stateCount;
+            break;
+        default:
+            SkASSERT(stateCount == 0);
+            break;
+    }
+    dst += stateCount;
+    dstWrite -= stateCount;
+
+    // copy at most dstWrite bytes into dst[]
+    while (dstWrite > 0) {
+        unsigned n = *src++;
+        if (n <= 127) {   // repeat count (n + 1)
+            n += 1;
+            if (n > dstWrite) {
+                n = dstWrite;
+            }
+            register_memset_count(n);
+            PB_MEMSET(dst, *src++, n);
+        } else {    // same count (n - 127)
+            n -= 127;
+            if (n > dstWrite) {
+                n = dstWrite;
+            }
+            register_memcpy_count(n);
+            PB_MEMCPY(dst, src, n);
+            src += n;
+        }
+        dst += n;
+        dstWrite -= n;
+    }
+    SkASSERT(0 == dstWrite);
+}
+
+
+
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
new file mode 100644
index 0000000..bb6b31e
--- /dev/null
+++ b/src/core/SkPaint.cpp
@@ -0,0 +1,1571 @@
+/* libs/graphics/sgl/SkPaint.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 "SkPaint.h"
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkFontHost.h"
+#include "SkMaskFilter.h"
+#include "SkPathEffect.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkScalerContext.h"
+#include "SkStroke.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+#include "SkAutoKern.h"
+
+#define SK_DefaultTextSize      SkIntToScalar(12)
+
+#define SK_DefaultFlags         0   //(kNativeHintsText_Flag)
+
+SkPaint::SkPaint()
+{
+    fTypeface   = NULL;
+    fTextSize   = SK_DefaultTextSize;
+    fTextScaleX = SK_Scalar1;
+    fTextSkewX  = 0;
+
+    fPathEffect  = NULL;
+    fShader      = NULL;
+    fXfermode    = NULL;
+    fMaskFilter  = NULL;
+    fColorFilter = NULL;
+    fRasterizer  = NULL;
+    fLooper      = NULL;
+
+    fColor      = SK_ColorBLACK;
+    fWidth      = 0;
+    fMiterLimit = SK_DefaultMiterLimit;
+    fFlags      = SK_DefaultFlags;
+    fCapType    = kDefault_Cap;
+    fJoinType   = kDefault_Join;
+    fTextAlign  = kLeft_Align;
+    fStyle      = kFill_Style;
+    fTextEncoding = kUTF8_TextEncoding;
+}
+
+SkPaint::SkPaint(const SkPaint& src)
+{
+    memcpy(this, &src, sizeof(src));
+
+    fTypeface->safeRef();
+    fPathEffect->safeRef();
+    fShader->safeRef();
+    fXfermode->safeRef();
+    fMaskFilter->safeRef();
+    fColorFilter->safeRef();
+    fRasterizer->safeRef();
+    fLooper->safeRef();
+}
+
+SkPaint::~SkPaint()
+{
+    fTypeface->safeUnref();
+    fPathEffect->safeUnref();
+    fShader->safeUnref();
+    fXfermode->safeUnref();
+    fMaskFilter->safeUnref();
+    fColorFilter->safeUnref();
+    fRasterizer->safeUnref();
+    fLooper->safeUnref();
+}
+
+SkPaint& SkPaint::operator=(const SkPaint& src)
+{
+    SkASSERT(&src);
+
+    src.fTypeface->safeRef();
+    src.fPathEffect->safeRef();
+    src.fShader->safeRef();
+    src.fXfermode->safeRef();
+    src.fMaskFilter->safeRef();
+    src.fColorFilter->safeRef();
+    src.fRasterizer->safeRef();
+    src.fLooper->safeRef();
+
+    fTypeface->safeUnref();
+    fPathEffect->safeUnref();
+    fShader->safeUnref();
+    fXfermode->safeUnref();
+    fMaskFilter->safeUnref();
+    fColorFilter->safeUnref();
+    fRasterizer->safeUnref();
+    fLooper->safeUnref();
+
+    memcpy(this, &src, sizeof(src));
+
+    return *this;
+}
+
+int operator==(const SkPaint& a, const SkPaint& b)
+{
+    return memcmp(&a, &b, sizeof(a)) == 0;
+}
+
+void SkPaint::reset()
+{
+    SkPaint init;
+
+    *this = init;
+}
+
+void SkPaint::setFlags(uint32_t flags)
+{
+    fFlags = flags;
+}
+
+void SkPaint::setAntiAlias(bool doAA)
+{
+    this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
+}
+
+void SkPaint::setDither(bool doDither)
+{
+    this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
+}
+
+void SkPaint::setSubpixelText(bool doSubpixel)
+{
+    this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
+}
+
+void SkPaint::setLinearText(bool doLinearText)
+{
+    this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
+}
+
+void SkPaint::setUnderlineText(bool doUnderline)
+{
+    this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
+}
+
+void SkPaint::setStrikeThruText(bool doStrikeThru)
+{
+    this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
+}
+
+void SkPaint::setFakeBoldText(bool doFakeBold)
+{
+    this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
+}
+
+void SkPaint::setDevKernText(bool doDevKern)
+{
+    this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
+}
+
+void SkPaint::setFilterBitmap(bool doFilter)
+{
+    this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
+}
+
+void SkPaint::setStyle(Style style)
+{
+    if ((unsigned)style < kStyleCount)
+        fStyle = style;
+#ifdef SK_DEBUG
+    else
+        SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
+#endif
+}
+
+void SkPaint::setColor(SkColor color)
+{
+    fColor = color;
+}
+
+void SkPaint::setAlpha(U8CPU a)
+{
+    fColor = SkColorSetARGB(a, SkColorGetR(fColor), SkColorGetG(fColor), SkColorGetB(fColor));
+}
+
+void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
+{
+    fColor = SkColorSetARGB(a, r, g, b);
+}
+
+void SkPaint::setStrokeWidth(SkScalar width)
+{
+    if (width >= 0)
+        fWidth = width;
+#ifdef SK_DEBUG
+    else
+        SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
+#endif
+}
+
+void SkPaint::setStrokeMiter(SkScalar limit)
+{
+    if (limit >= 0)
+        fMiterLimit = limit;
+#ifdef SK_DEBUG
+    else
+        SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
+#endif
+}
+
+void SkPaint::setStrokeCap(Cap ct)
+{
+    if ((unsigned)ct < kCapCount)
+        fCapType = SkToU8(ct);
+#ifdef SK_DEBUG
+    else
+        SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
+#endif
+}
+
+void SkPaint::setStrokeJoin(Join jt)
+{
+    if ((unsigned)jt < kJoinCount)
+        fJoinType = SkToU8(jt);
+#ifdef SK_DEBUG
+    else
+        SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////
+
+void SkPaint::setTextAlign(Align align)
+{
+    if ((unsigned)align < kAlignCount)
+        fTextAlign = SkToU8(align);
+#ifdef SK_DEBUG
+    else
+        SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
+#endif
+}
+
+void SkPaint::setTextSize(SkScalar ts)
+{
+    if (ts > 0)
+        fTextSize = ts;
+#ifdef SK_DEBUG
+    else
+        SkDebugf("SkPaint::setTextSize() called with non-positive value\n");
+#endif
+}
+
+void SkPaint::setTextScaleX(SkScalar scaleX)
+{
+    fTextScaleX = scaleX;
+}
+
+void SkPaint::setTextSkewX(SkScalar skewX)
+{
+    fTextSkewX = skewX;
+}
+
+void SkPaint::setTextEncoding(TextEncoding encoding)
+{
+    if ((unsigned)encoding <= kGlyphID_TextEncoding)
+        fTextEncoding = encoding;
+#ifdef SK_DEBUG
+    else
+        SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkPaint::setTypeface(SkTypeface* font)
+{
+    SkRefCnt_SafeAssign(fTypeface, font);
+    return font;
+}
+
+SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r)
+{
+    SkRefCnt_SafeAssign(fRasterizer, r);
+    return r;
+}
+
+SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper)
+{
+    SkRefCnt_SafeAssign(fLooper, looper);
+    return looper;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGlyphCache.h"
+#include "SkUtils.h"
+
+int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
+                          uint16_t glyphs[]) const {
+    if (byteLength == 0) {
+        return 0;
+    }
+    
+    SkASSERT(textData != NULL);
+
+    if (NULL == glyphs) {
+        switch (this->getTextEncoding()) {
+        case kUTF8_TextEncoding:
+            return SkUTF8_CountUnichars((const char*)textData, byteLength);
+        case kUTF16_TextEncoding:
+            return SkUTF16_CountUnichars((const uint16_t*)textData,
+                                         byteLength >> 1);
+        case kGlyphID_TextEncoding:
+            return byteLength >> 1;
+        default:
+            SkASSERT(!"unknown text encoding");
+        }
+        return 0;
+    }
+    
+    // if we get here, we have a valid glyphs[] array, so time to fill it in
+    
+    // handle this encoding before the setup for the glyphcache
+    if (this->getTextEncoding() == kGlyphID_TextEncoding) {
+        // we want to ignore the low bit of byteLength
+        memcpy(glyphs, textData, byteLength >> 1 << 1);
+        return byteLength >> 1;
+    }
+    
+    SkAutoGlyphCache autoCache(*this, NULL);
+    SkGlyphCache*    cache = autoCache.getCache();
+
+    const char* text = (const char*)textData;
+    const char* stop = text + byteLength;
+    uint16_t*   gptr = glyphs;
+
+    switch (this->getTextEncoding()) {
+        case SkPaint::kUTF8_TextEncoding:
+            while (text < stop) {
+                *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
+            }
+            break;
+        case SkPaint::kUTF16_TextEncoding: {
+            const uint16_t* text16 = (const uint16_t*)text;
+            const uint16_t* stop16 = (const uint16_t*)stop;
+            while (text16 < stop16) {
+                *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
+            }
+            break;
+        }
+        default:
+            SkASSERT(!"unknown text encoding");
+    }
+    return gptr - glyphs;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint32_t sk_glyphID_next(const char** text)
+{
+    const uint16_t* glyph = (const uint16_t*)text;
+    int32_t value = *glyph;
+    glyph += 1;
+    *text = (const char*)glyph;
+    return value;
+}
+
+static uint32_t sk_glyphID_prev(const char** text)
+{
+    const uint16_t* glyph = (const uint16_t*)text;
+    glyph -= 1;
+    int32_t value = *glyph;
+    *text = (const char*)glyph;
+    return value;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    const uint16_t* ptr = *(const uint16_t**)text;
+    unsigned glyphID = *ptr;
+    ptr += 1;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDMetrics(glyphID);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    const uint16_t* ptr = *(const uint16_t**)text;
+    ptr -= 1;
+    unsigned glyphID = *ptr;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDMetrics(glyphID);
+}
+
+///
+
+static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharAdvance(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text));
+}
+
+static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    const uint16_t* ptr = *(const uint16_t**)text;
+    unsigned glyphID = *ptr;
+    ptr += 1;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDAdvance(glyphID);
+}
+
+static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache, const char** text)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    const uint16_t* ptr = *(const uint16_t**)text;
+    ptr -= 1;
+    unsigned glyphID = *ptr;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDAdvance(glyphID);
+}
+
+SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd,
+                                                bool needFullMetrics) const
+{
+    static const SkMeasureCacheProc gMeasureCacheProcs[] = {
+        sk_getMetrics_utf8_next,
+        sk_getMetrics_utf16_next,
+        sk_getMetrics_glyph_next,
+        
+        sk_getMetrics_utf8_prev,
+        sk_getMetrics_utf16_prev,
+        sk_getMetrics_glyph_prev,
+        
+        sk_getAdvance_utf8_next,
+        sk_getAdvance_utf16_next,
+        sk_getAdvance_glyph_next,
+        
+        sk_getAdvance_utf8_prev,
+        sk_getAdvance_utf16_prev,
+        sk_getAdvance_glyph_prev
+    };
+    
+    unsigned index = this->getTextEncoding();
+
+    if (kBackward_TextBufferDirection == tbd)
+        index += 3;
+    if (!needFullMetrics && !this->isDevKernText())
+        index += 6;
+
+    SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
+    return gMeasureCacheProcs[index];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache,
+                                            const char** text, SkFixed, SkFixed)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharMetrics(SkUTF8_NextUnichar(text));
+}
+
+static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache,
+                                            const char** text, SkFixed x, SkFixed y)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y);
+}
+
+static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache, const char** text,
+                                             SkFixed, SkFixed)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text));
+}
+
+static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache,
+                                             const char** text, SkFixed x, SkFixed y)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text),
+                                    x, y);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache, const char** text,
+                                             SkFixed, SkFixed)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    const uint16_t* ptr = *(const uint16_t**)text;
+    unsigned glyphID = *ptr;
+    ptr += 1;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDMetrics(glyphID);
+}
+
+static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache,
+                                             const char** text, SkFixed x, SkFixed y)
+{
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+    
+    const uint16_t* ptr = *(const uint16_t**)text;
+    unsigned glyphID = *ptr;
+    ptr += 1;
+    *text = (const char*)ptr;
+    return cache->getGlyphIDMetrics(glyphID, x, y);
+}
+
+SkDrawCacheProc SkPaint::getDrawCacheProc() const
+{
+    static const SkDrawCacheProc gDrawCacheProcs[] = {
+        sk_getMetrics_utf8_00,
+        sk_getMetrics_utf16_00,
+        sk_getMetrics_glyph_00,
+        
+        sk_getMetrics_utf8_xy,
+        sk_getMetrics_utf16_xy,
+        sk_getMetrics_glyph_xy
+    };
+    
+    unsigned index = this->getTextEncoding();
+    if (fFlags & kSubpixelText_Flag)
+        index += 3;
+    
+    SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
+    return gDrawCacheProcs[index];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAutoRestorePaintTextSizeAndFrame {
+public:
+    SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint) : fPaint((SkPaint*)paint)
+    {
+        fTextSize = paint->getTextSize();
+        fStyle = paint->getStyle();
+        fPaint->setStyle(SkPaint::kFill_Style);
+    }
+    ~SkAutoRestorePaintTextSizeAndFrame()
+    {
+        fPaint->setStyle(fStyle);
+        fPaint->setTextSize(fTextSize);
+    }
+    
+private:
+    SkPaint*        fPaint;
+    SkScalar        fTextSize;
+    SkPaint::Style  fStyle;
+};
+
+static void set_bounds(const SkGlyph& g, SkRect* bounds)
+{
+    bounds->set(SkIntToScalar(g.fLeft),
+                SkIntToScalar(g.fTop),
+                SkIntToScalar(g.fLeft + g.fWidth),
+                SkIntToScalar(g.fTop + g.fHeight));
+}
+
+static void join_bounds(const SkGlyph& g, SkRect* bounds, SkFixed dx)
+{
+    SkScalar sx = SkFixedToScalar(dx);
+    bounds->join(SkIntToScalar(g.fLeft) + sx,
+                 SkIntToScalar(g.fTop),
+                 SkIntToScalar(g.fLeft + g.fWidth) + sx,
+                 SkIntToScalar(g.fTop + g.fHeight));
+}
+
+SkScalar SkPaint::measure_text(SkGlyphCache* cache,
+                               const char* text, size_t byteLength,
+                               int* count, SkRect* bounds) const
+{
+    SkASSERT(count);
+    if (byteLength == 0)
+    {
+        *count = 0;
+        if (bounds)
+            bounds->setEmpty();
+        return 0;
+    }
+
+    SkMeasureCacheProc glyphCacheProc;
+    glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
+                                               NULL != bounds);
+
+    int         n = 1;
+    const char* stop = (const char*)text + byteLength;
+    const SkGlyph* g = &glyphCacheProc(cache, &text);
+    SkFixed x = g->fAdvanceX;
+
+    SkAutoKern  autokern;
+
+    if (NULL == bounds)
+    {
+        if (this->isDevKernText())
+        {
+            int rsb;
+            for (; text < stop; n++) {
+                rsb = g->fRsbDelta;
+                g = &glyphCacheProc(cache, &text);
+                x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + g->fAdvanceX;
+            }
+        }
+        else
+        {
+            for (; text < stop; n++) {
+                x += glyphCacheProc(cache, &text).fAdvanceX;
+            }
+        }
+    }
+    else
+    {
+        set_bounds(*g, bounds);
+        if (this->isDevKernText())
+        {
+            int rsb;
+            for (; text < stop; n++) {
+                rsb = g->fRsbDelta;
+                g = &glyphCacheProc(cache, &text);
+                x += SkAutoKern_AdjustF(rsb, g->fLsbDelta);
+                join_bounds(*g, bounds, x);
+                x += g->fAdvanceX;
+            }
+        }
+        else
+        {
+            for (; text < stop; n++) {
+                g = &glyphCacheProc(cache, &text);
+                join_bounds(*g, bounds, x);
+                x += g->fAdvanceX;
+            }
+        }
+    }
+    SkASSERT(text == stop);
+
+    *count = n;
+    return SkFixedToScalar(x);
+}
+
+SkScalar SkPaint::measureText(const void* textData, size_t length,
+                              SkRect* bounds, SkScalar zoom) const
+{
+    const char* text = (const char*)textData;
+    SkASSERT(text != NULL || length == 0);
+
+    SkScalar                            scale = 0;
+    SkAutoRestorePaintTextSizeAndFrame  restore(this);
+
+    if (this->isLinearText())
+    {
+        scale = fTextSize / kCanonicalTextSizeForPaths;
+        // this gets restored by restore
+        ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+    }
+    
+    SkMatrix    zoomMatrix, *zoomPtr = NULL;
+    if (zoom)
+    {
+        zoomMatrix.setScale(zoom, zoom);
+        zoomPtr = &zoomMatrix;
+    }
+
+    SkAutoGlyphCache    autoCache(*this, zoomPtr);
+    SkGlyphCache*       cache = autoCache.getCache();
+
+    SkScalar width = 0;
+    
+    if (length > 0)
+    {
+        int tempCount;
+
+        width = this->measure_text(cache, text, length, &tempCount, bounds);
+        if (scale)
+        {
+            width = SkScalarMul(width, scale);
+            if (bounds)
+            {
+                bounds->fLeft = SkScalarMul(bounds->fLeft, scale);
+                bounds->fTop = SkScalarMul(bounds->fTop, scale);
+                bounds->fRight = SkScalarMul(bounds->fRight, scale);
+                bounds->fBottom = SkScalarMul(bounds->fBottom, scale);
+            }
+        }
+    }
+    return width;
+}
+
+typedef bool (*SkTextBufferPred)(const char* text, const char* stop);
+
+static bool forward_textBufferPred(const char* text, const char* stop)
+{
+    return text < stop;
+}
+
+static bool backward_textBufferPred(const char* text, const char* stop)
+{
+    return text > stop;
+}
+
+static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd,
+                            const char** text, size_t length, const char** stop)
+{
+    if (SkPaint::kForward_TextBufferDirection == tbd)
+    {
+        *stop = *text + length;
+        return forward_textBufferPred;
+    }
+    else
+    {
+        // text should point to the end of the buffer, and stop to the beginning
+        *stop = *text;
+        *text += length;
+        return backward_textBufferPred;
+    }
+}
+
+size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
+                          SkScalar* measuredWidth,
+                          TextBufferDirection tbd) const
+{
+    if (0 == length || 0 >= maxWidth)
+    {
+        if (measuredWidth)
+            *measuredWidth = 0;
+        return 0;
+    }
+
+    SkASSERT(textD != NULL);
+    const char* text = (const char*)textD;
+
+    SkScalar                            scale = 0;
+    SkAutoRestorePaintTextSizeAndFrame  restore(this);
+
+    if (this->isLinearText())
+    {
+        scale = fTextSize / kCanonicalTextSizeForPaths;
+        // this gets restored by restore
+        ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+    }
+    
+    SkAutoGlyphCache    autoCache(*this, NULL);
+    SkGlyphCache*       cache = autoCache.getCache();
+
+    SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
+    const char*      stop;
+    SkTextBufferPred pred = chooseTextBufferPred(tbd, &text, length, &stop);
+    SkFixed          max = SkScalarToFixed(maxWidth);
+    SkFixed          width = 0;
+
+    SkAutoKern  autokern;
+
+    if (this->isDevKernText())
+    {
+        int rsb = 0;
+        while (pred(text, stop))
+        {
+            const char* curr = text;
+            const SkGlyph& g = glyphCacheProc(cache, &text);
+            SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + g.fAdvanceX;
+            if ((width += x) > max)
+            {
+                width -= x;
+                text = curr;
+                break;
+            }
+            rsb = g.fRsbDelta;
+        }
+    }
+    else
+    {
+        while (pred(text, stop))
+        {
+            const char* curr = text;
+            SkFixed x = glyphCacheProc(cache, &text).fAdvanceX;
+            if ((width += x) > max)
+            {
+                width -= x;
+                text = curr;
+                break;
+            }
+        }
+    }
+    
+    if (measuredWidth)
+    {
+        
+        SkScalar scalarWidth = SkFixedToScalar(width);
+        if (scale)
+            scalarWidth = SkScalarMul(scalarWidth, scale);
+        *measuredWidth = scalarWidth;
+    }
+    
+    // return the number of bytes measured
+    return (kForward_TextBufferDirection == tbd) ?
+                text - stop + length : stop - text + length;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context)
+{
+    *(SkPaint::FontMetrics*)context = cache->getFontMetricsY();
+    return false;   // don't detach the cache
+}
+
+static void FontMetricsDescProc(const SkDescriptor* desc, void* context)
+{
+    SkGlyphCache::VisitCache(desc, FontMetricsCacheProc, context);
+}
+
+SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const
+{
+    SkScalar                            scale = 0;
+    SkAutoRestorePaintTextSizeAndFrame  restore(this);
+
+    if (this->isLinearText())
+    {
+        scale = fTextSize / kCanonicalTextSizeForPaths;
+        // this gets restored by restore
+        ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+    }
+    
+    SkMatrix    zoomMatrix, *zoomPtr = NULL;
+    if (zoom)
+    {
+        zoomMatrix.setScale(zoom, zoom);
+        zoomPtr = &zoomMatrix;
+    }
+
+#if 0
+    SkAutoGlyphCache    autoCache(*this, zoomPtr);
+    SkGlyphCache*       cache = autoCache.getCache();
+    const FontMetrics&  my = cache->getFontMetricsY();
+#endif
+    FontMetrics storage;
+    if (NULL == metrics)
+        metrics = &storage;
+    
+    this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics);
+
+    if (scale)
+    {
+        metrics->fTop = SkScalarMul(metrics->fTop, scale);
+        metrics->fAscent = SkScalarMul(metrics->fAscent, scale);
+        metrics->fDescent = SkScalarMul(metrics->fDescent, scale);
+        metrics->fBottom = SkScalarMul(metrics->fBottom, scale);
+        metrics->fLeading = SkScalarMul(metrics->fLeading, scale);
+    }
+    return metrics->fDescent - metrics->fAscent + metrics->fLeading;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale)
+{
+    bounds->set(g.fLeft * scale,
+                g.fTop * scale,
+                (g.fLeft + g.fWidth) * scale,
+                (g.fTop + g.fHeight) * scale);
+}
+
+int SkPaint::getTextWidths(const void* textData, size_t byteLength, SkScalar widths[],
+                           SkRect bounds[]) const
+{
+    if (0 == byteLength)
+        return 0;
+
+    SkASSERT(NULL != textData);
+
+    if (NULL == widths && NULL == bounds)
+        return this->countText(textData, byteLength);
+
+    SkAutoRestorePaintTextSizeAndFrame  restore(this);
+    SkScalar                            scale = 0;
+
+    if (this->isLinearText())
+    {
+        scale = fTextSize / kCanonicalTextSizeForPaths;
+        // this gets restored by restore
+        ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
+    }
+
+    SkAutoGlyphCache    autoCache(*this, NULL);
+    SkGlyphCache*       cache = autoCache.getCache();
+    SkMeasureCacheProc  glyphCacheProc;
+    glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
+                                               NULL != bounds);
+
+    const char* text = (const char*)textData;
+    const char* stop = text + byteLength;
+    int         count = 0;
+
+    if (this->isDevKernText())
+    {
+        // we adjust the widths returned here through auto-kerning
+        SkAutoKern  autokern;
+        SkFixed     prevWidth = 0;
+
+        if (scale) {
+            while (text < stop) {
+                const SkGlyph& g = glyphCacheProc(cache, &text);
+                if (widths) {
+                    SkFixed  adjust = autokern.adjust(g);
+
+                    if (count > 0) {
+                        SkScalar w = SkFixedToScalar(prevWidth + adjust);
+                        *widths++ = SkScalarMul(w, scale);
+                    }
+                    prevWidth = g.fAdvanceX;
+                }
+                if (bounds) {
+                    set_bounds(g, bounds++, scale);
+                }
+                ++count;
+            }
+            if (count > 0 && widths) {
+                *widths = SkScalarMul(SkFixedToScalar(prevWidth), scale);
+            }
+        } else {
+            while (text < stop) {
+                const SkGlyph& g = glyphCacheProc(cache, &text);
+                if (widths) {
+                    SkFixed  adjust = autokern.adjust(g);
+
+                    if (count > 0) {
+                        *widths++ = SkFixedToScalar(prevWidth + adjust);
+                    }
+                    prevWidth = g.fAdvanceX;
+                }
+                if (bounds) {
+                    set_bounds(g, bounds++);
+                }
+                ++count;
+            }
+            if (count > 0 && widths) {
+                *widths = SkFixedToScalar(prevWidth);
+            }
+        }
+    } else {    // no devkern
+        if (scale) {
+            while (text < stop) {
+                const SkGlyph& g = glyphCacheProc(cache, &text);
+                if (widths) {
+                    *widths++ = SkScalarMul(SkFixedToScalar(g.fAdvanceX),
+                                            scale);
+                }
+                if (bounds) {
+                    set_bounds(g, bounds++, scale);
+                }
+                ++count;
+            }
+        } else {
+            while (text < stop) {
+                const SkGlyph& g = glyphCacheProc(cache, &text);
+                if (widths) {
+                    *widths++ = SkFixedToScalar(g.fAdvanceX);
+                }
+                if (bounds) {
+                    set_bounds(g, bounds++);
+                }
+                ++count;
+            }
+        }
+    }
+
+    SkASSERT(text == stop);
+    return count;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkDraw.h"
+
+void SkPaint::getTextPath(const void* textData, size_t length, SkScalar x, SkScalar y, SkPath* path) const
+{
+    const char* text = (const char*)textData;
+    SkASSERT(length == 0 || text != NULL);
+    if (text == NULL || length == 0 || path == NULL)
+        return;
+
+    SkTextToPathIter    iter(text, length, *this, false, true);
+    SkMatrix            matrix;
+    SkScalar            prevXPos = 0;
+
+    matrix.setScale(iter.getPathScale(), iter.getPathScale());
+    matrix.postTranslate(x, y);
+    path->reset();
+
+    SkScalar        xpos;
+    const SkPath*   iterPath;
+    while ((iterPath = iter.next(&xpos)) != NULL)
+    {
+        matrix.postTranslate(xpos - prevXPos, 0);
+        path->addPath(*iterPath, matrix);
+        prevXPos = xpos;
+    }
+}
+
+static void add_flattenable(SkDescriptor* desc, uint32_t tag,
+                            SkFlattenableWriteBuffer* buffer) {
+    buffer->flatten(desc->addEntry(tag, buffer->size(), NULL));
+}
+
+/*
+ *  interpolates to find the right value for key, in the function represented by the 'length' number of pairs: (keys[i], values[i])
+    inspired by a desire to change the multiplier for thickness in fakebold
+    therefore, i assumed number of pairs (length) will be small, so a linear search is sufficient
+    repeated keys are allowed for discontinuous functions (so long as keys is monotonically increasing), and if 
+        key is the value of a repeated scalar in keys, the first one will be used 
+    - this may change if a binary search is used
+    - also, this ensures that there is no divide by zero (an assert also checks for that)
+*/
+static SkScalar interpolate(SkScalar key, const SkScalar keys[], const SkScalar values[], int length)
+{
+
+    SkASSERT(length > 0);
+    SkASSERT(keys != NULL);    
+    SkASSERT(values != NULL);
+#ifdef SK_DEBUG
+    for (int i = 1; i < length; i++)
+        SkASSERT(keys[i] >= keys[i-1]);
+#endif
+    int right = 0;
+    while (right < length && key > keys[right])
+        right++;
+    //could use sentinal values to eliminate conditionals
+    //i assume i am not in control of input values, so i want to make it simple
+    if (length == right)
+        return values[length-1];
+    if (0 == right)
+        return values[0];
+    //otherwise, we interpolate between right-1 and right
+    SkScalar rVal = values[right];
+    SkScalar lVal = values[right-1];
+    SkScalar rightKey = keys[right];
+    SkScalar leftKey = keys[right-1];
+    SkASSERT(rightKey != leftKey);
+    //fractional amount which we will multiply by the difference in the left value and right value
+    SkScalar fract = SkScalarDiv(key-leftKey,rightKey-leftKey);
+    return lVal + SkScalarMul(fract, rVal-lVal);
+}
+
+//used for interpolating in fakeBold
+static const SkScalar pointSizes[] = { SkIntToScalar(9), SkIntToScalar(36) };
+static const SkScalar multipliers[] = { SK_Scalar1/24, SK_Scalar1/32 };
+
+static SkMask::Format computeMaskFormat(const SkPaint& paint)
+{
+    uint32_t flags = paint.getFlags();
+    
+    return (flags & SkPaint::kAntiAlias_Flag) ? SkMask::kA8_Format : SkMask::kBW_Format;
+}
+
+static SkScalerContext::Hints computeScalerHints(const SkPaint& paint)
+{
+    uint32_t flags = paint.getFlags();
+    
+    if (flags & SkPaint::kLinearText_Flag)
+        return SkScalerContext::kNo_Hints;
+    else if (flags & SkPaint::kSubpixelText_Flag)
+        return SkScalerContext::kSubpixel_Hints;
+    else
+        return SkScalerContext::kNormal_Hints;
+}
+
+void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix, Rec* rec)
+{
+    SkASSERT(deviceMatrix == NULL || (deviceMatrix->getType() & SkMatrix::kPerspective_Mask) == 0);
+
+    rec->fFontID = SkTypeface::UniqueID(paint.getTypeface());
+    rec->fTextSize = paint.getTextSize();
+    rec->fPreScaleX = paint.getTextScaleX();
+    rec->fPreSkewX  = paint.getTextSkewX();
+
+    if (deviceMatrix)
+    {
+        rec->fPost2x2[0][0] = deviceMatrix->getScaleX();
+        rec->fPost2x2[0][1] = deviceMatrix->getSkewX();
+        rec->fPost2x2[1][0] = deviceMatrix->getSkewY();
+        rec->fPost2x2[1][1] = deviceMatrix->getScaleY();
+    }
+    else
+    {
+        rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
+        rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
+    }
+    
+    SkPaint::Style  style = paint.getStyle();
+    SkScalar        strokeWidth = paint.getStrokeWidth();
+    
+    if (paint.isFakeBoldText())
+    {
+        SkScalar fakeBoldScale = interpolate(paint.getTextSize(), pointSizes, multipliers, 2);
+        SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale);
+        
+        if (style == SkPaint::kFill_Style)
+        {
+            style = SkPaint::kStrokeAndFill_Style;
+            strokeWidth = extra;    // ignore paint's strokeWidth if it was "fill"
+        }
+        else
+            strokeWidth += extra;
+    }
+
+    unsigned flags = SkFontHost::ComputeGammaFlag(paint);
+
+    if (paint.isDevKernText())
+        flags |= SkScalerContext::kDevKernText_Flag;
+    
+    if (style != SkPaint::kFill_Style && strokeWidth > 0)
+    {
+        rec->fFrameWidth = strokeWidth;
+        rec->fMiterLimit = paint.getStrokeMiter();
+        rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
+
+        if (style == SkPaint::kStrokeAndFill_Style)
+            flags |= SkScalerContext::kFrameAndFill_Flag;
+    }
+    else
+    {
+        rec->fFrameWidth = 0;
+        rec->fMiterLimit = 0;
+        rec->fStrokeJoin = 0;
+    }
+
+    rec->fHints = SkToU8(computeScalerHints(paint));
+    rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
+    rec->fFlags = SkToU8(flags);
+}
+
+#define MIN_SIZE_FOR_EFFECT_BUFFER  1024
+
+void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
+                             void (*proc)(const SkDescriptor*, void*),
+                             void* context) const
+{
+    SkScalerContext::Rec    rec;
+
+    SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
+
+    size_t          descSize = sizeof(rec);
+    int             entryCount = 1;
+    SkPathEffect*   pe = this->getPathEffect();
+    SkMaskFilter*   mf = this->getMaskFilter();
+    SkRasterizer*   ra = this->getRasterizer();
+
+    SkFlattenableWriteBuffer    peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+    SkFlattenableWriteBuffer    mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+    SkFlattenableWriteBuffer    raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+
+    if (pe) {
+        peBuffer.writeFlattenable(pe);
+        descSize += peBuffer.size();
+        entryCount += 1;
+        rec.fMaskFormat = SkMask::kA8_Format;   // force antialiasing when we do the scan conversion
+        // seems like we could support kLCD as well at this point...
+    }
+    if (mf) {
+        mfBuffer.writeFlattenable(mf);
+        descSize += mfBuffer.size();
+        entryCount += 1;
+        rec.fMaskFormat = SkMask::kA8_Format;   // force antialiasing with maskfilters
+    }
+    if (ra) {
+        raBuffer.writeFlattenable(ra);
+        descSize += raBuffer.size();
+        entryCount += 1;
+        rec.fMaskFormat = SkMask::kA8_Format;   // force antialiasing when we do the scan conversion
+    }
+    descSize += SkDescriptor::ComputeOverhead(entryCount);
+
+    SkAutoDescriptor    ad(descSize);
+    SkDescriptor*       desc = ad.getDesc();
+
+    desc->init();
+    desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+
+    if (pe) {
+        add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
+    }
+    if (mf) {
+        add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
+    }
+    if (ra) {
+        add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
+    }
+
+    SkASSERT(descSize == desc->getLength());
+    desc->computeChecksum();
+
+    proc(desc, context);
+}
+
+static void DetachDescProc(const SkDescriptor* desc, void* context)
+{
+    *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc);
+}
+
+SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const
+{
+    SkGlyphCache* cache;
+    this->descriptorProc(deviceMatrix, DetachDescProc, &cache);
+    return cache;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
+    buffer.writeTypeface(this->getTypeface());
+    buffer.writeScalar(this->getTextSize());
+    buffer.writeScalar(this->getTextScaleX());
+    buffer.writeScalar(this->getTextSkewX());
+    buffer.writeFlattenable(this->getPathEffect());
+    buffer.writeFlattenable(this->getShader());
+    buffer.writeFlattenable(this->getXfermode());
+    buffer.writeFlattenable(this->getMaskFilter());
+    buffer.writeFlattenable(this->getColorFilter());
+    buffer.writeFlattenable(this->getRasterizer());
+    buffer.writeFlattenable(this->getLooper());
+    buffer.write32(this->getColor());
+    buffer.writeScalar(this->getStrokeWidth());
+    buffer.writeScalar(this->getStrokeMiter());
+    buffer.write16(this->getFlags());
+    buffer.write8(this->getTextAlign());
+    buffer.write8(this->getStrokeCap());
+    buffer.write8(this->getStrokeJoin());
+    buffer.write8(this->getStyle());
+    buffer.write8(this->getTextEncoding());
+}
+
+void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
+    this->setTypeface(buffer.readTypeface());
+    this->setTextSize(buffer.readScalar());
+    this->setTextScaleX(buffer.readScalar());
+    this->setTextSkewX(buffer.readScalar());
+    this->setPathEffect((SkPathEffect*) buffer.readFlattenable())->safeUnref();
+    this->setShader((SkShader*) buffer.readFlattenable())->safeUnref();
+    this->setXfermode((SkXfermode*) buffer.readFlattenable())->safeUnref();
+    this->setMaskFilter((SkMaskFilter*) buffer.readFlattenable())->safeUnref();
+    this->setColorFilter((SkColorFilter*) buffer.readFlattenable())->safeUnref();
+    this->setRasterizer((SkRasterizer*) buffer.readFlattenable())->safeUnref();
+    this->setLooper((SkDrawLooper*) buffer.readFlattenable())->safeUnref();
+    this->setColor(buffer.readU32());
+    this->setStrokeWidth(buffer.readScalar());
+    this->setStrokeMiter(buffer.readScalar());
+    this->setFlags(buffer.readU16());
+    this->setTextAlign((SkPaint::Align) buffer.readU8());
+    this->setStrokeCap((SkPaint::Cap) buffer.readU8());
+    this->setStrokeJoin((SkPaint::Join) buffer.readU8());
+    this->setStyle((SkPaint::Style) buffer.readU8());
+    this->setTextEncoding((SkPaint::TextEncoding) buffer.readU8());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkShader* SkPaint::setShader(SkShader* shader)
+{
+    SkRefCnt_SafeAssign(fShader, shader);
+    return shader;
+}
+
+SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter)
+{
+    SkRefCnt_SafeAssign(fColorFilter, filter);
+    return filter;
+}
+
+SkXfermode* SkPaint::setXfermode(SkXfermode* mode)
+{
+    SkRefCnt_SafeAssign(fXfermode, mode);
+    return mode;
+}
+
+SkXfermode* SkPaint::setPorterDuffXfermode(SkPorterDuff::Mode mode)
+{
+    fXfermode->safeUnref();
+    fXfermode = SkPorterDuff::CreateXfermode(mode);
+    return fXfermode;
+}
+
+SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect)
+{
+    SkRefCnt_SafeAssign(fPathEffect, effect);
+    return effect;
+}
+
+SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter)
+{
+    SkRefCnt_SafeAssign(fMaskFilter, filter);
+    return filter;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const
+{
+    SkPath          effectPath, strokePath;
+    const SkPath*   path = &src;
+
+    SkScalar width = this->getStrokeWidth();
+    
+    switch (this->getStyle()) {
+    case SkPaint::kFill_Style:
+        width = -1; // mark it as no-stroke
+        break;
+    case SkPaint::kStrokeAndFill_Style:
+        if (width == 0)
+            width = -1; // mark it as no-stroke
+        break;
+    case SkPaint::kStroke_Style:
+        break;
+    default:
+        SkASSERT(!"unknown paint style");
+    }
+
+    if (this->getPathEffect())
+    {
+        // lie to the pathEffect if our style is strokeandfill, so that it treats us as just fill
+        if (this->getStyle() == SkPaint::kStrokeAndFill_Style)
+            width = -1; // mark it as no-stroke
+
+        if (this->getPathEffect()->filterPath(&effectPath, src, &width))
+            path = &effectPath;
+        
+        // restore the width if we earlier had to lie, and if we're still set to no-stroke
+        // note: if we're now stroke (width >= 0), then the pathEffect asked for that change
+        // and we want to respect that (i.e. don't overwrite their setting for width)
+        if (this->getStyle() == SkPaint::kStrokeAndFill_Style && width < 0)
+        {
+            width = this->getStrokeWidth();
+            if (width == 0)
+                width = -1;
+        }
+    }
+    
+    if (width > 0 && !path->isEmpty())
+    {
+        SkStroke stroker(*this, width);
+        stroker.strokePath(*path, &strokePath);
+        path = &strokePath;
+    }
+
+    if (path == &src)
+        *dst = src;
+    else
+    {
+        SkASSERT(path == &effectPath || path == &strokePath);
+        dst->swap(*(SkPath*)path);
+    }
+
+    return width != 0;  // return true if we're filled, or false if we're hairline (width == 0)
+}
+
+bool SkPaint::canComputeFastBounds() const {
+    // use bit-or since no need for early exit
+    return (reinterpret_cast<uintptr_t>(this->getMaskFilter()) |
+            reinterpret_cast<uintptr_t>(this->getLooper()) |
+            reinterpret_cast<uintptr_t>(this->getRasterizer()) |
+            reinterpret_cast<uintptr_t>(this->getPathEffect())) == 0;
+}
+
+const SkRect& SkPaint::computeFastBounds(const SkRect& src,
+                                         SkRect* storage) const {
+    SkASSERT(storage);
+    
+    if (this->getStyle() != SkPaint::kFill_Style) {
+        // if we're stroked, outset the rect by the radius (and join type)
+        SkScalar radius = SkScalarHalf(this->getStrokeWidth());
+        
+        if (0 == radius) {  // hairline
+            radius = SK_Scalar1;
+        } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
+            SkScalar scale = this->getStrokeMiter();
+            if (scale > SK_Scalar1) {
+                radius = SkScalarMul(radius, scale);
+            }
+        }
+        storage->set(src.fLeft - radius, src.fTop - radius,
+                     src.fRight + radius, src.fBottom + radius);
+        return *storage;
+    }
+    // no adjustments needed, just return the original rect
+    return src;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+static bool has_thick_frame(const SkPaint& paint)
+{
+    return paint.getStrokeWidth() > 0 && paint.getStyle() != SkPaint::kFill_Style;
+}
+
+SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
+                                    const SkPaint& paint,
+                                    bool applyStrokeAndPathEffects,
+                                    bool forceLinearTextOn)
+                                    : fPaint(paint) /* make a copy of the paint */
+{
+    fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
+                                                true);
+
+    if (forceLinearTextOn)
+        fPaint.setLinearText(true);
+    fPaint.setMaskFilter(NULL);   // don't want this affecting our path-cache lookup
+
+    if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint))
+        applyStrokeAndPathEffects = false;
+
+    // can't use our canonical size if we need to apply patheffects/strokes
+    if (fPaint.isLinearText() && !applyStrokeAndPathEffects)
+    {
+        fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
+        fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
+    }
+    else
+        fScale = SK_Scalar1;
+    
+    if (!applyStrokeAndPathEffects)
+    {
+        fPaint.setStyle(SkPaint::kFill_Style);
+        fPaint.setPathEffect(NULL);
+    }
+
+    fCache = fPaint.detachCache(NULL);
+
+    SkPaint::Style  style = SkPaint::kFill_Style;
+    SkPathEffect*   pe = NULL;
+
+    if (!applyStrokeAndPathEffects)
+    {
+        style = paint.getStyle();   // restore
+        pe = paint.getPathEffect();     // restore
+    }
+    fPaint.setStyle(style);
+    fPaint.setPathEffect(pe);
+    fPaint.setMaskFilter(paint.getMaskFilter());    // restore
+
+    // now compute fXOffset if needed
+
+    SkScalar xOffset = 0;
+    if (paint.getTextAlign() != SkPaint::kLeft_Align)   // need to measure first
+    {
+        int      count;
+        SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length, &count, NULL), fScale);
+        if (paint.getTextAlign() == SkPaint::kCenter_Align)
+            width = SkScalarHalf(width);
+        xOffset = -width;
+    }
+    fXPos = xOffset;
+    fPrevAdvance = 0;
+
+    fText = text;
+    fStop = text + length;
+}
+
+SkTextToPathIter::~SkTextToPathIter()
+{
+    SkGlyphCache::AttachCache(fCache);
+}
+
+const SkPath* SkTextToPathIter::next(SkScalar* xpos)
+{
+    while (fText < fStop)
+    {
+        const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
+
+        fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
+        fPrevAdvance = glyph.fAdvanceX;   // + fPaint.getTextTracking();
+
+        if (glyph.fWidth)
+        {
+            if (xpos)
+                *xpos = fXPos;
+            return fCache->findPath(glyph);
+        }
+    }
+    return NULL;
+}
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
new file mode 100644
index 0000000..82eb980
--- /dev/null
+++ b/src/core/SkPath.cpp
@@ -0,0 +1,1438 @@
+/* libs/graphics/sgl/SkPath.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 "SkPath.h"
+#include "SkFlattenable.h"
+#include "SkMath.h"
+
+////////////////////////////////////////////////////////////////////////////
+
+/*  This guy's constructor/destructor bracket a path editing operation. It is
+    used when we know the bounds of the amount we are going to add to the path
+    (usually a new contour, but not required).
+ 
+    It captures some state about the path up front (i.e. if it already has a
+    cached bounds), and the if it can, it updates the cache bounds explicitly,
+    avoiding the need to revisit all of the points in computeBounds().
+ */
+class SkAutoPathBoundsUpdate {
+public:
+    SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
+        this->init(path);
+    }
+
+    SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
+                           SkScalar right, SkScalar bottom) {
+        fRect.set(left, top, right, bottom);
+        this->init(path);
+    }
+    
+    ~SkAutoPathBoundsUpdate() {
+        if (fEmpty) {
+            fPath->fFastBounds = fRect;
+            fPath->fFastBoundsIsDirty = false;
+        } else if (!fDirty) {
+            fPath->fFastBounds.join(fRect);
+            fPath->fFastBoundsIsDirty = false;
+        }
+    }
+    
+private:
+    const SkPath*   fPath;
+    SkRect          fRect;
+    bool            fDirty;
+    bool            fEmpty;
+    
+    // returns true if we should proceed
+    void init(const SkPath* path) {
+        fPath = path;
+        fDirty = path->fFastBoundsIsDirty;
+        fEmpty = path->isEmpty();
+    }
+};
+
+static void compute_fast_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
+    if (pts.count() <= 1) {  // we ignore just 1 point (moveto)
+        bounds->set(0, 0, 0, 0);
+    } else {
+        bounds->set(pts.begin(), pts.count());
+//        SkDebugf("------- compute bounds %p %d", &pts, pts.count());
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/*
+    Stores the verbs and points as they are given to us, with exceptions:
+    - we only record "Close" if it was immediately preceeded by Line | Quad | Cubic
+    - we insert a Move(0,0) if Line | Quad | Cubic is our first command
+
+    The iterator does more cleanup, especially if forceClose == true
+    1. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
+    2. if we encounter Move without a preceeding Close, and forceClose is true, goto #1
+    3. if we encounter Line | Quad | Cubic after Close, cons up a Move
+*/
+
+////////////////////////////////////////////////////////////////////////////
+
+SkPath::SkPath() : fFastBoundsIsDirty(true), fFillType(kWinding_FillType) {}
+
+SkPath::SkPath(const SkPath& src) {
+    SkDEBUGCODE(src.validate();)
+    *this = src;
+}
+
+SkPath::~SkPath() {
+    SkDEBUGCODE(this->validate();)
+}
+
+SkPath& SkPath::operator=(const SkPath& src) {
+    SkDEBUGCODE(src.validate();)
+
+    if (this != &src) {
+        fFastBounds         = src.fFastBounds;
+        fPts                = src.fPts;
+        fVerbs              = src.fVerbs;
+        fFillType           = src.fFillType;
+        fFastBoundsIsDirty  = src.fFastBoundsIsDirty;
+    }
+    SkDEBUGCODE(this->validate();)
+    return *this;
+}
+
+void SkPath::swap(SkPath& other) {
+    SkASSERT(&other != NULL);
+
+    if (this != &other) {
+        SkTSwap<SkRect>(fFastBounds, other.fFastBounds);
+        fPts.swap(other.fPts);
+        fVerbs.swap(other.fVerbs);
+        SkTSwap<uint8_t>(fFillType, other.fFillType);
+        SkTSwap<uint8_t>(fFastBoundsIsDirty, other.fFastBoundsIsDirty);
+    }
+}
+
+void SkPath::reset() {
+    SkDEBUGCODE(this->validate();)
+
+    fPts.reset();
+    fVerbs.reset();
+    fFastBoundsIsDirty = true;
+}
+
+void SkPath::rewind() {
+    SkDEBUGCODE(this->validate();)
+
+    fPts.rewind();
+    fVerbs.rewind();
+    fFastBoundsIsDirty = true;
+}
+
+bool SkPath::isEmpty() const {
+    SkDEBUGCODE(this->validate();)
+
+    int count = fVerbs.count();
+    return count == 0 || (count == 1 && fVerbs[0] == kMove_Verb);
+}
+
+bool SkPath::isRect(SkRect*) const {
+    SkDEBUGCODE(this->validate();)
+
+    SkASSERT(!"unimplemented");
+    return false;
+}
+
+int SkPath::getPoints(SkPoint copy[], int max) const {
+    SkDEBUGCODE(this->validate();)
+
+    SkASSERT(max >= 0);
+    int count = fPts.count();
+    if (copy && max > 0 && count > 0) {
+        memcpy(copy, fPts.begin(), sizeof(SkPoint) * SkMin32(max, count));
+    }
+    return count;
+}
+
+void SkPath::getLastPt(SkPoint* lastPt) const {
+    SkDEBUGCODE(this->validate();)
+
+    if (lastPt) {
+        int count = fPts.count();
+        if (count == 0) {
+            lastPt->set(0, 0);
+        } else {
+            *lastPt = fPts[count - 1];
+        }
+    }
+}
+
+void SkPath::setLastPt(SkScalar x, SkScalar y) {
+    SkDEBUGCODE(this->validate();)
+
+    int count = fPts.count();
+    if (count == 0) {
+        this->moveTo(x, y);
+    } else {
+        fPts[count - 1].set(x, y);
+    }
+}
+
+#define ALWAYS_FAST_BOUNDS_FOR_NOW  true
+
+void SkPath::computeBounds(SkRect* bounds, BoundsType bt) const {
+    SkDEBUGCODE(this->validate();)
+
+    SkASSERT(bounds);
+    
+    // we BoundsType for now
+
+    if (fFastBoundsIsDirty) {
+        fFastBoundsIsDirty = false;
+        compute_fast_bounds(&fFastBounds, fPts);
+    }
+    *bounds = fFastBounds;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//  Construction methods
+
+void SkPath::incReserve(U16CPU inc) {
+    SkDEBUGCODE(this->validate();)
+
+    fVerbs.setReserve(fVerbs.count() + inc);
+    fPts.setReserve(fPts.count() + inc);
+
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkPath::moveTo(SkScalar x, SkScalar y) {
+    SkDEBUGCODE(this->validate();)
+
+    int      vc = fVerbs.count();
+    SkPoint* pt;
+
+    if (vc > 0 && fVerbs[vc - 1] == kMove_Verb) {
+        pt = &fPts[fPts.count() - 1];
+    } else {
+        pt = fPts.append();
+        *fVerbs.append() = kMove_Verb;
+    }
+    pt->set(x, y);
+
+    fFastBoundsIsDirty = true;
+}
+
+void SkPath::rMoveTo(SkScalar x, SkScalar y) {
+    SkPoint pt;
+    this->getLastPt(&pt);
+    this->moveTo(pt.fX + x, pt.fY + y);
+}
+
+void SkPath::lineTo(SkScalar x, SkScalar y) {
+    SkDEBUGCODE(this->validate();)
+
+    if (fVerbs.count() == 0) {
+        fPts.append()->set(0, 0);
+        *fVerbs.append() = kMove_Verb;
+    }
+    fPts.append()->set(x, y);
+    *fVerbs.append() = kLine_Verb;
+
+    fFastBoundsIsDirty = true;
+}
+
+void SkPath::rLineTo(SkScalar x, SkScalar y) {
+    SkPoint pt;
+    this->getLastPt(&pt);
+    this->lineTo(pt.fX + x, pt.fY + y);
+}
+
+void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
+    SkDEBUGCODE(this->validate();)
+
+    if (fVerbs.count() == 0) {
+        fPts.append()->set(0, 0);
+        *fVerbs.append() = kMove_Verb;
+    }
+
+    SkPoint* pts = fPts.append(2);
+    pts[0].set(x1, y1);
+    pts[1].set(x2, y2);
+    *fVerbs.append() = kQuad_Verb;
+
+    fFastBoundsIsDirty = true;
+}
+
+void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
+    SkPoint pt;
+    this->getLastPt(&pt);
+    this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
+}
+
+void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                     SkScalar x3, SkScalar y3) {
+    SkDEBUGCODE(this->validate();)
+
+    if (fVerbs.count() == 0) {
+        fPts.append()->set(0, 0);
+        *fVerbs.append() = kMove_Verb;
+    }
+    SkPoint* pts = fPts.append(3);
+    pts[0].set(x1, y1);
+    pts[1].set(x2, y2);
+    pts[2].set(x3, y3);
+    *fVerbs.append() = kCubic_Verb;
+
+    fFastBoundsIsDirty = true;
+}
+
+void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                      SkScalar x3, SkScalar y3) {
+    SkPoint pt;
+    this->getLastPt(&pt);
+    this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
+                  pt.fX + x3, pt.fY + y3);
+}
+
+void SkPath::close() {
+    SkDEBUGCODE(this->validate();)
+
+    int count = fVerbs.count();
+    if (count > 0) {
+        switch (fVerbs[count - 1]) {
+            case kLine_Verb:
+            case kQuad_Verb:
+            case kCubic_Verb:
+                *fVerbs.append() = kClose_Verb;
+                break;
+            default:
+                // don't add a close if the prev wasn't a primitive
+                break;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+    
+void SkPath::addRect(const SkRect& rect, Direction dir) {
+    this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir);
+}
+
+void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
+                     SkScalar bottom, Direction dir) {
+    SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
+
+    this->incReserve(5);
+
+    this->moveTo(left, top);
+    if (dir == kCCW_Direction) {
+        this->lineTo(left, bottom);
+        this->lineTo(right, bottom);
+        this->lineTo(right, top);
+    } else {
+        this->lineTo(right, top);
+        this->lineTo(right, bottom);
+        this->lineTo(left, bottom);
+    }
+    this->close();
+}
+
+#define CUBIC_ARC_FACTOR    ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
+
+void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
+                          Direction dir) {
+    SkAutoPathBoundsUpdate apbu(this, rect);
+
+    SkScalar    w = rect.width();
+    SkScalar    halfW = SkScalarHalf(w);
+    SkScalar    h = rect.height();
+    SkScalar    halfH = SkScalarHalf(h);
+
+    if (halfW <= 0 || halfH <= 0) {
+        return;
+    }
+
+    bool    skip_hori = rx >= halfW;
+    bool    skip_vert = ry >= halfH;
+
+    if (skip_hori && skip_vert) {
+        this->addOval(rect, dir);
+        return;
+    }
+    if (skip_hori) {
+        rx = halfW;
+    } else if (skip_vert) {
+        ry = halfH;
+    }
+
+    SkScalar    sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
+    SkScalar    sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
+
+    this->incReserve(17);
+    this->moveTo(rect.fRight - rx, rect.fTop);
+    if (dir == kCCW_Direction) {
+        if (!skip_hori) {
+            this->lineTo(rect.fLeft + rx, rect.fTop);       // top
+        }
+        this->cubicTo(rect.fLeft + rx - sx, rect.fTop,
+                      rect.fLeft, rect.fTop + ry - sy,
+                      rect.fLeft, rect.fTop + ry);          // top-left
+        if (!skip_vert) {
+            this->lineTo(rect.fLeft, rect.fBottom - ry);        // left
+        }
+        this->cubicTo(rect.fLeft, rect.fBottom - ry + sy,
+                      rect.fLeft + rx - sx, rect.fBottom,
+                      rect.fLeft + rx, rect.fBottom);       // bot-left
+        if (!skip_hori) {
+            this->lineTo(rect.fRight - rx, rect.fBottom);   // bottom
+        }
+        this->cubicTo(rect.fRight - rx + sx, rect.fBottom,
+                      rect.fRight, rect.fBottom - ry + sy,
+                      rect.fRight, rect.fBottom - ry);      // bot-right
+        if (!skip_vert) {
+            this->lineTo(rect.fRight, rect.fTop + ry);
+        }
+        this->cubicTo(rect.fRight, rect.fTop + ry - sy,
+                      rect.fRight - rx + sx, rect.fTop,
+                      rect.fRight - rx, rect.fTop);         // top-right
+    } else {
+        this->cubicTo(rect.fRight - rx + sx, rect.fTop,
+                      rect.fRight, rect.fTop + ry - sy,
+                      rect.fRight, rect.fTop + ry);         // top-right
+        if (!skip_vert) {
+            this->lineTo(rect.fRight, rect.fBottom - ry);
+        }
+        this->cubicTo(rect.fRight, rect.fBottom - ry + sy,
+                      rect.fRight - rx + sx, rect.fBottom,
+                      rect.fRight - rx, rect.fBottom);      // bot-right
+        if (!skip_hori) {
+            this->lineTo(rect.fLeft + rx, rect.fBottom);    // bottom
+        }
+        this->cubicTo(rect.fLeft + rx - sx, rect.fBottom,
+                      rect.fLeft, rect.fBottom - ry + sy,
+                      rect.fLeft, rect.fBottom - ry);       // bot-left
+        if (!skip_vert) {
+            this->lineTo(rect.fLeft, rect.fTop + ry);       // left
+        }
+        this->cubicTo(rect.fLeft, rect.fTop + ry - sy,
+                      rect.fLeft + rx - sx, rect.fTop,
+                      rect.fLeft + rx, rect.fTop);          // top-left
+        if (!skip_hori) {
+            this->lineTo(rect.fRight - rx, rect.fTop);      // top
+        }
+    }
+    this->close();
+}
+
+static void add_corner_arc(SkPath* path, const SkRect& rect,
+                           SkScalar rx, SkScalar ry, int startAngle,
+                           SkPath::Direction dir, bool forceMoveTo) {
+    rx = SkMinScalar(SkScalarHalf(rect.width()), rx);
+    ry = SkMinScalar(SkScalarHalf(rect.height()), ry);
+    
+    SkRect   r;
+    r.set(-rx, -ry, rx, ry);
+
+    switch (startAngle) {
+        case   0:
+            r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
+            break;
+        case  90:
+            r.offset(rect.fLeft - r.fLeft,   rect.fBottom - r.fBottom);
+            break;
+        case 180: r.offset(rect.fLeft - r.fLeft,   rect.fTop - r.fTop); break;
+        case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
+        default: SkASSERT(!"unexpected startAngle in add_corner_arc");
+    }
+    
+    SkScalar start = SkIntToScalar(startAngle);
+    SkScalar sweep = SkIntToScalar(90);
+    if (SkPath::kCCW_Direction == dir) {
+        start += sweep;
+        sweep = -sweep;
+    }
+    
+    path->arcTo(r, start, sweep, forceMoveTo);
+}
+
+void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],
+                          Direction dir) {
+    SkAutoPathBoundsUpdate apbu(this, rect);
+
+    if (kCW_Direction == dir) {
+        add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
+        add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
+        add_corner_arc(this, rect, rad[4], rad[5],   0, dir, false);
+        add_corner_arc(this, rect, rad[6], rad[7],  90, dir, false);
+    } else {
+        add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
+        add_corner_arc(this, rect, rad[6], rad[7],  90, dir, false);
+        add_corner_arc(this, rect, rad[4], rad[5],   0, dir, false);
+        add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
+    }
+    this->close();
+}
+
+void SkPath::addOval(const SkRect& oval, Direction dir) {
+    SkAutoPathBoundsUpdate apbu(this, oval);
+
+    SkScalar    cx = oval.centerX();
+    SkScalar    cy = oval.centerY();
+    SkScalar    rx = SkScalarHalf(oval.width());
+    SkScalar    ry = SkScalarHalf(oval.height());
+#if 0   // these seem faster than using quads (1/2 the number of edges)
+    SkScalar    sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
+    SkScalar    sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
+
+    this->incReserve(13);
+    this->moveTo(cx + rx, cy);
+    if (dir == kCCW_Direction) {
+        this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry);
+        this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy);
+        this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry);
+        this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy);
+    } else {
+        this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry);
+        this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy);
+        this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry);
+        this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy);
+    }
+#else
+    SkScalar    sx = SkScalarMul(rx, SK_ScalarTanPIOver8);
+    SkScalar    sy = SkScalarMul(ry, SK_ScalarTanPIOver8);
+    SkScalar    mx = SkScalarMul(rx, SK_ScalarRoot2Over2);
+    SkScalar    my = SkScalarMul(ry, SK_ScalarRoot2Over2);
+
+    /*
+        To handle imprecision in computing the center and radii, we revert to
+        the provided bounds when we can (i.e. use oval.fLeft instead of cx-rx)
+        to ensure that we don't exceed the oval's bounds *ever*, since we want
+        to use oval for our fast-bounds, rather than have to recompute it.
+    */
+    const SkScalar L = oval.fLeft;      // cx - rx
+    const SkScalar T = oval.fTop;       // cy - ry
+    const SkScalar R = oval.fRight;     // cx + rx
+    const SkScalar B = oval.fBottom;    // cy + ry
+
+    this->incReserve(17);   // 8 quads + close
+    this->moveTo(R, cy);
+    if (dir == kCCW_Direction) {
+        this->quadTo(      R, cy - sy, cx + mx, cy - my);
+        this->quadTo(cx + sx,       T, cx     ,       T);
+        this->quadTo(cx - sx,       T, cx - mx, cy - my);
+        this->quadTo(      L, cy - sy,       L, cy     );
+        this->quadTo(      L, cy + sy, cx - mx, cy + my);
+        this->quadTo(cx - sx,       B, cx     ,       B);
+        this->quadTo(cx + sx,       B, cx + mx, cy + my);
+        this->quadTo(      R, cy + sy,       R, cy     );
+    } else {
+        this->quadTo(      R, cy + sy, cx + mx, cy + my);
+        this->quadTo(cx + sx,       B, cx     ,       B);
+        this->quadTo(cx - sx,       B, cx - mx, cy + my);
+        this->quadTo(      L, cy + sy,       L, cy     );
+        this->quadTo(      L, cy - sy, cx - mx, cy - my);
+        this->quadTo(cx - sx,       T, cx     ,       T);
+        this->quadTo(cx + sx,       T, cx + mx, cy - my);
+        this->quadTo(      R, cy - sy,       R, cy     );
+    }
+#endif
+    this->close();
+}
+
+void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
+    if (r > 0) {
+        SkRect  rect;
+        rect.set(x - r, y - r, x + r, y + r);
+        this->addOval(rect, dir);
+    }
+}
+
+#include "SkGeometry.h"
+
+static int build_arc_points(const SkRect& oval, SkScalar startAngle,
+                            SkScalar sweepAngle,
+                            SkPoint pts[kSkBuildQuadArcStorage]) {
+    SkVector start, stop;
+
+    start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX);
+    stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle),
+                             &stop.fX);
+        
+    SkMatrix    matrix;
+    
+    matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
+    matrix.postTranslate(oval.centerX(), oval.centerY());
+    
+    return SkBuildQuadArc(start, stop,
+          sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection,
+                          &matrix, pts);
+}
+
+void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+                   bool forceMoveTo) {
+    if (oval.width() < 0 || oval.height() < 0) {
+        return;
+    }
+
+    SkPoint pts[kSkBuildQuadArcStorage];
+    int count = build_arc_points(oval, startAngle, sweepAngle, pts);
+    SkASSERT((count & 1) == 1);
+
+    if (fVerbs.count() == 0) {
+        forceMoveTo = true;
+    }
+    this->incReserve(count);
+    forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]);
+    for (int i = 1; i < count; i += 2) {
+        this->quadTo(pts[i], pts[i+1]);
+    }
+}
+
+void SkPath::addArc(const SkRect& oval, SkScalar startAngle,
+                    SkScalar sweepAngle) {
+    if (oval.isEmpty() || 0 == sweepAngle) {
+        return;
+    }
+
+    const SkScalar kFullCircleAngle = SkIntToScalar(360);
+
+    if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
+        this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction);
+        return;
+    }
+
+    SkPoint pts[kSkBuildQuadArcStorage];
+    int count = build_arc_points(oval, startAngle, sweepAngle, pts);
+
+    this->incReserve(count);
+    this->moveTo(pts[0]);
+    for (int i = 1; i < count; i += 2) {
+        this->quadTo(pts[i], pts[i+1]);
+    }
+}
+
+/*
+    Need to handle the case when the angle is sharp, and our computed end-points
+    for the arc go behind pt1 and/or p2...
+*/
+void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                   SkScalar radius) {
+    SkVector    before, after;
+    
+    // need to know our prev pt so we can construct tangent vectors
+    {
+        SkPoint start;
+        this->getLastPt(&start);
+        before.setNormalize(x1 - start.fX, y1 - start.fY);
+        after.setNormalize(x2 - x1, y2 - y1);
+    }
+    
+    SkScalar cosh = SkPoint::DotProduct(before, after);
+    SkScalar sinh = SkPoint::CrossProduct(before, after);
+
+    if (SkScalarNearlyZero(sinh)) {   // angle is too tight
+        return;
+    }
+    
+    SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh);
+    if (dist < 0) {
+        dist = -dist;
+    }
+
+    SkScalar xx = x1 - SkScalarMul(dist, before.fX);
+    SkScalar yy = y1 - SkScalarMul(dist, before.fY);
+    SkRotationDirection arcDir;
+
+    // now turn before/after into normals
+    if (sinh > 0) {
+        before.rotateCCW();
+        after.rotateCCW();
+        arcDir = kCW_SkRotationDirection;
+    } else {
+        before.rotateCW();
+        after.rotateCW();
+        arcDir = kCCW_SkRotationDirection;
+    }
+
+    SkMatrix    matrix;
+    SkPoint     pts[kSkBuildQuadArcStorage];
+    
+    matrix.setScale(radius, radius);
+    matrix.postTranslate(xx - SkScalarMul(radius, before.fX),
+                         yy - SkScalarMul(radius, before.fY));
+    
+    int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts);
+    
+    this->incReserve(count);
+    // [xx,yy] == pts[0]
+    this->lineTo(xx, yy);
+    for (int i = 1; i < count; i += 2) {
+        this->quadTo(pts[i], pts[i+1]);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) {
+    SkMatrix matrix;
+
+    matrix.setTranslate(dx, dy);
+    this->addPath(path, matrix);
+}
+
+void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
+    this->incReserve(path.fPts.count());
+
+    Iter    iter(path, false);
+    SkPoint pts[4];
+    Verb    verb;
+
+    SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
+
+    while ((verb = iter.next(pts)) != kDone_Verb) {
+        switch (verb) {
+            case kMove_Verb:
+                proc(matrix, &pts[0], &pts[0], 1);
+                this->moveTo(pts[0]);
+                break;
+            case kLine_Verb:
+                proc(matrix, &pts[1], &pts[1], 1);
+                this->lineTo(pts[1]);
+                break;
+            case kQuad_Verb:
+                proc(matrix, &pts[1], &pts[1], 2);
+                this->quadTo(pts[1], pts[2]);
+                break;
+            case kCubic_Verb:
+                proc(matrix, &pts[1], &pts[1], 3);
+                this->cubicTo(pts[1], pts[2], pts[3]);
+                break;
+            case kClose_Verb:
+                this->close();
+                break;
+            default:
+                SkASSERT(!"unknown verb");
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const uint8_t gPtsInVerb[] = {
+    1,  // kMove
+    1,  // kLine
+    2,  // kQuad
+    3,  // kCubic
+    0,  // kClose
+    0   // kDone
+};
+
+// ignore the initial moveto, and stop when the 1st contour ends
+void SkPath::pathTo(const SkPath& path) {
+    int i, vcount = path.fVerbs.count();
+    if (vcount == 0) {
+        return;
+    }
+
+    this->incReserve(vcount);
+
+    const uint8_t*  verbs = path.fVerbs.begin();
+    const SkPoint*  pts = path.fPts.begin() + 1;    // 1 for the initial moveTo
+
+    SkASSERT(verbs[0] == kMove_Verb);
+    for (i = 1; i < vcount; i++) {
+        switch (verbs[i]) {
+            case kLine_Verb:
+                this->lineTo(pts[0].fX, pts[0].fY);
+                break;
+            case kQuad_Verb:
+                this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
+                break;
+            case kCubic_Verb:
+                this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY,
+                              pts[2].fX, pts[2].fY);
+                break;
+            case kClose_Verb:
+                return;
+        }
+        pts += gPtsInVerb[verbs[i]];
+    }
+}
+
+// ignore the last point of the 1st contour
+void SkPath::reversePathTo(const SkPath& path) {
+    int i, vcount = path.fVerbs.count();
+    if (vcount == 0) {
+        return;
+    }
+
+    this->incReserve(vcount);
+
+    const uint8_t*  verbs = path.fVerbs.begin();
+    const SkPoint*  pts = path.fPts.begin();
+
+    SkASSERT(verbs[0] == kMove_Verb);
+    for (i = 1; i < vcount; i++) {
+        int n = gPtsInVerb[verbs[i]];
+        if (n == 0) {
+            break;
+        }
+        pts += n;
+    }
+
+    while (--i > 0) {
+        switch (verbs[i]) {
+            case kLine_Verb:
+                this->lineTo(pts[-1].fX, pts[-1].fY);
+                break;
+            case kQuad_Verb:
+                this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
+                break;
+            case kCubic_Verb:
+                this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
+                              pts[-3].fX, pts[-3].fY);
+                break;
+            default:
+                SkASSERT(!"bad verb");
+                break;
+        }
+        pts -= gPtsInVerb[verbs[i]];
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
+    SkMatrix    matrix;
+
+    matrix.setTranslate(dx, dy);
+    this->transform(matrix, dst);
+}
+
+#include "SkGeometry.h"
+
+static void subdivide_quad_to(SkPath* path, const SkPoint pts[3],
+                              int level = 2) {
+    if (--level >= 0) {
+        SkPoint tmp[5];
+
+        SkChopQuadAtHalf(pts, tmp);
+        subdivide_quad_to(path, &tmp[0], level);
+        subdivide_quad_to(path, &tmp[2], level);
+    } else {
+        path->quadTo(pts[1], pts[2]);
+    }
+}
+
+static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
+                               int level = 2) {
+    if (--level >= 0) {
+        SkPoint tmp[7];
+
+        SkChopCubicAtHalf(pts, tmp);
+        subdivide_cubic_to(path, &tmp[0], level);
+        subdivide_cubic_to(path, &tmp[3], level);
+    } else {
+        path->cubicTo(pts[1], pts[2], pts[3]);
+    }
+}
+
+void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
+    SkDEBUGCODE(this->validate();)
+    if (dst == NULL) {
+        dst = (SkPath*)this;
+    }
+
+    if (matrix.getType() & SkMatrix::kPerspective_Mask) {
+        SkPath  tmp;
+        tmp.fFillType = fFillType;
+
+        SkPath::Iter    iter(*this, false);
+        SkPoint         pts[4];
+        SkPath::Verb    verb;
+
+        while ((verb = iter.next(pts)) != kDone_Verb) {
+            switch (verb) {
+                case kMove_Verb:
+                    tmp.moveTo(pts[0]);
+                    break;
+                case kLine_Verb:
+                    tmp.lineTo(pts[1]);
+                    break;
+                case kQuad_Verb:
+                    subdivide_quad_to(&tmp, pts);
+                    break;
+                case kCubic_Verb:
+                    subdivide_cubic_to(&tmp, pts);
+                    break;
+                case kClose_Verb:
+                    tmp.close();
+                    break;
+                default:
+                    SkASSERT(!"unknown verb");
+                    break;
+            }
+        }
+
+        dst->swap(tmp);
+        matrix.mapPoints(dst->fPts.begin(), dst->fPts.count());
+    } else {
+        // remember that dst might == this, so be sure to check
+        // fFastBoundsIsDirty before we set it
+        if (!fFastBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) {
+            // if we're empty, fastbounds should not be mapped
+            matrix.mapRect(&dst->fFastBounds, fFastBounds);
+            dst->fFastBoundsIsDirty = false;
+        } else {
+            dst->fFastBoundsIsDirty = true;
+        }
+
+        if (this != dst) {
+            dst->fVerbs = fVerbs;
+            dst->fPts.setCount(fPts.count());
+            dst->fFillType = fFillType;
+        }
+        matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
+        SkDEBUGCODE(dst->validate();)
+    }
+}
+
+void SkPath::updateBoundsCache() const {
+    if (fFastBoundsIsDirty) {
+        SkRect  r;
+        this->computeBounds(&r, kFast_BoundsType);
+        SkASSERT(!fFastBoundsIsDirty);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enum NeedMoveToState {
+    kAfterClose_NeedMoveToState,
+    kAfterCons_NeedMoveToState,
+    kAfterPrefix_NeedMoveToState
+};
+
+SkPath::Iter::Iter() {
+#ifdef SK_DEBUG
+    fPts = NULL;
+    fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
+    fForceClose = fNeedMoveTo = fCloseLine = false;
+#endif
+    // need to init enough to make next() harmlessly return kDone_Verb
+    fVerbs = NULL;
+    fVerbStop = NULL;
+    fNeedClose = false;
+}
+
+SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
+    this->setPath(path, forceClose);
+}
+
+void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
+    fPts = path.fPts.begin();
+    fVerbs = path.fVerbs.begin();
+    fVerbStop = path.fVerbs.end();
+    fForceClose = SkToU8(forceClose);
+    fNeedClose = false;
+    fNeedMoveTo = kAfterPrefix_NeedMoveToState;
+}
+
+bool SkPath::Iter::isClosedContour() const {
+    if (fVerbs == NULL || fVerbs == fVerbStop) {
+        return false;
+    }
+    if (fForceClose) {
+        return true;
+    }
+
+    const uint8_t* verbs = fVerbs;
+    const uint8_t* stop = fVerbStop;
+    
+    if (kMove_Verb == *verbs) {
+        verbs += 1; // skip the initial moveto
+    }
+
+    while (verbs < stop) {
+        unsigned v = *verbs++;        
+        if (kMove_Verb == v) {
+            break;
+        }
+        if (kClose_Verb == v) {
+            return true;
+        }
+    }
+    return false;
+}
+
+SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
+    if (fLastPt != fMoveTo) {
+        if (pts) {
+            pts[0] = fLastPt;
+            pts[1] = fMoveTo;
+        }
+        fLastPt = fMoveTo;
+        fCloseLine = true;
+        return kLine_Verb;
+    }
+    return kClose_Verb;
+}
+
+bool SkPath::Iter::cons_moveTo(SkPoint pts[1]) {
+    if (fNeedMoveTo == kAfterClose_NeedMoveToState) {
+        if (pts) {
+            *pts = fMoveTo;
+        }
+        fNeedClose = fForceClose;
+        fNeedMoveTo = kAfterCons_NeedMoveToState;
+        fVerbs -= 1;
+        return true;
+    }
+
+    if (fNeedMoveTo == kAfterCons_NeedMoveToState) {
+        if (pts) {
+            *pts = fMoveTo;
+        }
+        fNeedMoveTo = kAfterPrefix_NeedMoveToState;
+    } else {
+        SkASSERT(fNeedMoveTo == kAfterPrefix_NeedMoveToState);
+        if (pts) {
+            *pts = fPts[-1];
+        }
+    }
+    return false;
+}
+
+SkPath::Verb SkPath::Iter::next(SkPoint pts[4]) {
+    if (fVerbs == fVerbStop) {
+        if (fNeedClose) {
+            if (kLine_Verb == this->autoClose(pts)) {
+                return kLine_Verb;
+            }
+            fNeedClose = false;
+            return kClose_Verb;
+        }
+        return kDone_Verb;
+    }
+
+    unsigned        verb = *fVerbs++;
+    const SkPoint*  srcPts = fPts;
+
+    switch (verb) {
+        case kMove_Verb:
+            if (fNeedClose) {
+                fVerbs -= 1;
+                verb = this->autoClose(pts);
+                if (verb == kClose_Verb) {
+                    fNeedClose = false;
+                }
+                return (Verb)verb;
+            }
+            if (fVerbs == fVerbStop) {    // might be a trailing moveto
+                return kDone_Verb;
+            }
+            fMoveTo = *srcPts;
+            if (pts) {
+                pts[0] = *srcPts;
+            }
+            srcPts += 1;
+            fNeedMoveTo = kAfterCons_NeedMoveToState;
+            fNeedClose = fForceClose;
+            break;
+        case kLine_Verb:
+            if (this->cons_moveTo(pts)) {
+                return kMove_Verb;
+            }
+            if (pts) {
+                pts[1] = srcPts[0];
+            }
+            fLastPt = srcPts[0];
+            fCloseLine = false;
+            srcPts += 1;
+            break;
+        case kQuad_Verb:
+            if (this->cons_moveTo(pts)) {
+                return kMove_Verb;
+            }
+            if (pts) {
+                memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
+            }
+            fLastPt = srcPts[1];
+            srcPts += 2;
+            break;
+        case kCubic_Verb:
+            if (this->cons_moveTo(pts)) {
+                return kMove_Verb;
+            }
+            if (pts) {
+                memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
+            }
+            fLastPt = srcPts[2];
+            srcPts += 3;
+            break;
+        case kClose_Verb:
+            verb = this->autoClose(pts);
+            if (verb == kLine_Verb) {
+                fVerbs -= 1;
+            } else {
+                fNeedClose = false;
+            }
+            fNeedMoveTo = kAfterClose_NeedMoveToState;
+            break;
+    }
+    fPts = srcPts;
+    return (Verb)verb;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool exceeds_dist(const SkScalar p[], const SkScalar q[], SkScalar dist,
+                         int count) {
+    SkASSERT(dist > 0);
+
+    count *= 2;
+    for (int i = 0; i < count; i++) {
+        if (SkScalarAbs(p[i] - q[i]) > dist) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static void subdivide_quad(SkPath* dst, const SkPoint pts[3], SkScalar dist,
+                           int subLevel = 4) {
+    if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 4)) {
+        SkPoint tmp[5];
+        SkChopQuadAtHalf(pts, tmp);
+
+        subdivide_quad(dst, &tmp[0], dist, subLevel);
+        subdivide_quad(dst, &tmp[2], dist, subLevel);
+    } else {
+        dst->quadTo(pts[1], pts[2]);
+    }
+}
+
+static void subdivide_cubic(SkPath* dst, const SkPoint pts[4], SkScalar dist,
+                            int subLevel = 4) {
+    if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 6)) {
+        SkPoint tmp[7];
+        SkChopCubicAtHalf(pts, tmp);
+
+        subdivide_cubic(dst, &tmp[0], dist, subLevel);
+        subdivide_cubic(dst, &tmp[3], dist, subLevel);
+    } else {
+        dst->cubicTo(pts[1], pts[2], pts[3]);
+    }
+}
+
+void SkPath::subdivide(SkScalar dist, bool bendLines, SkPath* dst) const {
+    SkPath  tmpPath;
+    if (NULL == dst || this == dst) {
+        dst = &tmpPath;
+    }
+
+    SkPath::Iter    iter(*this, false);
+    SkPoint         pts[4];
+
+    for (;;) {
+        switch (iter.next(pts)) {
+            case SkPath::kMove_Verb:
+                dst->moveTo(pts[0]);
+                break;
+            case SkPath::kLine_Verb:
+                if (!bendLines) {
+                    dst->lineTo(pts[1]);
+                    break;
+                }
+                // construct a quad from the line
+                pts[2] = pts[1];
+                pts[1].set(SkScalarAve(pts[0].fX, pts[2].fX),
+                           SkScalarAve(pts[0].fY, pts[2].fY));
+                // fall through to the quad case
+            case SkPath::kQuad_Verb:
+                subdivide_quad(dst, pts, dist);
+                break;
+            case SkPath::kCubic_Verb:
+                subdivide_cubic(dst, pts, dist);
+                break;
+            case SkPath::kClose_Verb:
+                dst->close();
+                break;
+            case SkPath::kDone_Verb:
+                goto DONE;
+        }
+    }
+DONE:
+    if (&tmpPath == dst) {   // i.e. the dst should be us
+        dst->swap(*(SkPath*)this);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////
+/*
+    Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]]
+*/
+
+void SkPath::flatten(SkFlattenableWriteBuffer& buffer) const {
+    SkDEBUGCODE(this->validate();)
+
+    buffer.write32(fPts.count());
+    buffer.write32(fVerbs.count());
+    buffer.write32(fFillType);
+    buffer.writeMul4(fPts.begin(), sizeof(SkPoint) * fPts.count());
+    buffer.writePad(fVerbs.begin(), fVerbs.count());
+}
+
+void SkPath::unflatten(SkFlattenableReadBuffer& buffer) {
+    fPts.setCount(buffer.readS32());
+    fVerbs.setCount(buffer.readS32());
+    fFillType = buffer.readS32();
+    buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count());
+    buffer.read(fVerbs.begin(), fVerbs.count());
+    
+    fFastBoundsIsDirty = true;
+
+    SkDEBUGCODE(this->validate();)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkString.h"
+#include "SkStream.h"
+
+static void write_scalar(SkWStream* stream, SkScalar value) {
+    char    buffer[SkStrAppendScalar_MaxSize];
+    char*   stop = SkStrAppendScalar(buffer, value);
+    stream->write(buffer, stop - buffer);
+}
+
+static void append_scalars(SkWStream* stream, char verb, const SkScalar data[],
+                           int count) {
+    stream->write(&verb, 1);
+    write_scalar(stream, data[0]);
+    for (int i = 1; i < count; i++) {
+        if (data[i] >= 0) {
+            // can skip the separater if data[i] is negative
+            stream->write(" ", 1);
+        }
+        write_scalar(stream, data[i]);
+    }
+}
+
+void SkPath::toString(SkString* str) const {
+    SkDynamicMemoryWStream  stream;
+
+    SkPath::Iter    iter(*this, false);
+    SkPoint         pts[4];
+    
+    for (;;) {
+        switch (iter.next(pts)) {
+            case SkPath::kMove_Verb:
+                append_scalars(&stream, 'M', &pts[0].fX, 2);
+                break;
+            case SkPath::kLine_Verb:
+                append_scalars(&stream, 'L', &pts[1].fX, 2);
+                break;
+            case SkPath::kQuad_Verb:
+                append_scalars(&stream, 'Q', &pts[1].fX, 4);
+                break;
+            case SkPath::kCubic_Verb:
+                append_scalars(&stream, 'C', &pts[1].fX, 6);
+                break;
+            case SkPath::kClose_Verb:
+                stream.write("Z", 1);
+                break;
+            case SkPath::kDone_Verb:
+                str->resize(stream.getOffset());
+                stream.copyTo(str->writable_str());
+                return;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkPath::validate() const {
+    SkASSERT(this != NULL);
+    SkASSERT((fFillType & ~3) == 0);
+    fPts.validate();
+    fVerbs.validate();
+
+    if (!fFastBoundsIsDirty) {
+        SkRect bounds;
+        compute_fast_bounds(&bounds, fPts);
+        // can't call contains(), since it returns false if the rect is empty
+        SkASSERT(fFastBounds.fLeft <= bounds.fLeft);
+        SkASSERT(fFastBounds.fTop <= bounds.fTop);
+        SkASSERT(fFastBounds.fRight >= bounds.fRight);
+        SkASSERT(fFastBounds.fBottom >= bounds.fBottom);
+    }
+}
+
+#if 0   // test to ensure that the iterator returns the same data as the path
+void SkPath::test() const
+{
+    Iter    iter(*this, false);
+    SkPoint pts[4];
+    Verb    verb;
+
+    const uint8_t*  verbs = fVerbs.begin();
+    const SkPoint*  points = fPts.begin();
+
+    while ((verb = iter.next(pts)) != kDone_Verb)
+    {
+        SkASSERT(*verbs == verb);
+        verbs += 1;
+
+        int count;
+        switch (verb) {
+        case kMove_Verb:
+            count = 1;
+            break;
+        case kLine_Verb:
+            count = 2;
+            break;
+        case kQuad_Verb:
+            count = 3;
+            break;
+        case kCubic_Verb:
+            count = 4;
+            break;
+        case kClose_Verb:
+        default:
+            count = 0;
+            break;
+        }
+        if (count > 1)
+            points -= 1;
+        SkASSERT(memcmp(pts, points, count * sizeof(SkPoint)) == 0);
+        points += count;
+    }
+
+    int vc = fVerbs.count(), pc = fPts.count();
+    if (vc && fVerbs.begin()[vc-1] == kMove_Verb)
+    {
+        vc -= 1;
+        pc -= 1;
+    }
+    SkASSERT(verbs - fVerbs.begin() == vc);
+    SkASSERT(points - fPts.begin() == pc);
+}
+#endif
+
+void SkPath::dump(bool forceClose, const char title[]) const {
+    Iter    iter(*this, forceClose);
+    SkPoint pts[4];
+    Verb    verb;
+
+    SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false",
+             title ? title : "");
+
+    while ((verb = iter.next(pts)) != kDone_Verb) {
+        switch (verb) {
+            case kMove_Verb:
+#ifdef SK_CAN_USE_FLOAT
+                SkDebugf("  path: moveTo [%g %g]\n",
+                        SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY));
+#else
+                SkDebugf("  path: moveTo [%x %x]\n", pts[0].fX, pts[0].fY);
+#endif
+                break;
+            case kLine_Verb:
+#ifdef SK_CAN_USE_FLOAT
+                SkDebugf("  path: lineTo [%g %g]\n",
+                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY));
+#else
+                SkDebugf("  path: lineTo [%x %x]\n", pts[1].fX, pts[1].fY);
+#endif
+                break;
+            case kQuad_Verb:
+#ifdef SK_CAN_USE_FLOAT
+                SkDebugf("  path: quadTo [%g %g] [%g %g]\n",
+                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
+                        SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY));
+#else
+                SkDebugf("  path: quadTo [%x %x] [%x %x]\n",
+                         pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
+#endif
+                break;
+            case kCubic_Verb:
+#ifdef SK_CAN_USE_FLOAT
+                SkDebugf("  path: cubeTo [%g %g] [%g %g] [%g %g]\n",
+                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
+                        SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY),
+                        SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY));
+#else
+                SkDebugf("  path: cubeTo [%x %x] [%x %x] [%x %x]\n",
+                         pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
+                         pts[3].fX, pts[3].fY);
+#endif
+                break;
+            case kClose_Verb:
+                SkDebugf("  path: close\n");
+                break;
+            default:
+                SkDebugf("  path: UNKNOWN VERB %d, aborting dump...\n", verb);
+                verb = kDone_Verb;  // stop the loop
+                break;
+        }
+    }
+    SkDebugf("path: done %s\n", title ? title : "");
+}
+
+#include "SkTSort.h"
+
+void SkPath::UnitTest() {
+#ifdef SK_SUPPORT_UNITTEST
+    SkPath  p;
+    SkRect  r;
+
+    r.set(0, 0, 10, 20);
+    p.addRect(r);
+    p.dump(false);
+    p.dump(true);
+
+    {
+        int array[] = { 5, 3, 7, 2, 6, 1, 2, 9, 5, 0 };
+        int i;
+
+        for (i = 0; i < (int)SK_ARRAY_COUNT(array); i++) {
+            SkDebugf(" %d", array[i]);
+        }
+        SkDebugf("\n");
+        SkTHeapSort<int>(array, SK_ARRAY_COUNT(array));
+        for (i = 0; i < (int)SK_ARRAY_COUNT(array); i++)
+            SkDebugf(" %d", array[i]);
+        SkDebugf("\n");
+    }
+
+    {
+        SkPath  p;
+        SkPoint pt;
+
+        p.moveTo(SK_Scalar1, 0);
+        p.getLastPt(&pt);
+        SkASSERT(pt.fX == SK_Scalar1);
+    }
+#endif
+}
+
+#endif
diff --git a/src/core/SkPathEffect.cpp b/src/core/SkPathEffect.cpp
new file mode 100644
index 0000000..2905895
--- /dev/null
+++ b/src/core/SkPathEffect.cpp
@@ -0,0 +1,142 @@
+/* libs/graphics/sgl/SkPathEffect.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 "SkPathEffect.h"
+#include "SkPath.h"
+#include "SkBuffer.h"
+
+//////////////////////////////////////////////////////////////////////////////////
+
+SkPairPathEffect::SkPairPathEffect(SkPathEffect* pe0, SkPathEffect* pe1)
+    : fPE0(pe0), fPE1(pe1)
+{
+    SkASSERT(pe0);
+    SkASSERT(pe1);
+    fPE0->ref();
+    fPE1->ref();
+}
+
+SkPairPathEffect::~SkPairPathEffect()
+{
+    fPE0->unref();
+    fPE1->unref();
+}
+
+/*
+    Format: [oe0-factory][pe1-factory][pe0-size][pe0-data][pe1-data]
+*/
+void SkPairPathEffect::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    buffer.writeFlattenable(fPE0);
+    buffer.writeFlattenable(fPE1);
+}
+
+SkPairPathEffect::SkPairPathEffect(SkFlattenableReadBuffer& buffer)
+{
+    fPE0 = (SkPathEffect*)buffer.readFlattenable();
+    fPE1 = (SkPathEffect*)buffer.readFlattenable();
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+bool SkComposePathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
+{
+    SkPath          tmp;
+    const SkPath*   ptr = &src;
+
+    if (fPE1->filterPath(&tmp, src, width))
+        ptr = &tmp;
+    return fPE0->filterPath(dst, *ptr, width);
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+bool SkSumPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
+{
+    // use bit-or so that we always call both, even if the first one succeeds
+    return  fPE0->filterPath(dst, src, width) | fPE1->filterPath(dst, src, width);
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+
+#include "SkStroke.h"
+
+SkStrokePathEffect::SkStrokePathEffect(const SkPaint& paint)
+    : fWidth(paint.getStrokeWidth()), fMiter(paint.getStrokeMiter()),
+      fStyle(SkToU8(paint.getStyle())), fJoin(SkToU8(paint.getStrokeJoin())), fCap(SkToU8(paint.getStrokeCap()))
+{
+}
+
+SkStrokePathEffect::SkStrokePathEffect(SkScalar width, SkPaint::Style style, SkPaint::Join join, SkPaint::Cap cap, SkScalar miter)
+    : fWidth(width), fMiter(miter), fStyle(SkToU8(style)), fJoin(SkToU8(join)), fCap(SkToU8(cap))
+{
+    if (miter < 0)  // signal they want the default
+        fMiter = SK_DefaultMiterLimit;
+}
+
+bool SkStrokePathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
+{
+    if (fWidth < 0 || fStyle == SkPaint::kFill_Style)
+        return false;
+
+    if (fStyle == SkPaint::kStroke_Style && fWidth == 0)    // hairline
+    {
+        *width = 0;
+        return true;
+    }
+
+    SkStroke    stroke;
+
+    stroke.setWidth(fWidth);
+    stroke.setMiterLimit(fMiter);
+    stroke.setJoin((SkPaint::Join)fJoin);
+    stroke.setCap((SkPaint::Cap)fCap);
+    stroke.setDoFill(fStyle == SkPaint::kStrokeAndFill_Style);
+
+    stroke.strokePath(src, dst);
+    return true;
+}
+
+SkFlattenable::Factory SkStrokePathEffect::getFactory()
+{
+    return CreateProc;
+}
+
+SkFlattenable* SkStrokePathEffect::CreateProc(SkFlattenableReadBuffer& buffer)
+{
+    return SkNEW_ARGS(SkStrokePathEffect, (buffer));
+}
+
+void SkStrokePathEffect::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    buffer.writeScalar(fWidth);
+    buffer.writeScalar(fMiter);
+    buffer.write8(fStyle);
+    buffer.write8(fJoin);
+    buffer.write8(fCap);
+}
+
+SkStrokePathEffect::SkStrokePathEffect(SkFlattenableReadBuffer& buffer)
+{
+    fWidth = buffer.readScalar();
+    fMiter = buffer.readScalar();
+    fStyle = buffer.readU8();
+    fJoin = buffer.readU8();
+    fCap = buffer.readU8();
+}
+
+
diff --git a/src/core/SkPathHeap.cpp b/src/core/SkPathHeap.cpp
new file mode 100644
index 0000000..f6dae5d
--- /dev/null
+++ b/src/core/SkPathHeap.cpp
@@ -0,0 +1,55 @@
+#include "SkPathHeap.h"
+#include "SkPath.h"
+#include "SkStream.h"
+#include "SkFlattenable.h"
+#include <new>
+
+#define kPathCount  64
+
+SkPathHeap::SkPathHeap() : fHeap(kPathCount * sizeof(SkPath)) {
+}
+
+SkPathHeap::SkPathHeap(SkFlattenableReadBuffer& buffer)
+            : fHeap(kPathCount * sizeof(SkPath)) {
+    int count = buffer.readS32();
+
+    fPaths.setCount(count);
+    SkPath** ptr = fPaths.begin();
+    SkPath* p = (SkPath*)fHeap.allocThrow(count * sizeof(SkPath));
+
+    for (int i = 0; i < count; i++) {
+        new (p) SkPath;
+        p->unflatten(buffer);
+        *ptr++ = p; // record the pointer
+        p++;        // move to the next storage location
+    }
+}
+
+SkPathHeap::~SkPathHeap() {
+    SkPath** iter = fPaths.begin();
+    SkPath** stop = fPaths.end();
+    while (iter < stop) {
+        (*iter)->~SkPath();
+        iter++;
+    }
+}
+
+int SkPathHeap::append(const SkPath& path) {
+    SkPath* p = (SkPath*)fHeap.allocThrow(sizeof(SkPath));
+    new (p) SkPath(path);
+    *fPaths.append() = p;
+    return fPaths.count();
+}
+
+void SkPathHeap::flatten(SkFlattenableWriteBuffer& buffer) const {
+    int count = fPaths.count();
+    
+    buffer.write32(count);
+    SkPath** iter = fPaths.begin();
+    SkPath** stop = fPaths.end();
+    while (iter < stop) {
+        (*iter)->flatten(buffer);
+        iter++;
+    }
+}
+
diff --git a/src/core/SkPathHeap.h b/src/core/SkPathHeap.h
new file mode 100644
index 0000000..b8f3bd3
--- /dev/null
+++ b/src/core/SkPathHeap.h
@@ -0,0 +1,37 @@
+#ifndef SkPathHeap_DEFINED
+#define SkPathHeap_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkChunkAlloc.h"
+#include "SkTDArray.h"
+
+class SkPath;
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+
+class SkPathHeap : public SkRefCnt {
+public:
+            SkPathHeap();
+            SkPathHeap(SkFlattenableReadBuffer&);
+    virtual ~SkPathHeap();
+
+    // called during picture-record
+    int append(const SkPath&);
+    
+    // called during picture-playback
+    int count() const { return fPaths.count(); }
+    const SkPath& operator[](int index) const {
+        return *fPaths[index];
+    }
+    
+    void flatten(SkFlattenableWriteBuffer&) const;
+        
+private:
+    // we store the paths in the heap (placement new)
+    SkChunkAlloc        fHeap;
+    // we just store ptrs into fHeap here
+    SkTDArray<SkPath*>  fPaths;
+};
+
+#endif
+
diff --git a/src/core/SkPathMeasure.cpp b/src/core/SkPathMeasure.cpp
new file mode 100644
index 0000000..ec1510d
--- /dev/null
+++ b/src/core/SkPathMeasure.cpp
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPathMeasure.h"
+#include "SkGeometry.h"
+#include "SkPath.h"
+#include "SkTSearch.h"
+
+// these must be 0,1,2 since they are in our 2-bit field
+enum {
+    kLine_SegType,
+    kCloseLine_SegType,
+    kQuad_SegType,
+    kCubic_SegType
+};
+
+#define kMaxTValue  32767
+
+static inline SkScalar tValue2Scalar(int t) {
+    SkASSERT((unsigned)t <= kMaxTValue);
+
+#ifdef SK_SCALAR_IS_FLOAT
+    return t * 3.05185e-5f; // t / 32767
+#else
+    return (t + (t >> 14)) << 1;
+#endif
+}
+
+SkScalar SkPathMeasure::Segment::getScalarT() const {
+    return tValue2Scalar(fTValue);
+}
+
+const SkPathMeasure::Segment* SkPathMeasure::NextSegment(const Segment* seg) {
+    unsigned ptIndex = seg->fPtIndex;
+
+    do {
+        ++seg;
+    } while (seg->fPtIndex == ptIndex);
+    return seg;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static inline int tspan_big_enough(int tspan) {
+    SkASSERT((unsigned)tspan <= kMaxTValue);
+    return tspan >> 10;
+}
+
+#if 0
+static inline bool tangents_too_curvy(const SkVector& tan0, SkVector& tan1) {
+    static const SkScalar kFlatEnoughTangentDotProd = SK_Scalar1 * 99 / 100;
+
+    SkASSERT(kFlatEnoughTangentDotProd > 0 &&
+             kFlatEnoughTangentDotProd < SK_Scalar1);
+
+    return SkPoint::DotProduct(tan0, tan1) < kFlatEnoughTangentDotProd;
+}
+#endif
+
+// can't use tangents, since we need [0..1..................2] to be seen
+// as definitely not a line (it is when drawn, but not parametrically)
+// so we compare midpoints
+#define CHEAP_DIST_LIMIT    (SK_Scalar1/2)  // just made this value up
+
+static bool quad_too_curvy(const SkPoint pts[3]) {
+    // diff = (a/4 + b/2 + c/4) - (a/2 + c/2)
+    // diff = -a/4 + b/2 - c/4
+    SkScalar dx = SkScalarHalf(pts[1].fX) -
+                        SkScalarHalf(SkScalarHalf(pts[0].fX + pts[2].fX));
+    SkScalar dy = SkScalarHalf(pts[1].fY) -
+                        SkScalarHalf(SkScalarHalf(pts[0].fY + pts[2].fY));
+
+    SkScalar dist = SkMaxScalar(SkScalarAbs(dx), SkScalarAbs(dy));
+    return dist > CHEAP_DIST_LIMIT;
+}
+
+static bool cheap_dist_exceeds_limit(const SkPoint& pt,
+                                     SkScalar x, SkScalar y) {
+    SkScalar dist = SkMaxScalar(SkScalarAbs(x - pt.fX), SkScalarAbs(y - pt.fY));
+    // just made up the 1/2
+    return dist > CHEAP_DIST_LIMIT;
+}
+
+static bool cubic_too_curvy(const SkPoint pts[4]) {
+    return  cheap_dist_exceeds_limit(pts[1],
+                         SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3),
+                         SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1/3))
+                         ||
+            cheap_dist_exceeds_limit(pts[2],
+                         SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3),
+                         SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1*2/3));
+}
+
+SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3],
+                          SkScalar distance, int mint, int maxt, int ptIndex) {
+    if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts)) {
+        SkPoint tmp[5];
+        int     halft = (mint + maxt) >> 1;
+
+        SkChopQuadAtHalf(pts, tmp);
+        distance = this->compute_quad_segs(tmp, distance, mint, halft, ptIndex);
+        distance = this->compute_quad_segs(&tmp[2], distance, halft, maxt, ptIndex);
+    } else {
+        SkScalar d = SkPoint::Distance(pts[0], pts[2]);
+        SkASSERT(d >= 0);
+        if (!SkScalarNearlyZero(d)) {
+            distance += d;
+            Segment* seg = fSegments.append();
+            seg->fDistance = distance;
+            seg->fPtIndex = ptIndex;
+            seg->fType = kQuad_SegType;
+            seg->fTValue = maxt;
+        }
+    }
+    return distance;
+}
+
+SkScalar SkPathMeasure::compute_cubic_segs(const SkPoint pts[4],
+                           SkScalar distance, int mint, int maxt, int ptIndex) {
+    if (tspan_big_enough(maxt - mint) && cubic_too_curvy(pts)) {
+        SkPoint tmp[7];
+        int     halft = (mint + maxt) >> 1;
+
+        SkChopCubicAtHalf(pts, tmp);
+        distance = this->compute_cubic_segs(tmp, distance, mint, halft, ptIndex);
+        distance = this->compute_cubic_segs(&tmp[3], distance, halft, maxt, ptIndex);
+    } else {
+        SkScalar d = SkPoint::Distance(pts[0], pts[3]);
+        SkASSERT(d >= 0);
+        if (!SkScalarNearlyZero(d)) {
+            distance += d;
+            Segment* seg = fSegments.append();
+            seg->fDistance = distance;
+            seg->fPtIndex = ptIndex;
+            seg->fType = kCubic_SegType;
+            seg->fTValue = maxt;
+        }
+    }
+    return distance;
+}
+
+void SkPathMeasure::buildSegments() {
+    SkPoint         pts[4];
+    int             ptIndex = fFirstPtIndex;
+    SkScalar        d, distance = 0;
+    bool            isClosed = fForceClosed;
+    bool            firstMoveTo = ptIndex < 0;
+    Segment*        seg;
+
+    fSegments.reset();
+    for (;;) {
+        switch (fIter.next(pts)) {
+            case SkPath::kMove_Verb:
+                if (!firstMoveTo) {
+                    goto DONE;
+                }
+            ptIndex += 1;
+            firstMoveTo = false;
+            break;
+
+            case SkPath::kLine_Verb:
+                d = SkPoint::Distance(pts[0], pts[1]);
+                SkASSERT(d >= 0);
+                if (!SkScalarNearlyZero(d)) {
+                    distance += d;
+                    seg = fSegments.append();
+                    seg->fDistance = distance;
+                    seg->fPtIndex = ptIndex;
+                    seg->fType = fIter.isCloseLine() ?
+                                    kCloseLine_SegType : kLine_SegType;
+                    seg->fTValue = kMaxTValue;
+                }
+                ptIndex += !fIter.isCloseLine();
+                break;
+
+            case SkPath::kQuad_Verb:
+                distance = this->compute_quad_segs(pts, distance, 0,
+                                                   kMaxTValue, ptIndex);
+                ptIndex += 2;
+                break;
+
+            case SkPath::kCubic_Verb:
+                distance = this->compute_cubic_segs(pts, distance, 0,
+                                                    kMaxTValue, ptIndex);
+                ptIndex += 3;
+                break;
+
+            case SkPath::kClose_Verb:
+                isClosed = true;
+                break;
+                
+            case SkPath::kDone_Verb:
+                goto DONE;
+        }
+    }
+DONE:
+    fLength = distance;
+    fIsClosed = isClosed;
+    fFirstPtIndex = ptIndex + 1;
+
+#ifdef SK_DEBUG
+    {
+        const Segment* seg = fSegments.begin();
+        const Segment* stop = fSegments.end();
+        unsigned        ptIndex = 0;
+        SkScalar        distance = 0;
+
+        while (seg < stop) {
+            SkASSERT(seg->fDistance > distance);
+            SkASSERT(seg->fPtIndex >= ptIndex);
+            SkASSERT(seg->fTValue > 0);
+
+            const Segment* s = seg;
+            while (s < stop - 1 && s[0].fPtIndex == s[1].fPtIndex) {
+                SkASSERT(s[0].fType == s[1].fType);
+                SkASSERT(s[0].fTValue < s[1].fTValue);
+                s += 1;
+            }
+
+            distance = seg->fDistance;
+            ptIndex = seg->fPtIndex;
+            seg += 1;
+        }
+    //  SkDebugf("\n");
+    }
+#endif
+}
+
+// marked as a friend in SkPath.h
+const SkPoint* sk_get_path_points(const SkPath& path, int index) {
+    return &path.fPts[index];
+}
+
+static void compute_pos_tan(const SkPath& path, int firstPtIndex, int ptIndex,
+                    int segType, SkScalar t, SkPoint* pos, SkVector* tangent) {
+    const SkPoint*  pts = sk_get_path_points(path, ptIndex);
+
+    switch (segType) {
+        case kLine_SegType:
+        case kCloseLine_SegType: {
+            const SkPoint* endp = (segType == kLine_SegType) ?
+                                    &pts[1] :
+                                    sk_get_path_points(path, firstPtIndex);
+
+            if (pos) {
+                pos->set(SkScalarInterp(pts[0].fX, endp->fX, t),
+                        SkScalarInterp(pts[0].fY, endp->fY, t));
+            }
+            if (tangent) {
+                tangent->setNormalize(endp->fX - pts[0].fX, endp->fY - pts[0].fY);
+            }
+            break;
+        }
+        case kQuad_SegType:
+            SkEvalQuadAt(pts, t, pos, tangent);
+            if (tangent) {
+                tangent->normalize();
+            }
+            break;
+        case kCubic_SegType:
+            SkEvalCubicAt(pts, t, pos, tangent, NULL);
+            if (tangent) {
+                tangent->normalize();
+            }
+            break;
+        default:
+            SkASSERT(!"unknown segType");
+    }
+}
+
+static void seg_to(const SkPath& src, int firstPtIndex, int ptIndex,
+                   int segType, SkScalar startT, SkScalar stopT, SkPath* dst) {
+    SkASSERT(startT >= 0 && startT <= SK_Scalar1);
+    SkASSERT(stopT >= 0 && stopT <= SK_Scalar1);
+    SkASSERT(startT <= stopT);
+
+    if (SkScalarNearlyZero(stopT - startT)) {
+        return;
+    }
+
+    const SkPoint*  pts = sk_get_path_points(src, ptIndex);
+    SkPoint         tmp0[7], tmp1[7];
+
+    switch (segType) {
+        case kLine_SegType:
+        case kCloseLine_SegType: {
+            const SkPoint* endp = (segType == kLine_SegType) ?
+                                    &pts[1] :
+                                    sk_get_path_points(src, firstPtIndex);
+
+            if (stopT == kMaxTValue) {
+                dst->lineTo(*endp);
+            } else {
+                dst->lineTo(SkScalarInterp(pts[0].fX, endp->fX, stopT),
+                            SkScalarInterp(pts[0].fY, endp->fY, stopT));
+            }
+            break;
+        }
+        case kQuad_SegType:
+            if (startT == 0) {
+                if (stopT == SK_Scalar1) {
+                    dst->quadTo(pts[1], pts[2]);
+                } else {
+                    SkChopQuadAt(pts, tmp0, stopT);
+                    dst->quadTo(tmp0[1], tmp0[2]);
+                }
+            } else {
+                SkChopQuadAt(pts, tmp0, startT);
+                if (stopT == SK_Scalar1) {
+                    dst->quadTo(tmp0[3], tmp0[4]);
+                } else {
+                    SkChopQuadAt(&tmp0[2], tmp1, SkScalarDiv(stopT - startT,
+                                                         SK_Scalar1 - startT));
+                    dst->quadTo(tmp1[1], tmp1[2]);
+                }
+            }
+            break;
+        case kCubic_SegType:
+            if (startT == 0) {
+                if (stopT == SK_Scalar1) {
+                    dst->cubicTo(pts[1], pts[2], pts[3]);
+                } else {
+                    SkChopCubicAt(pts, tmp0, stopT);
+                    dst->cubicTo(tmp0[1], tmp0[2], tmp0[3]);
+                }
+            } else {
+                SkChopCubicAt(pts, tmp0, startT);
+                if (stopT == SK_Scalar1) {
+                    dst->cubicTo(tmp0[4], tmp0[5], tmp0[6]);
+                } else {
+                    SkChopCubicAt(&tmp0[3], tmp1, SkScalarDiv(stopT - startT,
+                                                        SK_Scalar1 - startT));
+                    dst->cubicTo(tmp1[1], tmp1[2], tmp1[3]);
+                }
+            }
+            break;
+        default:
+            SkASSERT(!"unknown segType");
+            sk_throw();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+SkPathMeasure::SkPathMeasure() {
+    fPath = NULL;
+    fLength = -1;   // signal we need to compute it
+    fForceClosed = false;
+    fFirstPtIndex = -1;
+}
+
+SkPathMeasure::SkPathMeasure(const SkPath& path, bool forceClosed) {
+    fPath = &path;
+    fLength = -1;   // signal we need to compute it
+    fForceClosed = forceClosed;
+    fFirstPtIndex = -1;
+
+    fIter.setPath(path, forceClosed);
+}
+
+SkPathMeasure::~SkPathMeasure() {}
+
+/** Assign a new path, or null to have none.
+*/
+void SkPathMeasure::setPath(const SkPath* path, bool forceClosed) {
+    fPath = path;
+    fLength = -1;   // signal we need to compute it
+    fForceClosed = forceClosed;
+    fFirstPtIndex = -1;
+
+    if (path) {
+        fIter.setPath(*path, forceClosed);
+    }
+    fSegments.reset();
+}
+
+SkScalar SkPathMeasure::getLength() {
+    if (fPath == NULL) {
+        return 0;
+    }
+    if (fLength < 0) {
+        this->buildSegments();
+    }
+    SkASSERT(fLength >= 0);
+    return fLength;
+}
+
+const SkPathMeasure::Segment* SkPathMeasure::distanceToSegment(
+                                            SkScalar distance, SkScalar* t) {
+    SkDEBUGCODE(SkScalar length = ) this->getLength();
+    SkASSERT(distance >= 0 && distance <= length);
+
+    const Segment*  seg = fSegments.begin();
+    int             count = fSegments.count();
+
+    int index = SkTSearch<SkScalar>(&seg->fDistance, count, distance,
+                                    sizeof(Segment));
+    // don't care if we hit an exact match or not, so we xor index if it is negative
+    index ^= (index >> 31);
+    seg = &seg[index];
+
+    // now interpolate t-values with the prev segment (if possible)
+    SkScalar    startT = 0, startD = 0;
+    // check if the prev segment is legal, and references the same set of points
+    if (index > 0) {
+        startD = seg[-1].fDistance;
+        if (seg[-1].fPtIndex == seg->fPtIndex) {
+            SkASSERT(seg[-1].fType == seg->fType);
+            startT = seg[-1].getScalarT();
+        }
+    }
+
+    SkASSERT(seg->getScalarT() > startT);
+    SkASSERT(distance >= startD);
+    SkASSERT(seg->fDistance > startD);
+
+    *t = startT + SkScalarMulDiv(seg->getScalarT() - startT,
+                                 distance - startD,
+                                 seg->fDistance - startD);
+    return seg;
+}
+
+bool SkPathMeasure::getPosTan(SkScalar distance, SkPoint* pos,
+                              SkVector* tangent) {
+    SkASSERT(fPath);
+    if (fPath == NULL) {
+    EMPTY:
+        return false;
+    }
+
+    SkScalar    length = this->getLength(); // call this to force computing it
+    int         count = fSegments.count();
+
+    if (count == 0 || length == 0) {
+        goto EMPTY;
+    }
+
+    // pin the distance to a legal range
+    if (distance < 0) {
+        distance = 0;
+    } else if (distance > length) {
+        distance = length;
+    }
+    
+    SkScalar        t;
+    const Segment*  seg = this->distanceToSegment(distance, &t);
+
+    compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType,
+                    t, pos, tangent);
+    return true;
+}
+
+bool SkPathMeasure::getMatrix(SkScalar distance, SkMatrix* matrix,
+                              MatrixFlags flags) {
+    SkPoint     position;
+    SkVector    tangent;
+
+    if (this->getPosTan(distance, &position, &tangent)) {
+        if (matrix) {
+            if (flags & kGetTangent_MatrixFlag) {
+                matrix->setSinCos(tangent.fY, tangent.fX, 0, 0);
+            } else {
+                matrix->reset();
+            }
+            if (flags & kGetPosition_MatrixFlag) {
+                matrix->postTranslate(position.fX, position.fY);
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst,
+                               bool startWithMoveTo) {
+    SkASSERT(dst);
+
+    SkScalar length = this->getLength();    // ensure we have built our segments
+
+    if (startD < 0) {
+        startD = 0;
+    }
+    if (stopD > length) {
+        stopD = length;
+    }
+    if (startD >= stopD) {
+        return false;
+    }
+
+    SkPoint  p;
+    SkScalar startT, stopT;
+    const Segment* seg = this->distanceToSegment(startD, &startT);
+    const Segment* stopSeg = this->distanceToSegment(stopD, &stopT);
+    SkASSERT(seg <= stopSeg);
+
+    if (startWithMoveTo) {
+        compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex,
+                        seg->fType, startT, &p, NULL);
+        dst->moveTo(p);
+    }
+
+    if (seg->fPtIndex == stopSeg->fPtIndex) {
+        seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType,
+               startT, stopT, dst);
+    } else {
+        do {
+            seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType,
+                   startT, SK_Scalar1, dst);
+            seg = SkPathMeasure::NextSegment(seg);
+            startT = 0;
+        } while (seg->fPtIndex < stopSeg->fPtIndex);
+        seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType,
+               0, stopT, dst);
+    }
+    return true;
+}
+
+bool SkPathMeasure::isClosed() {
+    (void)this->getLength();
+    return fIsClosed;
+}
+
+/** Move to the next contour in the path. Return true if one exists, or false if
+    we're done with the path.
+*/
+bool SkPathMeasure::nextContour() {
+    fLength = -1;
+    return this->getLength() > 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkPathMeasure::dump() {
+    SkDebugf("pathmeas: length=%g, segs=%d\n", fLength, fSegments.count());
+
+    for (int i = 0; i < fSegments.count(); i++) {
+        const Segment* seg = &fSegments[i];
+        SkDebugf("pathmeas: seg[%d] distance=%g, point=%d, t=%g, type=%d\n",
+                i, seg->fDistance, seg->fPtIndex, seg->getScalarT(),
+                 seg->fType);
+    }
+}
+
+void SkPathMeasure::UnitTest() {
+#ifdef SK_SUPPORT_UNITTEST
+    SkPath  path;
+
+    path.moveTo(0, 0);
+    path.lineTo(SK_Scalar1, 0);
+    path.lineTo(SK_Scalar1, SK_Scalar1);
+    path.lineTo(0, SK_Scalar1);
+
+    SkPathMeasure   meas(path, true);
+    SkScalar        length = meas.getLength();
+    SkASSERT(length == SK_Scalar1*4);
+
+    path.reset();
+    path.moveTo(0, 0);
+    path.lineTo(SK_Scalar1*3, SK_Scalar1*4);
+    meas.setPath(&path, false);
+    length = meas.getLength();
+    SkASSERT(length == SK_Scalar1*5);
+
+    path.reset();
+    path.addCircle(0, 0, SK_Scalar1);
+    meas.setPath(&path, true);
+    length = meas.getLength();
+    SkDebugf("circle arc-length = %g\n", length);
+
+    for (int i = 0; i < 8; i++) {
+        SkScalar    d = length * i / 8;
+        SkPoint     p;
+        SkVector    v;
+        meas.getPosTan(d, &p, &v);
+        SkDebugf("circle arc-length=%g, pos[%g %g] tan[%g %g]\n",
+                 d, p.fX, p.fY, v.fX, v.fY);
+    }
+#endif
+}
+
+#endif
diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
new file mode 100644
index 0000000..07cb0a6
--- /dev/null
+++ b/src/core/SkPicture.cpp
@@ -0,0 +1,246 @@
+/*
+**
+** Copyright 2007, 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 "SkPictureFlat.h"
+#include "SkPicturePlayback.h"
+#include "SkPictureRecord.h"
+
+#include "SkCanvas.h"
+#include "SkChunkAlloc.h"
+#include "SkPicture.h"
+#include "SkRegion.h"
+#include "SkStream.h"
+#include "SkTDArray.h"
+#include "SkTSearch.h"
+#include "SkTime.h"
+
+#include "SkReader32.h"
+#include "SkWriter32.h"
+
+#define DUMP_BUFFER_SIZE 65536
+
+//#define ENABLE_TIME_DRAW    // dumps milliseconds for each draw
+
+
+#ifdef SK_DEBUG
+// enable SK_DEBUG_TRACE to trace DrawType elements when 
+//     recorded and played back
+// #define SK_DEBUG_TRACE
+// enable SK_DEBUG_SIZE to see the size of picture components
+// #define SK_DEBUG_SIZE
+// enable SK_DEBUG_DUMP to see the contents of recorded elements
+// #define SK_DEBUG_DUMP
+// enable SK_DEBUG_VALIDATE to check internal structures for consistency
+// #define SK_DEBUG_VALIDATE
+#endif
+
+#if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP
+const char* DrawTypeToString(DrawType drawType) {
+    switch (drawType) {
+        case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break;
+        case CLIP_PATH: return "CLIP_PATH";
+        case CLIP_REGION: return "CLIP_REGION";
+        case CLIP_RECT: return "CLIP_RECT";
+        case CONCAT: return "CONCAT";
+        case DRAW_BITMAP: return "DRAW_BITMAP";
+        case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX";
+        case DRAW_BITMAP_RECT: return "DRAW_BITMAP_RECT";
+        case DRAW_PAINT: return "DRAW_PAINT";
+        case DRAW_PATH: return "DRAW_PATH";
+        case DRAW_PICTURE: return "DRAW_PICTURE";
+        case DRAW_POINTS: return "DRAW_POINTS";
+        case DRAW_POS_TEXT: return "DRAW_POS_TEXT";
+        case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H";
+        case DRAW_RECT_GENERAL: return "DRAW_RECT_GENERAL";
+        case DRAW_RECT_SIMPLE: return "DRAW_RECT_SIMPLE";
+        case DRAW_SPRITE: return "DRAW_SPRITE";
+        case DRAW_TEXT: return "DRAW_TEXT";
+        case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH";
+        case RESTORE: return "RESTORE";
+        case ROTATE: return "ROTATE";
+        case SAVE: return "SAVE";
+        case SAVE_LAYER: return "SAVE_LAYER";
+        case SCALE: return "SCALE";
+        case SKEW: return "SKEW";
+        case TRANSLATE: return "TRANSLATE";
+        default: 
+            SkDebugf("DrawType error 0x%08x\n", drawType); 
+            SkASSERT(0); 
+            break;
+    }
+    SkASSERT(0); 
+    return NULL;
+}
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+static void validateMatrix(const SkMatrix* matrix) {
+    SkScalar scaleX = matrix->getScaleX();
+    SkScalar scaleY = matrix->getScaleY();
+    SkScalar skewX = matrix->getSkewX();
+    SkScalar skewY = matrix->getSkewY();
+    SkScalar perspX = matrix->getPerspX();
+    SkScalar perspY = matrix->getPerspY();
+    if (scaleX != 0 && skewX != 0)
+        SkDebugf("scaleX != 0 && skewX != 0\n");
+    SkASSERT(scaleX == 0 || skewX == 0);
+    SkASSERT(scaleY == 0 || skewY == 0);
+    SkASSERT(perspX == 0);
+    SkASSERT(perspY == 0);
+}
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPicture::SkPicture() {
+    fRecord = NULL;
+    fPlayback = NULL;
+    fWidth = fHeight = 0;
+}
+
+SkPicture::SkPicture(const SkPicture& src) : SkRefCnt() {
+    fWidth = src.fWidth;
+    fHeight = src.fHeight;
+    fRecord = NULL;
+
+    /*  We want to copy the src's playback. However, if that hasn't been built
+        yet, we need to fake a call to endRecording() without actually calling
+        it (since it is destructive, and we don't want to change src).
+     */
+    if (src.fPlayback) {
+        fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback));
+    } else if (src.fRecord) {
+        // here we do a fake src.endRecording()
+        fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord));
+    } else {
+        fPlayback = NULL;
+    }
+}
+
+SkPicture::~SkPicture() {
+    fRecord->safeUnref();
+    SkDELETE(fPlayback);
+}
+
+void SkPicture::swap(SkPicture& other) {
+    SkTSwap(fRecord, other.fRecord);
+    SkTSwap(fPlayback, other.fPlayback);
+    SkTSwap(fWidth, other.fWidth);
+    SkTSwap(fHeight, other.fHeight);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas* SkPicture::beginRecording(int width, int height) {
+    if (fPlayback) {
+        SkDELETE(fPlayback);
+        fPlayback = NULL;
+    }
+
+    if (NULL != fRecord) {
+        fRecord->unref();
+        fRecord = NULL;
+    }
+
+    fRecord = SkNEW(SkPictureRecord);
+
+    fWidth = width;
+    fHeight = height;
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kNo_Config, width, height);
+    fRecord->setBitmapDevice(bm);
+    
+    return fRecord;
+}
+
+SkCanvas* SkPicture::getRecordingCanvas() const {
+    // will be null if we are not recording
+    return fRecord;
+}
+
+void SkPicture::endRecording() {
+    if (NULL == fPlayback) {
+        if (NULL != fRecord) {
+            fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
+            fRecord->unref();
+            fRecord = NULL;
+        }
+    }
+    SkASSERT(NULL == fRecord);
+}
+
+void SkPicture::draw(SkCanvas* surface) {
+    this->endRecording();
+    if (fPlayback) {
+        fPlayback->draw(*surface);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+#define PICTURE_VERSION     1
+
+SkPicture::SkPicture(SkStream* stream) : SkRefCnt() {
+    if (stream->readU32() != PICTURE_VERSION) {
+        sk_throw();
+    }
+
+    fWidth = stream->readU32();
+    fHeight = stream->readU32();
+
+    fRecord = NULL;
+    fPlayback = NULL;
+
+    if (stream->readBool()) {
+        fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream));
+    }
+}
+
+void SkPicture::serialize(SkWStream* stream) const {
+    SkPicturePlayback* playback = fPlayback;
+    
+    if (NULL == playback && fRecord) {
+        playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
+    }
+
+    stream->write32(PICTURE_VERSION);
+    stream->write32(fWidth);
+    stream->write32(fHeight);
+    if (playback) {
+        stream->writeBool(true);
+        playback->serialize(stream);
+        // delete playback if it is a local version (i.e. cons'd up just now)
+        if (playback != fPlayback) {
+            SkDELETE(playback);
+        }
+    } else {
+        stream->writeBool(false);
+    }
+}
+
+void SkPicture::abortPlayback() {
+    if (NULL == fPlayback) {
+        return;
+    }
+    fPlayback->abort();
+}
+
+
diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
new file mode 100644
index 0000000..e221e55
--- /dev/null
+++ b/src/core/SkPictureFlat.cpp
@@ -0,0 +1,257 @@
+#include "SkPictureFlat.h"
+
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkMaskFilter.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+SkFlatData* SkFlatData::Alloc(SkChunkAlloc* heap, int32_t size, int index) {
+    SkFlatData* result = (SkFlatData*) heap->allocThrow(size + sizeof(SkFlatData));
+    result->fIndex = index;
+    result->fAllocSize = size + sizeof(result->fAllocSize);
+    return result;
+}
+
+SkFlatBitmap* SkFlatBitmap::Flatten(SkChunkAlloc* heap, const SkBitmap& bitmap,
+                                    int index, SkRefCntRecorder* rec) {
+    SkFlattenableWriteBuffer buffer(1024);
+    buffer.setRefCntRecorder(rec);
+    
+    bitmap.flatten(buffer);
+    size_t size = buffer.size();
+    SkFlatBitmap* result = (SkFlatBitmap*) INHERITED::Alloc(heap, size, index);
+    buffer.flatten(result->fBitmapData);
+    return result;
+}
+
+SkFlatMatrix* SkFlatMatrix::Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index) {
+    int32_t size = sizeof(SkMatrix);
+    SkFlatMatrix* result = (SkFlatMatrix*) INHERITED::Alloc(heap, size, index);
+    memcpy(&result->fMatrixData, &matrix, sizeof(SkMatrix));
+    return result;
+}
+
+#ifdef SK_DEBUG_DUMP
+void SkFlatMatrix::dump() const {
+    const SkMatrix* matrix = (const SkMatrix*) fMatrixData;
+    char pBuffer[DUMP_BUFFER_SIZE];
+    char* bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+    "matrix: ");
+    SkScalar scaleX = matrix->getScaleX();
+    SkMatrix defaultMatrix;
+    defaultMatrix.reset();
+    if (scaleX != defaultMatrix.getScaleX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "scaleX:%g ", SkScalarToFloat(scaleX));
+    SkScalar scaleY = matrix->getScaleY();
+    if (scaleY != defaultMatrix.getScaleY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "scaleY:%g ", SkScalarToFloat(scaleY));
+    SkScalar skewX = matrix->getSkewX();
+    if (skewX != defaultMatrix.getSkewX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "skewX:%g ", SkScalarToFloat(skewX));
+    SkScalar skewY = matrix->getSkewY();
+    if (skewY != defaultMatrix.getSkewY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "skewY:%g ", SkScalarToFloat(skewY));
+    SkScalar translateX = matrix->getTranslateX();
+    if (translateX != defaultMatrix.getTranslateX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "translateX:%g ", SkScalarToFloat(translateX));
+    SkScalar translateY = matrix->getTranslateY();
+    if (translateY != defaultMatrix.getTranslateY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "translateY:%g ", SkScalarToFloat(translateY));
+    SkScalar perspX = matrix->getPerspX();
+    if (perspX != defaultMatrix.getPerspX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "perspX:%g ", SkFractToFloat(perspX));
+    SkScalar perspY = matrix->getPerspY();
+    if (perspY != defaultMatrix.getPerspY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "perspY:%g ", SkFractToFloat(perspY));
+    SkDebugf("%s\n", pBuffer);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFlatPaint* SkFlatPaint::Flatten(SkChunkAlloc* heap, const SkPaint& paint,
+                                  int index, SkRefCntRecorder* rec,
+                                  SkRefCntRecorder* faceRecorder) {
+    SkFlattenableWriteBuffer buffer(2*sizeof(SkPaint));
+    buffer.setRefCntRecorder(rec);
+    buffer.setTypefaceRecorder(faceRecorder);
+
+    paint.flatten(buffer);
+    uint32_t size = buffer.size();
+    SkFlatPaint* result = (SkFlatPaint*) INHERITED::Alloc(heap, size, index);
+    buffer.flatten(&result->fPaintData);
+    return result;
+}
+    
+void SkFlatPaint::Read(const void* storage, SkPaint* paint,
+                   SkRefCntPlayback* rcp, SkTypefacePlayback* facePlayback) {
+    SkFlattenableReadBuffer buffer(storage);
+    if (rcp) {
+        rcp->setupBuffer(buffer);
+    }
+    if (facePlayback) {
+        facePlayback->setupBuffer(buffer);
+    }
+    paint->unflatten(buffer);
+}
+
+#ifdef SK_DEBUG_DUMP
+void SkFlatPaint::dump() const {
+    SkPaint defaultPaint;
+    SkFlattenableReadBuffer buffer(fPaintData);
+    SkTypeface* typeface = (SkTypeface*) buffer.readPtr();
+    char pBuffer[DUMP_BUFFER_SIZE];
+    char* bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+        "paint: ");
+    if (typeface != defaultPaint.getTypeface())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "typeface:%p ", typeface);
+    SkScalar textSize = buffer.readScalar();
+    if (textSize != defaultPaint.getTextSize())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "textSize:%g ", SkScalarToFloat(textSize));
+    SkScalar textScaleX = buffer.readScalar();
+    if (textScaleX != defaultPaint.getTextScaleX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "textScaleX:%g ", SkScalarToFloat(textScaleX));
+    SkScalar textSkewX = buffer.readScalar();
+    if (textSkewX != defaultPaint.getTextSkewX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "textSkewX:%g ", SkScalarToFloat(textSkewX));
+    const SkPathEffect* pathEffect = (const SkPathEffect*) buffer.readFlattenable();
+    if (pathEffect != defaultPaint.getPathEffect())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "pathEffect:%p ", pathEffect);
+    SkDELETE(pathEffect);
+    const SkShader* shader = (const SkShader*) buffer.readFlattenable();
+    if (shader != defaultPaint.getShader())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "shader:%p ", shader);
+    SkDELETE(shader);
+    const SkXfermode* xfermode = (const SkXfermode*) buffer.readFlattenable();
+    if (xfermode != defaultPaint.getXfermode())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "xfermode:%p ", xfermode);
+    SkDELETE(xfermode);
+    const SkMaskFilter* maskFilter = (const SkMaskFilter*) buffer.readFlattenable();
+    if (maskFilter != defaultPaint.getMaskFilter())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "maskFilter:%p ", maskFilter);
+    SkDELETE(maskFilter);
+    const SkColorFilter* colorFilter = (const SkColorFilter*) buffer.readFlattenable();
+    if (colorFilter != defaultPaint.getColorFilter())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "colorFilter:%p ", colorFilter);
+    SkDELETE(colorFilter);
+    const SkRasterizer* rasterizer = (const SkRasterizer*) buffer.readFlattenable();
+    if (rasterizer != defaultPaint.getRasterizer())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "rasterizer:%p ", rasterizer);
+    SkDELETE(rasterizer);
+    const SkDrawLooper* drawLooper = (const SkDrawLooper*) buffer.readFlattenable();
+    if (drawLooper != defaultPaint.getLooper())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "drawLooper:%p ", drawLooper);
+    SkDELETE(drawLooper);
+    unsigned color = buffer.readU32();
+    if (color != defaultPaint.getColor())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "color:0x%x ", color);
+    SkScalar strokeWidth = buffer.readScalar();
+    if (strokeWidth != defaultPaint.getStrokeWidth())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "strokeWidth:%g ", SkScalarToFloat(strokeWidth));
+    SkScalar strokeMiter = buffer.readScalar();
+    if (strokeMiter != defaultPaint.getStrokeMiter())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "strokeMiter:%g ", SkScalarToFloat(strokeMiter));
+    unsigned flags = buffer.readU16();
+    if (flags != defaultPaint.getFlags())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "flags:0x%x ", flags);
+    int align = buffer.readU8();
+    if (align != defaultPaint.getTextAlign())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "align:0x%x ", align);
+    int strokeCap = buffer.readU8();
+    if (strokeCap != defaultPaint.getStrokeCap())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "strokeCap:0x%x ", strokeCap);
+    int strokeJoin = buffer.readU8();
+    if (strokeJoin != defaultPaint.getStrokeJoin())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "align:0x%x ", strokeJoin);
+    int style = buffer.readU8();
+    if (style != defaultPaint.getStyle())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "style:0x%x ", style);
+    int textEncoding = buffer.readU8();
+    if (textEncoding != defaultPaint.getTextEncoding())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "textEncoding:0x%x ", textEncoding);
+    SkDebugf("%s\n", pBuffer);
+}
+#endif
+
+SkFlatRegion* SkFlatRegion::Flatten(SkChunkAlloc* heap, const SkRegion& region, int index) {
+    uint32_t size = region.flatten(NULL);
+    SkFlatRegion* result = (SkFlatRegion*) INHERITED::Alloc(heap, size, index);
+    region.flatten(&result->fRegionData);
+    return result;
+}
+    
+///////////////////////////////////////////////////////////////////////////////
+
+SkRefCntPlayback::SkRefCntPlayback() : fCount(0), fArray(NULL) {}
+
+SkRefCntPlayback::~SkRefCntPlayback() {
+    this->reset(NULL);
+}
+
+void SkRefCntPlayback::reset(const SkRefCntRecorder* rec) {
+    for (int i = 0; i < fCount; i++) {
+        SkASSERT(fArray[i]);
+        fArray[i]->unref();
+    }
+    SkDELETE_ARRAY(fArray);
+    
+    if (rec) {
+        fCount = rec->count();
+        fArray = SkNEW_ARRAY(SkRefCnt*, fCount);
+        rec->get(fArray);
+        for (int i = 0; i < fCount; i++) {
+            fArray[i]->ref();
+        }
+    } else {
+        fCount = 0;
+        fArray = NULL;
+    }
+}
+
+void SkRefCntPlayback::setCount(int count) {
+    this->reset(NULL);
+    
+    fCount = count;
+    fArray = SkNEW_ARRAY(SkRefCnt*, count);
+    bzero(fArray, count * sizeof(SkRefCnt*));
+}
+
+SkRefCnt* SkRefCntPlayback::set(int index, SkRefCnt* obj) {
+    SkASSERT((unsigned)index < (unsigned)fCount);
+    SkRefCnt_SafeAssign(fArray[index], obj);
+    return obj;
+}
+
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
new file mode 100644
index 0000000..9e7fc5e
--- /dev/null
+++ b/src/core/SkPictureFlat.h
@@ -0,0 +1,206 @@
+#ifndef SkPictureFlat_DEFINED
+#define SkPictureFlat_DEFINED
+
+#include "SkChunkAlloc.h"
+#include "SkBitmap.h"
+#include "SkPicture.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+
+enum DrawType {
+    UNUSED,
+    CLIP_PATH,
+    CLIP_REGION,
+    CLIP_RECT,
+    CONCAT,
+    DRAW_BITMAP,
+    DRAW_BITMAP_MATRIX,
+    DRAW_BITMAP_RECT,
+    DRAW_PAINT,
+    DRAW_PATH,
+    DRAW_PICTURE,
+    DRAW_POINTS,
+    DRAW_POS_TEXT,
+    DRAW_POS_TEXT_H,
+    DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
+    DRAW_RECT,
+    DRAW_SPRITE,
+    DRAW_TEXT,
+    DRAW_TEXT_ON_PATH,
+    DRAW_TEXT_TOP_BOTTOM,   // fast variant of DRAW_TEXT
+    DRAW_VERTICES,
+    RESTORE,
+    ROTATE,
+    SAVE,
+    SAVE_LAYER,
+    SCALE,
+    SKEW,
+    TRANSLATE
+};
+
+enum DrawVertexFlags {
+    DRAW_VERTICES_HAS_TEXS    = 0x01,
+    DRAW_VERTICES_HAS_COLORS  = 0x02,
+    DRAW_VERTICES_HAS_INDICES = 0x04
+};
+
+class SkRefCntPlayback {
+public:
+    SkRefCntPlayback();
+    ~SkRefCntPlayback();
+    
+    int count() const { return fCount; }
+    
+    void reset(const SkRefCntRecorder*);
+
+    void setCount(int count);
+    SkRefCnt* set(int index, SkRefCnt*);
+
+    virtual void setupBuffer(SkFlattenableReadBuffer& buffer) const {
+        buffer.setRefCntArray(fArray, fCount);
+    }
+    
+protected:
+    int fCount;
+    SkRefCnt** fArray;
+};
+
+class SkTypefacePlayback : public SkRefCntPlayback {
+public:
+    virtual void setupBuffer(SkFlattenableReadBuffer& buffer) const {
+        buffer.setTypefaceArray((SkTypeface**)fArray, fCount);
+    }
+};
+
+class SkFactoryPlayback {
+public:
+    SkFactoryPlayback(int count) : fCount(count) {
+        fArray = SkNEW_ARRAY(SkFlattenable::Factory, count);
+    }
+
+    ~SkFactoryPlayback() {
+        SkDELETE_ARRAY(fArray);
+    }
+    
+    SkFlattenable::Factory* base() const { return fArray; }
+
+    void setupBuffer(SkFlattenableReadBuffer& buffer) const {
+        buffer.setFactoryPlayback(fArray, fCount);
+    }
+    
+private:
+    int fCount;
+    SkFlattenable::Factory* fArray;
+};
+
+class SkFlatData {
+public:
+    static int Compare(const SkFlatData* a, const SkFlatData* b) {
+        return memcmp(&a->fAllocSize, &b->fAllocSize, a->fAllocSize);
+    }
+    
+    int index() const { return fIndex; }
+    
+#ifdef SK_DEBUG_SIZE
+    size_t size() const { return sizeof(fIndex) + fAllocSize; }
+#endif
+
+protected:
+    static SkFlatData* Alloc(SkChunkAlloc* heap, int32_t size, int index);
+    
+    int fIndex;
+    int32_t fAllocSize;
+};
+
+class SkFlatBitmap : public SkFlatData {
+public:
+    static SkFlatBitmap* Flatten(SkChunkAlloc*, const SkBitmap&, int index,
+                                 SkRefCntRecorder*);
+
+    void unflatten(SkBitmap* bitmap, SkRefCntPlayback* rcp) const {
+        SkFlattenableReadBuffer buffer(fBitmapData);
+        if (rcp) {
+            rcp->setupBuffer(buffer);
+        }
+        bitmap->unflatten(buffer);
+    }
+
+#ifdef SK_DEBUG_VALIDATE
+    void validate() const {
+        // to be written
+    }
+#endif
+
+private:
+    char fBitmapData[1];
+    typedef SkFlatData INHERITED;
+};
+
+class SkFlatMatrix : public SkFlatData {
+public:
+    static SkFlatMatrix* Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index);
+
+    void unflatten(SkMatrix* result) const {
+        memcpy(result, fMatrixData, sizeof(SkMatrix));
+    }
+
+#ifdef SK_DEBUG_DUMP
+    void dump() const;
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+    void validate() const {
+        // to be written
+    }
+#endif
+
+private:
+    char fMatrixData[1];
+    typedef SkFlatData INHERITED;
+};
+
+class SkFlatPaint : public SkFlatData {
+public:
+    static SkFlatPaint* Flatten(SkChunkAlloc* heap, const SkPaint& paint,
+                                int index, SkRefCntRecorder*,
+                                SkRefCntRecorder* faceRecorder);
+    
+    void unflatten(SkPaint* result, SkRefCntPlayback* rcp,
+                   SkTypefacePlayback* facePlayback) const {
+        Read(fPaintData, result, rcp, facePlayback);
+    }
+    
+    static void Read(const void* storage, SkPaint* paint, SkRefCntPlayback*,
+                     SkTypefacePlayback* facePlayback);
+
+#ifdef SK_DEBUG_DUMP
+    void dump() const;
+#endif
+    
+private:
+    char fPaintData[1];
+    typedef SkFlatData INHERITED;
+};
+
+class SkFlatRegion : public SkFlatData {
+public:
+    static SkFlatRegion* Flatten(SkChunkAlloc* heap, const SkRegion& region, int index);
+    
+    void unflatten(SkRegion* result) const {
+        result->unflatten(fRegionData);
+    }
+
+#ifdef SK_DEBUG_VALIDATE
+    void validate() const {
+        // to be written
+    }
+#endif
+
+private:
+    char fRegionData[1];
+    typedef SkFlatData INHERITED;
+};
+
+#endif
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
new file mode 100644
index 0000000..64f8f1c
--- /dev/null
+++ b/src/core/SkPicturePlayback.cpp
@@ -0,0 +1,1338 @@
+#include "SkPicturePlayback.h"
+#include "SkPictureRecord.h"
+#include "SkTypeface.h"
+#include <new>
+
+SkPicturePlayback::SkPicturePlayback() {
+    this->init();
+}
+
+SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) {
+#ifdef SK_DEBUG_SIZE
+    size_t overallBytes, bitmapBytes, matricesBytes, 
+    paintBytes, pathBytes, pictureBytes, regionBytes;
+    int bitmaps = record.bitmaps(&bitmapBytes);
+    int matrices = record.matrices(&matricesBytes);
+    int paints = record.paints(&paintBytes);
+    int paths = record.paths(&pathBytes);
+    int pictures = record.pictures(&pictureBytes);
+    int regions = record.regions(&regionBytes);
+    SkDebugf("picture record mem used %zd (stream %zd) ", record.size(),
+             record.streamlen());
+    if (bitmaps != 0)
+        SkDebugf("bitmaps size %zd (bitmaps:%d) ", bitmapBytes, bitmaps);
+    if (matrices != 0)
+        SkDebugf("matrices size %zd (matrices:%d) ", matricesBytes, matrices);
+    if (paints != 0)
+        SkDebugf("paints size %zd (paints:%d) ", paintBytes, paints);
+    if (paths != 0)
+        SkDebugf("paths size %zd (paths:%d) ", pathBytes, paths);
+    if (pictures != 0)
+        SkDebugf("pictures size %zd (pictures:%d) ", pictureBytes, pictures);
+    if (regions != 0)
+        SkDebugf("regions size %zd (regions:%d) ", regionBytes, regions);
+    if (record.fPointWrites != 0)
+        SkDebugf("points size %zd (points:%d) ", record.fPointBytes, record.fPointWrites);
+    if (record.fRectWrites != 0)
+        SkDebugf("rects size %zd (rects:%d) ", record.fRectBytes, record.fRectWrites);
+    if (record.fTextWrites != 0)
+        SkDebugf("text size %zd (text strings:%d) ", record.fTextBytes, record.fTextWrites);
+    
+    SkDebugf("\n");
+#endif
+#ifdef SK_DEBUG_DUMP
+    record.dumpMatrices();
+    record.dumpPaints();
+#endif
+
+    record.validate();
+    const SkWriter32& writer = record.writeStream();
+    init();
+    if (writer.size() == 0)
+        return;
+    
+    {
+        size_t size = writer.size();
+        void* buffer = sk_malloc_throw(size);
+        writer.flatten(buffer);
+        fReader.setMemory(buffer, size);    // fReader owns buffer now
+    }
+    
+    // copy over the refcnt dictionary to our reader
+    //
+    fRCPlayback.reset(&record.fRCRecorder);
+    fRCPlayback.setupBuffer(fReader);
+    
+    fTFPlayback.reset(&record.fTFRecorder);
+    fTFPlayback.setupBuffer(fReader);
+    
+    const SkTDArray<const SkFlatBitmap* >& bitmaps = record.getBitmaps();
+    fBitmapCount = bitmaps.count();
+    if (fBitmapCount > 0) {
+        fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount);
+        for (const SkFlatBitmap** flatBitmapPtr = bitmaps.begin(); 
+             flatBitmapPtr != bitmaps.end(); flatBitmapPtr++) {
+            const SkFlatBitmap* flatBitmap = *flatBitmapPtr;
+            int index = flatBitmap->index() - 1;
+            flatBitmap->unflatten(&fBitmaps[index], &fRCPlayback);
+        }
+    }
+
+    const SkTDArray<const SkFlatMatrix* >& matrices = record.getMatrices();
+    fMatrixCount = matrices.count();
+    if (fMatrixCount > 0) {
+        fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount);
+        for (const SkFlatMatrix** matrixPtr = matrices.begin(); 
+             matrixPtr != matrices.end(); matrixPtr++) {
+            const SkFlatMatrix* flatMatrix = *matrixPtr;
+            flatMatrix->unflatten(&fMatrices[flatMatrix->index() - 1]);
+        }
+    }
+
+    const SkTDArray<const SkFlatPaint* >& paints = record.getPaints();
+    fPaintCount = paints.count();
+    if (fPaintCount > 0) {
+        fPaints = SkNEW_ARRAY(SkPaint, fPaintCount);
+        for (const SkFlatPaint** flatPaintPtr = paints.begin(); 
+             flatPaintPtr != paints.end(); flatPaintPtr++) {
+            const SkFlatPaint* flatPaint = *flatPaintPtr;
+            int index = flatPaint->index() - 1;
+            SkASSERT((unsigned)index < (unsigned)fPaintCount);
+            flatPaint->unflatten(&fPaints[index], &fRCPlayback, &fTFPlayback);
+        }
+    }
+
+    fPathHeap = record.fPathHeap;
+    fPathHeap->safeRef();
+
+    const SkTDArray<SkPicture* >& pictures = record.getPictureRefs();
+    fPictureCount = pictures.count();
+    if (fPictureCount > 0) {
+        fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
+        for (int i = 0; i < fPictureCount; i++) {
+            fPictureRefs[i] = pictures[i];
+            fPictureRefs[i]->ref();
+        }
+    }
+
+    const SkTDArray<const SkFlatRegion* >& regions = record.getRegions();
+    fRegionCount = regions.count();
+    if (fRegionCount > 0) {
+        fRegions = SkNEW_ARRAY(SkRegion, fRegionCount);
+        for (const SkFlatRegion** flatRegionPtr = regions.begin(); 
+             flatRegionPtr != regions.end(); flatRegionPtr++) {
+            const SkFlatRegion* flatRegion = *flatRegionPtr;
+            flatRegion->unflatten(&fRegions[flatRegion->index() - 1]);
+        }
+    }
+    
+#ifdef SK_DEBUG_SIZE
+    int overall = fPlayback->size(&overallBytes);
+    bitmaps = fPlayback->bitmaps(&bitmapBytes);
+    paints = fPlayback->paints(&paintBytes);
+    paths = fPlayback->paths(&pathBytes);
+    pictures = fPlayback->pictures(&pictureBytes);
+    regions = fPlayback->regions(&regionBytes);
+    SkDebugf("playback size %zd (objects:%d) ", overallBytes, overall);
+    if (bitmaps != 0)
+        SkDebugf("bitmaps size %zd (bitmaps:%d) ", bitmapBytes, bitmaps);
+    if (paints != 0)
+        SkDebugf("paints size %zd (paints:%d) ", paintBytes, paints);
+    if (paths != 0)
+        SkDebugf("paths size %zd (paths:%d) ", pathBytes, paths);
+    if (pictures != 0)
+        SkDebugf("pictures size %zd (pictures:%d) ", pictureBytes, pictures);
+    if (regions != 0)
+        SkDebugf("regions size %zd (regions:%d) ", regionBytes, regions);
+    SkDebugf("\n");
+#endif
+}
+
+SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src) {
+    this->init();
+
+    // copy the data from fReader
+    {
+        size_t size = src.fReader.size();
+        void* buffer = sk_malloc_throw(size);
+        memcpy(buffer, src.fReader.base(), size);
+        fReader.setMemory(buffer, size);
+    }
+
+    int i;
+
+    fBitmapCount = src.fBitmapCount;
+    fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount);
+    for (i = 0; i < fBitmapCount; i++) {
+        fBitmaps[i] = src.fBitmaps[i];
+    }
+
+    fMatrixCount = src.fMatrixCount;
+    fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount);
+    memcpy(fMatrices, src.fMatrices, fMatrixCount * sizeof(SkMatrix));
+
+    fPaintCount = src.fPaintCount;
+    fPaints = SkNEW_ARRAY(SkPaint, fPaintCount);
+    for (i = 0; i < fPaintCount; i++) {
+        fPaints[i] = src.fPaints[i];
+    }
+
+    fPathHeap = src.fPathHeap;
+    fPathHeap->safeRef();
+
+    fPictureCount = src.fPictureCount;
+    fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
+    for (int i = 0; i < fPictureCount; i++) {
+        fPictureRefs[i] = src.fPictureRefs[i];
+        fPictureRefs[i]->ref();
+    }
+    
+    fRegionCount = src.fRegionCount;
+    fRegions = SkNEW_ARRAY(SkRegion, fRegionCount);
+    for (i = 0; i < fRegionCount; i++) {
+        fRegions[i] = src.fRegions[i];
+    }
+}
+
+void SkPicturePlayback::init() {
+    fBitmaps = NULL;
+    fMatrices = NULL;
+    fPaints = NULL;
+    fPathHeap = NULL;
+    fPictureRefs = NULL;
+    fRegions = NULL;
+    fBitmapCount = fMatrixCount = fPaintCount = fPictureCount = 
+    fRegionCount = 0;
+    
+    fFactoryPlayback = NULL;
+}
+
+SkPicturePlayback::~SkPicturePlayback() {
+    sk_free((void*) fReader.base());
+
+    SkDELETE_ARRAY(fBitmaps);
+    SkDELETE_ARRAY(fMatrices);
+    SkDELETE_ARRAY(fPaints);
+    SkDELETE_ARRAY(fRegions);
+    
+    fPathHeap->safeUnref();
+
+    for (int i = 0; i < fPictureCount; i++) {
+        fPictureRefs[i]->unref();
+    }
+    SkDELETE_ARRAY(fPictureRefs);
+    
+    SkDELETE(fFactoryPlayback);
+}
+
+void SkPicturePlayback::dumpSize() const {
+    SkDebugf("--- picture size: ops=%d bitmaps=%d [%d] matrices=%d [%d] paints=%d [%d] paths=%d regions=%d\n",
+             fReader.size(),
+             fBitmapCount, fBitmapCount * sizeof(SkBitmap),
+             fMatrixCount, fMatrixCount * sizeof(SkMatrix),
+             fPaintCount, fPaintCount * sizeof(SkPaint),
+             fPathHeap ? fPathHeap->count() : 0,
+             fRegionCount);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+// The chunks are writte/read in this order...
+
+#define PICT_READER_TAG     SkSetFourByteTag('r', 'e', 'a', 'd')
+#define PICT_FACTORY_TAG    SkSetFourByteTag('f', 'a', 'c', 't')
+#define PICT_TYPEFACE_TAG   SkSetFourByteTag('t', 'p', 'f', 'c')
+#define PICT_PICTURE_TAG    SkSetFourByteTag('p', 'c', 't', 'r')
+#define PICT_ARRAYS_TAG     SkSetFourByteTag('a', 'r', 'a', 'y')
+// these are all inside the ARRAYS tag
+#define PICT_BITMAP_TAG     SkSetFourByteTag('b', 't', 'm', 'p')
+#define PICT_MATRIX_TAG     SkSetFourByteTag('m', 't', 'r', 'x')
+#define PICT_PAINT_TAG      SkSetFourByteTag('p', 'n', 't', ' ')
+#define PICT_PATH_TAG       SkSetFourByteTag('p', 't', 'h', ' ')
+#define PICT_REGION_TAG     SkSetFourByteTag('r', 'g', 'n', ' ')
+
+
+#include "SkStream.h"
+
+static void writeTagSize(SkFlattenableWriteBuffer& buffer, uint32_t tag,
+                         uint32_t size) {
+    buffer.write32(tag);
+    buffer.write32(size);
+}
+
+static void writeTagSize(SkWStream* stream, uint32_t tag,
+                         uint32_t size) {
+    stream->write32(tag);
+    stream->write32(size);
+}
+
+static void writeFactories(SkWStream* stream, const SkFactoryRecorder& rec) {
+    int count = rec.count();
+    
+    writeTagSize(stream, PICT_FACTORY_TAG, count);
+    
+    SkAutoSTMalloc<16, SkFlattenable::Factory> storage(count);
+    SkFlattenable::Factory* array = (SkFlattenable::Factory*)storage.get();
+    rec.get(array);
+    
+    for (int i = 0; i < count; i++) {
+        const char* name = SkFlattenable::FactoryToName(array[i]);
+//        SkDebugf("---- write factories [%d] %p <%s>\n", i, array[i], name);
+        if (NULL == name || 0 == *name) {
+            stream->writePackedUInt(0);
+        } else {
+            uint32_t len = strlen(name);
+            stream->writePackedUInt(len);
+            stream->write(name, len);
+        }
+    }
+}
+
+static void writeTypefaces(SkWStream* stream, const SkRefCntRecorder& rec) {
+    int count = rec.count();
+    
+    writeTagSize(stream, PICT_TYPEFACE_TAG, count);
+    
+    SkAutoSTMalloc<16, SkTypeface*> storage(count);
+    SkTypeface** array = (SkTypeface**)storage.get();
+    rec.get((SkRefCnt**)array);
+    
+    for (int i = 0; i < count; i++) {
+        array[i]->serialize(stream);
+    }
+}
+
+void SkPicturePlayback::serialize(SkWStream* stream) const {
+    writeTagSize(stream, PICT_READER_TAG, fReader.size());
+    stream->write(fReader.base(), fReader.size());
+    
+    SkRefCntRecorder  typefaceRecorder;
+    SkFactoryRecorder factRecorder;
+
+    SkFlattenableWriteBuffer buffer(1024);
+
+    buffer.setFlags(SkFlattenableWriteBuffer::kCrossProcess_Flag);
+    buffer.setTypefaceRecorder(&typefaceRecorder);
+    buffer.setFactoryRecorder(&factRecorder);
+
+    int i;
+    
+    writeTagSize(buffer, PICT_BITMAP_TAG, fBitmapCount);
+    for (i = 0; i < fBitmapCount; i++) {
+        fBitmaps[i].flatten(buffer);
+    }
+
+    writeTagSize(buffer, PICT_MATRIX_TAG, fMatrixCount);
+    buffer.writeMul4(fMatrices, fMatrixCount * sizeof(SkMatrix));
+
+    writeTagSize(buffer, PICT_PAINT_TAG, fPaintCount);
+    for (i = 0; i < fPaintCount; i++) {
+        fPaints[i].flatten(buffer);
+    }
+    
+    {
+        int count = fPathHeap ? fPathHeap->count() : 0;
+        writeTagSize(buffer, PICT_PATH_TAG, count);
+        if (count > 0) {
+            fPathHeap->flatten(buffer);
+        }
+    }
+    
+    writeTagSize(buffer, PICT_REGION_TAG, fRegionCount);
+    for (i = 0; i < fRegionCount; i++) {
+        uint32_t size = fRegions[i].flatten(NULL);
+        buffer.write32(size);
+        SkAutoSMalloc<512> storage(size);
+        fRegions[i].flatten(storage.get());
+        buffer.writePad(storage.get(), size);
+    }
+    
+    // now we can write to the stream again
+
+    writeFactories(stream, factRecorder);
+    writeTypefaces(stream, typefaceRecorder);
+
+    writeTagSize(stream, PICT_PICTURE_TAG, fPictureCount);
+    for (i = 0; i < fPictureCount; i++) {
+        fPictureRefs[i]->serialize(stream);
+    }
+
+    writeTagSize(stream, PICT_ARRAYS_TAG, buffer.size());
+    buffer.writeToStream(stream);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int readTagSize(SkFlattenableReadBuffer& buffer, uint32_t expectedTag) {
+    uint32_t tag = buffer.readU32();
+    if (tag != expectedTag) {
+        sk_throw();
+    }
+    return buffer.readU32();
+}
+
+static int readTagSize(SkStream* stream, uint32_t expectedTag) {
+    uint32_t tag = stream->readU32();
+    if (tag != expectedTag) {
+        sk_throw();
+    }
+    return stream->readU32();
+}
+
+SkPicturePlayback::SkPicturePlayback(SkStream* stream) {
+    this->init();
+
+    int i;
+    
+    {
+        size_t size = readTagSize(stream, PICT_READER_TAG);
+        void* storage = sk_malloc_throw(size);
+        stream->read(storage, size);
+        fReader.setMemory(storage, size);
+    }
+    
+    int factoryCount = readTagSize(stream, PICT_FACTORY_TAG);
+    fFactoryPlayback = SkNEW_ARGS(SkFactoryPlayback, (factoryCount));
+    for (i = 0; i < factoryCount; i++) {
+        SkString str;
+        int len = stream->readPackedUInt();
+        str.resize(len);
+        stream->read(str.writable_str(), len);
+//        SkDebugf("--- factory playback [%d] <%s>\n", i, str.c_str());
+        fFactoryPlayback->base()[i] = SkFlattenable::NameToFactory(str.c_str());
+    }
+    
+    int typefaceCount = readTagSize(stream, PICT_TYPEFACE_TAG);
+    fTFPlayback.setCount(typefaceCount);
+    for (i = 0; i < typefaceCount; i++) {
+        fTFPlayback.set(i, SkTypeface::Deserialize(stream))->unref();
+    }    
+
+    fPictureCount = readTagSize(stream, PICT_PICTURE_TAG);
+    fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
+    for (i = 0; i < fPictureCount; i++) {
+        fPictureRefs[i] = SkNEW_ARGS(SkPicture, (stream));
+    }
+    
+    /*
+        Now read the arrays chunk, and parse using a read buffer
+    */
+    uint32_t size = readTagSize(stream, PICT_ARRAYS_TAG);
+    SkAutoMalloc storage(size);
+    stream->read(storage.get(), size);
+
+    SkFlattenableReadBuffer buffer(storage.get(), size);
+    fFactoryPlayback->setupBuffer(buffer);
+    fTFPlayback.setupBuffer(buffer);
+
+    fBitmapCount = readTagSize(buffer, PICT_BITMAP_TAG);
+    fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount);
+    for (i = 0; i < fBitmapCount; i++) {
+        fBitmaps[i].unflatten(buffer);
+    }
+
+    fMatrixCount = readTagSize(buffer, PICT_MATRIX_TAG);
+    fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount);
+    buffer.read(fMatrices, fMatrixCount * sizeof(SkMatrix));
+    
+    fPaintCount = readTagSize(buffer, PICT_PAINT_TAG);
+    fPaints = SkNEW_ARRAY(SkPaint, fPaintCount);
+    for (i = 0; i < fPaintCount; i++) {
+        fPaints[i].unflatten(buffer);
+    }
+    
+    {
+        int count = readTagSize(buffer, PICT_PATH_TAG);
+        if (count > 0) {
+            fPathHeap = SkNEW_ARGS(SkPathHeap, (buffer));
+        }
+    }
+    
+    fRegionCount = readTagSize(buffer, PICT_REGION_TAG);
+    fRegions = SkNEW_ARRAY(SkRegion, fRegionCount);
+    for (i = 0; i < fRegionCount; i++) {
+        uint32_t size = buffer.readU32();
+        uint32_t bytes = fRegions[i].unflatten(buffer.skip(size));
+        SkASSERT(size == bytes);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPicturePlayback::draw(SkCanvas& canvas) {
+#ifdef ENABLE_TIME_DRAW
+    SkAutoTime  at("SkPicture::draw", 50);
+#endif
+
+    TextContainer text;
+    fReader.rewind();
+
+    while (!fReader.eof()) {
+        switch (fReader.readInt()) {
+            case CLIP_PATH: {
+                const SkPath& path = getPath();
+                SkRegion::Op op = (SkRegion::Op) getInt();
+                size_t offsetToRestore = getInt();
+                // HACK (false) until I can handle op==kReplace 
+                if (!canvas.clipPath(path, op) && false) {
+                    //SkDebugf("---- skip clipPath for %d bytes\n", offsetToRestore - fReader.offset());
+                    fReader.setOffset(offsetToRestore);
+                }
+            } break;
+            case CLIP_REGION: {
+                const SkRegion& region = getRegion();
+                SkRegion::Op op = (SkRegion::Op) getInt();
+                size_t offsetToRestore = getInt();
+                if (!canvas.clipRegion(region, op)) {
+                    //SkDebugf("---- skip clipDeviceRgn for %d bytes\n", offsetToRestore - fReader.offset());
+                    fReader.setOffset(offsetToRestore);
+                }
+            } break;
+            case CLIP_RECT: {
+                const SkRect* rect = fReader.skipRect();
+                SkRegion::Op op = (SkRegion::Op) getInt();
+                size_t offsetToRestore = getInt();
+                if (!canvas.clipRect(*rect, op)) {
+                    //SkDebugf("---- skip clipRect for %d bytes\n", offsetToRestore - fReader.offset());
+                    fReader.setOffset(offsetToRestore);
+                }
+            } break;
+            case CONCAT:
+                canvas.concat(*getMatrix());
+                break;
+            case DRAW_BITMAP: {
+                const SkPaint* paint = getPaint();
+                const SkBitmap& bitmap = getBitmap();
+                const SkPoint* loc = fReader.skipPoint();
+                canvas.drawBitmap(bitmap, loc->fX, loc->fY, paint);
+            } break;
+            case DRAW_BITMAP_RECT: {
+                const SkPaint* paint = getPaint();
+                const SkBitmap& bitmap = getBitmap();
+                const SkIRect* src = this->getIRectPtr();   // may be null
+                const SkRect* dst = fReader.skipRect();     // required
+                canvas.drawBitmapRect(bitmap, src, *dst, paint);
+            } break;
+            case DRAW_BITMAP_MATRIX: {
+                const SkPaint* paint = getPaint();
+                const SkBitmap& bitmap = getBitmap();
+                const SkMatrix* matrix = getMatrix();
+                canvas.drawBitmapMatrix(bitmap, *matrix, paint);
+            } break;
+            case DRAW_PAINT:
+                canvas.drawPaint(*getPaint());
+                break;
+            case DRAW_PATH: {
+                const SkPaint& paint = *getPaint();
+                canvas.drawPath(getPath(), paint);
+            } break;
+            case DRAW_PICTURE:
+                canvas.drawPicture(getPicture());
+                break;
+            case DRAW_POINTS: {
+                const SkPaint& paint = *getPaint();
+                SkCanvas::PointMode mode = (SkCanvas::PointMode)getInt();
+                size_t count = getInt();
+                const SkPoint* pts = (const SkPoint*)fReader.skip(sizeof(SkPoint) * count);
+                canvas.drawPoints(mode, count, pts, paint);
+            } break;
+            case DRAW_POS_TEXT: {
+                const SkPaint& paint = *getPaint();
+                getText(&text);
+                size_t points = getInt();
+                const SkPoint* pos = (const SkPoint*)fReader.skip(points * sizeof(SkPoint));
+                canvas.drawPosText(text.text(), text.length(), pos, paint);
+            } break;
+            case DRAW_POS_TEXT_H: {
+                const SkPaint& paint = *getPaint();
+                getText(&text);
+                size_t xCount = getInt();
+                const SkScalar constY = getScalar();
+                const SkScalar* xpos = (const SkScalar*)fReader.skip(xCount * sizeof(SkScalar));
+                canvas.drawPosTextH(text.text(), text.length(), xpos, constY,
+                                    paint);
+            } break;
+            case DRAW_POS_TEXT_H_TOP_BOTTOM: {
+                const SkPaint& paint = *getPaint();
+                getText(&text);
+                size_t xCount = getInt();
+                const SkScalar* xpos = (const SkScalar*)fReader.skip((3 + xCount) * sizeof(SkScalar));
+                const SkScalar top = *xpos++;
+                const SkScalar bottom = *xpos++;
+                const SkScalar constY = *xpos++;
+                if (!canvas.quickRejectY(top, bottom, SkCanvas::kAA_EdgeType)) {
+                    canvas.drawPosTextH(text.text(), text.length(), xpos,
+                                        constY, paint);
+                }
+            } break;
+            case DRAW_RECT: {
+                const SkPaint& paint = *getPaint();
+                canvas.drawRect(*fReader.skipRect(), paint); 
+            } break;
+            case DRAW_SPRITE: {
+                const SkPaint* paint = getPaint();
+                const SkBitmap& bitmap = getBitmap(); 
+                int left = getInt();
+                int top = getInt();
+                canvas.drawSprite(bitmap, left, top, paint); 
+            } break;
+            case DRAW_TEXT: {
+                const SkPaint& paint = *getPaint();
+                getText(&text);
+                SkScalar x = getScalar();
+                SkScalar y = getScalar();
+                canvas.drawText(text.text(), text.length(), x, y, paint);
+            } break;
+            case DRAW_TEXT_TOP_BOTTOM: {
+                const SkPaint& paint = *getPaint();
+                getText(&text);
+                const SkScalar* ptr = (const SkScalar*)fReader.skip(4 * sizeof(SkScalar));
+                // ptr[0] == x
+                // ptr[1] == y
+                // ptr[2] == top
+                // ptr[3] == bottom
+                if (!canvas.quickRejectY(ptr[2], ptr[3],
+                                         SkCanvas::kAA_EdgeType)) {
+                    canvas.drawText(text.text(), text.length(), ptr[0], ptr[1],
+                                    paint);
+                }
+            } break;
+            case DRAW_TEXT_ON_PATH: {
+                const SkPaint& paint = *getPaint();
+                getText(&text);
+                const SkPath& path = getPath();
+                const SkMatrix* matrix = getMatrix();
+                canvas.drawTextOnPath(text.text(), text.length(), path, 
+                                      matrix, paint);
+            } break;
+            case DRAW_VERTICES: {
+                const SkPaint& paint = *getPaint();
+                DrawVertexFlags flags = (DrawVertexFlags)getInt();
+                SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)getInt();
+                int vCount = getInt();
+                const SkPoint* verts = (const SkPoint*)fReader.skip(
+                                                    vCount * sizeof(SkPoint));
+                const SkPoint* texs = NULL;
+                const SkColor* colors = NULL;
+                const uint16_t* indices = NULL;
+                int iCount = 0;
+                if (flags & DRAW_VERTICES_HAS_TEXS) {
+                    texs = (const SkPoint*)fReader.skip(
+                                                    vCount * sizeof(SkPoint));
+                }
+                if (flags & DRAW_VERTICES_HAS_COLORS) {
+                    colors = (const SkColor*)fReader.skip(
+                                                    vCount * sizeof(SkColor));
+                }
+                if (flags & DRAW_VERTICES_HAS_INDICES) {
+                    iCount = getInt();
+                    indices = (const uint16_t*)fReader.skip(
+                                                    iCount * sizeof(uint16_t));
+                }
+                canvas.drawVertices(vmode, vCount, verts, texs, colors, NULL,
+                                    indices, iCount, paint);
+            } break;
+            case RESTORE:
+                canvas.restore();
+                break;
+            case ROTATE:
+                canvas.rotate(getScalar());
+                break;
+            case SAVE:
+                canvas.save((SkCanvas::SaveFlags) getInt());
+                break;
+            case SAVE_LAYER: {
+                const SkRect* boundsPtr = getRectPtr();
+                const SkPaint* paint = getPaint();
+                canvas.saveLayer(boundsPtr, paint, (SkCanvas::SaveFlags) getInt());
+                } break;
+            case SCALE: {
+                SkScalar sx = getScalar();
+                SkScalar sy = getScalar();
+                canvas.scale(sx, sy);
+            } break;
+            case SKEW: {
+                SkScalar sx = getScalar();
+                SkScalar sy = getScalar();
+                canvas.skew(sx, sy);
+            } break;
+            case TRANSLATE: {
+                SkScalar dx = getScalar();
+                SkScalar dy = getScalar();
+                canvas.translate(dx, dy);
+            } break;
+            default:
+                SkASSERT(0);
+        }
+    }
+    
+//    this->dumpSize();
+}
+
+void SkPicturePlayback::abort() {
+    fReader.skip(fReader.size() - fReader.offset());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if 0
+uint32_t SkPicturePlayback::flatten(void* storage) const {    
+    SkWBuffer buffer(storage);
+    buffer.write32(fBitmapCount);
+    int index;
+    for (index = 0; index < fBitmapCount; index++) {
+        const SkBitmap& bitmap = fBitmaps[index];
+        uint32_t size = bitmap.flatten(NULL, true);
+        buffer.write32(size);
+        void* local = buffer.skip(size);
+        bitmap.flatten(local, true);
+    }
+    buffer.write32(fPaintCount);
+    for (index = 0; index < fPaintCount; index++) {
+        SkFlattenableWriteBuffer flatWrite;
+        const SkPaint& paint = fPaints[index];
+        SkFlatPaint::Write(&flatWrite, paint);
+        uint32_t size = flatWrite.pos();
+        buffer.write32(size);
+        void* local = buffer.skip(size);
+        flatWrite.reset(local);
+        SkFlatPaint::Write(&flatWrite, paint);
+    }
+    buffer.write32(fPathCount);
+    for (index = 0; index < fPathCount; index++) {
+        const SkPath& path = fPaths[index];
+        uint32_t size = path.flatten(NULL);
+        buffer.write32(size);
+        void* local = buffer.skip(size);
+        path.flatten(local);
+    }
+
+#if 0
+    buffer.write32(fPictureCount);
+    for (index = 0; index < fPictureCount; index++) {
+        const SkPicture& picture = fPictures[index];
+        uint32_t size = picture.flatten(NULL);
+        buffer.write32(size);
+        void* local = buffer.skip(size);
+        picture.flatten(local);
+    }
+#endif
+
+    buffer.write32(fRegionCount);
+    for (index = 0; index < fRegionCount; index++) {
+        const SkRegion& region = fRegions[index];
+        size_t size = region.computeBufferSize();
+        buffer.write32(size);
+        void* local = buffer.skip(size);
+        region.writeToBuffer(local);
+    }
+    fReader.rewind();
+    size_t length = fReader.size();
+    buffer.write32(length);
+    memcpy(buffer.skip(length), fReader.base(), length);
+    return (uint32_t) buffer.pos();
+}
+
+void SkPicturePlayback::unflatten(const void* storage) {
+    SkRBuffer buffer(storage);
+    int index;
+    fBitmapCount = buffer.readU32();
+    fBitmaps = new SkBitmap[fBitmapCount];
+    for (index = 0; index < fBitmapCount; index++) {
+        uint32_t size = buffer.readU32();
+        const void* local = buffer.skip(size);
+        fBitmaps[index].unflatten(local);
+    }
+    fPaintCount = buffer.readU32();
+    fPaints = new SkPaint[fPaintCount];
+    for (index = 0; index < fPaintCount; index++) {
+        uint32_t size = buffer.readU32();
+        const void* local = buffer.skip(size);
+        SkFlatPaint::Read(local, &fPaints[index]);
+    }
+    fPathCount = buffer.readU32();
+    fPaths = new SkPath[fPathCount];
+    for (index = 0; index < fPathCount; index++) {
+        uint32_t size = buffer.readU32();
+        const void* local = buffer.skip(size);
+        fPaths[index].unflatten(local);
+    }
+    
+#if 0
+    fPictureCount = buffer.readU32();
+    fPictures = new SkPicture[fPictureCount];
+    for (index = 0; index < fPictureCount; index++) {
+        uint32_t size = buffer.readU32();
+        const void* local = buffer.skip(size);
+        fPictures[index].unflatten(local);
+    }
+#endif
+    
+    fRegionCount = buffer.readU32();
+    fRegions = new SkRegion[fRegionCount];
+    for (index = 0; index < fRegionCount; index++) {
+        uint32_t size = buffer.readU32();
+        const void* local = buffer.skip(size);
+        fRegions[index].readFromBuffer(local);
+    }
+    int32_t length = buffer.readS32();
+    const void* stream = buffer.skip(length);
+    fReader.setMemory(stream, length);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG_SIZE
+int SkPicturePlayback::size(size_t* sizePtr) { 
+    int objects = bitmaps(sizePtr);
+    objects += paints(sizePtr);
+    objects += paths(sizePtr);
+    objects += pictures(sizePtr);
+    objects += regions(sizePtr);
+    *sizePtr = fReader.size();
+    return objects; 
+}
+
+int SkPicturePlayback::bitmaps(size_t* size) {
+    size_t result = 0;
+    for (int index = 0; index < fBitmapCount; index++) {
+     //   const SkBitmap& bitmap = fBitmaps[index];
+        result += sizeof(SkBitmap); // bitmap->size();
+    }
+    *size = result;
+    return fBitmapCount;
+}
+
+int SkPicturePlayback::paints(size_t* size) {
+    size_t result = 0;
+    for (int index = 0; index < fPaintCount; index++) {
+    //    const SkPaint& paint = fPaints[index];
+        result += sizeof(SkPaint); // paint->size();
+    }
+    *size = result;
+    return fPaintCount;
+}
+
+int SkPicturePlayback::paths(size_t* size) {
+    size_t result = 0;
+    for (int index = 0; index < fPathCount; index++) {
+        const SkPath& path = fPaths[index];
+        result += path.flatten(NULL);
+    }
+    *size = result;
+    return fPathCount;
+}
+
+int SkPicturePlayback::regions(size_t* size) {
+    size_t result = 0;
+    for (int index = 0; index < fRegionCount; index++) {
+    //    const SkRegion& region = fRegions[index];
+        result += sizeof(SkRegion); // region->size();
+    }
+    *size = result;
+    return fRegionCount;
+}
+#endif
+
+#ifdef SK_DEBUG_DUMP
+void SkPicturePlayback::dumpBitmap(const SkBitmap& bitmap) const {
+    char pBuffer[DUMP_BUFFER_SIZE];
+    char* bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "BitmapData bitmap%p = {", &bitmap);
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "{kWidth, %d}, ", bitmap.width());
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "{kHeight, %d}, ", bitmap.height());
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "{kRowBytes, %d}, ", bitmap.rowBytes());
+//        start here;
+    SkDebugf("%s{0}};\n", pBuffer);
+}
+
+void dumpMatrix(const SkMatrix& matrix) const {
+    SkMatrix defaultMatrix;
+    defaultMatrix.reset();
+    char pBuffer[DUMP_BUFFER_SIZE];
+    char* bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "MatrixData matrix%p = {", &matrix);
+    SkScalar scaleX = matrix.getScaleX();
+    if (scaleX != defaultMatrix.getScaleX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kScaleX, %g}, ", SkScalarToFloat(scaleX));
+    SkScalar scaleY = matrix.getScaleY();
+    if (scaleY != defaultMatrix.getScaleY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kScaleY, %g}, ", SkScalarToFloat(scaleY));
+    SkScalar skewX = matrix.getSkewX();
+    if (skewX != defaultMatrix.getSkewX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kSkewX, %g}, ", SkScalarToFloat(skewX));
+    SkScalar skewY = matrix.getSkewY();
+    if (skewY != defaultMatrix.getSkewY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kSkewY, %g}, ", SkScalarToFloat(skewY));
+    SkScalar translateX = matrix.getTranslateX();
+    if (translateX != defaultMatrix.getTranslateX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTranslateX, %g}, ", SkScalarToFloat(translateX));
+    SkScalar translateY = matrix.getTranslateY();
+    if (translateY != defaultMatrix.getTranslateY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTranslateY, %g}, ", SkScalarToFloat(translateY));
+    SkScalar perspX = matrix.getPerspX();
+    if (perspX != defaultMatrix.getPerspX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kPerspX, %g}, ", SkFractToFloat(perspX));
+    SkScalar perspY = matrix.getPerspY();
+    if (perspY != defaultMatrix.getPerspY())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kPerspY, %g}, ", SkFractToFloat(perspY));
+    SkDebugf("%s{0}};\n", pBuffer);
+}
+
+void dumpPaint(const SkPaint& paint) const {
+    SkPaint defaultPaint;
+    char pBuffer[DUMP_BUFFER_SIZE];
+    char* bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "PaintPointers paintPtrs%p = {", &paint);
+    const SkTypeface* typeface = paint.getTypeface();
+    if (typeface != defaultPaint.getTypeface())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTypeface, %p}, ", typeface);
+    const SkPathEffect* pathEffect = paint.getPathEffect();
+    if (pathEffect != defaultPaint.getPathEffect())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kPathEffect, %p}, ", pathEffect);
+    const SkShader* shader = paint.getShader();
+    if (shader != defaultPaint.getShader())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kShader, %p}, ", shader);
+    const SkXfermode* xfermode = paint.getXfermode();
+    if (xfermode != defaultPaint.getXfermode())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kXfermode, %p}, ", xfermode);
+    const SkMaskFilter* maskFilter = paint.getMaskFilter();
+    if (maskFilter != defaultPaint.getMaskFilter())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kMaskFilter, %p}, ", maskFilter);
+    const SkColorFilter* colorFilter = paint.getColorFilter();
+    if (colorFilter != defaultPaint.getColorFilter())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kColorFilter, %p}, ", colorFilter);
+    const SkRasterizer* rasterizer = paint.getRasterizer();
+    if (rasterizer != defaultPaint.getRasterizer())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kRasterizer, %p}, ", rasterizer);
+    const SkDrawLooper* drawLooper = paint.getLooper();
+    if (drawLooper != defaultPaint.getLooper())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kDrawLooper, %p}, ", drawLooper);
+    SkDebugf("%s{0}};\n", pBuffer);
+    bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "PaintScalars paintScalars%p = {", &paint);
+    SkScalar textSize = paint.getTextSize();
+    if (textSize != defaultPaint.getTextSize())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTextSize, %g}, ", SkScalarToFloat(textSize));
+    SkScalar textScaleX = paint.getTextScaleX();
+    if (textScaleX != defaultPaint.getTextScaleX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTextScaleX, %g}, ", SkScalarToFloat(textScaleX));
+    SkScalar textSkewX = paint.getTextSkewX();
+    if (textSkewX != defaultPaint.getTextSkewX())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTextSkewX, %g}, ", SkScalarToFloat(textSkewX));
+    SkScalar strokeWidth = paint.getStrokeWidth();
+    if (strokeWidth != defaultPaint.getStrokeWidth())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kStrokeWidth, %g}, ", SkScalarToFloat(strokeWidth));
+    SkScalar strokeMiter = paint.getStrokeMiter();
+    if (strokeMiter != defaultPaint.getStrokeMiter())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kStrokeMiter, %g}, ", SkScalarToFloat(strokeMiter));
+    SkDebugf("%s{0}};\n", pBuffer);
+    bufferPtr = pBuffer;
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+        "PaintInts = paintInts%p = {", &paint);
+    unsigned color = paint.getColor();
+    if (color != defaultPaint.getColor())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kColor, 0x%x}, ", color);
+    unsigned flags = paint.getFlags();
+    if (flags != defaultPaint.getFlags())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kFlags, 0x%x}, ", flags);
+    int align = paint.getTextAlign();
+    if (align != defaultPaint.getTextAlign())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kAlign, 0x%x}, ", align);
+    int strokeCap = paint.getStrokeCap();
+    if (strokeCap != defaultPaint.getStrokeCap())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kStrokeCap, 0x%x}, ", strokeCap);
+    int strokeJoin = paint.getStrokeJoin();
+    if (strokeJoin != defaultPaint.getStrokeJoin())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kAlign, 0x%x}, ", strokeJoin);
+    int style = paint.getStyle();
+    if (style != defaultPaint.getStyle())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kStyle, 0x%x}, ", style);
+    int textEncoding = paint.getTextEncoding();
+    if (textEncoding != defaultPaint.getTextEncoding())
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer),
+            "{kTextEncoding, 0x%x}, ", textEncoding);
+    SkDebugf("%s{0}};\n", pBuffer);
+
+    SkDebugf("PaintData paint%p = {paintPtrs%p, paintScalars%p, paintInts%p};\n",
+        &paint, &paint, &paint, &paint);
+}
+
+void SkPicturePlayback::dumpPath(const SkPath& path) const {
+    SkDebugf("path dump unimplemented\n");
+}
+
+void SkPicturePlayback::dumpPicture(const SkPicture& picture) const {
+    SkDebugf("picture dump unimplemented\n");
+}
+
+void SkPicturePlayback::dumpRegion(const SkRegion& region) const {
+    SkDebugf("region dump unimplemented\n");
+}
+
+int SkPicturePlayback::dumpDrawType(char* bufferPtr, char* buffer, DrawType drawType) {
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "k%s, ", DrawTypeToString(drawType));
+}
+
+int SkPicturePlayback::dumpInt(char* bufferPtr, char* buffer, char* name) {
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "%s:%d, ", name, getInt());
+}
+
+int SkPicturePlayback::dumpRect(char* bufferPtr, char* buffer, char* name) {
+    const SkRect* rect = fReader.skipRect();
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "%s:{l:%g t:%g r:%g b:%g}, ", name, SkScalarToFloat(rect.fLeft), 
+        SkScalarToFloat(rect.fTop),
+        SkScalarToFloat(rect.fRight), SkScalarToFloat(rect.fBottom));
+}
+
+int SkPicturePlayback::dumpPoint(char* bufferPtr, char* buffer, char* name) {
+    SkPoint pt;
+    getPoint(&pt);
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "%s:{x:%g y:%g}, ", name, SkScalarToFloat(pt.fX), 
+        SkScalarToFloat(pt.fY));
+}
+
+void SkPicturePlayback::dumpPointArray(char** bufferPtrPtr, char* buffer, int count) {
+    char* bufferPtr = *bufferPtrPtr;
+    const SkPoint* pts = (const SkPoint*)fReadStream.getAtPos();
+    fReadStream.skip(sizeof(SkPoint) * count);
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "count:%d {", count);
+    for (int index = 0; index < count; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "{x:%g y:%g}, ", SkScalarToFloat(pts[index].fX), 
+        SkScalarToFloat(pts[index].fY));
+    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "} ");
+    *bufferPtrPtr = bufferPtr;
+}
+
+int SkPicturePlayback::dumpPtr(char* bufferPtr, char* buffer, char* name, void* ptr) {
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "%s:%p, ", name, ptr);
+}
+
+int SkPicturePlayback::dumpRectPtr(char* bufferPtr, char* buffer, char* name) {
+    char result;
+    fReadStream.read(&result, sizeof(result));
+    if (result)
+        return dumpRect(bufferPtr, buffer, name);
+    else
+        return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+            "%s:NULL, ", name);
+}
+
+int SkPicturePlayback::dumpScalar(char* bufferPtr, char* buffer, char* name) {
+    return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer),
+        "%s:%d, ", name, getScalar());
+}
+
+void SkPicturePlayback::dumpText(char** bufferPtrPtr, char* buffer) {
+    char* bufferPtr = *bufferPtrPtr;
+    int length = getInt();
+    bufferPtr += dumpDrawType(bufferPtr, buffer);
+    fReadStream.skipToAlign4();
+    char* text = (char*) fReadStream.getAtPos();
+    fReadStream.skip(length);
+    bufferPtr += dumpInt(bufferPtr, buffer, "length");
+    int limit = DUMP_BUFFER_SIZE - (bufferPtr - buffer) - 2;
+    length >>= 1;
+    if (limit > length)
+        limit = length;
+    if (limit > 0) {
+        *bufferPtr++ = '"';
+        for (int index = 0; index < limit; index++) {
+            *bufferPtr++ = *(unsigned short*) text;
+            text += sizeof(unsigned short);
+        }
+        *bufferPtr++ = '"';
+    }
+    *bufferPtrPtr = bufferPtr;
+}
+
+#define DUMP_DRAWTYPE(drawType) \
+    bufferPtr += dumpDrawType(bufferPtr, buffer, drawType)
+
+#define DUMP_INT(name) \
+    bufferPtr += dumpInt(bufferPtr, buffer, #name)
+
+#define DUMP_RECT_PTR(name) \
+    bufferPtr += dumpRectPtr(bufferPtr, buffer, #name)
+
+#define DUMP_POINT(name) \
+    bufferPtr += dumpRect(bufferPtr, buffer, #name)
+
+#define DUMP_RECT(name) \
+    bufferPtr += dumpRect(bufferPtr, buffer, #name)
+
+#define DUMP_POINT_ARRAY(count) \
+    dumpPointArray(&bufferPtr, buffer, count)
+
+#define DUMP_PTR(name, ptr) \
+    bufferPtr += dumpPtr(bufferPtr, buffer, #name, (void*) ptr)
+
+#define DUMP_SCALAR(name) \
+    bufferPtr += dumpScalar(bufferPtr, buffer, #name)
+
+#define DUMP_TEXT() \
+    dumpText(&bufferPtr, buffer)
+
+void SkPicturePlayback::dumpStream() {
+    SkDebugf("RecordStream stream = {\n");
+    DrawType drawType;
+    TextContainer text;
+    fReadStream.rewind();
+    char buffer[DUMP_BUFFER_SIZE], * bufferPtr;
+    while (fReadStream.read(&drawType, sizeof(drawType))) {
+        bufferPtr = buffer;
+        DUMP_DRAWTYPE(drawType);
+        switch (drawType) {
+            case CLIP_PATH: {
+                DUMP_PTR(SkPath, &getPath());
+                DUMP_INT(SkRegion::Op);
+                DUMP_INT(offsetToRestore);
+                } break;
+            case CLIP_REGION: {
+                DUMP_PTR(SkRegion, &getRegion());
+                DUMP_INT(SkRegion::Op);
+                DUMP_INT(offsetToRestore);
+            } break;
+            case CLIP_RECT: {
+                DUMP_RECT(rect);
+                DUMP_INT(SkRegion::Op);
+                DUMP_INT(offsetToRestore);
+                } break;
+            case CONCAT:
+                DUMP_PTR(SkMatrix, getMatrix());
+                break;
+            case DRAW_BITMAP: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_PTR(SkBitmap, &getBitmap());
+                DUMP_SCALAR(left);
+                DUMP_SCALAR(top);
+                } break;
+            case DRAW_PAINT:
+                DUMP_PTR(SkPaint, getPaint());
+                break;
+            case DRAW_PATH: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_PTR(SkPath, &getPath());
+                } break;
+            case DRAW_PICTURE: {
+                DUMP_PTR(SkPicture, &getPicture());
+                } break;
+            case DRAW_POINTS: {
+                DUMP_PTR(SkPaint, getPaint());
+                (void)getInt(); // PointMode
+                size_t count = getInt();
+                fReadStream.skipToAlign4();
+                DUMP_POINT_ARRAY(count);
+                } break;
+            case DRAW_POS_TEXT: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_TEXT();
+                size_t points = getInt();
+                fReadStream.skipToAlign4();
+                DUMP_POINT_ARRAY(points);
+                } break;
+            case DRAW_POS_TEXT_H: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_TEXT();
+                size_t points = getInt();
+                fReadStream.skipToAlign4();
+                DUMP_SCALAR(top);
+                DUMP_SCALAR(bottom);
+                DUMP_SCALAR(constY);
+                DUMP_POINT_ARRAY(points);
+                } break;
+            case DRAW_RECT: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_RECT(rect);
+                } break;
+            case DRAW_SPRITE: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_PTR(SkBitmap, &getBitmap());
+                DUMP_SCALAR(left);
+                DUMP_SCALAR(top);
+                } break;
+            case DRAW_TEXT: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_TEXT();
+                DUMP_SCALAR(x);
+                DUMP_SCALAR(y);
+                } break;
+            case DRAW_TEXT_ON_PATH: {
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_TEXT();
+                DUMP_PTR(SkPath, &getPath());
+                DUMP_PTR(SkMatrix, getMatrix());
+                } break;
+            case RESTORE:
+                break;
+            case ROTATE:
+                DUMP_SCALAR(rotate);
+                break;
+            case SAVE:
+                DUMP_INT(SkCanvas::SaveFlags);
+                break;
+            case SAVE_LAYER: {
+                DUMP_RECT_PTR(layer);
+                DUMP_PTR(SkPaint, getPaint());
+                DUMP_INT(SkCanvas::SaveFlags);
+                } break;
+            case SCALE: {
+                DUMP_SCALAR(sx);
+                DUMP_SCALAR(sy);
+                } break;
+            case SKEW: {
+                DUMP_SCALAR(sx);
+                DUMP_SCALAR(sy);
+                } break;
+            case TRANSLATE: {
+                DUMP_SCALAR(dx);
+                DUMP_SCALAR(dy);
+                } break;
+            default:
+                SkASSERT(0);
+        }
+        SkDebugf("%s\n", buffer);
+    }
+}
+
+void SkPicturePlayback::dump() const {
+    char pBuffer[DUMP_BUFFER_SIZE];
+    char* bufferPtr = pBuffer;
+    int index;
+    if (fBitmapCount > 0)
+        SkDebugf("// bitmaps (%d)\n", fBitmapCount);
+    for (index = 0; index < fBitmapCount; index++) {
+        const SkBitmap& bitmap = fBitmaps[index];
+        dumpBitmap(bitmap);
+    }
+    if (fBitmapCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "Bitmaps bitmaps = {");
+    for (index = 0; index < fBitmapCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "bitmap%p, ", &fBitmaps[index]);
+    if (fBitmapCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    if (fMatrixCount > 0)
+        SkDebugf("// matrices (%d)\n", fMatrixCount);
+    for (index = 0; index < fMatrixCount; index++) {
+        const SkMatrix& matrix = fMatrices[index];
+        dumpMatrix(matrix);
+    }
+    bufferPtr = pBuffer;
+    if (fMatrixCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "Matrices matrices = {");
+    for (index = 0; index < fMatrixCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "matrix%p, ", &fMatrices[index]);
+    if (fMatrixCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    if (fPaintCount > 0)
+        SkDebugf("// paints (%d)\n", fPaintCount);
+    for (index = 0; index < fPaintCount; index++) {
+        const SkPaint& paint = fPaints[index];
+        dumpPaint(paint);
+    }
+    bufferPtr = pBuffer;
+    if (fPaintCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "Paints paints = {");
+    for (index = 0; index < fPaintCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "paint%p, ", &fPaints[index]);
+    if (fPaintCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    for (index = 0; index < fPathCount; index++) {
+        const SkPath& path = fPaths[index];
+        dumpPath(path);
+    }
+    bufferPtr = pBuffer;
+    if (fPathCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "Paths paths = {");
+    for (index = 0; index < fPathCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "path%p, ", &fPaths[index]);
+    if (fPathCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    for (index = 0; index < fPictureCount; index++) {
+        dumpPicture(*fPictureRefs[index]);
+    }
+    bufferPtr = pBuffer;
+    if (fPictureCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "Pictures pictures = {");
+    for (index = 0; index < fPictureCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "picture%p, ", fPictureRefs[index]);
+    if (fPictureCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    for (index = 0; index < fRegionCount; index++) {
+        const SkRegion& region = fRegions[index];
+        dumpRegion(region);
+    }
+    bufferPtr = pBuffer;
+    if (fRegionCount > 0)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "Regions regions = {");
+    for (index = 0; index < fRegionCount; index++)
+        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
+            "region%p, ", &fRegions[index]);
+    if (fRegionCount > 0)
+        SkDebugf("%s0};\n", pBuffer);
+
+    const_cast<SkPicturePlayback*>(this)->dumpStream();
+}
+
+#endif
diff --git a/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h
new file mode 100644
index 0000000..b4e69ca
--- /dev/null
+++ b/src/core/SkPicturePlayback.h
@@ -0,0 +1,168 @@
+#ifndef SkPicturePlayback_DEFINED
+#define SkPicturePlayback_DEFINED
+
+#include "SkPicture.h"
+#include "SkReader32.h"
+
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPathHeap.h"
+#include "SkRegion.h"
+#include "SkPictureFlat.h"
+
+class SkPictureRecord;
+class SkStream;
+class SkWStream;
+
+class SkPicturePlayback {
+public:
+    SkPicturePlayback();
+    SkPicturePlayback(const SkPicturePlayback& src);
+    explicit SkPicturePlayback(const SkPictureRecord& record);
+    explicit SkPicturePlayback(SkStream*);
+
+    virtual ~SkPicturePlayback();
+
+    void draw(SkCanvas& canvas);
+
+    void serialize(SkWStream*) const;
+
+    void dumpSize() const;
+    
+    // Can be called in the middle of playback (the draw() call). WIll abort the
+    // drawing and return from draw() after the "current" op code is done
+    void abort();
+
+private:
+
+    class TextContainer {
+    public:
+        size_t length() { return fByteLength; }
+        const void* text() { return (const void*) fText; }
+        size_t fByteLength;
+        const char* fText;
+    };
+
+    const SkBitmap& getBitmap() {
+        int index = getInt();
+        SkASSERT(index > 0);
+        return fBitmaps[index - 1];
+    }
+
+    int getIndex() { return fReader.readInt(); }
+    int getInt() { return fReader.readInt(); }
+
+    const SkMatrix* getMatrix() {
+        int index = getInt();
+        if (index == 0) {
+            return NULL;
+        }
+        SkASSERT(index > 0 && index <= fMatrixCount);
+        return &fMatrices[index - 1];
+    }
+
+    const SkPath& getPath() {
+        return (*fPathHeap)[getInt() - 1];
+    }
+
+    SkPicture& getPicture() {
+        int index = getInt();
+        SkASSERT(index > 0 && index <= fPictureCount);
+        return *fPictureRefs[index - 1];
+    }
+
+    const SkPaint* getPaint() {
+        int index = getInt();
+        if (index == 0) {
+            return NULL;
+        }
+        SkASSERT(index > 0 && index <= fPaintCount);
+        return &fPaints[index - 1];
+    }
+
+    const SkRect* getRectPtr() {
+        if (fReader.readBool()) {
+            return fReader.skipRect();
+        } else {
+            return NULL;
+        }
+    }
+
+    const SkIRect* getIRectPtr() {
+        if (fReader.readBool()) {
+            return (const SkIRect*)fReader.skip(sizeof(SkIRect));
+        } else {
+            return NULL;
+        }
+    }
+
+    const SkRegion& getRegion() {
+        int index = getInt();
+        SkASSERT(index > 0);
+        return fRegions[index - 1];
+    }
+
+    SkScalar getScalar() { return fReader.readScalar(); }
+
+    void getText(TextContainer* text) {
+        size_t length = text->fByteLength = getInt();
+        text->fText = (const char*)fReader.skip(length);
+    }
+
+    void init();
+
+#ifdef SK_DEBUG_SIZE
+public:
+    int size(size_t* sizePtr);
+    int bitmaps(size_t* size);
+    int paints(size_t* size);
+    int paths(size_t* size);
+    int regions(size_t* size);
+#endif
+
+#ifdef SK_DEBUG_DUMP
+private:
+    void dumpBitmap(const SkBitmap& bitmap) const;
+    void dumpMatrix(const SkMatrix& matrix) const;
+    void dumpPaint(const SkPaint& paint) const;
+    void dumpPath(const SkPath& path) const;
+    void dumpPicture(const SkPicture& picture) const;
+    void dumpRegion(const SkRegion& region) const;
+    int dumpDrawType(char* bufferPtr, char* buffer, DrawType drawType);
+    int dumpInt(char* bufferPtr, char* buffer, char* name);
+    int dumpRect(char* bufferPtr, char* buffer, char* name);
+    int dumpPoint(char* bufferPtr, char* buffer, char* name);
+    void dumpPointArray(char** bufferPtrPtr, char* buffer, int count);
+    int dumpPtr(char* bufferPtr, char* buffer, char* name, void* ptr);
+    int dumpRectPtr(char* bufferPtr, char* buffer, char* name);
+    int dumpScalar(char* bufferPtr, char* buffer, char* name);
+    void dumpText(char** bufferPtrPtr, char* buffer);
+    void dumpStream();
+
+public:
+    void dump() const;
+#endif
+
+private:
+    SkPathHeap* fPathHeap;  // reference counted
+    SkBitmap* fBitmaps;
+    int fBitmapCount;
+    SkMatrix* fMatrices;
+    int fMatrixCount;
+    SkPaint* fPaints;
+    int fPaintCount;
+    SkRegion* fRegions;
+    int fRegionCount;
+    mutable SkFlattenableReadBuffer fReader;
+
+    SkPicture** fPictureRefs;
+    int fPictureCount;
+    
+    SkRefCntPlayback fRCPlayback;
+    SkTypefacePlayback fTFPlayback;
+    SkFactoryPlayback*   fFactoryPlayback;
+};
+
+#endif
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
new file mode 100644
index 0000000..6e7c645
--- /dev/null
+++ b/src/core/SkPictureRecord.cpp
@@ -0,0 +1,698 @@
+#include "SkPictureRecord.h"
+#include "SkTSearch.h"
+
+#define MIN_WRITER_SIZE 16384
+#define HEAP_BLOCK_SIZE 4096
+
+SkPictureRecord::SkPictureRecord() :
+        fHeap(HEAP_BLOCK_SIZE), fWriter(MIN_WRITER_SIZE) {
+    fBitmapIndex = fMatrixIndex = fPaintIndex = fRegionIndex = 1;
+#ifdef SK_DEBUG_SIZE
+    fPointBytes = fRectBytes = fTextBytes = 0;
+    fPointWrites = fRectWrites = fTextWrites = 0;
+#endif
+
+    fRestoreOffsetStack.setReserve(32);
+    fRestoreOffsetStack.push(0);
+            
+    fPathHeap = NULL;   // lazy allocate
+}
+
+SkPictureRecord::~SkPictureRecord() {
+    reset();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkPictureRecord::save(SaveFlags flags) {
+    addDraw(SAVE);
+    addInt(flags);
+    
+    fRestoreOffsetStack.push(0);
+    
+    validate();
+    return this->INHERITED::save(flags);
+}
+
+int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                               SaveFlags flags) {
+    addDraw(SAVE_LAYER);
+    addRectPtr(bounds);
+    addPaintPtr(paint);
+    addInt(flags);
+
+    fRestoreOffsetStack.push(0);
+
+    validate();
+    return this->INHERITED::saveLayer(bounds, paint, flags);
+}
+
+void SkPictureRecord::restore() {
+    
+    // patch up the clip offsets
+    {
+        uint32_t restoreOffset = (uint32_t)fWriter.size();
+        uint32_t offset = fRestoreOffsetStack.top();
+        while (offset) {
+            uint32_t* peek = fWriter.peek32(offset);
+            offset = *peek;
+            *peek = restoreOffset;
+        }
+        fRestoreOffsetStack.pop();
+    }
+    
+    addDraw(RESTORE);
+    validate();
+    return this->INHERITED::restore();
+}
+
+bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
+    addDraw(TRANSLATE);
+    addScalar(dx);
+    addScalar(dy);
+    validate();
+    return this->INHERITED::translate(dx, dy);
+}
+
+bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
+    addDraw(SCALE); 
+    addScalar(sx);
+    addScalar(sy);
+    validate();
+    return this->INHERITED::scale(sx, sy);
+}
+
+bool SkPictureRecord::rotate(SkScalar degrees) {
+    addDraw(ROTATE); 
+    addScalar(degrees); 
+    validate();
+    return this->INHERITED::rotate(degrees);
+}
+
+bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
+    addDraw(SKEW); 
+    addScalar(sx);
+    addScalar(sy);
+    validate();
+    return this->INHERITED::skew(sx, sy);
+}
+
+bool SkPictureRecord::concat(const SkMatrix& matrix) {
+    validate();
+    addDraw(CONCAT);
+    addMatrix(matrix);
+    validate();
+    return this->INHERITED::concat(matrix);
+}
+
+bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op) {
+    addDraw(CLIP_RECT);
+    addRect(rect);
+    addInt(op);
+    
+    size_t offset = fWriter.size();
+    addInt(fRestoreOffsetStack.top());
+    fRestoreOffsetStack.top() = offset;
+
+    validate();
+    return this->INHERITED::clipRect(rect, op);
+}
+
+bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op) {
+    addDraw(CLIP_PATH);
+    addPath(path);
+    addInt(op);
+    
+    size_t offset = fWriter.size();
+    addInt(fRestoreOffsetStack.top());
+    fRestoreOffsetStack.top() = offset;
+    
+    validate();
+    return this->INHERITED::clipPath(path, op);
+}
+
+bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
+    addDraw(CLIP_REGION); 
+    addRegion(region);
+    addInt(op);
+    
+    size_t offset = fWriter.size();
+    addInt(fRestoreOffsetStack.top());
+    fRestoreOffsetStack.top() = offset;
+    
+    validate();
+    return this->INHERITED::clipRegion(region, op);
+}
+
+void SkPictureRecord::drawPaint(const SkPaint& paint) {
+    addDraw(DRAW_PAINT);
+    addPaint(paint);
+    validate();
+}
+
+void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                        const SkPaint& paint) {
+    addDraw(DRAW_POINTS);
+    addPaint(paint);
+    addInt(mode);
+    addInt(count);
+    fWriter.writeMul4(pts, count * sizeof(SkPoint));
+    validate();
+}
+
+void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
+    addDraw(DRAW_RECT);
+    addPaint(paint);
+    addRect(rect);
+    validate();
+}
+
+void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
+    addDraw(DRAW_PATH);
+    addPaint(paint);
+    addPath(path);
+    validate();
+}
+
+void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+                        const SkPaint* paint = NULL) {
+    addDraw(DRAW_BITMAP);
+    addPaintPtr(paint);
+    addBitmap(bitmap);
+    addScalar(left);
+    addScalar(top);
+    validate();
+}
+
+void SkPictureRecord::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                            const SkRect& dst, const SkPaint* paint) {
+    addDraw(DRAW_BITMAP_RECT);
+    addPaintPtr(paint);
+    addBitmap(bitmap);
+    addIRectPtr(src);  // may be null
+    addRect(dst);
+    validate();
+}
+
+void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
+                              const SkPaint* paint) {
+    addDraw(DRAW_BITMAP_MATRIX);
+    addPaintPtr(paint);
+    addBitmap(bitmap);
+    addMatrix(matrix);
+    validate();
+}
+
+void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
+                        const SkPaint* paint = NULL) {
+    addDraw(DRAW_SPRITE);
+    addPaintPtr(paint);
+    addBitmap(bitmap);
+    addInt(left);
+    addInt(top);
+    validate();
+}
+
+void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint,
+                                              SkScalar baselineY) {
+    SkPaint::FontMetrics metrics;
+    paint.getFontMetrics(&metrics);
+    SkRect bounds;
+    // construct a rect so we can see any adjustments from the paint.
+    // we use 0,1 for left,right, just so the rect isn't empty
+    bounds.set(0, metrics.fTop + baselineY,
+               SK_Scalar1, metrics.fBottom + baselineY);
+    (void)paint.computeFastBounds(bounds, &bounds);
+    // now record the top and bottom
+    addScalar(bounds.fTop);
+    addScalar(bounds.fBottom);
+}
+
+void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x, 
+                      SkScalar y, const SkPaint& paint) {
+    bool fast = paint.canComputeFastBounds();
+    
+    addDraw(fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT);
+    addPaint(paint);
+    addText(text, byteLength);
+    addScalar(x);
+    addScalar(y);
+    if (fast) {
+        addFontMetricsTopBottom(paint, y);
+    }
+    validate();
+}
+
+void SkPictureRecord::drawPosText(const void* text, size_t byteLength, 
+                         const SkPoint pos[], const SkPaint& paint) {
+    size_t points = paint.countText(text, byteLength);
+    if (0 == points)
+        return;
+
+    bool canUseDrawH = true;
+    // check if the caller really should have used drawPosTextH()
+    {
+        const SkScalar firstY = pos[0].fY;
+        for (size_t index = 1; index < points; index++) {
+            if (pos[index].fY != firstY) {
+                canUseDrawH = false;
+                break;
+            }
+        }
+    }
+    
+    bool fast = canUseDrawH && paint.canComputeFastBounds();
+
+    if (fast) {
+        addDraw(DRAW_POS_TEXT_H_TOP_BOTTOM);
+    } else {
+        addDraw(canUseDrawH ? DRAW_POS_TEXT_H : DRAW_POS_TEXT);
+    }
+    addPaint(paint);
+    addText(text, byteLength);
+    addInt(points);
+
+#ifdef SK_DEBUG_SIZE
+    size_t start = fWriter.size();
+#endif
+    if (canUseDrawH) {
+        if (fast) {
+            addFontMetricsTopBottom(paint, pos[0].fY);
+        }
+        addScalar(pos[0].fY);
+        SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
+        for (size_t index = 0; index < points; index++) 
+            *xptr++ = pos[index].fX;
+    }
+    else {
+        fWriter.writeMul4(pos, points * sizeof(SkPoint));
+    }
+#ifdef SK_DEBUG_SIZE
+    fPointBytes += fWriter.size() - start;
+    fPointWrites += points;
+#endif
+    validate();
+}
+
+void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
+                          const SkScalar xpos[], SkScalar constY,
+                          const SkPaint& paint) {
+    size_t points = paint.countText(text, byteLength);
+    if (0 == points)
+        return;
+    
+    bool fast = paint.canComputeFastBounds();
+
+    addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H);
+    addPaint(paint);
+    addText(text, byteLength);
+    addInt(points);
+    
+#ifdef SK_DEBUG_SIZE
+    size_t start = fWriter.size();
+#endif
+    if (fast) {
+        addFontMetricsTopBottom(paint, constY);
+    }
+    addScalar(constY);
+    fWriter.writeMul4(xpos, points * sizeof(SkScalar));
+#ifdef SK_DEBUG_SIZE
+    fPointBytes += fWriter.size() - start;
+    fPointWrites += points;
+#endif
+    validate();
+}
+
+void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength, 
+                            const SkPath& path, const SkMatrix* matrix, 
+                            const SkPaint& paint) {
+    addDraw(DRAW_TEXT_ON_PATH);
+    addPaint(paint);
+    addText(text, byteLength);
+    addPath(path);
+    addMatrixPtr(matrix);
+    validate();
+}
+
+void SkPictureRecord::drawPicture(SkPicture& picture) {
+    addDraw(DRAW_PICTURE);
+    addPicture(picture);
+    validate();
+}
+
+void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
+                          const SkPoint vertices[], const SkPoint texs[],
+                          const SkColor colors[], SkXfermode*,
+                          const uint16_t indices[], int indexCount,
+                          const SkPaint& paint) {
+    uint32_t flags = 0;
+    if (texs) {
+        flags |= DRAW_VERTICES_HAS_TEXS;
+    }
+    if (colors) {
+        flags |= DRAW_VERTICES_HAS_COLORS;
+    }
+    if (indexCount > 0) {
+        flags |= DRAW_VERTICES_HAS_INDICES;
+    }
+
+    addDraw(DRAW_VERTICES);
+    addPaint(paint);
+    addInt(flags);
+    addInt(vmode);
+    addInt(vertexCount);
+    addPoints(vertices, vertexCount);
+    if (flags & DRAW_VERTICES_HAS_TEXS) {
+        addPoints(texs, vertexCount);
+    }
+    if (flags & DRAW_VERTICES_HAS_COLORS) {
+        fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
+    }
+    if (flags & DRAW_VERTICES_HAS_INDICES) {
+        addInt(indexCount);
+        fWriter.writePad(indices, indexCount * sizeof(uint16_t));
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+    
+void SkPictureRecord::reset() {
+    fPathHeap->safeUnref();
+    fPathHeap = NULL;
+
+    fBitmaps.reset();
+    fMatrices.reset();
+    fPaints.reset();
+    fPictureRefs.unrefAll();
+    fRegions.reset();
+    fWriter.reset();
+    fHeap.reset();
+    
+    fRestoreOffsetStack.setCount(1);
+    fRestoreOffsetStack.top() = 0;
+    
+    fRCRecorder.reset();
+    fTFRecorder.reset();
+}
+
+void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
+    addInt(find(fBitmaps, bitmap));
+}
+
+void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
+    addMatrixPtr(&matrix);
+}
+
+void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
+    addInt(find(fMatrices, matrix));
+}
+
+void SkPictureRecord::addPaint(const SkPaint& paint) {
+    addPaintPtr(&paint);
+}
+
+void SkPictureRecord::addPaintPtr(const SkPaint* paint) {
+    addInt(find(fPaints, paint));
+}
+
+void SkPictureRecord::addPath(const SkPath& path) {
+    if (NULL == fPathHeap) {
+        fPathHeap = SkNEW(SkPathHeap);
+    }
+    addInt(fPathHeap->append(path));
+}
+
+void SkPictureRecord::addPicture(SkPicture& picture) {
+    int index = fPictureRefs.find(&picture);
+    if (index < 0) {    // not found
+        index = fPictureRefs.count();
+        *fPictureRefs.append() = &picture;
+        picture.ref();
+    }
+    // follow the convention of recording a 1-based index
+    addInt(index + 1);
+}
+
+void SkPictureRecord::addPoint(const SkPoint& point) {
+#ifdef SK_DEBUG_SIZE
+    size_t start = fWriter.size();
+#endif
+    fWriter.writePoint(point);
+#ifdef SK_DEBUG_SIZE
+    fPointBytes += fWriter.size() - start;
+    fPointWrites++;
+#endif
+}
+    
+void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
+    fWriter.writeMul4(pts, count * sizeof(SkPoint));
+#ifdef SK_DEBUG_SIZE
+    fPointBytes += count * sizeof(SkPoint);
+    fPointWrites++;
+#endif
+}
+
+void SkPictureRecord::addRect(const SkRect& rect) {
+#ifdef SK_DEBUG_SIZE
+    size_t start = fWriter.size();
+#endif
+    fWriter.writeRect(rect);
+#ifdef SK_DEBUG_SIZE
+    fRectBytes += fWriter.size() - start;
+    fRectWrites++;
+#endif
+}
+
+void SkPictureRecord::addRectPtr(const SkRect* rect) {
+    if (fWriter.writeBool(rect != NULL)) {
+        fWriter.writeRect(*rect);
+    }
+}
+
+void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
+    if (fWriter.writeBool(rect != NULL)) {
+        *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
+    }
+}
+
+void SkPictureRecord::addRegion(const SkRegion& region) {
+    addInt(find(fRegions, region));
+}
+
+void SkPictureRecord::addText(const void* text, size_t byteLength) {
+#ifdef SK_DEBUG_SIZE
+    size_t start = fWriter.size();
+#endif
+    addInt(byteLength);
+    fWriter.writePad(text, byteLength);
+#ifdef SK_DEBUG_SIZE
+    fTextBytes += fWriter.size() - start;
+    fTextWrites++;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkPictureRecord::find(SkTDArray<const SkFlatBitmap* >& bitmaps, const SkBitmap& bitmap) {
+    SkFlatBitmap* flat = SkFlatBitmap::Flatten(&fHeap, bitmap, fBitmapIndex,
+                                               &fRCRecorder);
+    int index = SkTSearch<SkFlatData>((const SkFlatData**) bitmaps.begin(), 
+        bitmaps.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
+    if (index >= 0) {
+//        SkBitmap bitmap;
+//        flat->unflatten(&bitmap); // balance ref count
+        return bitmaps[index]->index();
+    }
+    index = ~index;
+    *bitmaps.insert(index) = flat;
+    return fBitmapIndex++;
+}
+
+int SkPictureRecord::find(SkTDArray<const SkFlatMatrix* >& matrices, const SkMatrix* matrix) {
+    if (matrix == NULL)
+        return 0;
+    SkFlatMatrix* flat = SkFlatMatrix::Flatten(&fHeap, *matrix, fMatrixIndex);
+    int index = SkTSearch<SkFlatData>((const SkFlatData**) matrices.begin(), 
+        matrices.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
+    if (index >= 0)
+        return matrices[index]->index();
+    index = ~index;
+    *matrices.insert(index) = flat;
+    return fMatrixIndex++;
+}
+
+int SkPictureRecord::find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* paint) {
+    if (paint == NULL) {
+        return 0;
+    }
+    
+    SkFlatPaint* flat = SkFlatPaint::Flatten(&fHeap, *paint, fPaintIndex,
+                                             &fRCRecorder, &fTFRecorder);
+    int index = SkTSearch<SkFlatData>((const SkFlatData**) paints.begin(), 
+        paints.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
+    if (index >= 0) {
+        return paints[index]->index();
+    }
+
+    index = ~index;
+    *paints.insert(index) = flat;
+    return fPaintIndex++;
+}
+
+int SkPictureRecord::find(SkTDArray<const SkFlatRegion* >& regions, const SkRegion& region) {
+    SkFlatRegion* flat = SkFlatRegion::Flatten(&fHeap, region, fRegionIndex);
+    int index = SkTSearch<SkFlatData>((const SkFlatData**) regions.begin(), 
+        regions.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
+    if (index >= 0)
+        return regions[index]->index();
+    index = ~index;
+    *regions.insert(index) = flat;
+    return fRegionIndex++;
+}
+
+#ifdef SK_DEBUG_DUMP
+void SkPictureRecord::dumpMatrices() {
+    int count = fMatrices.count();
+    SkMatrix defaultMatrix;
+    defaultMatrix.reset();
+    for (int index = 0; index < count; index++) {
+        const SkFlatMatrix* flatMatrix = fMatrices[index];
+        flatMatrix->dump();
+    }
+}
+
+void SkPictureRecord::dumpPaints() {
+    int count = fPaints.count();
+    for (int index = 0; index < count; index++) 
+        fPaints[index]->dump();
+}
+#endif
+
+#ifdef SK_DEBUG_SIZE
+size_t SkPictureRecord::size() const {
+    size_t result = 0;
+    size_t sizeData;
+    bitmaps(&sizeData);
+    result += sizeData;
+    matrices(&sizeData);
+    result += sizeData;
+    paints(&sizeData);
+    result += sizeData;
+    paths(&sizeData);
+    result += sizeData;
+    pictures(&sizeData);
+    result += sizeData;
+    regions(&sizeData);
+    result += sizeData;
+    result += streamlen();
+    return result;
+}
+
+int SkPictureRecord::bitmaps(size_t* size) const {
+    size_t result = 0;
+    int count = fBitmaps.count();
+    for (int index = 0; index < count; index++) 
+        result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
+    *size = result;
+    return count;
+}
+
+int SkPictureRecord::matrices(size_t* size) const {
+    int count = fMatrices.count();
+    *size = sizeof(fMatrices[0]) * count;
+    return count;
+}
+
+int SkPictureRecord::paints(size_t* size) const {
+    size_t result = 0;
+    int count = fPaints.count();
+    for (int index = 0; index < count; index++) 
+        result += sizeof(fPaints[index]) + fPaints[index]->size();
+    *size = result;
+    return count;
+}
+
+int SkPictureRecord::paths(size_t* size) const {
+    size_t result = 0;
+    int count = fPaths.count();
+    for (int index = 0; index < count; index++) 
+        result += sizeof(fPaths[index]) + fPaths[index]->size();
+    *size = result;
+    return count;
+}
+
+int SkPictureRecord::regions(size_t* size) const {
+    size_t result = 0;
+    int count = fRegions.count();
+    for (int index = 0; index < count; index++) 
+        result += sizeof(fRegions[index]) + fRegions[index]->size();
+    *size = result;
+    return count;
+}
+
+size_t SkPictureRecord::streamlen() const {
+    return fWriter.size();
+}
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+void SkPictureRecord::validate() const {
+    validateBitmaps();
+    validateMatrices();
+    validatePaints();
+    validatePaths();
+    validatePictures();
+    validateRegions();
+}
+
+void SkPictureRecord::validateBitmaps() const {
+    int count = fBitmaps.count();
+    SkASSERT((unsigned) count < 0x1000);
+    for (int index = 0; index < count; index++) {
+        const SkFlatBitmap* bitPtr = fBitmaps[index];
+        SkASSERT(bitPtr);
+        bitPtr->validate();
+    }
+}
+
+void SkPictureRecord::validateMatrices() const {
+    int count = fMatrices.count();
+    SkASSERT((unsigned) count < 0x1000);
+    for (int index = 0; index < count; index++) {
+        const SkFlatMatrix* matrix = fMatrices[index];
+        SkASSERT(matrix);
+        matrix->validate(); 
+    }
+}
+
+void SkPictureRecord::validatePaints() const {
+    int count = fPaints.count();
+    SkASSERT((unsigned) count < 0x1000);
+    for (int index = 0; index < count; index++) {
+        const SkFlatPaint* paint = fPaints[index];
+        SkASSERT(paint);
+//            paint->validate();
+    }
+}
+
+void SkPictureRecord::validatePaths() const {
+    int count = fPaths.count();
+    SkASSERT((unsigned) count < 0x1000);
+    for (int index = 0; index < count; index++) {
+        const SkFlatPath* path = fPaths[index];
+        SkASSERT(path);
+        path->validate();
+    }
+}
+
+void SkPictureRecord::validateRegions() const {
+    int count = fRegions.count();
+    SkASSERT((unsigned) count < 0x1000);
+    for (int index = 0; index < count; index++) {
+        const SkFlatRegion* region = fRegions[index];
+        SkASSERT(region);
+        region->validate();
+    }
+}
+#endif
+
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
new file mode 100644
index 0000000..d25d23a
--- /dev/null
+++ b/src/core/SkPictureRecord.h
@@ -0,0 +1,178 @@
+#ifndef SkPictureRecord_DEFINED
+#define SkPictureRecord_DEFINED
+
+#include "SkCanvas.h"
+#include "SkFlattenable.h"
+#include "SkPathHeap.h"
+#include "SkPicture.h"
+#include "SkPictureFlat.h"
+#include "SkTemplates.h"
+#include "SkWriter32.h"
+
+class SkPictureRecord : public SkCanvas {
+public:
+    SkPictureRecord();
+    virtual ~SkPictureRecord();
+
+    // 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 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 drawPaint(const SkPaint& paint);
+    virtual void drawPoints(PointMode, size_t count, const SkPoint pts[],
+                            const SkPaint&);
+    virtual void drawRect(const SkRect& rect, const SkPaint&);
+    virtual void drawPath(const SkPath& path, const SkPaint&);
+    virtual void drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+                            const SkPaint*);
+    virtual void drawBitmapRect(const SkBitmap&, const SkIRect* src,
+                                const SkRect& dst, const SkPaint*);
+    virtual void drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
+                                  const SkPaint*);
+    virtual void drawSprite(const SkBitmap&, int left, int top,
+                            const SkPaint*);
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x, 
+                          SkScalar y, const SkPaint&);
+    virtual void drawPosText(const void* text, size_t byteLength, 
+                             const SkPoint pos[], const SkPaint&);
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                      const SkScalar xpos[], SkScalar constY, const SkPaint&);
+    virtual void drawTextOnPath(const void* text, size_t byteLength, 
+                            const SkPath& path, const SkMatrix* matrix, 
+                                const SkPaint&);
+    virtual void drawPicture(SkPicture& picture);
+    virtual void drawVertices(VertexMode, int vertexCount,
+                          const SkPoint vertices[], const SkPoint texs[],
+                          const SkColor colors[], SkXfermode*,
+                          const uint16_t indices[], int indexCount,
+                              const SkPaint&);
+
+    void addFontMetricsTopBottom(const SkPaint& paint, SkScalar baselineY);
+    
+    const SkTDArray<const SkFlatBitmap* >& getBitmaps() const {
+        return fBitmaps;
+    }
+    const SkTDArray<const SkFlatMatrix* >& getMatrices() const {
+        return fMatrices;
+    }
+    const SkTDArray<const SkFlatPaint* >& getPaints() const {
+        return fPaints;
+    }
+    const SkTDArray<SkPicture* >& getPictureRefs() const {
+        return fPictureRefs;
+    }
+    const SkTDArray<const SkFlatRegion* >& getRegions() const {
+        return fRegions;
+    }
+    
+    void reset();
+
+    const SkWriter32& writeStream() const {
+        return fWriter;
+    }
+
+private:
+    SkTDArray<uint32_t> fRestoreOffsetStack;
+
+    void addDraw(DrawType drawType) {
+#ifdef SK_DEBUG_TRACE
+        SkDebugf("add %s\n", DrawTypeToString(drawType));
+#endif
+        fWriter.writeInt(drawType);
+    }    
+    void addInt(int value) {
+        fWriter.writeInt(value);
+    }
+    void addScalar(SkScalar scalar) {
+        fWriter.writeScalar(scalar);
+    }
+    
+    void addBitmap(const SkBitmap& bitmap);
+    void addMatrix(const SkMatrix& matrix);
+    void addMatrixPtr(const SkMatrix* matrix);
+    void addPaint(const SkPaint& paint);
+    void addPaintPtr(const SkPaint* paint);
+    void addPath(const SkPath& path);
+    void addPicture(SkPicture& picture);
+    void addPoint(const SkPoint& point);
+    void addPoints(const SkPoint pts[], int count);
+    void addRect(const SkRect& rect);
+    void addRectPtr(const SkRect* rect);
+    void addIRectPtr(const SkIRect* rect);
+    void addRegion(const SkRegion& region);
+    void addText(const void* text, size_t byteLength);
+
+    int find(SkTDArray<const SkFlatBitmap* >& bitmaps,
+                   const SkBitmap& bitmap);
+    int find(SkTDArray<const SkFlatMatrix* >& matrices,
+                   const SkMatrix* matrix);
+    int find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* paint);
+    int find(SkTDArray<const SkFlatRegion* >& regions, const SkRegion& region);
+
+#ifdef SK_DEBUG_DUMP
+public:
+    void dumpMatrices();
+    void dumpPaints();
+#endif
+
+#ifdef SK_DEBUG_SIZE
+public:
+    size_t size() const;    
+    int bitmaps(size_t* size) const;
+    int matrices(size_t* size) const;
+    int paints(size_t* size) const;
+    int paths(size_t* size) const;
+    int regions(size_t* size) const;
+    size_t streamlen() const;
+    
+    size_t fPointBytes, fRectBytes, fTextBytes;
+    int fPointWrites, fRectWrites, fTextWrites;
+#endif
+
+#ifdef SK_DEBUG_VALIDATE
+public:
+    void validate() const;
+private:
+    void validateBitmaps() const;
+    void validateMatrices() const;
+    void validatePaints() const;
+    void validatePaths() const;
+    void validateRegions() const;
+#else
+public:
+    void validate() const {}
+#endif
+
+private:
+    SkChunkAlloc fHeap;
+    int fBitmapIndex;
+    SkTDArray<const SkFlatBitmap* > fBitmaps;
+    int fMatrixIndex;
+    SkTDArray<const SkFlatMatrix* > fMatrices;
+    int fPaintIndex;
+    SkTDArray<const SkFlatPaint* > fPaints;
+    int fRegionIndex;
+    SkTDArray<const SkFlatRegion* > fRegions;
+    SkPathHeap* fPathHeap;  // reference counted
+    SkWriter32 fWriter;
+
+    // we ref each item in this array
+    SkTDArray<SkPicture*> fPictureRefs;
+
+    SkRefCntRecorder fRCRecorder;
+    SkRefCntRecorder fTFRecorder;
+    
+    friend class SkPicturePlayback;
+
+    typedef SkCanvas INHERITED;
+};
+
+#endif
diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp
new file mode 100644
index 0000000..adfc3c0
--- /dev/null
+++ b/src/core/SkPixelRef.cpp
@@ -0,0 +1,129 @@
+#include "SkPixelRef.h"
+#include "SkFlattenable.h"
+#include "SkThread.h"
+
+static SkMutex  gPixelRefMutex;
+static int32_t  gPixelRefGenerationID;
+
+SkPixelRef::SkPixelRef(SkMutex* mutex) {
+    if (NULL == mutex) {
+        mutex = &gPixelRefMutex;
+    }
+    fMutex = mutex;
+    fPixels = NULL;
+    fColorTable = NULL; // we do not track ownership of this
+    fLockCount = 0;
+    fGenerationID = 0;  // signal to rebuild
+    fIsImmutable = false;
+}
+
+SkPixelRef::SkPixelRef(SkFlattenableReadBuffer& buffer, SkMutex* mutex) {
+    if (NULL == mutex) {
+        mutex = &gPixelRefMutex;
+    }
+    fMutex = mutex;
+    fPixels = NULL;
+    fColorTable = NULL; // we do not track ownership of this
+    fLockCount = 0;
+    fGenerationID = 0;  // signal to rebuild
+    fIsImmutable = buffer.readBool();
+}
+
+void SkPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+    buffer.writeBool(fIsImmutable);
+}
+
+void SkPixelRef::lockPixels() {
+    SkAutoMutexAcquire  ac(*fMutex);
+    
+    if (1 == ++fLockCount) {
+        fPixels = this->onLockPixels(&fColorTable);
+    }
+}
+
+void SkPixelRef::unlockPixels() {
+    SkAutoMutexAcquire  ac(*fMutex);
+    
+    SkASSERT(fLockCount > 0);
+    if (0 == --fLockCount) {
+        this->onUnlockPixels();
+        fPixels = NULL;
+        fColorTable = NULL;
+    }
+}
+
+uint32_t SkPixelRef::getGenerationID() const {
+    uint32_t genID = fGenerationID;
+    if (0 == genID) {
+        // do a loop in case our global wraps around, as we never want to
+        // return a 0
+        do {
+            genID = sk_atomic_inc(&gPixelRefGenerationID) + 1;
+        } while (0 == genID);
+        fGenerationID = genID;
+    }
+    return genID;
+}
+
+void SkPixelRef::notifyPixelsChanged() {
+    if (fIsImmutable) {
+        SkDebugf("========== notifyPixelsChanged called on immutable pixelref");
+        sk_throw();
+    }
+    // this signals us to recompute this next time around
+    fGenerationID = 0;
+}
+
+void SkPixelRef::setImmutable() {
+    fIsImmutable = true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define MAX_PAIR_COUNT  16
+
+struct Pair {
+    const char*          fName;
+    SkPixelRef::Factory  fFactory;
+};
+
+static int gCount;
+static Pair gPairs[MAX_PAIR_COUNT];
+
+void SkPixelRef::Register(const char name[], Factory factory) {
+    SkASSERT(name);
+    SkASSERT(factory);
+    
+    static bool gOnce;
+    if (!gOnce) {
+        gCount = 0;
+        gOnce = true;
+    }
+    
+    SkASSERT(gCount < MAX_PAIR_COUNT);
+    
+    gPairs[gCount].fName = name;
+    gPairs[gCount].fFactory = factory;
+    gCount += 1;
+}
+
+SkPixelRef::Factory SkPixelRef::NameToFactory(const char name[]) {
+    const Pair* pairs = gPairs;
+    for (int i = gCount - 1; i >= 0; --i) {
+        if (strcmp(pairs[i].fName, name) == 0) {
+            return pairs[i].fFactory;
+        }
+    }
+    return NULL;
+}
+
+const char* SkPixelRef::FactoryToName(Factory fact) {
+    const Pair* pairs = gPairs;
+    for (int i = gCount - 1; i >= 0; --i) {
+        if (pairs[i].fFactory == fact) {
+            return pairs[i].fName;
+        }
+    }
+    return NULL;
+}
+
diff --git a/src/core/SkPoint.cpp b/src/core/SkPoint.cpp
new file mode 100644
index 0000000..704c2ba
--- /dev/null
+++ b/src/core/SkPoint.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPoint.h"
+
+void SkIPoint::rotateCW(SkIPoint* dst) const {
+    SkASSERT(dst);
+
+    // use a tmp in case this == dst
+    int32_t tmp = fX;
+    dst->fX = -fY;
+    dst->fY = tmp;
+}
+
+void SkIPoint::rotateCCW(SkIPoint* dst) const {
+    SkASSERT(dst);
+
+    // use a tmp in case this == dst
+    int32_t tmp = fX;
+    dst->fX = fY;
+    dst->fY = -tmp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPoint::rotateCW(SkPoint* dst) const {
+    SkASSERT(dst);
+
+    // use a tmp in case this == dst
+    SkScalar tmp = fX;
+    dst->fX = -fY;
+    dst->fY = tmp;
+}
+
+void SkPoint::rotateCCW(SkPoint* dst) const {
+    SkASSERT(dst);
+
+    // use a tmp in case this == dst
+    SkScalar tmp = fX;
+    dst->fX = fY;
+    dst->fY = -tmp;
+}
+
+void SkPoint::scale(SkScalar scale, SkPoint* dst) const {
+    SkASSERT(dst);
+    dst->set(SkScalarMul(fX, scale), SkScalarMul(fY, scale));
+}
+
+#define kNearlyZero     (SK_Scalar1 / 8092)
+
+bool SkPoint::normalize() {
+    return this->setLength(fX, fY, SK_Scalar1);
+}
+
+bool SkPoint::setNormalize(SkScalar x, SkScalar y) {
+    return this->setLength(x, y, SK_Scalar1);
+}
+
+bool SkPoint::setLength(SkScalar length) {
+    return this->setLength(fX, fY, length);
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
+    return sk_float_sqrt(dx * dx + dy * dy);
+}
+
+bool SkPoint::setLength(float x, float y, float length) {
+    float mag = sk_float_sqrt(x * x + y * y);
+    if (mag > kNearlyZero) {
+        length /= mag;
+        fX = x * length;
+        fY = y * length;
+        return true;
+    }
+    return false;
+}
+
+#else
+
+#include "Sk64.h"
+
+SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
+    Sk64    tmp1, tmp2;
+
+    tmp1.setMul(dx, dx);
+    tmp2.setMul(dy, dy);
+    tmp1.add(tmp2);
+
+    return tmp1.getSqrt();
+}
+
+#ifdef SK_DEBUGx
+static SkFixed fixlen(SkFixed x, SkFixed y) {
+    float fx = (float)x;
+    float fy = (float)y;
+
+    return (int)floorf(sqrtf(fx*fx + fy*fy) + 0.5f);
+}
+#endif
+
+static inline uint32_t squarefixed(unsigned x) {
+    x >>= 16;
+    return x*x;
+}
+
+#if 1   // Newton iter for setLength
+
+static inline unsigned invsqrt_iter(unsigned V, unsigned U) {
+    unsigned x = V * U >> 14;
+    x = x * U >> 14;
+    x = (3 << 14) - x;
+    x = (U >> 1) * x >> 14;
+    return x;
+}
+
+static const uint16_t gInvSqrt14GuessTable[] = {
+    0x4000, 0x3c57, 0x393e, 0x3695, 0x3441, 0x3235, 0x3061,
+    0x2ebd, 0x2d41, 0x2be7, 0x2aaa, 0x2987, 0x287a, 0x2780,
+    0x2698, 0x25be, 0x24f3, 0x2434, 0x2380, 0x22d6, 0x2235,
+    0x219d, 0x210c, 0x2083, 0x2000, 0x1f82, 0x1f0b, 0x1e99,
+    0x1e2b, 0x1dc2, 0x1d5d, 0x1cfc, 0x1c9f, 0x1c45, 0x1bee,
+    0x1b9b, 0x1b4a, 0x1afc, 0x1ab0, 0x1a67, 0x1a20, 0x19dc,
+    0x1999, 0x1959, 0x191a, 0x18dd, 0x18a2, 0x1868, 0x1830,
+    0x17fa, 0x17c4, 0x1791, 0x175e, 0x172d, 0x16fd, 0x16ce
+};
+
+#define BUILD_INVSQRT_TABLEx
+#ifdef BUILD_INVSQRT_TABLE
+static void build_invsqrt14_guess_table() {
+    for (int i = 8; i <= 63; i++) {
+        unsigned x = SkToU16((1 << 28) / SkSqrt32(i << 25));
+        printf("0x%x, ", x);
+    }
+    printf("\n");
+}
+#endif
+
+static unsigned fast_invsqrt(uint32_t x) {
+#ifdef BUILD_INVSQRT_TABLE
+    unsigned top2 = x >> 25;
+    SkASSERT(top2 >= 8 && top2 <= 63);
+
+    static bool gOnce;
+    if (!gOnce) {
+        build_invsqrt14_guess_table();
+        gOnce = true;
+    }
+#endif
+
+    unsigned V = x >> 14;   // make V .14
+
+    unsigned top = x >> 25;
+    SkASSERT(top >= 8 && top <= 63);
+    SkASSERT(top - 8 < SK_ARRAY_COUNT(gInvSqrt14GuessTable));
+    unsigned U = gInvSqrt14GuessTable[top - 8];
+    
+    U = invsqrt_iter(V, U);
+    return invsqrt_iter(V, U);
+}
+
+/*  We "normalize" x,y to be .14 values (so we can square them and stay 32bits.
+    Then we Newton-iterate this in .14 space to compute the invser-sqrt, and
+    scale by it at the end. The .14 space means we can execute our iterations
+    and stay in 32bits as well, making the multiplies much cheaper than calling
+    SkFixedMul.
+*/
+bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) {
+    if (ox == 0) {
+        if (oy == 0) {
+            return false;
+        }
+        this->set(0, SkApplySign(length, SkExtractSign(oy)));
+        return true;
+    }
+    if (oy == 0) {
+        this->set(SkApplySign(length, SkExtractSign(ox)), 0);
+        return true;
+    }
+
+    unsigned x = SkAbs32(ox);
+    unsigned y = SkAbs32(oy);
+    int zeros = SkCLZ(x | y);
+
+    // make x,y 1.14 values so our fast sqr won't overflow
+    if (zeros > 17) {
+        x <<= zeros - 17;
+        y <<= zeros - 17; 
+    } else {
+        x >>= 17 - zeros;
+        y >>= 17 - zeros;
+    }
+    SkASSERT((x | y) <= 0x7FFF);
+
+    unsigned invrt = fast_invsqrt(x*x + y*y);
+
+    x = x * invrt >> 12;
+    y = y * invrt >> 12;
+
+    if (length != SK_Fixed1) {
+        x = SkFixedMul(x, length);
+        y = SkFixedMul(y, length);
+    }
+    this->set(SkApplySign(x, SkExtractSign(ox)),
+              SkApplySign(y, SkExtractSign(oy)));
+    return true;
+}
+#else
+/*
+    Normalize x,y, and then scale them by length.
+
+    The obvious way to do this would be the following:
+        S64 tmp1, tmp2;
+        tmp1.setMul(x,x);
+        tmp2.setMul(y,y);
+        tmp1.add(tmp2);
+        len = tmp1.getSqrt();
+        x' = SkFixedDiv(x, len);
+        y' = SkFixedDiv(y, len);
+    This is fine, but slower than what we do below.
+
+    The present technique does not compute the starting length, but
+    rather fiddles with x,y iteratively, all the while checking its
+    magnitude^2 (avoiding a sqrt).
+
+    We normalize by first shifting x,y so that at least one of them
+    has bit 31 set (after taking the abs of them).
+    Then we loop, refining x,y by squaring them and comparing
+    against a very large 1.0 (1 << 28), and then adding or subtracting
+    a delta (which itself is reduced by half each time through the loop).
+    For speed we want the squaring to be with a simple integer mul. To keep
+    that from overflowing we shift our coordinates down until we are dealing
+    with at most 15 bits (2^15-1)^2 * 2 says withing 32 bits)
+    When our square is close to 1.0, we shift x,y down into fixed range.
+*/
+bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) {
+    if (ox == 0) {
+        if (oy == 0)
+            return false;
+        this->set(0, SkApplySign(length, SkExtractSign(oy)));
+        return true;
+    }
+    if (oy == 0) {
+        this->set(SkApplySign(length, SkExtractSign(ox)), 0);
+        return true;
+    }
+
+    SkFixed x = SkAbs32(ox);
+    SkFixed y = SkAbs32(oy);
+
+    // shift x,y so that the greater of them is 15bits (1.14 fixed point)
+    {
+        int shift = SkCLZ(x | y);
+        // make them .30
+        x <<= shift - 1;
+        y <<= shift - 1;
+    }
+
+    SkFixed dx = x;
+    SkFixed dy = y;
+
+    for (int i = 0; i < 17; i++) {
+        dx >>= 1;
+        dy >>= 1;
+
+        U32 len2 = squarefixed(x) + squarefixed(y);
+        if (len2 >> 28) {
+            x -= dx;
+            y -= dy;
+        } else {
+            x += dx;
+            y += dy;
+        }
+    }
+    x >>= 14;
+    y >>= 14;
+
+#ifdef SK_DEBUGx    // measure how far we are from unit-length
+    {
+        static int gMaxError;
+        static int gMaxDiff;
+
+        SkFixed len = fixlen(x, y);
+        int err = len - SK_Fixed1;
+        err = SkAbs32(err);
+
+        if (err > gMaxError) {
+            gMaxError = err;
+            SkDebugf("gMaxError %d\n", err);
+        }
+
+        float fx = SkAbs32(ox)/65536.0f;
+        float fy = SkAbs32(oy)/65536.0f;
+        float mag = sqrtf(fx*fx + fy*fy);
+        fx /= mag;
+        fy /= mag;
+        SkFixed xx = (int)floorf(fx * 65536 + 0.5f);
+        SkFixed yy = (int)floorf(fy * 65536 + 0.5f);
+        err = SkMax32(SkAbs32(xx-x), SkAbs32(yy-y));
+        if (err > gMaxDiff) {
+            gMaxDiff = err;
+            SkDebugf("gMaxDiff %d\n", err);
+        }
+    }
+#endif
+
+    x = SkApplySign(x, SkExtractSign(ox));
+    y = SkApplySign(y, SkExtractSign(oy));
+    if (length != SK_Fixed1) {
+        x = SkFixedMul(x, length);
+        y = SkFixedMul(y, length);
+    }
+    
+    this->set(x, y);
+    return true;
+}
+#endif
+
+#endif
+
diff --git a/src/core/SkProcSpriteBlitter.cpp b/src/core/SkProcSpriteBlitter.cpp
new file mode 100644
index 0000000..f727581
--- /dev/null
+++ b/src/core/SkProcSpriteBlitter.cpp
@@ -0,0 +1,55 @@
+/* libs/graphics/sgl/SkProcSpriteBlitter.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.
+*/
+
+#if 0   // experimental
+
+class SkProcSpriteBlitter : public SkSpriteBlitter {
+public:
+    typedef void (*Proc)(void* dst, const void* src, int count, const SkPMColor ctable[]);
+
+    SkProcSpriteBlitter(const SkBitmap& source, Proc proc, unsigned srcShift, unsigned dstShift)
+        : SkSpriteBlitter(source), fProc(proc), fSrcShift(SkToU8(srcShift)), fDstShift(SkToU8(dstShift)) {}
+
+    virtual void blitRect(int x, int y, int width, int height)
+    {
+        size_t      dstRB = fDevice.rowBytes();
+        size_t      srcRB = fSource.rowBytes();
+        char*       dst = (char*)fDevice.getPixels() + y * dstRB + (x << fDstShift);
+        const char* src = (const char*)fSource.getPixels() + (y - fTop) * srcRB + ((x - fLeft) << fSrcShift);
+        Proc        proc = fProc;
+        const SkPMColor* ctable = NULL;
+
+        if fSource.getColorTable())
+            ctable = fSource.getColorTable()->lockColors();
+
+        while (--height >= 0)
+        {
+            proc(dst, src, width, ctable);
+            dst += dstRB;
+            src += srcRB;
+        }
+
+        if fSource.getColorTable())
+            fSource.getColorTable()->unlockColors(false);
+    }
+
+private:
+    Proc    fProc;
+    uint8_t fSrcShift, fDstShift;
+};
+
+#endif
diff --git a/src/core/SkPtrRecorder.cpp b/src/core/SkPtrRecorder.cpp
new file mode 100644
index 0000000..4f774ec
--- /dev/null
+++ b/src/core/SkPtrRecorder.cpp
@@ -0,0 +1,53 @@
+#include "SkPtrRecorder.h"
+#include "SkTSearch.h"
+
+void SkPtrRecorder::reset() {
+    Pair* p = fList.begin();
+    Pair* stop = fList.end();
+    while (p < stop) {
+        this->decPtr(p->fPtr);
+        p += 1;
+    }
+    fList.reset();
+}
+
+int SkPtrRecorder::Cmp(const Pair& a, const Pair& b) {
+    return (char*)a.fPtr - (char*)b.fPtr;
+}
+
+uint32_t SkPtrRecorder::recordPtr(void* ptr) {
+    if (NULL == ptr) {
+        return 0;
+    }
+
+    int count = fList.count();
+    Pair pair;
+    pair.fPtr = ptr;
+
+    int index = SkTSearch<Pair>(fList.begin(), count, pair, sizeof(pair), &Cmp);
+    if (index < 0) {
+        index = ~index; // turn it back into an index for insertion
+        this->incPtr(ptr);
+        pair.fIndex = count + 1;
+        *fList.insert(index) = pair;
+        return count + 1;
+    } else {
+        return fList[index].fIndex;
+    }
+}
+
+void SkPtrRecorder::getPtrs(void* array[]) const {
+    int count = fList.count();
+    if (count > 0) {
+        SkASSERT(array);
+        const Pair* p = fList.begin();
+        // p->fIndex is base-1, so we need to subtract to find its slot
+        for (int i = 0; i < count; i++) {
+            int index = p[i].fIndex - 1;
+            SkASSERT((unsigned)index < (unsigned)count);
+            array[index] = p[i].fPtr;
+        }
+    }
+}
+
+
diff --git a/src/core/SkRasterizer.cpp b/src/core/SkRasterizer.cpp
new file mode 100644
index 0000000..f1d087d
--- /dev/null
+++ b/src/core/SkRasterizer.cpp
@@ -0,0 +1,62 @@
+/* libs/graphics/sgl/SkRasterizer.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 "SkRasterizer.h"
+#include "SkDraw.h"
+#include "SkMaskFilter.h"
+#include "SkPath.h"
+
+// do nothing for now, since we don't store anything at flatten time
+SkRasterizer::SkRasterizer(SkFlattenableReadBuffer&) {}
+
+bool SkRasterizer::rasterize(const SkPath& fillPath, const SkMatrix& matrix,
+                             const SkIRect* clipBounds, SkMaskFilter* filter,
+                             SkMask* mask, SkMask::CreateMode mode)
+{
+    SkIRect storage;
+    
+    if (clipBounds && filter && SkMask::kJustRenderImage_CreateMode != mode)
+    {        
+        SkIPoint    margin;
+        SkMask      srcM, dstM;
+        
+        srcM.fFormat = SkMask::kA8_Format;
+        srcM.fBounds.set(0, 0, 1, 1);
+        srcM.fImage = NULL;
+        if (!filter->filterMask(&dstM, srcM, matrix, &margin))
+            return false;
+        
+        storage = *clipBounds;
+        storage.inset(-margin.fX, -margin.fY);
+        clipBounds = &storage;
+    }
+    
+    return this->onRasterize(fillPath, matrix, clipBounds, mask, mode);
+}
+
+/*  Our default implementation of the virtual method just scan converts
+*/
+bool SkRasterizer::onRasterize(const SkPath& fillPath, const SkMatrix& matrix,
+                             const SkIRect* clipBounds,
+                             SkMask* mask, SkMask::CreateMode mode)
+{
+    SkPath  devPath;
+    
+    fillPath.transform(matrix, &devPath);
+    return SkDraw::DrawToMask(devPath, clipBounds, NULL, NULL, mask, mode);
+}
+
diff --git a/src/core/SkRect.cpp b/src/core/SkRect.cpp
new file mode 100644
index 0000000..d602754
--- /dev/null
+++ b/src/core/SkRect.cpp
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+#include "SkRect.h"
+
+void SkIRect::join(int32_t left, int32_t top, int32_t right, int32_t bottom)
+{
+    // do nothing if the params are empty
+    if (left >= right || top >= bottom)
+        return;
+
+    // if we are empty, just assign
+    if (fLeft >= fRight || fTop >= fBottom)
+        this->set(left, top, right, bottom);
+    else
+    {
+        if (left < fLeft) fLeft = left;
+        if (top < fTop) fTop = top;
+        if (right > fRight) fRight = right;
+        if (bottom > fBottom) fBottom = bottom;
+    }
+}
+
+void SkIRect::sort()
+{
+    if (fLeft > fRight)
+        SkTSwap<int32_t>(fLeft, fRight);
+    if (fTop > fBottom)
+        SkTSwap<int32_t>(fTop, fBottom);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void SkRect::sort()
+{
+    if (fLeft > fRight)
+        SkTSwap<SkScalar>(fLeft, fRight);
+    if (fTop > fBottom)
+        SkTSwap<SkScalar>(fTop, fBottom);
+}
+
+void SkRect::toQuad(SkPoint quad[4]) const
+{
+    SkASSERT(quad);
+
+    quad[0].set(fLeft, fTop);
+    quad[1].set(fRight, fTop);
+    quad[2].set(fRight, fBottom);
+    quad[3].set(fLeft, fBottom);
+}
+
+void SkRect::set(const SkPoint pts[], int count)
+{
+    SkASSERT((pts && count > 0) || count == 0);
+
+    if (count <= 0) {
+        bzero(this, sizeof(SkRect));
+    } else {
+#ifdef SK_SCALAR_SLOW_COMPARES
+        int32_t    l, t, r, b;
+        
+        l = r = SkScalarAs2sCompliment(pts[0].fX);
+        t = b = SkScalarAs2sCompliment(pts[0].fY);
+        
+        for (int i = 1; i < count; i++) {
+            int32_t x = SkScalarAs2sCompliment(pts[i].fX);
+            int32_t y = SkScalarAs2sCompliment(pts[i].fY);
+            
+            if (x < l) l = x; else if (x > r) r = x;
+            if (y < t) t = y; else if (y > b) b = y;
+        }
+        this->set(Sk2sComplimentAsScalar(l),
+                  Sk2sComplimentAsScalar(t),
+                  Sk2sComplimentAsScalar(r),
+                  Sk2sComplimentAsScalar(b));
+#else
+        SkScalar    l, t, r, b;
+
+        l = r = pts[0].fX;
+        t = b = pts[0].fY;
+
+        for (int i = 1; i < count; i++) {
+            SkScalar x = pts[i].fX;
+            SkScalar y = pts[i].fY;
+
+            if (x < l) l = x; else if (x > r) r = x;
+            if (y < t) t = y; else if (y > b) b = y;
+        }
+        this->set(l, t, r, b);
+#endif
+    }
+}
+
+bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
+{
+    if (left < right && top < bottom && !this->isEmpty() && // check for empties
+        fLeft < right && left < fRight && fTop < bottom && top < fBottom)
+    {
+        if (fLeft < left) fLeft = left;
+        if (fTop < top) fTop = top;
+        if (fRight > right) fRight = right;
+        if (fBottom > bottom) fBottom = bottom;
+        return true;
+    }
+    return false;
+}
+
+bool SkRect::intersect(const SkRect& r)
+{
+    SkASSERT(&r);
+    return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
+}
+
+void SkRect::join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
+{
+    // do nothing if the params are empty
+    if (left >= right || top >= bottom)
+        return;
+    
+    // if we are empty, just assign
+    if (fLeft >= fRight || fTop >= fBottom)
+        this->set(left, top, right, bottom);
+    else
+    {
+        if (left < fLeft) fLeft = left;
+        if (top < fTop) fTop = top;
+        if (right > fRight) fRight = right;
+        if (bottom > fBottom) fBottom = bottom;
+    }
+}
+
+
diff --git a/src/core/SkRefCnt.cpp b/src/core/SkRefCnt.cpp
new file mode 100644
index 0000000..fea1005
--- /dev/null
+++ b/src/core/SkRefCnt.cpp
@@ -0,0 +1,48 @@
+/* libs/graphics/sgl/SkRefCnt.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 "SkRefCnt.h"
+
+SkAutoUnref::~SkAutoUnref() {
+    if (fObj) {
+        fObj->unref();
+    }
+}
+
+bool SkAutoUnref::ref() {
+    if (fObj) {
+        fObj->ref();
+        return true;
+    }
+    return false;
+}
+
+bool SkAutoUnref::unref() {
+    if (fObj) {
+        fObj->unref();
+        fObj = NULL;
+        return true;
+    }
+    return false;
+}
+
+SkRefCnt* SkAutoUnref::detach() {
+    SkRefCnt* obj = fObj;
+    fObj = NULL;
+    return obj;
+}
+
diff --git a/src/core/SkRegion.cpp b/src/core/SkRegion.cpp
new file mode 100644
index 0000000..b48f3bb
--- /dev/null
+++ b/src/core/SkRegion.cpp
@@ -0,0 +1,1341 @@
+/* libs/corecg/SkRegion.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 "SkRegionPriv.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+
+SkDEBUGCODE(int32_t gRgnAllocCounter;)
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*  Pass in a scanline, beginning with the Left value of the pair (i.e. not the Y beginning)
+*/
+static SkRegion::RunType* skip_scanline(const SkRegion::RunType runs[])
+{
+    while (runs[0] != SkRegion::kRunTypeSentinel)
+    {
+        SkASSERT(runs[0] < runs[1]);    // valid span
+        runs += 2;
+    }
+    return (SkRegion::RunType*)(runs + 1);  // return past the X-sentinel
+}
+
+static SkRegion::RunType* find_y(const SkRegion::RunType runs[], int y)
+{
+    int top = *runs++;
+    if (top <= y)
+    {
+        for (;;)
+        {
+            int bot = *runs++;
+            if (bot > y)
+            {
+                if (bot == SkRegion::kRunTypeSentinel || *runs == SkRegion::kRunTypeSentinel)
+                    break;
+                return (SkRegion::RunType*)runs;
+            }
+            top = bot;
+            runs = skip_scanline(runs);
+        }
+    }
+    return NULL;
+}
+
+// returns true if runs are just a rect
+bool SkRegion::ComputeRunBounds(const SkRegion::RunType runs[], int count, SkIRect* bounds)
+{
+    assert_sentinel(runs[0], false);    // top
+
+    if (count == kRectRegionRuns)
+    {
+        assert_sentinel(runs[1], false);    // bottom
+        assert_sentinel(runs[2], false);    // left
+        assert_sentinel(runs[3], false);    // right
+        assert_sentinel(runs[4], true);
+        assert_sentinel(runs[5], true);
+
+        SkASSERT(runs[0] < runs[1]);    // valid height
+        SkASSERT(runs[2] < runs[3]);    // valid width
+
+        bounds->set(runs[2], runs[0], runs[3], runs[1]);
+        return true;
+    }
+
+    int left = SK_MaxS32;
+    int rite = SK_MinS32;
+    int bot;
+
+    bounds->fTop = *runs++;
+    do {
+        bot = *runs++;
+        if (*runs < SkRegion::kRunTypeSentinel)
+        {
+            if (left > *runs)
+                left = *runs;
+            runs = skip_scanline(runs);
+            if (rite < runs[-2])
+                rite = runs[-2];
+        }
+        else
+            runs += 1;  // skip X-sentinel
+    } while (runs[0] < SkRegion::kRunTypeSentinel);
+    bounds->fLeft = left;
+    bounds->fRight = rite;
+    bounds->fBottom = bot;
+    return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+SkRegion::SkRegion()
+{
+    fBounds.set(0, 0, 0, 0);
+    fRunHead = SkRegion_gEmptyRunHeadPtr;
+}
+
+SkRegion::SkRegion(const SkRegion& src)
+{
+    fRunHead = SkRegion_gEmptyRunHeadPtr;   // just need a value that won't trigger sk_free(fRunHead)
+    this->setRegion(src);
+}
+
+SkRegion::SkRegion(const SkIRect& rect)
+{
+    fRunHead = SkRegion_gEmptyRunHeadPtr;   // just need a value that won't trigger sk_free(fRunHead)
+    this->setRect(rect);
+}
+
+SkRegion::~SkRegion()
+{
+    this->freeRuns();
+}
+
+void SkRegion::freeRuns()
+{
+    if (fRunHead->isComplex())
+    {
+        SkASSERT(fRunHead->fRefCnt >= 1);
+        if (sk_atomic_dec(&fRunHead->fRefCnt) == 1)
+        {
+            //SkASSERT(gRgnAllocCounter > 0);
+            //SkDEBUGCODE(sk_atomic_dec(&gRgnAllocCounter));
+            //SkDEBUGF(("************** gRgnAllocCounter::free %d\n", gRgnAllocCounter));
+            sk_free(fRunHead);
+        }
+    }
+}
+
+void SkRegion::allocateRuns(int count)
+{
+    fRunHead = RunHead::Alloc(count);
+}
+
+SkRegion& SkRegion::operator=(const SkRegion& src)
+{
+    (void)this->setRegion(src);
+    return *this;
+}
+
+void SkRegion::swap(SkRegion& other)
+{
+    SkTSwap<SkIRect>(fBounds, other.fBounds);
+    SkTSwap<RunHead*>(fRunHead, other.fRunHead);
+}
+
+bool SkRegion::setEmpty()
+{
+    this->freeRuns();
+    fBounds.set(0, 0, 0, 0);
+    fRunHead = SkRegion_gEmptyRunHeadPtr;
+    return false;
+}
+
+bool SkRegion::setRect(int32_t left, int32_t top, int32_t right, int32_t bottom)
+{
+    if (left >= right || top >= bottom)
+        return this->setEmpty();
+
+    this->freeRuns();
+    fBounds.set(left, top, right, bottom);
+    fRunHead = SkRegion_gRectRunHeadPtr;
+    return true;
+}
+
+bool SkRegion::setRect(const SkIRect& r)
+{
+    return this->setRect(r.fLeft, r.fTop, r.fRight, r.fBottom);
+}
+
+bool SkRegion::setRegion(const SkRegion& src)
+{
+    if (this != &src)
+    {
+        this->freeRuns();
+
+        fBounds = src.fBounds;
+        fRunHead = src.fRunHead;
+        if (fRunHead->isComplex())
+            sk_atomic_inc(&fRunHead->fRefCnt);
+    }
+    return fRunHead != SkRegion_gEmptyRunHeadPtr;
+}
+
+bool SkRegion::op(const SkIRect& rect, const SkRegion& rgn, Op op)
+{
+    SkRegion tmp(rect);
+
+    return this->op(tmp, rgn, op);
+}
+
+bool SkRegion::op(const SkRegion& rgn, const SkIRect& rect, Op op)
+{
+    SkRegion tmp(rect);
+
+    return this->op(rgn, tmp, op);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+int SkRegion::count_runtype_values(int* itop, int* ibot) const
+{
+    if (this == NULL)
+    {
+        *itop = SK_MinS32;
+        *ibot = SK_MaxS32;
+        return 0;
+    }
+
+    int maxT;
+
+    if (this->isRect())
+        maxT = 2;
+    else
+    {
+        SkASSERT(this->isComplex());
+        // skip the top
+        const RunType*  runs = fRunHead->readonly_runs() + 1;
+        maxT = 0;
+
+        do {
+            const RunType* next = skip_scanline(runs + 1);
+            SkASSERT(next > runs);
+            int         T = (int)(next - runs - 1);
+            if (maxT < T)
+                maxT = T;
+            runs = next;
+        } while (runs[0] < SkRegion::kRunTypeSentinel);
+    }
+    *itop = fBounds.fTop;
+    *ibot = fBounds.fBottom;
+    return maxT;
+}
+
+bool SkRegion::setRuns(RunType runs[], int count)
+{
+    SkDEBUGCODE(this->validate();)
+    SkASSERT(count > 0);
+
+    if (count <= 2)
+    {
+    //  SkDEBUGF(("setRuns: empty\n"));
+        assert_sentinel(runs[count-1], true);
+        return this->setEmpty();
+    }
+
+    // trim off any empty spans from the top and bottom
+    // weird I should need this, perhaps op() could be smarter...
+    if (count > kRectRegionRuns)
+    {
+        RunType* stop = runs + count;
+        assert_sentinel(runs[0], false);    // top
+        assert_sentinel(runs[1], false);    // bottom
+        if (runs[2] == SkRegion::kRunTypeSentinel)    // should be first left...
+        {
+            runs += 2;  // skip empty initial span
+            runs[0] = runs[-1]; // set new top to prev bottom
+            assert_sentinel(runs[1], false);    // bot: a sentinal would mean two in a row
+            assert_sentinel(runs[2], false);    // left
+            assert_sentinel(runs[3], false);    // right
+        }
+
+        // now check for a trailing empty span
+        assert_sentinel(stop[-1], true);
+        assert_sentinel(stop[-2], true);
+        assert_sentinel(stop[-3], false);   // should be last right
+        if (stop[-4] == SkRegion::kRunTypeSentinel)   // eek, stop[-3] was a bottom with no x-runs
+        {
+            stop[-3] = SkRegion::kRunTypeSentinel;    // kill empty last span
+            stop -= 2;
+            assert_sentinel(stop[-1], true);
+            assert_sentinel(stop[-2], true);
+            assert_sentinel(stop[-3], false);
+            assert_sentinel(stop[-4], false);
+            assert_sentinel(stop[-5], false);
+        }
+        count = (int)(stop - runs);
+    }
+
+    SkASSERT(count >= kRectRegionRuns);
+
+    if (ComputeRunBounds(runs, count, &fBounds))
+    {
+    //  SkDEBUGF(("setRuns: rect[%d %d %d %d]\n", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom));
+        return this->setRect(fBounds);
+    }
+
+    //  if we get here, we need to become a complex region
+
+    if (!fRunHead->isComplex() || fRunHead->fRunCount != count)
+    {
+#ifdef SK_DEBUGx
+        SkDebugf("setRuns: rgn [");
+        {
+            const RunType* r = runs;
+
+            SkDebugf(" top: %d\n", *r++);
+            while (*r < SkRegion::kRunTypeSentinel)
+            {
+                SkDebugf(" bottom: %d", *r++);
+                while (*r < SkRegion::kRunTypeSentinel)
+                {
+                    SkDebugf(" [%d %d]", r[0], r[1]);
+                    r += 2;
+                }
+                SkDebugf("\n");
+            }
+        }
+#endif
+        this->freeRuns();
+        this->allocateRuns(count);
+    }
+    
+    // must call this before we can write directly into runs()
+    // in case we are sharing the buffer with another region (copy on write)
+    fRunHead = fRunHead->ensureWritable();
+    memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
+
+    SkDEBUGCODE(this->validate();)
+
+    return true;
+}
+
+void SkRegion::BuildRectRuns(const SkIRect& bounds,
+                             RunType runs[kRectRegionRuns])
+{
+    runs[0] = bounds.fTop;
+    runs[1] = bounds.fBottom;
+    runs[2] = bounds.fLeft;
+    runs[3] = bounds.fRight;
+    runs[4] = kRunTypeSentinel;
+    runs[5] = kRunTypeSentinel;
+}
+
+static SkRegion::RunType* find_scanline(const SkRegion::RunType runs[], int y)
+{
+    SkASSERT(y >= runs[0]); // if this fails, we didn't do a quick check on the boudns
+
+    runs += 1;  // skip top-Y
+    for (;;)
+    {
+        if (runs[0] == SkRegion::kRunTypeSentinel)
+            break;
+        if (y < runs[0])
+            return (SkRegion::RunType*)&runs[1];
+        runs = skip_scanline(runs + 1); // skip the Y value before calling
+    }
+    return NULL;
+}
+
+bool SkRegion::contains(int x, int y) const
+{
+    if (!fBounds.contains(x, y))
+        return false;
+
+    if (this->isRect())
+        return true;
+
+    SkASSERT(this->isComplex());
+    const RunType* runs = find_scanline(fRunHead->readonly_runs(), y);
+
+    if (runs)
+    {   for (;;)
+        {   if (x < runs[0])
+                break;
+            if (x < runs[1])
+                return true;
+            runs += 2;
+        }
+    }
+    return false;
+}
+
+bool SkRegion::contains(const SkIRect& r) const
+{
+    SkRegion tmp(r);
+    
+    return this->contains(tmp);
+}
+
+bool SkRegion::contains(const SkRegion& rgn) const
+{
+    if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds))
+        return false;
+
+    if (this->isRect())
+        return true;
+
+    SkRegion    tmp;
+    
+    tmp.op(*this, rgn, kUnion_Op);
+    return tmp == *this;
+}
+
+const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[], int* count) const
+{
+    SkASSERT(tmpStorage && count);
+    const RunType* runs = tmpStorage;
+
+    if (this->isEmpty())
+    {
+        tmpStorage[0] = kRunTypeSentinel;
+        *count = 1;
+    }
+    else if (this->isRect())
+    {
+        BuildRectRuns(fBounds, tmpStorage);
+        *count = kRectRegionRuns;
+    }
+    else
+    {
+        *count = fRunHead->fRunCount;
+        runs = fRunHead->readonly_runs();
+    }
+    return runs;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+bool SkRegion::intersects(const SkIRect& r) const {
+    if (this->isEmpty() || r.isEmpty()) {
+        return false;
+    }
+    
+    if (!SkIRect::Intersects(fBounds, r)) {
+        return false;
+    }
+
+    if (this->isRect()) {
+        return true;
+    }
+    
+    // we are complex
+    SkRegion tmp;
+    return tmp.op(*this, r, kIntersect_Op);
+}
+
+bool SkRegion::intersects(const SkRegion& rgn) const {
+    if (this->isEmpty() || rgn.isEmpty()) {
+        return false;
+    }
+    
+    if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
+        return false;
+    }
+    
+    if (this->isRect() && rgn.isRect()) {
+        return true;
+    }
+    
+    // one or both of us is complex
+    // TODO: write a faster version that aborts as soon as we write the first
+    //       non-empty span, to avoid build the entire result
+    SkRegion tmp;
+    return tmp.op(*this, rgn, kIntersect_Op);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+int operator==(const SkRegion& a, const SkRegion& b)
+{
+    SkDEBUGCODE(a.validate();)
+    SkDEBUGCODE(b.validate();)
+
+    if (&a == &b)
+        return true;
+    if (a.fBounds != b.fBounds)
+        return false;
+    
+    const SkRegion::RunHead* ah = a.fRunHead;
+    const SkRegion::RunHead* bh = b.fRunHead;
+
+    // this catches empties and rects being equal
+    if (ah == bh) 
+        return true;
+    
+    // now we insist that both are complex (but different ptrs)
+    if (!ah->isComplex() || !bh->isComplex())
+        return false;
+
+    return  ah->fRunCount == bh->fRunCount &&
+            !memcmp(ah->readonly_runs(), bh->readonly_runs(),
+                    ah->fRunCount * sizeof(SkRegion::RunType));
+}
+
+void SkRegion::translate(int dx, int dy, SkRegion* dst) const
+{
+    SkDEBUGCODE(this->validate();)
+
+    if (NULL == dst)
+        return;
+
+    if (this->isEmpty())
+        dst->setEmpty();
+    else if (this->isRect())
+        dst->setRect(fBounds.fLeft + dx, fBounds.fTop + dy,
+                     fBounds.fRight + dx, fBounds.fBottom + dy);
+    else
+    {
+        if (this == dst)
+        {
+            dst->fRunHead = dst->fRunHead->ensureWritable();
+        }
+        else
+        {
+            SkRegion    tmp;
+            tmp.allocateRuns(fRunHead->fRunCount);
+            tmp.fBounds = fBounds;
+            dst->swap(tmp);
+        }
+
+        dst->fBounds.offset(dx, dy);
+        
+        const RunType*  sruns = fRunHead->readonly_runs();
+        RunType*        druns = dst->fRunHead->writable_runs();
+
+        *druns++ = (SkRegion::RunType)(*sruns++ + dy);    // top
+        for (;;)
+        {
+            int bottom = *sruns++;
+            if (bottom == kRunTypeSentinel)
+                break;
+            *druns++ = (SkRegion::RunType)(bottom + dy);  // bottom;
+            for (;;)
+            {
+                int x = *sruns++;
+                if (x == kRunTypeSentinel)
+                    break;
+                *druns++ = (SkRegion::RunType)(x + dx);
+                *druns++ = (SkRegion::RunType)(*sruns++ + dx);
+            }
+            *druns++ = kRunTypeSentinel;    // x sentinel
+        }
+        *druns++ = kRunTypeSentinel;    // y sentinel
+
+        SkASSERT(sruns - fRunHead->readonly_runs() == fRunHead->fRunCount);
+        SkASSERT(druns - dst->fRunHead->readonly_runs() == dst->fRunHead->fRunCount);
+    }
+
+    SkDEBUGCODE(this->validate();)
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable warning : local variable used without having been initialized
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+#ifdef SK_DEBUG
+static void assert_valid_pair(int left, int rite)
+{
+    SkASSERT(left == SkRegion::kRunTypeSentinel || left < rite);
+}
+#else
+    #define assert_valid_pair(left, rite)
+#endif
+
+struct spanRec {
+    const SkRegion::RunType*    fA_runs;
+    const SkRegion::RunType*    fB_runs;
+    int                         fA_left, fA_rite, fB_left, fB_rite;
+    int                         fLeft, fRite, fInside;
+    
+    void init(const SkRegion::RunType a_runs[], const SkRegion::RunType b_runs[])
+    {        
+        fA_left = *a_runs++;
+        fA_rite = *a_runs++;
+        fB_left = *b_runs++;
+        fB_rite = *b_runs++;
+
+        fA_runs = a_runs;
+        fB_runs = b_runs;
+    }
+    
+    bool done() const
+    {
+        SkASSERT(fA_left <= SkRegion::kRunTypeSentinel);
+        SkASSERT(fB_left <= SkRegion::kRunTypeSentinel);
+        return fA_left == SkRegion::kRunTypeSentinel && fB_left == SkRegion::kRunTypeSentinel;
+    }
+
+    void next()
+    {
+        assert_valid_pair(fA_left, fA_rite);
+        assert_valid_pair(fB_left, fB_rite);
+
+        int     inside, left, rite SK_INIT_TO_AVOID_WARNING;
+        bool    a_flush = false;
+        bool    b_flush = false;
+        
+        int a_left = fA_left;
+        int a_rite = fA_rite;
+        int b_left = fB_left;
+        int b_rite = fB_rite;
+
+        if (a_left < b_left)
+        {
+            inside = 1;
+            left = a_left;
+            if (a_rite <= b_left)   // [...] <...>
+            {
+                rite = a_rite;
+                a_flush = true;
+            }
+            else // [...<..]...> or [...<...>...]
+                rite = a_left = b_left;
+        }
+        else if (b_left < a_left)
+        {
+            inside = 2;
+            left = b_left;
+            if (b_rite <= a_left)   // [...] <...>
+            {
+                rite = b_rite;
+                b_flush = true;
+            }
+            else // [...<..]...> or [...<...>...]
+                rite = b_left = a_left;
+        }
+        else    // a_left == b_left
+        {
+            inside = 3;
+            left = a_left;  // or b_left
+            if (a_rite <= b_rite)
+            {
+                rite = b_left = a_rite;
+                a_flush = true;
+            }
+            if (b_rite <= a_rite)
+            {
+                rite = a_left = b_rite;
+                b_flush = true;
+            }
+        }
+
+        if (a_flush)
+        {
+            a_left = *fA_runs++;
+            a_rite = *fA_runs++;
+        }
+        if (b_flush)
+        {
+            b_left = *fB_runs++;
+            b_rite = *fB_runs++;
+        }
+
+        SkASSERT(left <= rite);
+        
+        // now update our state
+        fA_left = a_left;
+        fA_rite = a_rite;
+        fB_left = b_left;
+        fB_rite = b_rite;
+        
+        fLeft = left;
+        fRite = rite;
+        fInside = inside;
+    }
+};
+
+static SkRegion::RunType* operate_on_span(const SkRegion::RunType a_runs[],
+                                          const SkRegion::RunType b_runs[],
+                                          SkRegion::RunType dst[],
+                                          int min, int max)
+{
+    spanRec rec;
+    bool    firstInterval = true;
+    
+    rec.init(a_runs, b_runs);
+
+    while (!rec.done())
+    {
+        rec.next();
+        
+        int left = rec.fLeft;
+        int rite = rec.fRite;
+        
+        // add left,rite to our dst buffer (checking for coincidence
+        if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) &&
+            left < rite)    // skip if equal
+        {
+            if (firstInterval || dst[-1] < left)
+            {
+                *dst++ = (SkRegion::RunType)(left);
+                *dst++ = (SkRegion::RunType)(rite);
+                firstInterval = false;
+            }
+            else    // update the right edge
+                dst[-1] = (SkRegion::RunType)(rite);
+        }
+    }
+
+    *dst++ = SkRegion::kRunTypeSentinel;
+    return dst;
+}
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+static const struct {
+    uint8_t fMin;
+    uint8_t fMax;
+} gOpMinMax[] = {
+    { 1, 1 },   // Difference
+    { 3, 3 },   // Intersection
+    { 1, 3 },   // Union
+    { 1, 2 }    // XOR
+};
+
+class RgnOper {
+public:
+    RgnOper(int top, SkRegion::RunType dst[], SkRegion::Op op)
+    {
+        // need to ensure that the op enum lines up with our minmax array
+        SkASSERT(SkRegion::kDifference_Op == 0);
+        SkASSERT(SkRegion::kIntersect_Op == 1);
+        SkASSERT(SkRegion::kUnion_Op == 2);
+        SkASSERT(SkRegion::kXOR_Op == 3);
+        SkASSERT((unsigned)op <= 3);
+
+        fStartDst = dst;
+        fPrevDst = dst + 1;
+        fPrevLen = 0;       // will never match a length from operate_on_span
+        fTop = (SkRegion::RunType)(top);    // just a first guess, we might update this
+
+        fMin = gOpMinMax[op].fMin;
+        fMax = gOpMinMax[op].fMax;
+    }
+
+    void addSpan(int bottom, const SkRegion::RunType a_runs[], const SkRegion::RunType b_runs[])
+    {
+        SkRegion::RunType*  start = fPrevDst + fPrevLen + 1;    // skip X values and slot for the next Y
+        SkRegion::RunType*  stop = operate_on_span(a_runs, b_runs, start, fMin, fMax);
+        size_t              len = stop - start;
+
+        if (fPrevLen == len && !memcmp(fPrevDst, start, len * sizeof(SkRegion::RunType)))   // update Y value
+            fPrevDst[-1] = (SkRegion::RunType)(bottom);
+        else    // accept the new span
+        {
+            if (len == 1 && fPrevLen == 0) {
+                fTop = (SkRegion::RunType)(bottom); // just update our bottom
+            } else {
+                start[-1] = (SkRegion::RunType)(bottom);
+                fPrevDst = start;
+                fPrevLen = len;
+            }
+        }
+    }
+    
+    int flush()
+    {
+        fStartDst[0] = fTop;
+        fPrevDst[fPrevLen] = SkRegion::kRunTypeSentinel;
+        return (int)(fPrevDst - fStartDst + fPrevLen + 1);
+    }
+
+    uint8_t fMin, fMax;
+
+private:
+    SkRegion::RunType*  fStartDst;
+    SkRegion::RunType*  fPrevDst;
+    size_t              fPrevLen;
+    SkRegion::RunType   fTop;
+};
+
+static int operate( const SkRegion::RunType a_runs[],
+                    const SkRegion::RunType b_runs[],
+                    SkRegion::RunType dst[],
+                    SkRegion::Op op)
+{
+    const SkRegion::RunType sentinel = SkRegion::kRunTypeSentinel;
+
+    int a_top = *a_runs++;
+    int a_bot = *a_runs++;
+    int b_top = *b_runs++;
+    int b_bot = *b_runs++;
+
+    assert_sentinel(a_top, false);
+    assert_sentinel(a_bot, false);
+    assert_sentinel(b_top, false);
+    assert_sentinel(b_bot, false);
+
+    RgnOper oper(SkMin32(a_top, b_top), dst, op);
+    
+    bool firstInterval = true;
+    int prevBot = SkRegion::kRunTypeSentinel; // so we fail the first test
+    
+    while (a_bot < SkRegion::kRunTypeSentinel || b_bot < SkRegion::kRunTypeSentinel)
+    {
+        int         top, bot SK_INIT_TO_AVOID_WARNING;
+        const SkRegion::RunType*    run0 = &sentinel;
+        const SkRegion::RunType*    run1 = &sentinel;
+        bool        a_flush = false;
+        bool        b_flush = false;
+        int         inside;
+
+        if (a_top < b_top)
+        {
+            inside = 1;
+            top = a_top;
+            run0 = a_runs;
+            if (a_bot <= b_top) // [...] <...>
+            {
+                bot = a_bot;
+                a_flush = true;
+            }
+            else // [...<..]...> or [...<...>...]
+                bot = a_top = b_top;
+        }
+        else if (b_top < a_top)
+        {
+            inside = 2;
+            top = b_top;
+            run1 = b_runs;
+            if (b_bot <= a_top) // [...] <...>
+            {
+                bot = b_bot;
+                b_flush = true;
+            }
+            else // [...<..]...> or [...<...>...]
+                bot = b_top = a_top;
+        }
+        else    // a_top == b_top
+        {
+            inside = 3;
+            top = a_top;    // or b_top
+            run0 = a_runs;
+            run1 = b_runs;
+            if (a_bot <= b_bot)
+            {
+                bot = b_top = a_bot;
+                a_flush = true;
+            }
+            if (b_bot <= a_bot)
+            {
+                bot = a_top = b_bot;
+                b_flush = true;
+            }
+        }
+        
+        if (top > prevBot)
+            oper.addSpan(top, &sentinel, &sentinel);
+
+//      if ((unsigned)(inside - oper.fMin) <= (unsigned)(oper.fMax - oper.fMin))
+        {
+            oper.addSpan(bot, run0, run1);
+            firstInterval = false;
+        }
+
+        if (a_flush)
+        {
+            a_runs = skip_scanline(a_runs);
+            a_top = a_bot;
+            a_bot = *a_runs++;
+            if (a_bot == SkRegion::kRunTypeSentinel)
+                a_top = a_bot;
+        }
+        if (b_flush)
+        {
+            b_runs = skip_scanline(b_runs);
+            b_top = b_bot;
+            b_bot = *b_runs++;
+            if (b_bot == SkRegion::kRunTypeSentinel)
+                b_top = b_bot;
+        }
+        
+        prevBot = bot;
+    }
+    return oper.flush();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  Given count RunTypes in a complex region, return the worst case number of
+    logical intervals that represents (i.e. number of rects that would be
+    returned from the iterator).
+ 
+    We could just return count/2, since there must be at least 2 values per
+    interval, but we can first trim off the const overhead of the initial TOP
+    value, plus the final BOTTOM + 2 sentinels.
+ */
+static int count_to_intervals(int count) {
+    SkASSERT(count >= 6);   // a single rect is 6 values
+    return (count - 4) >> 1;
+}
+
+/*  Given a number of intervals, what is the worst case representation of that
+    many intervals?
+ 
+    Worst case (from a storage perspective), is a vertical stack of single
+    intervals:  TOP + N * (BOTTOM LEFT RIGHT SENTINEL) + SENTINEL
+ */
+static int intervals_to_count(int intervals) {
+    return 1 + intervals * 4 + 1;
+}
+
+/*  Given the counts of RunTypes in two regions, return the worst-case number
+    of RunTypes need to store the result after a region-op.
+ */
+static int compute_worst_case_count(int a_count, int b_count) {
+    int a_intervals = count_to_intervals(a_count);
+    int b_intervals = count_to_intervals(b_count);
+    // Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
+    int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
+    // convert back to number of RunType values
+    return intervals_to_count(intervals);
+}
+
+bool SkRegion::op(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op)
+{
+    SkDEBUGCODE(this->validate();)
+
+    SkASSERT((unsigned)op < kOpCount);
+    
+    if (kReplace_Op == op)
+        return this->set(rgnbOrig);
+    
+    // swith to using pointers, so we can swap them as needed
+    const SkRegion* rgna = &rgnaOrig;
+    const SkRegion* rgnb = &rgnbOrig;
+    // after this point, do not refer to rgnaOrig or rgnbOrig!!!
+
+    // collaps difference and reverse-difference into just difference
+    if (kReverseDifference_Op == op)
+    {
+        SkTSwap<const SkRegion*>(rgna, rgnb);
+        op = kDifference_Op;
+    }
+
+    SkIRect bounds;
+    bool    a_empty = rgna->isEmpty();
+    bool    b_empty = rgnb->isEmpty();
+    bool    a_rect = rgna->isRect();
+    bool    b_rect = rgnb->isRect();
+
+    switch (op) {
+    case kDifference_Op:
+        if (a_empty)
+            return this->setEmpty();
+        if (b_empty || !SkIRect::Intersects(rgna->fBounds, rgnb->fBounds))
+            return this->setRegion(*rgna);
+        break;
+
+    case kIntersect_Op:
+        if ((a_empty | b_empty)
+                || !bounds.intersect(rgna->fBounds, rgnb->fBounds))
+            return this->setEmpty();
+        if (a_rect & b_rect)
+            return this->setRect(bounds);
+        break;
+
+    case kUnion_Op:
+        if (a_empty)
+            return this->setRegion(*rgnb);
+        if (b_empty)
+            return this->setRegion(*rgna);
+        if (a_rect && rgna->fBounds.contains(rgnb->fBounds))
+            return this->setRegion(*rgna);
+        if (b_rect && rgnb->fBounds.contains(rgna->fBounds))
+            return this->setRegion(*rgnb);
+        break;
+
+    case kXOR_Op:
+        if (a_empty)
+            return this->setRegion(*rgnb);
+        if (b_empty)
+            return this->setRegion(*rgna);
+        break;
+    default:
+        SkASSERT(!"unknown region op");
+        return !this->isEmpty();
+    }
+
+    RunType tmpA[kRectRegionRuns];
+    RunType tmpB[kRectRegionRuns];
+
+    int a_count, b_count;
+    const RunType* a_runs = rgna->getRuns(tmpA, &a_count);
+    const RunType* b_runs = rgnb->getRuns(tmpB, &b_count);
+
+    int dstCount = compute_worst_case_count(a_count, b_count);
+    SkAutoSTMalloc<32, RunType> array(dstCount);
+
+    int count = operate(a_runs, b_runs, array.get(), op);
+    SkASSERT(count <= dstCount);
+    return this->setRuns(array.get(), count);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkBuffer.h"
+
+uint32_t SkRegion::flatten(void* storage) const {
+    if (NULL == storage) {
+        uint32_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
+        if (!this->isEmpty()) {
+            size += sizeof(fBounds);
+            if (this->isComplex()) {
+                size += fRunHead->fRunCount * sizeof(RunType);
+            }
+        }
+        return size;
+    }
+
+    SkWBuffer   buffer(storage);
+
+    if (this->isEmpty()) {
+        buffer.write32(-1);
+    } else {
+        bool isRect = this->isRect();
+
+        buffer.write32(isRect ? 0 : fRunHead->fRunCount);
+        buffer.write(&fBounds, sizeof(fBounds));
+
+        if (!isRect) {
+            buffer.write(fRunHead->readonly_runs(),
+                         fRunHead->fRunCount * sizeof(RunType));
+        }
+    }
+    return buffer.pos();
+}
+
+uint32_t SkRegion::unflatten(const void* storage) {
+    SkRBuffer   buffer(storage);
+    SkRegion    tmp;
+    int32_t     count;
+    
+    count = buffer.readS32();
+    if (count >= 0) {
+        buffer.read(&tmp.fBounds, sizeof(tmp.fBounds));
+        if (count == 0) {
+            tmp.fRunHead = SkRegion_gRectRunHeadPtr;
+        } else {
+            tmp.allocateRuns(count);
+            buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
+        }
+    }
+    this->swap(tmp);
+    return buffer.pos();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+static const SkRegion::RunType* validate_line(const SkRegion::RunType run[], const SkIRect& bounds)
+{
+    // *run is the bottom of the current span
+    SkASSERT(*run > bounds.fTop);
+    SkASSERT(*run <= bounds.fBottom);
+    run += 1;
+
+    // check for empty span
+    if (*run != SkRegion::kRunTypeSentinel)
+    {
+        int prevRite = bounds.fLeft - 1;
+        do {
+            int left = *run++;
+            int rite = *run++;
+            SkASSERT(left < rite);
+            SkASSERT(left > prevRite);
+            SkASSERT(rite <= bounds.fRight);
+            prevRite = rite;
+        } while (*run < SkRegion::kRunTypeSentinel);
+    }
+    return run + 1; // skip sentinel
+}
+
+void SkRegion::validate() const
+{
+    if (this->isEmpty())
+    {
+        // check for explicit empty (the zero rect), so we can compare rects to know when
+        // two regions are equal (i.e. emptyRectA == emptyRectB)
+        // this is stricter than just asserting fBounds.isEmpty()
+        SkASSERT(fBounds.fLeft == 0 && fBounds.fTop == 0 && fBounds.fRight == 0 && fBounds.fBottom == 0);
+    }
+    else
+    {
+        SkASSERT(!fBounds.isEmpty());
+        if (!this->isRect())
+        {
+            SkASSERT(fRunHead->fRefCnt >= 1);
+            SkASSERT(fRunHead->fRunCount >= kRectRegionRuns);
+
+            const RunType* run = fRunHead->readonly_runs();
+            const RunType* stop = run + fRunHead->fRunCount;
+
+            // check that our bounds match our runs
+            {
+                SkIRect bounds;
+                bool isARect = ComputeRunBounds(run, stop - run, &bounds);
+                SkASSERT(!isARect);
+                SkASSERT(bounds == fBounds);
+            }
+
+            SkASSERT(*run == fBounds.fTop);
+            run++;
+            do {
+                run = validate_line(run, fBounds);
+            } while (*run < kRunTypeSentinel);
+            SkASSERT(run + 1 == stop);
+        }
+    }
+}
+
+void SkRegion::dump() const
+{
+    if (this->isEmpty())
+        SkDebugf("  rgn: empty\n");
+    else
+    {
+        SkDebugf("  rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
+        if (this->isComplex())
+        {
+            const RunType* runs = fRunHead->readonly_runs();
+            for (int i = 0; i < fRunHead->fRunCount; i++)
+                SkDebugf(" %d", runs[i]);
+        }
+        SkDebugf("\n");
+    }
+}
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+SkRegion::Iterator::Iterator(const SkRegion& rgn) {
+    this->reset(rgn);
+}
+
+bool SkRegion::Iterator::rewind() {
+    if (fRgn) {
+        this->reset(*fRgn);
+        return true;
+    }
+    return false;
+}
+
+void SkRegion::Iterator::reset(const SkRegion& rgn) {
+    fRgn = &rgn;
+    if (rgn.isEmpty()) {
+        fDone = true;
+    } else {
+        fDone = false;
+        if (rgn.isRect()) {
+            fRect = rgn.fBounds;
+            fRuns = NULL;
+        } else {
+            fRuns = rgn.fRunHead->readonly_runs();
+            fRect.set(fRuns[2], fRuns[0], fRuns[3], fRuns[1]);
+            fRuns += 4;
+        }
+    }
+}
+
+void SkRegion::Iterator::next() {
+    if (fDone) {
+        return;
+    }
+
+    if (fRuns == NULL) {   // rect case
+        fDone = true;
+        return;
+    }
+
+    const RunType* runs = fRuns;
+
+    if (runs[0] < kRunTypeSentinel) { // valid X value
+        fRect.fLeft = runs[0];
+        fRect.fRight = runs[1];
+        runs += 2;
+    } else {    // we're at the end of a line
+        runs += 1;
+        if (runs[0] < kRunTypeSentinel) { // valid Y value
+            if (runs[1] == kRunTypeSentinel) {    // empty line
+                fRect.fTop = runs[0];
+                runs += 2;
+            } else {
+                fRect.fTop = fRect.fBottom;
+            }
+    
+            fRect.fBottom = runs[0];
+            assert_sentinel(runs[1], false);
+            fRect.fLeft = runs[1];
+            fRect.fRight = runs[2];
+            runs += 3;
+        } else {    // end of rgn
+            fDone = true;
+        }
+    }
+    fRuns = runs;
+}
+
+SkRegion::Cliperator::Cliperator(const SkRegion& rgn, const SkIRect& clip)
+        : fIter(rgn), fClip(clip), fDone(true) {
+    const SkIRect& r = fIter.rect();
+
+    while (!fIter.done()) {
+        if (r.fTop >= clip.fBottom) {
+            break;
+        }
+        if (fRect.intersect(clip, r)) {
+            fDone = false;
+            break;
+        }
+        fIter.next();
+    }
+}
+
+void SkRegion::Cliperator::next() {
+    if (fDone) {
+        return;
+    }
+
+    const SkIRect& r = fIter.rect();
+
+    fDone = true;
+    fIter.next();
+    while (!fIter.done()) {
+        if (r.fTop >= fClip.fBottom) {
+            break;
+        }
+        if (fRect.intersect(fClip, r)) {
+            fDone = false;
+            break;
+        }
+        fIter.next();
+    }
+}
+
+//////////////////////////////////////////////////////////////////////
+
+SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left, int right)
+{
+    SkDEBUGCODE(rgn.validate();)
+
+    const SkIRect& r = rgn.getBounds();
+
+    fDone = true;
+    if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom && right > r.fLeft && left < r.fRight)
+    {
+        if (rgn.isRect())
+        {
+            if (left < r.fLeft)
+                left = r.fLeft;
+            if (right > r.fRight)
+                right = r.fRight;
+
+            fLeft = left;
+            fRight = right;
+            fRuns = NULL;    // means we're a rect, not a rgn
+            fDone = false;
+        }
+        else
+        {
+            const SkRegion::RunType* runs = find_y(rgn.fRunHead->readonly_runs(), y);
+            if (runs)
+            {
+                for (;;)
+                {
+                    if (runs[0] >= right)   // runs[0..1] is to the right of the span, so we're done
+                        break;
+                    if (runs[1] <= left)    // runs[0..1] is to the left of the span, so continue
+                    {
+                        runs += 2;
+                        continue;
+                    }
+                    // runs[0..1] intersects the span
+                    fRuns = runs;
+                    fLeft = left;
+                    fRight = right;
+                    fDone = false;
+                    break;
+                }
+            }
+        }
+    }
+}
+
+bool SkRegion::Spanerator::next(int* left, int* right)
+{
+    if (fDone) return false;
+
+    if (fRuns == NULL)   // we're a rect
+    {
+        fDone = true;   // ok, now we're done
+        if (left) *left = fLeft;
+        if (right) *right = fRight;
+        return true;    // this interval is legal
+    }
+
+    const SkRegion::RunType* runs = fRuns;
+
+    if (runs[0] >= fRight)
+    {
+        fDone = true;
+        return false;
+    }
+
+    SkASSERT(runs[1] > fLeft);
+
+    if (left)
+        *left = SkMax32(fLeft, runs[0]);
+    if (right)
+        *right = SkMin32(fRight, runs[1]);
+    fRuns = runs + 2;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+bool SkRegion::debugSetRuns(const RunType runs[], int count) {
+    // we need to make a copy, since the real method may modify the array, and
+    // so it cannot be const.
+    
+    SkAutoTArray<RunType> storage(count);
+    memcpy(storage.get(), runs, count * sizeof(RunType));
+    return this->setRuns(storage.get(), count);
+}
+
+#endif
+
+
diff --git a/src/core/SkRegionPriv.h b/src/core/SkRegionPriv.h
new file mode 100644
index 0000000..70f8828
--- /dev/null
+++ b/src/core/SkRegionPriv.h
@@ -0,0 +1,89 @@
+/* libs/corecg/SkRegionPriv.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 SkRegionPriv_DEFINED
+#define SkRegionPriv_DEFINED
+
+#include "SkRegion.h"
+#include "SkThread.h"
+
+#define assert_sentinel(value, isSentinel) \
+    SkASSERT(((value) == SkRegion::kRunTypeSentinel) == isSentinel)
+
+//SkDEBUGCODE(extern int32_t gRgnAllocCounter;)
+
+struct SkRegion::RunHead {
+    int32_t fRefCnt;
+    int32_t fRunCount;
+    
+    static RunHead* Alloc(int count)
+    {
+        //SkDEBUGCODE(sk_atomic_inc(&gRgnAllocCounter);)
+        //SkDEBUGF(("************** gRgnAllocCounter::alloc %d\n", gRgnAllocCounter));
+
+        SkASSERT(count >= SkRegion::kRectRegionRuns);
+
+        RunHead* head = (RunHead*)sk_malloc_throw(sizeof(RunHead) + count * sizeof(RunType));
+        head->fRefCnt = 1;
+        head->fRunCount = count;
+        return head;
+    }
+    
+    bool isComplex() const
+    {
+        return this != SkRegion_gEmptyRunHeadPtr && this != SkRegion_gRectRunHeadPtr;
+    }
+
+    SkRegion::RunType* writable_runs()
+    {
+        SkASSERT(this->isComplex());
+        SkASSERT(fRefCnt == 1);
+        return (SkRegion::RunType*)(this + 1);
+    }
+    const SkRegion::RunType* readonly_runs() const
+    {
+        SkASSERT(this->isComplex());
+        return (const SkRegion::RunType*)(this + 1);
+    }
+    
+    RunHead* ensureWritable()
+    {
+        SkASSERT(this->isComplex());
+        
+        RunHead* writable = this;
+        if (fRefCnt > 1)
+        {
+            // We need to alloc & copy the current region before we call
+            // sk_atomic_dec because it could be freed in the meantime,
+            // otherwise.            
+            writable = Alloc(fRunCount);
+            memcpy(writable->writable_runs(), this->readonly_runs(),
+                   fRunCount * sizeof(RunType));
+
+            // fRefCount might have changed since we last checked.
+            // If we own the last reference at this point, we need to
+            // free the memory.
+            if (sk_atomic_dec(&fRefCnt) == 1)
+            {
+                sk_free(this);
+            }
+        }
+        return writable;
+    }
+};
+
+#endif
diff --git a/src/core/SkRegion_path.cpp b/src/core/SkRegion_path.cpp
new file mode 100644
index 0000000..d00baf9
--- /dev/null
+++ b/src/core/SkRegion_path.cpp
@@ -0,0 +1,480 @@
+/* libs/graphics/sgl/SkRegion_path.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 "SkRegionPriv.h"
+#include "SkBlitter.h"
+#include "SkScan.h"
+#include "SkTDArray.h"
+#include "SkPath.h"
+
+class SkRgnBuilder : public SkBlitter {
+public:
+    virtual ~SkRgnBuilder();
+    
+    // returns true if it could allocate the working storage needed
+    bool init(int maxHeight, int maxTransitions);
+
+    void done() {
+        if (fCurrScanline != NULL) {
+            fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
+            if (!this->collapsWithPrev()) { // flush the last line
+                fCurrScanline = fCurrScanline->nextScanline();
+            }
+        }
+    }
+
+    int     computeRunCount() const;
+    void    copyToRect(SkIRect*) const;
+    void    copyToRgn(SkRegion::RunType runs[]) const;
+
+    virtual void blitH(int x, int y, int width);
+
+#ifdef SK_DEBUG
+    void dump() const {
+        SkDebugf("SkRgnBuilder: Top = %d\n", fTop);
+        const Scanline* line = (Scanline*)fStorage;
+        while (line < fCurrScanline) {
+            SkDebugf("SkRgnBuilder::Scanline: LastY=%d, fXCount=%d", line->fLastY, line->fXCount);
+            for (int i = 0; i < line->fXCount; i++) {
+                SkDebugf(" %d", line->firstX()[i]);
+            }
+            SkDebugf("\n");
+
+            line = line->nextScanline();
+        }
+    }
+#endif
+private:
+    struct Scanline {
+        SkRegion::RunType fLastY;
+        SkRegion::RunType fXCount;
+
+        SkRegion::RunType* firstX() const { return (SkRegion::RunType*)(this + 1); }
+        Scanline* nextScanline() const {
+            return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount);
+        }
+    };
+    SkRegion::RunType*  fStorage;
+    Scanline*           fCurrScanline;
+    Scanline*           fPrevScanline;
+    //  points at next avialable x[] in fCurrScanline
+    SkRegion::RunType*  fCurrXPtr;
+    SkRegion::RunType   fTop;           // first Y value
+    
+    int fStorageCount;
+
+    bool collapsWithPrev() {
+        if (fPrevScanline != NULL &&
+            fPrevScanline->fLastY + 1 == fCurrScanline->fLastY &&
+            fPrevScanline->fXCount == fCurrScanline->fXCount &&
+            !memcmp(fPrevScanline->firstX(),
+                    fCurrScanline->firstX(),
+                    fCurrScanline->fXCount * sizeof(SkRegion::RunType)))
+        {
+            // update the height of fPrevScanline
+            fPrevScanline->fLastY = fCurrScanline->fLastY;
+            return true;
+        }
+        return false;
+    }
+};
+
+SkRgnBuilder::~SkRgnBuilder() {
+    sk_free(fStorage);
+}
+
+bool SkRgnBuilder::init(int maxHeight, int maxTransitions) {
+    if ((maxHeight | maxTransitions) < 0) {
+        return false;
+    }
+
+    Sk64 count, size;
+
+    // compute the count with +1 and +3 slop for the working buffer
+    count.setMul(maxHeight + 1, 3 + maxTransitions);
+    if (!count.is32() || count.isNeg()) {
+        return false;
+    }
+    fStorageCount = count.get32();
+
+    size.setMul(fStorageCount, sizeof(SkRegion::RunType));
+    if (!size.is32() || size.isNeg()) {
+        return false;
+    }
+
+    fStorage = (SkRegion::RunType*)sk_malloc_flags(size.get32(), 0);
+    if (NULL == fStorage) {
+        return false;
+    }
+
+    fCurrScanline = NULL;    // signal empty collection
+    fPrevScanline = NULL;    // signal first scanline
+    return true;
+}
+
+void SkRgnBuilder::blitH(int x, int y, int width) {
+    if (fCurrScanline == NULL) {  // first time
+        fTop = (SkRegion::RunType)(y);
+        fCurrScanline = (Scanline*)fStorage;
+        fCurrScanline->fLastY = (SkRegion::RunType)(y);
+        fCurrXPtr = fCurrScanline->firstX();
+    } else {
+        SkASSERT(y >= fCurrScanline->fLastY);
+
+        if (y > fCurrScanline->fLastY) {
+            // if we get here, we're done with fCurrScanline
+            fCurrScanline->fXCount = (SkRegion::RunType)((int)(fCurrXPtr - fCurrScanline->firstX()));
+
+            int prevLastY = fCurrScanline->fLastY;
+            if (!this->collapsWithPrev()) {
+                fPrevScanline = fCurrScanline;
+                fCurrScanline = fCurrScanline->nextScanline();
+
+            }
+            if (y - 1 > prevLastY) {  // insert empty run
+                fCurrScanline->fLastY = (SkRegion::RunType)(y - 1);
+                fCurrScanline->fXCount = 0;
+                fCurrScanline = fCurrScanline->nextScanline();
+            }
+            // setup for the new curr line
+            fCurrScanline->fLastY = (SkRegion::RunType)(y);
+            fCurrXPtr = fCurrScanline->firstX();
+        }
+    }
+    //  check if we should extend the current run, or add a new one
+    if (fCurrXPtr > fCurrScanline->firstX() && fCurrXPtr[-1] == x) {
+        fCurrXPtr[-1] = (SkRegion::RunType)(x + width);
+    } else {
+        fCurrXPtr[0] = (SkRegion::RunType)(x);
+        fCurrXPtr[1] = (SkRegion::RunType)(x + width);
+        fCurrXPtr += 2;
+    }
+    SkASSERT(fCurrXPtr - fStorage < fStorageCount);
+}
+
+int SkRgnBuilder::computeRunCount() const {
+    if (fCurrScanline == NULL) {
+        return 0;
+    }
+
+    const SkRegion::RunType*  line = fStorage;
+    const SkRegion::RunType*  stop = (const SkRegion::RunType*)fCurrScanline;
+
+    return 2 + (int)(stop - line);
+}
+
+void SkRgnBuilder::copyToRect(SkIRect* r) const {
+    SkASSERT(fCurrScanline != NULL);
+    SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage == 4);
+
+    const Scanline* line = (const Scanline*)fStorage;
+    SkASSERT(line->fXCount == 2);
+
+    r->set(line->firstX()[0], fTop, line->firstX()[1], line->fLastY + 1);
+}
+
+void SkRgnBuilder::copyToRgn(SkRegion::RunType runs[]) const {
+    SkASSERT(fCurrScanline != NULL);
+    SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage > 4);
+
+    const Scanline* line = (const Scanline*)fStorage;
+    const Scanline* stop = fCurrScanline;
+
+    *runs++ = fTop;
+    do {
+        *runs++ = (SkRegion::RunType)(line->fLastY + 1);
+        int count = line->fXCount;
+        if (count) {
+            memcpy(runs, line->firstX(), count * sizeof(SkRegion::RunType));
+            runs += count;
+        }
+        *runs++ = SkRegion::kRunTypeSentinel;
+        line = line->nextScanline();
+    } while (line < stop);
+    SkASSERT(line == stop);
+    *runs = SkRegion::kRunTypeSentinel;
+}
+
+static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
+    static const uint8_t gPathVerbToInitialLastIndex[] = {
+        0,  //  kMove_Verb
+        1,  //  kLine_Verb
+        2,  //  kQuad_Verb
+        3,  //  kCubic_Verb
+        0,  //  kClose_Verb
+        0   //  kDone_Verb
+    };
+
+    static const uint8_t gPathVerbToMaxEdges[] = {
+        0,  //  kMove_Verb
+        1,  //  kLine_Verb
+        2,  //  kQuad_VerbB
+        3,  //  kCubic_Verb
+        0,  //  kClose_Verb
+        0   //  kDone_Verb
+    };
+
+    SkPath::Iter    iter(path, true);
+    SkPoint         pts[4];
+    SkPath::Verb    verb;
+
+    int maxEdges = 0;
+    SkScalar    top = SkIntToScalar(SK_MaxS16);
+    SkScalar    bot = SkIntToScalar(SK_MinS16);
+
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        maxEdges += gPathVerbToMaxEdges[verb];
+
+        int lastIndex = gPathVerbToInitialLastIndex[verb];
+        if (lastIndex > 0) {
+            for (int i = 1; i <= lastIndex; i++) {
+                if (top > pts[i].fY) {
+                    top = pts[i].fY;
+                } else if (bot < pts[i].fY) {
+                    bot = pts[i].fY;
+                }
+            }
+        } else if (SkPath::kMove_Verb == verb) {
+            if (top > pts[0].fY) {
+                top = pts[0].fY;
+            } else if (bot < pts[0].fY) {
+                bot = pts[0].fY;
+            }
+        }
+    }
+    SkASSERT(top <= bot);
+
+    *itop = SkScalarRound(top);
+    *ibot = SkScalarRound(bot);
+    return maxEdges;
+}
+
+bool SkRegion::setPath(const SkPath& path, const SkRegion& clip) {
+    SkDEBUGCODE(this->validate();)
+
+    if (clip.isEmpty()) {
+        return this->setEmpty();
+    }
+
+    if (path.isEmpty()) {
+        if (path.isInverseFillType()) {
+            return this->set(clip);
+        } else {
+            return this->setEmpty();
+        }
+    }
+
+    //  compute worst-case rgn-size for the path
+    int pathTop, pathBot;
+    int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot);
+    int clipTop, clipBot;
+    int clipTransitions;
+    
+    clipTransitions = clip.count_runtype_values(&clipTop, &clipBot);
+
+    int top = SkMax32(pathTop, clipTop);
+    int bot = SkMin32(pathBot, clipBot);
+
+    if (top >= bot)
+        return this->setEmpty();
+
+    SkRgnBuilder builder;
+    
+    if (!builder.init(bot - top, SkMax32(pathTransitions, clipTransitions))) {
+        // can't allocate working space, so return false
+        return this->setEmpty();
+    }
+
+    SkScan::FillPath(path, clip, &builder);
+    builder.done();
+
+    int count = builder.computeRunCount();
+    if (count == 0) {
+        return this->setEmpty();
+    } else if (count == kRectRegionRuns) {
+        builder.copyToRect(&fBounds);
+        this->setRect(fBounds);
+    } else {
+        SkRegion    tmp;
+
+        tmp.fRunHead = RunHead::Alloc(count);
+        builder.copyToRgn(tmp.fRunHead->writable_runs());
+        ComputeRunBounds(tmp.fRunHead->readonly_runs(), count, &tmp.fBounds);
+        this->swap(tmp);
+    }
+    SkDEBUGCODE(this->validate();)
+    return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct Edge {
+    enum {
+        kY0Link = 0x01,
+        kY1Link = 0x02,
+        
+        kCompleteLink = (kY0Link | kY1Link)
+    };
+
+    SkRegion::RunType fX;
+    SkRegion::RunType fY0, fY1;
+    uint8_t fFlags;
+    Edge*   fNext;
+    
+    void set(int x, int y0, int y1) {
+        SkASSERT(y0 != y1);
+
+        fX = (SkRegion::RunType)(x);
+        fY0 = (SkRegion::RunType)(y0);
+        fY1 = (SkRegion::RunType)(y1);
+        fFlags = 0;
+        SkDEBUGCODE(fNext = NULL;)
+    }
+    
+    int top() const {
+        return SkFastMin32(fY0, fY1);
+    }
+};
+
+static void find_link(Edge* base, Edge* stop) {
+    SkASSERT(base < stop);
+
+    if (base->fFlags == Edge::kCompleteLink) {
+        SkASSERT(base->fNext);
+        return;
+    }
+
+    SkASSERT(base + 1 < stop);
+
+    int y0 = base->fY0;
+    int y1 = base->fY1;
+
+    Edge* e = base;
+    if ((base->fFlags & Edge::kY0Link) == 0) {
+        for (;;) {
+            e += 1;
+            if ((e->fFlags & Edge::kY1Link) == 0 && y0 == e->fY1) {
+                SkASSERT(NULL == e->fNext);
+                e->fNext = base;
+                e->fFlags = SkToU8(e->fFlags | Edge::kY1Link);
+                break;
+            }
+        }
+    }
+    
+    e = base;
+    if ((base->fFlags & Edge::kY1Link) == 0) {
+        for (;;) {
+            e += 1;
+            if ((e->fFlags & Edge::kY0Link) == 0 && y1 == e->fY0) {
+                SkASSERT(NULL == base->fNext);
+                base->fNext = e;
+                e->fFlags = SkToU8(e->fFlags | Edge::kY0Link);
+                break;
+            }
+        }
+    }
+        
+    base->fFlags = Edge::kCompleteLink;
+}
+
+static int extract_path(Edge* edge, Edge* stop, SkPath* path) {
+    while (0 == edge->fFlags) {
+        edge++; // skip over "used" edges
+    }
+
+    SkASSERT(edge < stop);
+
+    Edge* base = edge;
+    Edge* prev = edge;
+    edge = edge->fNext;
+    SkASSERT(edge != base);
+
+    int count = 1;
+    path->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0));
+    prev->fFlags = 0;
+    do {
+        if (prev->fX != edge->fX || prev->fY1 != edge->fY0) { // skip collinear
+            path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1));    // V
+            path->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0));    // H
+        }
+        prev = edge;
+        edge = edge->fNext;
+        count += 1;
+        prev->fFlags = 0;
+    } while (edge != base);
+    path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1));    // V
+    path->close();
+    return count;
+}
+
+#include "SkTSearch.h"
+
+static int EdgeProc(const Edge* a, const Edge* b) {
+    return (a->fX == b->fX) ? a->top() - b->top() : a->fX - b->fX;
+}
+
+bool SkRegion::getBoundaryPath(SkPath* path) const {
+    if (this->isEmpty()) {
+        return false;
+    }
+
+    const SkIRect& bounds = this->getBounds();
+
+    if (this->isRect()) {
+        SkRect  r;        
+        r.set(bounds);      // this converts the ints to scalars
+        path->addRect(r);
+        return true;
+    }
+
+    SkRegion::Iterator  iter(*this);
+    SkTDArray<Edge>     edges;
+    
+    for (const SkIRect& r = iter.rect(); !iter.done(); iter.next()) {
+        Edge* edge = edges.append(2);
+        edge[0].set(r.fLeft, r.fBottom, r.fTop);
+        edge[1].set(r.fRight, r.fTop, r.fBottom);
+    }
+    SkQSort(edges.begin(), edges.count(), sizeof(Edge), (SkQSortCompareProc)EdgeProc);
+    
+    int count = edges.count();
+    Edge* start = edges.begin();
+    Edge* stop = start + count;
+    Edge* e;
+
+    for (e = start; e != stop; e++) {
+        find_link(e, stop);
+    }
+
+#ifdef SK_DEBUG
+    for (e = start; e != stop; e++) {
+        SkASSERT(e->fNext != NULL);
+        SkASSERT(e->fFlags == Edge::kCompleteLink);
+    }
+#endif
+
+    path->incReserve(count << 1);
+    do {
+        SkASSERT(count > 1);
+        count -= extract_path(start, stop, path);
+    } while (count > 0);
+
+    return true;
+}
+
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
new file mode 100644
index 0000000..854c4de
--- /dev/null
+++ b/src/core/SkScalerContext.cpp
@@ -0,0 +1,541 @@
+/* libs/graphics/sgl/SkScalerContext.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 "SkScalerContext.h"
+#include "SkDescriptor.h"
+#include "SkDraw.h"
+#include "SkFontHost.h"
+#include "SkMaskFilter.h"
+#include "SkPathEffect.h"
+#include "SkRasterizer.h"
+#include "SkRegion.h"
+#include "SkStroke.h"
+#include "SkThread.h"
+
+#ifdef SK_DEBUG
+//    #define TRACK_MISSING_CHARS
+#endif
+
+#define ComputeBWRowBytes(width)        (((unsigned)(width) + 7) >> 3)
+
+static const uint8_t* gBlackGammaTable;
+static const uint8_t* gWhiteGammaTable;
+
+void SkGlyph::toMask(SkMask* mask) const {
+    SkASSERT(mask);
+
+    mask->fImage = (uint8_t*)fImage;
+    mask->fBounds.set(fLeft, fTop, fLeft + fWidth, fTop + fHeight);
+    mask->fRowBytes = this->rowBytes();
+    mask->fFormat = fMaskFormat;
+}
+
+size_t SkGlyph::computeImageSize() const {
+    size_t size = this->rowBytes() * fHeight;
+    if (fMaskFormat == SkMask::k3D_Format) {
+        size *= 3;
+    }
+    return size;
+}
+
+#ifdef SK_DEBUG
+    #define DUMP_RECx
+#endif
+
+static SkFlattenable* load_flattenable(const SkDescriptor* desc, uint32_t tag) {
+    SkFlattenable*  obj = NULL;
+    uint32_t        len;
+    const void*     data = desc->findEntry(tag, &len);
+
+    if (data) {
+        SkFlattenableReadBuffer   buffer(data, len);
+        obj = buffer.readFlattenable();
+        SkASSERT(buffer.offset() == buffer.size());
+    }
+    return obj;
+}
+
+SkScalerContext::SkScalerContext(const SkDescriptor* desc)
+    : fPathEffect(NULL), fMaskFilter(NULL)
+{
+    static bool gHaveGammaTables;
+    if (!gHaveGammaTables) {
+        const uint8_t* tables[2];
+        SkFontHost::GetGammaTables(tables);
+        gBlackGammaTable = tables[0];
+        gWhiteGammaTable = tables[1];
+        gHaveGammaTables = true;
+    }
+
+    fBaseGlyphCount = 0;
+    fAuxScalerContext = NULL;
+
+    const Rec* rec = (const Rec*)desc->findEntry(kRec_SkDescriptorTag, NULL);
+    SkASSERT(rec);
+
+    fRec = *rec;
+
+#ifdef DUMP_REC
+    desc->assertChecksum();
+    SkDebugf("SkScalarContext checksum %x count %d length %d\n", desc->getChecksum(), desc->getCount(), desc->getLength());
+    SkDebugf(" textsize %g prescale %g preskew %g post [%g %g %g %g]\n",
+        rec->fTextSize, rec->fPreScaleX, rec->fPreSkewX, rec->fPost2x2[0][0],
+        rec->fPost2x2[0][1], rec->fPost2x2[1][0], rec->fPost2x2[1][1]);
+    SkDebugf("  frame %g miter %g hints %d framefill %d format %d join %d\n",
+        rec->fFrameWidth, rec->fMiterLimit, rec->fHints, rec->fFrameAndFill,
+        rec->fMaskFormat, rec->fStrokeJoin);
+    SkDebugf("  pathEffect %x maskFilter %x\n", desc->findEntry(kPathEffect_SkDescriptorTag, NULL),
+        desc->findEntry(kMaskFilter_SkDescriptorTag, NULL));
+#endif
+
+    fPathEffect = (SkPathEffect*)load_flattenable(desc, kPathEffect_SkDescriptorTag);
+    fMaskFilter = (SkMaskFilter*)load_flattenable(desc, kMaskFilter_SkDescriptorTag);
+    fRasterizer = (SkRasterizer*)load_flattenable(desc, kRasterizer_SkDescriptorTag);
+}
+
+SkScalerContext::~SkScalerContext() {
+    fPathEffect->safeUnref();
+    fMaskFilter->safeUnref();
+    fRasterizer->safeUnref();
+
+    SkDELETE(fAuxScalerContext);
+}
+
+SkScalerContext* SkScalerContext::loadAuxContext() const {
+    if (NULL == fAuxScalerContext) {
+        fAuxScalerContext = SkFontHost::CreateFallbackScalerContext(fRec);
+        if (NULL != fAuxScalerContext) {
+            fAuxScalerContext->setBaseGlyphCount(this->getGlyphCount());
+        }
+    }
+    return fAuxScalerContext;
+}
+
+#ifdef TRACK_MISSING_CHARS
+    static uint8_t gMissingChars[1 << 13];
+#endif
+
+uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) {
+    unsigned glyphID = this->generateCharToGlyph(uni);
+
+    if (0 == glyphID) {   // try auxcontext
+        SkScalerContext* ctx = this->loadAuxContext();
+        if (NULL != ctx) {
+            glyphID = ctx->generateCharToGlyph(uni);
+            if (0 != glyphID) {   // only fiddle with it if its not missing
+                glyphID += this->getGlyphCount();
+                if (glyphID > 0xFFFF) {
+                    glyphID = 0;
+                }
+            }
+        }
+    }
+#ifdef TRACK_MISSING_CHARS
+    if (0 == glyphID) {
+        bool announce = false;
+        if (uni > 0xFFFF) {   // we don't record these
+            announce = true;
+        } else {
+            unsigned index = uni >> 3;
+            unsigned mask = 1 << (uni & 7);
+            SkASSERT(index < SK_ARRAY_COUNT(gMissingChars));
+            if ((gMissingChars[index] & mask) == 0) {
+                gMissingChars[index] |= mask;
+                announce = true;
+            }
+        }
+        if (announce) {
+            printf(">>> MISSING CHAR <<< 0x%04X\n", uni);
+        }
+    }
+#endif
+    return SkToU16(glyphID);
+}
+
+/*  Internal routine to resolve auxContextID into a real context.
+    Only makes sense to call once the glyph has been given a
+    valid auxGlyphID.
+*/
+SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) const {
+    SkScalerContext* ctx = const_cast<SkScalerContext*>(this);
+    
+    if (glyph.getGlyphID() >= this->getGlyphCount()) {
+        ctx = this->loadAuxContext();
+        if (NULL == ctx) {    // if no aux, just return us
+            ctx = const_cast<SkScalerContext*>(this);
+        }
+    }
+    return ctx;
+}
+
+static int plus_minus_pin(int value, int max) {
+    SkASSERT(max >= 0);
+    
+    if (value > max) {
+        value = max;
+    } else if (value < -max) {
+        value = -max;
+    }
+    return value;
+}    
+
+void SkScalerContext::getAdvance(SkGlyph* glyph) {
+    // mark us as just having a valid advance
+    glyph->fMaskFormat = MASK_FORMAT_JUST_ADVANCE;
+    // we mark the format before making the call, in case the impl
+    // internally ends up calling its generateMetrics, which is OK
+    // albeit slower than strictly necessary
+    this->getGlyphContext(*glyph)->generateAdvance(glyph);
+}
+
+void SkScalerContext::getMetrics(SkGlyph* glyph) {
+    this->getGlyphContext(*glyph)->generateMetrics(glyph);
+
+    // for now we have separate cache entries for devkerning on and off
+    // in the future we might share caches, but make our measure/draw
+    // code make the distinction. Thus we zap the values if the caller
+    // has not asked for them.
+    if ((fRec.fFlags & SkScalerContext::kDevKernText_Flag) == 0) {
+        // no devkern, so zap the fields
+        glyph->fLsbDelta = glyph->fRsbDelta = 0;
+    }
+
+    // if either dimension is empty, zap the image bounds of the glyph
+    if (0 == glyph->fWidth || 0 == glyph->fHeight) {
+        glyph->fWidth   = 0;
+        glyph->fHeight  = 0;
+        glyph->fTop     = 0;
+        glyph->fLeft    = 0;
+        glyph->fMaskFormat = 0;
+        return;
+    }
+    
+    if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) {
+        SkPath      devPath, fillPath;
+        SkMatrix    fillToDevMatrix;
+
+        this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
+
+        if (fRasterizer) {
+            SkMask  mask;
+
+            if (fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
+                                       fMaskFilter, &mask,
+                                       SkMask::kJustComputeBounds_CreateMode)) {
+                glyph->fLeft    = mask.fBounds.fLeft;
+                glyph->fTop     = mask.fBounds.fTop;
+                glyph->fWidth   = SkToU16(mask.fBounds.width());
+                glyph->fHeight  = SkToU16(mask.fBounds.height());
+            } else {
+                // draw nothing 'cause we failed
+                glyph->fLeft    = 0;
+                glyph->fTop     = 0;
+                glyph->fWidth   = 0;
+                glyph->fHeight  = 0;
+                return;
+            }
+        } else {
+            // just use devPath
+            SkRect  r;
+            SkIRect ir;
+
+            devPath.computeBounds(&r, SkPath::kExact_BoundsType);
+            r.roundOut(&ir);
+            
+            glyph->fLeft    = ir.fLeft;
+            glyph->fTop     = ir.fTop;
+            glyph->fWidth   = SkToU16(ir.width());
+            glyph->fHeight  = SkToU16(ir.height());
+        }
+    }
+
+    glyph->fMaskFormat = fRec.fMaskFormat;
+
+    if (fMaskFilter) {
+        SkMask      src, dst;
+        SkMatrix    matrix;
+
+        glyph->toMask(&src);
+        fRec.getMatrixFrom2x2(&matrix);
+
+        src.fImage = NULL;  // only want the bounds from the filter
+        if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) {
+            SkASSERT(dst.fImage == NULL);
+            glyph->fLeft    = dst.fBounds.fLeft;
+            glyph->fTop     = dst.fBounds.fTop;
+            glyph->fWidth   = SkToU16(dst.fBounds.width());
+            glyph->fHeight  = SkToU16(dst.fBounds.height());
+            glyph->fMaskFormat = dst.fFormat;
+        }
+    }
+}
+
+void SkScalerContext::getImage(const SkGlyph& origGlyph) {
+    const SkGlyph*  glyph = &origGlyph;
+    SkGlyph         tmpGlyph;
+
+    if (fMaskFilter) {   // restore the prefilter bounds
+        tmpGlyph.fID = origGlyph.fID;
+
+        // need the original bounds, sans our maskfilter
+        SkMaskFilter* mf = fMaskFilter;
+        fMaskFilter = NULL;             // temp disable
+        this->getMetrics(&tmpGlyph);
+        fMaskFilter = mf;               // restore
+
+        tmpGlyph.fImage = origGlyph.fImage;
+
+        // we need the prefilter bounds to be <= filter bounds
+        SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth);
+        SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight);
+        glyph = &tmpGlyph;
+    }
+
+    if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) {
+        SkPath      devPath, fillPath;
+        SkMatrix    fillToDevMatrix;
+
+        this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
+
+        if (fRasterizer) {
+            SkMask  mask;
+            
+            glyph->toMask(&mask);
+            mask.fFormat = SkMask::kA8_Format;
+            bzero(glyph->fImage, mask.computeImageSize());
+            
+            if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
+                                        fMaskFilter, &mask,
+                                        SkMask::kJustRenderImage_CreateMode)) {
+                return;
+            }
+        } else {
+            SkBitmap    bm;
+            SkBitmap::Config config;
+            SkMatrix    matrix;
+            SkRegion    clip;
+            SkPaint     paint;
+            SkDraw      draw;
+
+            if (SkMask::kA8_Format == fRec.fMaskFormat) {
+                config = SkBitmap::kA8_Config;
+                paint.setAntiAlias(true);
+            } else {
+                SkASSERT(SkMask::kBW_Format == fRec.fMaskFormat);
+                config = SkBitmap::kA1_Config;
+                paint.setAntiAlias(false);
+            }
+
+            clip.setRect(0, 0, glyph->fWidth, glyph->fHeight);
+            matrix.setTranslate(-SkIntToScalar(glyph->fLeft),
+                                -SkIntToScalar(glyph->fTop));
+            bm.setConfig(config, glyph->fWidth, glyph->fHeight,
+                         glyph->rowBytes());
+            bm.setPixels(glyph->fImage);
+            bzero(glyph->fImage, bm.height() * bm.rowBytes());
+
+            draw.fClip  = &clip;
+            draw.fMatrix = &matrix;
+            draw.fBitmap = &bm;
+            draw.fBounder = NULL;
+            draw.drawPath(devPath, paint);
+        }
+    } else {
+        this->getGlyphContext(*glyph)->generateImage(*glyph);
+    }
+
+    if (fMaskFilter) {
+        SkMask      srcM, dstM;
+        SkMatrix    matrix;
+
+        // the src glyph image shouldn't be 3D
+        SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
+        glyph->toMask(&srcM);
+        fRec.getMatrixFrom2x2(&matrix);
+
+        if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) {
+            int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width());
+            int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height());
+            int dstRB = origGlyph.rowBytes();
+            int srcRB = dstM.fRowBytes;
+            
+            const uint8_t* src = (const uint8_t*)dstM.fImage;
+            uint8_t* dst = (uint8_t*)origGlyph.fImage;
+
+            if (SkMask::k3D_Format == dstM.fFormat) {
+                // we have to copy 3 times as much
+                height *= 3;
+            }
+
+            // clean out our glyph, since it may be larger than dstM
+            //bzero(dst, height * dstRB);
+
+            while (--height >= 0) {
+                memcpy(dst, src, width);
+                src += srcRB;
+                dst += dstRB;
+            }
+            SkMask::FreeImage(dstM.fImage);
+        }
+    }
+    
+    // check to see if we should filter the alpha channel
+
+    if (NULL == fMaskFilter &&
+        fRec.fMaskFormat != SkMask::kBW_Format &&
+        (fRec.fFlags & (kGammaForBlack_Flag | kGammaForWhite_Flag)) != 0)
+    {
+        const uint8_t* table = (fRec.fFlags & kGammaForBlack_Flag) ? gBlackGammaTable : gWhiteGammaTable;
+        if (NULL != table)
+        {
+            uint8_t* dst = (uint8_t*)origGlyph.fImage;
+            unsigned rowBytes = origGlyph.rowBytes();
+            
+            for (int y = origGlyph.fHeight - 1; y >= 0; --y)
+            {
+                for (int x = origGlyph.fWidth - 1; x >= 0; --x)
+                    dst[x] = table[dst[x]];
+                dst += rowBytes;
+            }
+        }
+    }
+}
+
+void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path)
+{
+    this->internalGetPath(glyph, NULL, path, NULL);
+}
+
+void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my)
+{
+    this->generateFontMetrics(mx, my);
+}
+
+///////////////////////////////////////////////////////////////////////
+
+void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix)
+{
+    SkPath  path;
+
+    this->getGlyphContext(glyph)->generatePath(glyph, &path);
+    
+    if (fRec.fFrameWidth > 0 || fPathEffect != NULL)
+    {
+        // need the path in user-space, with only the point-size applied
+        // so that our stroking and effects will operate the same way they
+        // would if the user had extracted the path themself, and then
+        // called drawPath
+        SkPath      localPath;
+        SkMatrix    matrix, inverse;
+
+        fRec.getMatrixFrom2x2(&matrix);
+        matrix.invert(&inverse);
+        path.transform(inverse, &localPath);
+        // now localPath is only affected by the paint settings, and not the canvas matrix
+
+        SkScalar width = fRec.fFrameWidth;
+
+        if (fPathEffect)
+        {
+            SkPath effectPath;
+
+            if (fPathEffect->filterPath(&effectPath, localPath, &width))
+                localPath.swap(effectPath);
+        }
+
+        if (width > 0)
+        {
+            SkStroke    stroker;
+            SkPath      outline;
+
+            stroker.setWidth(width);
+            stroker.setMiterLimit(fRec.fMiterLimit);
+            stroker.setJoin((SkPaint::Join)fRec.fStrokeJoin);
+            stroker.setDoFill(SkToBool(fRec.fFlags & kFrameAndFill_Flag));
+            stroker.strokePath(localPath, &outline);
+            localPath.swap(outline);
+        }
+        
+        // now return stuff to the caller
+        if (fillToDevMatrix)
+            *fillToDevMatrix = matrix;
+        
+        if (devPath)
+            localPath.transform(matrix, devPath);
+        
+        if (fillPath)
+            fillPath->swap(localPath);
+    }
+    else    // nothing tricky to do
+    {
+        if (fillToDevMatrix)
+            fillToDevMatrix->reset();
+        
+        if (devPath)
+        {
+            if (fillPath == NULL)
+                devPath->swap(path);
+            else
+                *devPath = path;
+        }
+        
+        if (fillPath)
+            fillPath->swap(path);
+    }
+    
+    if (devPath)
+        devPath->updateBoundsCache();
+    if (fillPath)
+        fillPath->updateBoundsCache();
+}
+
+
+void SkScalerContext::Rec::getMatrixFrom2x2(SkMatrix* dst) const
+{
+    dst->reset();
+    dst->setScaleX(fPost2x2[0][0]);
+    dst->setSkewX( fPost2x2[0][1]);
+    dst->setSkewY( fPost2x2[1][0]);
+    dst->setScaleY(fPost2x2[1][1]);
+}
+
+void SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const
+{
+    m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize);
+    if (fPreSkewX)
+        m->postSkew(fPreSkewX, 0);
+}
+
+void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const
+{
+    this->getLocalMatrix(m);
+
+    //  now concat the device matrix
+    {
+        SkMatrix    deviceMatrix;
+        this->getMatrixFrom2x2(&deviceMatrix);
+        m->postConcat(deviceMatrix);
+    }
+}
+
+#include "SkFontHost.h"
+
+SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc)
+{
+    return SkFontHost::CreateScalerContext(desc);
+}
+
diff --git a/src/core/SkScan.cpp b/src/core/SkScan.cpp
new file mode 100644
index 0000000..013b0ea
--- /dev/null
+++ b/src/core/SkScan.cpp
@@ -0,0 +1,75 @@
+/* libs/graphics/sgl/SkScan.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 "SkScan.h"
+#include "SkBlitter.h"
+#include "SkRegion.h"
+
+static inline void blitrect(SkBlitter* blitter, const SkIRect& r) {
+    blitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
+}
+
+void SkScan::FillIRect(const SkIRect& r, const SkRegion* clip,
+                       SkBlitter* blitter) {
+    if (!r.isEmpty()) {
+        if (clip) {
+            if (clip->isRect()) {
+                const SkIRect& clipBounds = clip->getBounds();
+                
+                if (clipBounds.contains(r)) {
+                    blitrect(blitter, r);
+                } else {
+                    SkIRect rr = r;
+                    if (rr.intersect(clipBounds)) {
+                        blitrect(blitter, rr);
+                    }
+                }
+            } else {
+                SkRegion::Cliperator    cliper(*clip, r);
+                const SkIRect&          rr = cliper.rect();
+                
+                while (!cliper.done()) {
+                    blitrect(blitter, rr);
+                    cliper.next();
+                }
+            }
+        } else {
+            blitrect(blitter, r);
+        }
+    }
+}
+
+void SkScan::FillXRect(const SkXRect& xr, const SkRegion* clip,
+                       SkBlitter* blitter) {
+    SkIRect r;
+    
+    XRect_round(xr, &r);
+    SkScan::FillIRect(r, clip, blitter);
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+void SkScan::FillRect(const SkRect& r, const SkRegion* clip,
+                       SkBlitter* blitter) {
+    SkIRect ir;
+    
+    r.round(&ir);
+    SkScan::FillIRect(ir, clip, blitter);
+}
+
+#endif
+
diff --git a/src/core/SkScan.h b/src/core/SkScan.h
new file mode 100644
index 0000000..379c016
--- /dev/null
+++ b/src/core/SkScan.h
@@ -0,0 +1,123 @@
+/* libs/graphics/sgl/SkScan.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 SkScan_DEFINED
+#define SkScan_DEFINED
+
+#include "SkRect.h"
+
+class SkRegion;
+class SkBlitter;
+class SkPath;
+
+/** Defines a fixed-point rectangle, identical to the integer SkIRect, but its
+    coordinates are treated as SkFixed rather than int32_t.
+*/
+typedef SkIRect SkXRect;
+
+class SkScan {
+public:
+    static void FillIRect(const SkIRect&, const SkRegion* clip, SkBlitter*);
+    static void FillXRect(const SkXRect&, const SkRegion* clip, SkBlitter*);
+
+#ifdef SK_SCALAR_IS_FIXED
+    static void FillRect(const SkRect& rect, const SkRegion* clip,
+                         SkBlitter* blitter) {
+        SkScan::FillXRect(*(const SkXRect*)&rect, clip, blitter);
+    }
+#else
+    static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*);
+#endif
+
+    static void FillTriangle(const SkPoint pts[], const SkRegion*, SkBlitter*);
+    static void FillPath(const SkPath&, const SkRegion& clip, SkBlitter*);
+
+    static void FillTriangle(const SkPoint& a, const SkPoint& b,
+                             const SkPoint& c, const SkRegion* clip,
+                             SkBlitter* blitter) {
+        SkPoint pts[3];
+        pts[0] = a;
+        pts[1] = b;
+        pts[2] = c;
+        FillTriangle(pts, clip, blitter);
+    }
+
+    static void HairLine(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*);
+    static void HairRect(const SkRect&, const SkRegion* clip, SkBlitter*);
+    static void HairPath(const SkPath&, const SkRegion* clip, SkBlitter*);
+
+    static void FrameRect(const SkRect&, SkScalar width, const SkRegion* clip, SkBlitter*);
+
+    static void AntiFillXRect(const SkXRect&, const SkRegion* clip, SkBlitter*);
+#ifdef SK_SCALAR_IS_FIXED
+    static void AntiFillRect(const SkRect& rect, const SkRegion* clip,
+                         SkBlitter* blitter) {
+        SkScan::AntiFillXRect(*(const SkXRect*)&rect, clip, blitter);
+    }
+#else
+    static void AntiFillRect(const SkRect&, const SkRegion* clip, SkBlitter*);
+#endif
+    
+    static void AntiFillPath(const SkPath&, const SkRegion& clip, SkBlitter*);
+
+    static void AntiHairLine(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*);
+    static void AntiHairRect(const SkRect&, const SkRegion* clip, SkBlitter*);
+    static void AntiHairPath(const SkPath&, const SkRegion* clip, SkBlitter*);
+};
+
+/** Assign an SkXRect from a SkIRect, by promoting the src rect's coordinates
+    from int to SkFixed. Does not check for overflow if the src coordinates
+    exceed 32K
+*/
+static void XRect_set(SkXRect* xr, const SkIRect& src) {
+    xr->fLeft = SkIntToFixed(src.fLeft);
+    xr->fTop = SkIntToFixed(src.fTop);
+    xr->fRight = SkIntToFixed(src.fRight);
+    xr->fBottom = SkIntToFixed(src.fBottom);
+}
+
+/** Assign an SkXRect from a SkRect, by promoting the src rect's coordinates
+    from SkScalar to SkFixed. Does not check for overflow if the src coordinates
+    exceed 32K
+*/
+static void XRect_set(SkXRect* xr, const SkRect& src) {
+    xr->fLeft = SkScalarToFixed(src.fLeft);
+    xr->fTop = SkScalarToFixed(src.fTop);
+    xr->fRight = SkScalarToFixed(src.fRight);
+    xr->fBottom = SkScalarToFixed(src.fBottom);
+}
+
+/** Round the SkXRect coordinates, and store the result in the SkIRect.
+*/
+static void XRect_round(const SkXRect& xr, SkIRect* dst) {
+    dst->fLeft = SkFixedRound(xr.fLeft);
+    dst->fTop = SkFixedRound(xr.fTop);
+    dst->fRight = SkFixedRound(xr.fRight);
+    dst->fBottom = SkFixedRound(xr.fBottom);
+}
+
+/** Round the SkXRect coordinates out (i.e. use floor for left/top, and ceiling
+    for right/bottom), and store the result in the SkIRect.
+*/
+static void XRect_roundOut(const SkXRect& xr, SkIRect* dst) {
+    dst->fLeft = SkFixedFloor(xr.fLeft);
+    dst->fTop = SkFixedFloor(xr.fTop);
+    dst->fRight = SkFixedCeil(xr.fRight);
+    dst->fBottom = SkFixedCeil(xr.fBottom);
+}
+
+#endif
diff --git a/src/core/SkScanPriv.h b/src/core/SkScanPriv.h
new file mode 100644
index 0000000..43dfdef
--- /dev/null
+++ b/src/core/SkScanPriv.h
@@ -0,0 +1,48 @@
+/* libs/graphics/sgl/SkScanPriv.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 SkScanPriv_DEFINED
+#define SkScanPriv_DEFINED
+
+#include "SkScan.h"
+#include "SkBlitter.h"
+
+class SkScanClipper {
+public:
+    SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& bounds);
+
+    SkBlitter*      getBlitter() const { return fBlitter; }
+    const SkIRect*  getClipRect() const { return fClipRect; }
+
+private:
+    SkRectClipBlitter   fRectBlitter;
+    SkRgnClipBlitter    fRgnBlitter;
+    SkBlitter*          fBlitter;
+    const SkIRect*      fClipRect;
+};
+
+// clipRect == null means path is entirely inside the clip
+void sk_fill_path(const SkPath& path, const SkIRect* clipRect,
+                  SkBlitter* blitter, int stop_y, int shiftEdgesUp,
+                  const SkRegion& clipRgn);
+
+// blit the rects above and below avoid, clipped to clp
+void sk_blit_above_and_below(SkBlitter* blitter, const SkIRect& avoid,
+                             const SkRegion& clip);
+
+#endif
+
diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
new file mode 100644
index 0000000..9cdeeaa
--- /dev/null
+++ b/src/core/SkScan_AntiPath.cpp
@@ -0,0 +1,406 @@
+/* libs/graphics/sgl/SkScan_AntiPath.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 "SkScanPriv.h"
+#include "SkPath.h"
+#include "SkMatrix.h"
+#include "SkBlitter.h"
+#include "SkRegion.h"
+#include "SkAntiRun.h"
+
+#define SHIFT   2
+#define SCALE   (1 << SHIFT)
+#define MASK    (SCALE - 1)
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+class BaseSuperBlitter : public SkBlitter {
+public:
+    BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                     const SkRegion& clip);
+
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[],
+                           const int16_t runs[]) {
+        SkASSERT(!"How did I get here?");
+    }
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) {
+        SkASSERT(!"How did I get here?");
+    }
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(!"How did I get here?");
+    }
+
+protected:
+    SkBlitter*  fRealBlitter;
+    int         fCurrIY;
+    int         fWidth, fLeft, fSuperLeft;
+
+    SkDEBUGCODE(int fCurrX;)
+    SkDEBUGCODE(int fCurrY;)
+};
+
+BaseSuperBlitter::BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                                   const SkRegion& clip) {
+    fRealBlitter = realBlitter;
+    
+    // take the union of the ir bounds and clip, since we may be called with an
+    // inverse filltype
+    const int left = SkMin32(ir.fLeft, clip.getBounds().fLeft);
+    const int right = SkMax32(ir.fRight, clip.getBounds().fRight);
+    
+    fLeft = left;
+    fSuperLeft = left << SHIFT;
+    fWidth = right - left;
+    fCurrIY = -1;
+    SkDEBUGCODE(fCurrX = -1; fCurrY = -1;)
+}
+
+class SuperBlitter : public BaseSuperBlitter {
+public:
+    SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                 const SkRegion& clip);
+
+    virtual ~SuperBlitter() {
+        this->flush();
+        sk_free(fRuns.fRuns);
+    }
+
+    void flush();
+
+    virtual void blitH(int x, int y, int width);
+
+private:
+    SkAlphaRuns fRuns;
+};
+
+SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                           const SkRegion& clip)
+        : BaseSuperBlitter(realBlitter, ir, clip) {
+    const int width = fWidth;
+
+    // extra one to store the zero at the end
+    fRuns.fRuns = (int16_t*)sk_malloc_throw((width + 1 + (width + 2)/2) * sizeof(int16_t));
+    fRuns.fAlpha = (uint8_t*)(fRuns.fRuns + width + 1);
+    fRuns.reset(width);
+}
+
+void SuperBlitter::flush()
+{
+    if (fCurrIY >= 0)
+    {
+        if (!fRuns.empty())
+        {
+        //  SkDEBUGCODE(fRuns.dump();)
+            fRealBlitter->blitAntiH(fLeft, fCurrIY, fRuns.fAlpha, fRuns.fRuns);
+            fRuns.reset(fWidth);
+        }
+        fCurrIY = -1;
+        SkDEBUGCODE(fCurrX = -1;)
+    }
+}
+
+static inline int coverage_to_alpha(int aa)
+{
+    aa <<= 8 - 2*SHIFT;
+    aa -= aa >> (8 - SHIFT - 1);
+    return aa;
+}
+
+#define SUPER_Mask      ((1 << SHIFT) - 1)
+
+void SuperBlitter::blitH(int x, int y, int width)
+{
+    int iy = y >> SHIFT;
+    SkASSERT(iy >= fCurrIY);
+
+    x -= fSuperLeft;
+    // hack, until I figure out why my cubics (I think) go beyond the bounds
+    if (x < 0)
+    {
+        width += x;
+        x = 0;
+    }
+
+#ifdef SK_DEBUG
+    SkASSERT(y >= fCurrY);
+    SkASSERT(y != fCurrY || x >= fCurrX);
+    fCurrY = y;
+#endif
+
+    if (iy != fCurrIY)  // new scanline
+    {
+        this->flush();
+        fCurrIY = iy;
+    }
+
+    // we sub 1 from maxValue 1 time for each block, so that we don't
+    // 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;
+
+        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;
+        }
+        else
+        {
+            if (fb == 0)
+                n += 1;
+            else
+                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
+
+#ifdef SK_DEBUG
+    fRuns.assertValid(y & MASK, (1 << (8 - SHIFT)));
+    fCurrX = x + width;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MaskSuperBlitter : public BaseSuperBlitter {
+public:
+    MaskSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                     const SkRegion& clip);
+    virtual ~MaskSuperBlitter() {
+        fRealBlitter->blitMask(fMask, fClipRect);
+    }
+
+    virtual void blitH(int x, int y, int width);
+
+    static bool CanHandleRect(const SkIRect& bounds)
+    {
+        int width = bounds.width();
+        int rb = SkAlign4(width);
+        
+        return (width <= MaskSuperBlitter::kMAX_WIDTH) &&
+        (rb * bounds.height() <= MaskSuperBlitter::kMAX_STORAGE);
+    }
+    
+private:
+    enum {
+        kMAX_WIDTH = 32,    // so we don't try to do very wide things, where the RLE blitter would be faster
+        kMAX_STORAGE = 1024
+    };
+
+    SkMask      fMask;
+    SkIRect     fClipRect;
+    // we add 1 because add_aa_span can write (unchanged) 1 extra byte at the end, rather than
+    // perform a test to see if stopAlpha != 0
+    uint32_t    fStorage[(kMAX_STORAGE >> 2) + 1];
+};
+
+MaskSuperBlitter::MaskSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
+                                   const SkRegion& clip)
+        : BaseSuperBlitter(realBlitter, ir, clip) {
+    SkASSERT(CanHandleRect(ir));
+
+    fMask.fImage    = (uint8_t*)fStorage;
+    fMask.fBounds   = ir;
+    fMask.fRowBytes = ir.width();
+    fMask.fFormat   = SkMask::kA8_Format;
+    
+    fClipRect = ir;
+    fClipRect.intersect(clip.getBounds());
+    
+    // For valgrind, write 1 extra byte at the end so we don't read
+    // uninitialized memory. See comment in add_aa_span and fStorage[].
+    memset(fStorage, 0, fMask.fBounds.height() * fMask.fRowBytes + 1);
+}
+
+static void add_aa_span(uint8_t* alpha, U8CPU startAlpha)
+{
+    /*  I should be able to just add alpha[x] + startAlpha.
+        However, if the trailing edge of the previous span and the leading
+        edge of the current span round to the same super-sampled x value,
+        I might overflow to 256 with this add, hence the funny subtract.
+    */
+    unsigned tmp = *alpha + startAlpha;
+    SkASSERT(tmp <= 256);
+    *alpha = SkToU8(tmp - (tmp >> 8));
+}
+
+static void add_aa_span(uint8_t* alpha, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue)
+{
+    SkASSERT(middleCount >= 0);
+
+    /*  I should be able to just add alpha[x] + startAlpha.
+        However, if the trailing edge of the previous span and the leading
+        edge of the current span round to the same super-sampled x value,
+        I might overflow to 256 with this add, hence the funny subtract.
+    */
+    unsigned tmp = *alpha + startAlpha;
+    SkASSERT(tmp <= 256);
+    *alpha++ = SkToU8(tmp - (tmp >> 8));
+
+    while (--middleCount >= 0)
+    {
+        alpha[0] = SkToU8(alpha[0] + maxValue);
+        alpha += 1;
+    }
+
+    // potentially this can be off the end of our "legal" alpha values, but that
+    // only happens if stopAlpha is also 0. Rather than test for stopAlpha != 0
+    // every time (slow), we just do it, and ensure that we've allocated extra space
+    // (see the + 1 comment in fStorage[]
+    *alpha = SkToU8(*alpha + stopAlpha);
+}
+
+void MaskSuperBlitter::blitH(int x, int y, int width)
+{
+    int iy = (y >> SHIFT);
+    
+    SkASSERT(iy >= fMask.fBounds.fTop && iy < fMask.fBounds.fBottom);
+    iy -= fMask.fBounds.fTop;   // make it relative to 0
+
+#ifdef SK_DEBUG
+    {
+        int ix = x >> SHIFT;
+        SkASSERT(ix >= fMask.fBounds.fLeft && ix < fMask.fBounds.fRight);
+    }
+#endif
+    
+    x -= (fMask.fBounds.fLeft << SHIFT);
+
+    // hack, until I figure out why my cubics (I think) go beyond the bounds
+    if (x < 0)
+    {
+        width += x;
+        x = 0;
+    }
+
+    // we sub 1 from maxValue 1 time for each block, so that we don't
+    // hit 256 as a summed max, but 255.
+//  int maxValue = (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT);
+
+    uint8_t* row = fMask.fImage + iy * fMask.fRowBytes + (x >> SHIFT);
+
+    int start = x;
+    int stop = x + width;
+
+    SkASSERT(start >= 0 && stop > start);
+    int fb = start & SUPER_Mask;
+    int fe = stop & SUPER_Mask;
+    int n = (stop >> SHIFT) - (start >> SHIFT) - 1;
+
+
+    if (n < 0)
+    {
+        add_aa_span(row, coverage_to_alpha(fe - fb));
+    }
+    else
+    {
+        fb = (1 << SHIFT) - fb;
+        add_aa_span(row,  coverage_to_alpha(fb), n, coverage_to_alpha(fe),
+                    (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT));
+    }
+
+#ifdef SK_DEBUG
+    fCurrX = x + width;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int overflows_short(int value) {
+    return value - (short)value;
+}
+
+void SkScan::AntiFillPath(const SkPath& path, const SkRegion& clip,
+                          SkBlitter* blitter) {
+    if (clip.isEmpty()) {
+        return;
+    }
+
+    SkRect      r;
+    SkIRect     ir;
+
+    path.computeBounds(&r, SkPath::kFast_BoundsType);
+    r.roundOut(&ir);
+    if (ir.isEmpty()) {
+        return;
+    }
+
+    if (overflows_short(ir.fLeft << SHIFT) ||
+            overflows_short(ir.fRight << SHIFT) ||
+            overflows_short(ir.width() << SHIFT) ||
+            overflows_short(ir.fTop << SHIFT) ||
+            overflows_short(ir.fBottom << SHIFT) ||
+            overflows_short(ir.height() << SHIFT)) {
+        // can't supersample, try drawing w/o antialiasing
+        SkScan::FillPath(path, clip, blitter);
+        return;
+    }
+
+    SkScanClipper   clipper(blitter, &clip, ir);
+    const SkIRect*  clipRect = clipper.getClipRect();
+
+    if (clipper.getBlitter() == NULL) { // clipped out
+        if (path.isInverseFillType()) {
+            blitter->blitRegion(clip);
+        }
+        return;
+    }
+    
+    // now use the (possibly wrapped) blitter
+    blitter = clipper.getBlitter();
+
+    if (path.isInverseFillType()) {
+        sk_blit_above_and_below(blitter, ir, clip);
+    }
+
+    SkIRect superRect, *superClipRect = NULL;
+
+    if (clipRect)
+    {
+        superRect.set(  clipRect->fLeft << SHIFT, clipRect->fTop << SHIFT,
+                        clipRect->fRight << SHIFT, clipRect->fBottom << SHIFT);
+        superClipRect = &superRect;
+    }
+
+    // MaskSuperBlitter can't handle drawing outside of ir, so we can't use it
+    // if we're an inverse filltype
+    if (!path.isInverseFillType() && MaskSuperBlitter::CanHandleRect(ir))
+    {
+        MaskSuperBlitter    superBlit(blitter, ir, clip);
+        sk_fill_path(path, superClipRect, &superBlit, ir.fBottom, SHIFT, clip);
+    }
+    else
+    {
+        SuperBlitter    superBlit(blitter, ir, clip);
+        sk_fill_path(path, superClipRect, &superBlit, ir.fBottom, SHIFT, clip);
+    }
+}
diff --git a/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp
new file mode 100644
index 0000000..04e4690
--- /dev/null
+++ b/src/core/SkScan_Antihair.cpp
@@ -0,0 +1,645 @@
+/* libs/graphics/sgl/SkScan_Antihair.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 "SkScan.h"
+#include "SkBlitter.h"
+#include "SkColorPriv.h"
+#include "SkRegion.h"
+#include "SkFDot6.h"
+
+#define HLINE_STACK_BUFFER      100
+
+static inline int SmallDot6Scale(int value, int dot6) {
+    SkASSERT((int16_t)value == value);
+    SkASSERT((unsigned)dot6 <= 64);
+    return SkMulS16(value, dot6) >> 6;
+}
+
+//#define TEST_GAMMA
+
+#ifdef TEST_GAMMA
+    static uint8_t gGammaTable[256];
+    #define ApplyGamma(table, alpha)    (table)[alpha]
+
+    static void build_gamma_table()
+    {
+        static bool gInit = false;
+
+        if (gInit == false)
+        {
+            for (int i = 0; i < 256; i++)
+            {
+                SkFixed n = i * 257;
+                n += n >> 15;
+                SkASSERT(n >= 0 && n <= SK_Fixed1);
+                n = SkFixedSqrt(n);
+                n = n * 255 >> 16;
+            //  SkDebugf("morph %d -> %d\n", i, n);
+                gGammaTable[i] = SkToU8(n);
+            }
+            gInit = true;
+        }
+    }
+#else
+    #define ApplyGamma(table, alpha)    SkToU8(alpha)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count, U8CPU alpha)
+{
+    SkASSERT(count > 0);
+
+    int16_t runs[HLINE_STACK_BUFFER + 1];
+    uint8_t  aa[HLINE_STACK_BUFFER];
+
+    aa[0] = ApplyGamma(gGammaTable, alpha);
+    do {
+        int n = count;
+        if (n > HLINE_STACK_BUFFER)
+            n = HLINE_STACK_BUFFER;
+
+        runs[0] = SkToS16(n);
+        runs[n] = 0;
+        blitter->blitAntiH(x, y, aa, runs);
+        x += n;
+        count -= n;
+    } while (count > 0);
+}
+
+static SkFixed hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/, SkBlitter* blitter, int mod64)
+{
+    SkASSERT(x < stopx);
+    int count = stopx - x;
+    fy += SK_Fixed1/2;
+
+    int y = fy >> 16;
+    uint8_t  a = (uint8_t)(fy >> 8);
+
+    // lower line
+    unsigned ma = SmallDot6Scale(a, mod64);
+    if (ma) {
+        call_hline_blitter(blitter, x, y, count, ma);
+    }
+
+    // upper line
+    ma = SmallDot6Scale(255 - a, mod64);
+    if (ma) {
+        call_hline_blitter(blitter, x, y - 1, count, ma);
+    }
+    
+    return fy - SK_Fixed1/2;
+}
+
+static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter, int mod64)
+{
+    SkASSERT(x < stopx);
+
+#ifdef TEST_GAMMA
+    const uint8_t* gamma = gGammaTable;
+#endif
+    int16_t runs[2];
+    uint8_t  aa[1];
+
+    runs[0] = 1;
+    runs[1] = 0;
+
+    fy += SK_Fixed1/2;
+    do {
+        int lower_y = fy >> 16;
+        uint8_t  a = (uint8_t)(fy >> 8);
+        unsigned ma = SmallDot6Scale(a, mod64);
+        if (ma)
+        {
+            aa[0] = ApplyGamma(gamma, ma);
+            blitter->blitAntiH(x, lower_y, aa, runs);
+            // the clipping blitters might edit runs, but should not affect us
+            SkASSERT(runs[0] == 1);
+            SkASSERT(runs[1] == 0);
+        }
+        ma = SmallDot6Scale(255 - a, mod64);
+        if (ma)
+        {
+            aa[0] = ApplyGamma(gamma, ma);
+            blitter->blitAntiH(x, lower_y - 1, aa, runs);
+            // the clipping blitters might edit runs, but should not affect us
+            SkASSERT(runs[0] == 1);
+            SkASSERT(runs[1] == 0);
+        }
+        fy += dy;
+    } while (++x < stopx);
+    
+    return fy - SK_Fixed1/2;
+}
+
+static SkFixed vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/, SkBlitter* blitter, int mod64)
+{
+    SkASSERT(y < stopy);
+    fx += SK_Fixed1/2;
+
+    int x = fx >> 16;
+    int a = (uint8_t)(fx >> 8);
+
+    unsigned ma = SmallDot6Scale(a, mod64);
+    if (ma)
+        blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, ma));
+    ma = SmallDot6Scale(255 - a, mod64);
+    if (ma)
+        blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma));
+    
+    return fx - SK_Fixed1/2;
+}
+
+static SkFixed vertish(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter, int mod64)
+{
+    SkASSERT(y < stopy);
+#ifdef TEST_GAMMA
+    const uint8_t* gamma = gGammaTable;
+#endif
+    int16_t runs[3];
+    uint8_t  aa[2];
+
+    runs[0] = 1;
+    runs[2] = 0;
+
+    fx += SK_Fixed1/2;
+    do {
+        int x = fx >> 16;
+        uint8_t  a = (uint8_t)(fx >> 8);
+
+        aa[0] = ApplyGamma(gamma, SmallDot6Scale(255 - a, mod64));
+        aa[1] = ApplyGamma(gamma, SmallDot6Scale(a, mod64));
+        // the clippng blitters might overwrite this guy, so we have to reset it each time
+        runs[1] = 1;
+        blitter->blitAntiH(x - 1, y, aa, runs);
+        // the clipping blitters might edit runs, but should not affect us
+        SkASSERT(runs[0] == 1);
+        SkASSERT(runs[2] == 0);
+        fx += dx;
+    } while (++y < stopy);
+
+    return fx - SK_Fixed1/2;
+}
+
+typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart, SkFixed slope, SkBlitter*, int);
+
+static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b)
+{
+    SkASSERT((a << 16 >> 16) == a);
+    SkASSERT(b != 0);
+    return (a << 16) / b;
+}
+
+static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
+                             const SkIRect* clip, SkBlitter* blitter)
+{
+    // check that we're no larger than 511 pixels (so we can do a faster div).
+    // if we are, subdivide and call again
+
+    if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511))
+    {
+        int hx = (x0 + x1) >> 1;
+        int hy = (y0 + y1) >> 1;
+        do_anti_hairline(x0, y0, hx, hy, clip, blitter);
+        do_anti_hairline(hx, hy, x1, y1, clip, blitter);
+        return;
+    }
+
+    int         scaleStart, scaleStop;
+    int         istart, istop;
+    SkFixed     fstart, slope; 
+    LineProc    proc;
+
+    if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0))    // mostly horizontal
+    {
+        if (x0 > x1) {    // we want to go left-to-right
+            SkTSwap<SkFDot6>(x0, x1);
+            SkTSwap<SkFDot6>(y0, y1);
+        }
+
+        istart = SkFDot6Floor(x0);
+        istop = SkFDot6Ceil(x1);
+        fstart = SkFDot6ToFixed(y0);
+        if (y0 == y1) {   // completely horizontal, take fast case
+            slope = 0;
+            proc = hline;
+        } else {
+            slope = fastfixdiv(y1 - y0, x1 - x0);
+            SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
+            fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
+            proc = horish;
+        }
+        
+        SkASSERT(istop > istart);
+        if (istop - istart == 1) {
+            scaleStart = x1 - x0;
+            SkASSERT(scaleStart >= 0 && scaleStart <= 64);
+            scaleStop = 0;
+        } else {
+            scaleStart = 64 - (x0 & 63);
+            scaleStop = x1 & 63;
+        }
+
+        if (clip)
+        {
+            if (istart >= clip->fRight || istop <= clip->fLeft)
+                return;
+            if (istart < clip->fLeft)
+            {
+                fstart += slope * (clip->fLeft - istart);
+                istart = clip->fLeft;
+                scaleStart = 64;
+            }
+            if (istop > clip->fRight) {
+                istop = clip->fRight;
+                scaleStop = 64;
+            }
+            SkASSERT(istart <= istop);
+            if (istart == istop)
+                return;
+
+            // now test if our Y values are completely inside the clip
+            int top, bottom;
+            if (slope >= 0) // T2B
+            {
+                top = SkFixedFloor(fstart - SK_FixedHalf);
+                bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
+            }
+            else            // B2T
+            {
+                bottom = SkFixedCeil(fstart + SK_FixedHalf);
+                top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
+            }
+            if (top >= clip->fBottom || bottom <= clip->fTop)
+                return;
+            if (clip->fTop <= top && clip->fBottom >= bottom)
+                clip = NULL;
+        }
+    }
+    else    // mostly vertical
+    {
+        if (y0 > y1)    // we want to go top-to-bottom
+        {
+            SkTSwap<SkFDot6>(x0, x1);
+            SkTSwap<SkFDot6>(y0, y1);
+        }
+
+        istart = SkFDot6Floor(y0);
+        istop = SkFDot6Ceil(y1);
+        fstart = SkFDot6ToFixed(x0);
+        if (x0 == x1)
+        {
+            if (y0 == y1) { // are we zero length?
+                return;     // nothing to do
+            }
+            slope = 0;
+            proc = vline;
+        }
+        else
+        {
+            slope = fastfixdiv(x1 - x0, y1 - y0);
+            SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
+            fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
+            proc = vertish;
+        }
+
+        SkASSERT(istop > istart);
+        if (istop - istart == 1) {
+            scaleStart = y1 - y0;
+            SkASSERT(scaleStart >= 0 && scaleStart <= 64);
+            scaleStop = 0;
+        } else {
+            scaleStart = 64 - (y0 & 63);
+            scaleStop = y1 & 63;
+        }
+        
+        if (clip)
+        {
+            if (istart >= clip->fBottom || istop <= clip->fTop)
+                return;
+            if (istart < clip->fTop)
+            {
+                fstart += slope * (clip->fTop - istart);
+                istart = clip->fTop;
+                scaleStart = 64;
+            }
+            if (istop > clip->fBottom) {
+                istop = clip->fBottom;
+                scaleStop = 64;
+            }
+            SkASSERT(istart <= istop);
+            if (istart == istop)
+                return;
+
+            // now test if our X values are completely inside the clip
+            int left, right;
+            if (slope >= 0) // L2R
+            {
+                left = SkFixedFloor(fstart - SK_FixedHalf);
+                right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
+            }
+            else            // R2L
+            {
+                right = SkFixedCeil(fstart + SK_FixedHalf);
+                left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
+            }
+            if (left >= clip->fRight || right <= clip->fLeft)
+                return;
+            if (clip->fLeft <= left && clip->fRight >= right)
+                clip = NULL;
+        }
+    }
+
+    SkRectClipBlitter   rectClipper;
+    if (clip)
+    {
+        rectClipper.init(blitter, *clip);
+        blitter = &rectClipper;
+    }
+    
+    fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart);
+    istart += 1;
+    int fullSpans = istop - istart - 1;
+    if (fullSpans > 0) {
+        fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64);
+    }
+    if (scaleStop > 0) {
+        proc(istop - 1, istop, fstart, slope, blitter, scaleStop);
+    }
+}
+
+void SkScan::AntiHairLine(const SkPoint& pt0, const SkPoint& pt1,
+                          const SkRegion* clip, SkBlitter* blitter)
+{
+    if (clip && clip->isEmpty())
+        return;
+
+    SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
+
+#ifdef TEST_GAMMA
+    build_gamma_table();
+#endif
+
+    SkFDot6 x0 = SkScalarToFDot6(pt0.fX);
+    SkFDot6 y0 = SkScalarToFDot6(pt0.fY);
+    SkFDot6 x1 = SkScalarToFDot6(pt1.fX);
+    SkFDot6 y1 = SkScalarToFDot6(pt1.fY);
+
+    if (clip)
+    {
+        SkFDot6     left = SkMin32(x0, x1);
+        SkFDot6     top = SkMin32(y0, y1);
+        SkFDot6     right = SkMax32(x0, x1);
+        SkFDot6     bottom = SkMax32(y0, y1);
+        SkIRect     ir;
+
+        ir.set( SkFDot6Floor(left) - 1,
+                SkFDot6Floor(top) - 1,
+                SkFDot6Ceil(right) + 1,
+                SkFDot6Ceil(bottom) + 1);
+
+        if (clip->quickReject(ir))
+            return;
+        if (!clip->quickContains(ir))
+        {
+            SkRegion::Cliperator iter(*clip, ir);
+            const SkIRect*       r = &iter.rect();
+
+            while (!iter.done())
+            {
+                do_anti_hairline(x0, y0, x1, y1, r, blitter);
+                iter.next();
+            }
+            return;
+        }
+        // fall through to no-clip case
+    }
+    do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
+}
+
+void SkScan::AntiHairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter)
+{
+    if (clip)
+    {
+        SkIRect ir;
+        SkRect  r = rect;
+
+        r.inset(-SK_Scalar1/2, -SK_Scalar1/2);
+        r.roundOut(&ir);
+        if (clip->quickReject(ir))
+            return;
+        if (clip->quickContains(ir))
+            clip = NULL;
+    }
+
+    SkPoint p0, p1;
+
+    p0.set(rect.fLeft, rect.fTop);
+    p1.set(rect.fRight, rect.fTop);
+    SkScan::AntiHairLine(p0, p1, clip, blitter);
+    p0.set(rect.fRight, rect.fBottom);
+    SkScan::AntiHairLine(p0, p1, clip, blitter);
+    p1.set(rect.fLeft, rect.fBottom);
+    SkScan::AntiHairLine(p0, p1, clip, blitter);
+    p0.set(rect.fLeft, rect.fTop);
+    SkScan::AntiHairLine(p0, p1, clip, blitter);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+typedef int FDot8;  // 24.8 integer fixed point
+
+static inline FDot8 SkFixedToFDot8(SkFixed x) {
+    return (x + 0x80) >> 8;
+}
+
+static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, SkBlitter* blitter)
+{
+    SkASSERT(L < R);
+    
+    if ((L >> 8) == ((R - 1) >> 8))  // 1x1 pixel
+    {
+        blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
+        return;
+    }
+    
+    int left = L >> 8;
+    
+    if (L & 0xFF)
+    {
+        blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
+        left += 1;
+    }
+
+    int rite = R >> 8;
+    int width = rite - left;
+    if (width > 0)
+        call_hline_blitter(blitter, left, top, width, alpha);
+
+    if (R & 0xFF)
+        blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
+}
+
+static void antifillrect(const SkXRect& xr, SkBlitter* blitter)
+{
+    FDot8 L = SkFixedToFDot8(xr.fLeft);
+    FDot8 T = SkFixedToFDot8(xr.fTop);
+    FDot8 R = SkFixedToFDot8(xr.fRight);
+    FDot8 B = SkFixedToFDot8(xr.fBottom);
+    
+    // check for empty now that we're in our reduced precision space
+    if (L >= R || T >= B)
+        return;
+    
+    int top = T >> 8;
+    if (top == ((B - 1) >> 8))   // just one scanline high
+    {
+        do_scanline(L, top, R, B - T - 1, blitter);
+        return;
+    }
+    
+    if (T & 0xFF)
+    {
+        do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
+        top += 1;
+    }
+    
+    int bot = B >> 8;
+    int height = bot - top;
+    if (height > 0)
+    {
+        int left = L >> 8;
+        if (L & 0xFF)
+        {
+            blitter->blitV(left, top, height, 256 - (L & 0xFF));
+            left += 1;
+        }
+        int rite = R >> 8;
+        int width = rite - left;
+        if (width > 0)
+            blitter->blitRect(left, top, width, height);
+        if (R & 0xFF)
+            blitter->blitV(rite, top, height, R & 0xFF);
+    }
+    
+    if (B & 0xFF)
+        do_scanline(L, bot, R, B & 0xFF, blitter);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
+                          SkBlitter* blitter) {
+    if (clip) {
+        SkIRect outerBounds;
+        XRect_roundOut(xr, &outerBounds);
+
+        if (clip->isRect()) {
+            const SkIRect& clipBounds = clip->getBounds();
+
+            if (clipBounds.contains(outerBounds)) {
+                antifillrect(xr, blitter);
+            } else {
+                SkXRect tmpR;
+                // this keeps our original edges fractional
+                XRect_set(&tmpR, clipBounds);
+                if (tmpR.intersect(xr)) {
+                    antifillrect(tmpR, blitter);
+                }
+            }
+        } else {
+            SkRegion::Cliperator clipper(*clip, outerBounds);
+            const SkIRect&       rr = clipper.rect();
+            
+            while (!clipper.done()) {
+                SkXRect  tmpR;
+                
+                // this keeps our original edges fractional
+                XRect_set(&tmpR, rr);
+                if (tmpR.intersect(xr)) {
+                    antifillrect(tmpR, blitter);
+                }
+                clipper.next();
+            }
+        }
+    } else {
+        antifillrect(xr, blitter);
+    }
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+
+/*  This guy takes a float-rect, but with the key improvement that it has
+    already been clipped, so we know that it is safe to convert it into a
+    XRect (fixedpoint), as it won't overflow.
+*/
+static void antifillrect(const SkRect& r, SkBlitter* blitter) {
+    SkXRect xr;
+    
+    XRect_set(&xr, r);
+    antifillrect(xr, blitter);
+}
+
+/*  We repeat the clipping logic of AntiFillXRect because the float rect might
+    overflow if we blindly converted it to an XRect. This sucks that we have to
+    repeat the clipping logic, but I don't see how to share the code/logic.
+ 
+    We clip r (as needed) into one or more (smaller) float rects, and then pass
+    those to our version of antifillrect, which converts it into an XRect and
+    then calls the blit.
+*/
+void SkScan::AntiFillRect(const SkRect& r, const SkRegion* clip,
+                          SkBlitter* blitter) {
+    if (clip) {
+        SkIRect outerBounds;
+        r.roundOut(&outerBounds);
+        
+        if (clip->isRect()) {
+            const SkIRect& clipBounds = clip->getBounds();
+            
+            if (clipBounds.contains(outerBounds)) {
+                antifillrect(r, blitter);
+            } else {
+                SkRect tmpR;
+                // this keeps our original edges fractional
+                tmpR.set(clipBounds);
+                if (tmpR.intersect(r)) {
+                    antifillrect(tmpR, blitter);
+                }
+            }
+        } else {
+            SkRegion::Cliperator clipper(*clip, outerBounds);
+            const SkIRect&       rr = clipper.rect();
+            
+            while (!clipper.done()) {
+                SkRect  tmpR;
+                // this keeps our original edges fractional
+                tmpR.set(rr);
+                if (tmpR.intersect(r)) {
+                    antifillrect(tmpR, blitter);
+                }
+                clipper.next();
+            }
+        }
+    } else {
+        antifillrect(r, blitter);
+    }
+}
+
+#endif
+
+
diff --git a/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp
new file mode 100644
index 0000000..6a66754
--- /dev/null
+++ b/src/core/SkScan_Hairline.cpp
@@ -0,0 +1,345 @@
+/* libs/graphics/sgl/SkScan_Hairline.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 "SkScan.h"
+#include "SkBlitter.h"
+#include "SkRegion.h"
+#include "SkFDot6.h"
+
+static void horiline(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter)
+{
+    SkASSERT(x < stopx);
+
+    do {
+        blitter->blitH(x, fy >> 16, 1);
+        fy += dy;
+    } while (++x < stopx);
+}
+
+static void vertline(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter)
+{
+    SkASSERT(y < stopy);
+
+    do {
+        blitter->blitH(fx >> 16, y, 1);
+        fx += dx;
+    } while (++y < stopy);
+}
+
+void SkScan::HairLine(const SkPoint& pt0, const SkPoint& pt1, const SkRegion* clip, SkBlitter* blitter)
+{
+    SkBlitterClipper    clipper;
+
+    SkFDot6 x0 = SkScalarToFDot6(pt0.fX);
+    SkFDot6 y0 = SkScalarToFDot6(pt0.fY);
+    SkFDot6 x1 = SkScalarToFDot6(pt1.fX);
+    SkFDot6 y1 = SkScalarToFDot6(pt1.fY);
+
+    if (clip)
+    {
+        SkRect      r;
+        SkIRect     ir;
+        SkPoint     pts[2];
+
+        pts[0] = pt0;
+        pts[1] = pt1;
+        r.set(pts, 2);
+        r.roundOut(&ir);
+        
+        // if we're given a horizontal or vertical line
+        // this rect could be empty (in area), in which case
+        // clip->quickReject() will always return true.
+        // hence we bloat the rect to avoid that case
+        if (ir.width() == 0)
+            ir.fRight += 1;
+        if (ir.height() == 0)
+            ir.fBottom += 1;
+
+        if (clip->quickReject(ir))
+            return;
+        if (clip->quickContains(ir))
+            clip = NULL;
+        else
+        {
+            blitter = clipper.apply(blitter, clip);
+        }
+    }
+
+    SkFDot6 dx = x1 - x0;
+    SkFDot6 dy = y1 - y0;
+
+    if (SkAbs32(dx) > SkAbs32(dy))  // mostly horizontal
+    {
+        if (x0 > x1)    // we want to go left-to-right
+        {
+            SkTSwap<SkFDot6>(x0, x1);
+            SkTSwap<SkFDot6>(y0, y1);
+        }
+        int ix0 = SkFDot6Round(x0);
+        int ix1 = SkFDot6Round(x1);
+        if (ix0 == ix1) // too short to draw
+            return;
+
+        SkFixed slope = SkFixedDiv(dy, dx);
+        SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6);
+
+        horiline(ix0, ix1, startY, slope, blitter);
+    }
+    else                // mostly vertical
+    {
+        if (y0 > y1)    // we want to go top-to-bottom
+        {
+            SkTSwap<SkFDot6>(x0, x1);
+            SkTSwap<SkFDot6>(y0, y1);
+        }
+        int iy0 = SkFDot6Round(y0);
+        int iy1 = SkFDot6Round(y1);
+        if (iy0 == iy1) // too short to draw
+            return;
+
+        SkFixed slope = SkFixedDiv(dx, dy);
+        SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6);
+
+        vertline(iy0, iy1, startX, slope, blitter);
+    }
+}
+
+// we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right
+// and double-hit the top-left.
+void SkScan::HairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter)
+{
+    SkBlitterClipper    clipper;
+    SkIRect             r;
+
+    r.set(SkScalarToFixed(rect.fLeft) >> 16,
+          SkScalarToFixed(rect.fTop) >> 16,
+          (SkScalarToFixed(rect.fRight) >> 16) + 1,
+          (SkScalarToFixed(rect.fBottom) >> 16) + 1);
+
+    if (clip)
+    {
+        if (clip->quickReject(r))
+            return;
+        if (!clip->quickContains(r))
+            blitter = clipper.apply(blitter, clip);
+    }
+
+    int width = r.width();
+    int height = r.height();
+    
+    if ((width | height) == 0)
+        return;
+    if (width <= 2 || height <= 2)
+    {
+        blitter->blitRect(r.fLeft, r.fTop, width, height);
+        return;
+    }
+    // if we get here, we know we have 4 segments to draw
+    blitter->blitH(r.fLeft, r.fTop, width);                     // top
+    blitter->blitRect(r.fLeft, r.fTop + 1, 1, height - 2);      // left
+    blitter->blitRect(r.fRight - 1, r.fTop + 1, 1, height - 2); // right
+    blitter->blitH(r.fLeft, r.fBottom - 1, width);              // bottom
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkPath.h"
+#include "SkGeometry.h"
+
+static bool quad_too_curvy(const SkPoint pts[3])
+{
+    return true;
+}
+
+static int compute_int_quad_dist(const SkPoint pts[3]) {
+    // compute the vector between the control point ([1]) and the middle of the
+    // line connecting the start and end ([0] and [2])
+    SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
+    SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
+    // we want everyone to be positive
+    dx = SkScalarAbs(dx);
+    dy = SkScalarAbs(dy);
+    // convert to whole pixel values (use ceiling to be conservative)
+    int idx = SkScalarCeil(dx);
+    int idy = SkScalarCeil(dy);
+    // use the cheap approx for distance
+    if (idx > idy) {
+        return idx + (idy >> 1);
+    } else {
+        return idy + (idx >> 1);
+    }
+}
+
+static void hairquad(const SkPoint pts[3], const SkRegion* clip, SkBlitter* blitter, int level,
+                     void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*))
+{
+#if 1
+    if (level > 0 && quad_too_curvy(pts))
+    {
+        SkPoint tmp[5];
+
+        SkChopQuadAtHalf(pts, tmp);
+        hairquad(tmp, clip, blitter, level - 1, lineproc);
+        hairquad(&tmp[2], clip, blitter, level - 1, lineproc);
+    }
+    else
+        lineproc(pts[0], pts[2], clip, blitter);
+#else
+    int count = 1 << level;
+    const SkScalar dt = SkFixedToScalar(SK_Fixed1 >> level);
+    SkScalar t = dt;
+    SkPoint prevPt = pts[0];
+    for (int i = 1; i < count; i++) {
+        SkPoint nextPt;
+        SkEvalQuadAt(pts, t, &nextPt);
+        lineproc(prevPt, nextPt, clip, blitter);
+        t += dt;
+        prevPt = nextPt;
+    }
+    // draw the last line explicitly to 1.0, in case t didn't match that exactly
+    lineproc(prevPt, pts[2], clip, blitter);
+#endif
+}
+
+static bool cubic_too_curvy(const SkPoint pts[4])
+{
+    return true;
+}
+
+static void haircubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter, int level,
+                      void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
+{
+    if (level > 0 && cubic_too_curvy(pts))
+    {
+        SkPoint tmp[7];
+
+        SkChopCubicAt(pts, tmp, SK_Scalar1/2);
+        haircubic(tmp, clip, blitter, level - 1, lineproc);
+        haircubic(&tmp[3], clip, blitter, level - 1, lineproc);
+    }
+    else
+        lineproc(pts[0], pts[3], clip, blitter);
+}
+
+#define kMaxCubicSubdivideLevel 6
+#define kMaxQuadSubdivideLevel  5
+
+static void hair_path(const SkPath& path, const SkRegion* clip, SkBlitter* blitter,
+                      void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
+{
+    if (path.isEmpty())
+        return;
+
+    const SkIRect* clipR = NULL;
+
+    if (clip)
+    {
+        SkRect      bounds;
+        SkIRect     ibounds;
+
+        path.computeBounds(&bounds, SkPath::kFast_BoundsType);
+        bounds.roundOut(&ibounds);
+        ibounds.inset(-1, -1);
+
+        if (clip->quickReject(ibounds))
+            return;
+
+        if (clip->quickContains(ibounds))
+            clip = NULL;
+        else
+            clipR = &clip->getBounds();
+    }
+
+    SkPath::Iter    iter(path, false);
+    SkPoint         pts[4];
+    SkPath::Verb    verb;
+
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb)
+    {
+        switch (verb) {
+        case SkPath::kLine_Verb:
+            lineproc(pts[0], pts[1], clip, blitter);
+            break;
+        case SkPath::kQuad_Verb: {
+            int d = compute_int_quad_dist(pts);
+            /*  quadratics approach the line connecting their start and end points
+             4x closer with each subdivision, so we compute the number of
+             subdivisions to be the minimum need to get that distance to be less
+             than a pixel.
+             */
+            int level = (33 - SkCLZ(d)) >> 1;
+//          SkDebugf("----- distance %d computedLevel %d\n", d, computedLevel);
+            // sanity check on level (from the previous version)
+            if (level > kMaxQuadSubdivideLevel) {
+                level = kMaxQuadSubdivideLevel;
+            }
+            hairquad(pts, clip, blitter, level, lineproc);
+            break;
+        }
+        case SkPath::kCubic_Verb:
+            haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc);
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+void SkScan::HairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter)
+{
+    hair_path(path, clip, blitter, SkScan::HairLine);
+}
+
+void SkScan::AntiHairPath(const SkPath& path, const SkRegion* clip, SkBlitter* blitter)
+{
+    hair_path(path, clip, blitter, SkScan::AntiHairLine);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void SkScan::FrameRect(const SkRect& r, SkScalar diameter, const SkRegion* clip, SkBlitter* blitter)
+{
+    SkASSERT(diameter > 0);
+
+    if (r.isEmpty())
+        return;
+
+    SkScalar radius = diameter / 2;
+    SkRect   outer, tmp;
+
+    outer.set(  r.fLeft - radius, r.fTop - radius,
+                r.fRight + radius, r.fBottom + radius);
+
+    if (r.width() <= diameter || r.height() <= diameter)
+    {
+        SkScan::FillRect(outer, clip, blitter);
+        return;
+    }
+
+    tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + diameter);
+    SkScan::FillRect(tmp, clip, blitter);
+    tmp.fTop = outer.fBottom - diameter;
+    tmp.fBottom = outer.fBottom;
+    SkScan::FillRect(tmp, clip, blitter);
+
+    tmp.set(outer.fLeft, outer.fTop + diameter, outer.fLeft + diameter, outer.fBottom - diameter);
+    SkScan::FillRect(tmp, clip, blitter);
+    tmp.fLeft = outer.fRight - diameter;
+    tmp.fRight = outer.fRight;
+    SkScan::FillRect(tmp, clip, blitter);
+}
+
diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp
new file mode 100644
index 0000000..8b58991
--- /dev/null
+++ b/src/core/SkScan_Path.cpp
@@ -0,0 +1,671 @@
+/* libs/graphics/sgl/SkScan_Path.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 "SkScanPriv.h"
+#include "SkBlitter.h"
+#include "SkEdge.h"
+#include "SkGeometry.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkTemplates.h"
+
+#define kEDGE_HEAD_Y    SK_MinS32
+#define kEDGE_TAIL_Y    SK_MaxS32
+
+#ifdef SK_DEBUG
+    static void validate_sort(const SkEdge* edge)
+    {
+        int y = kEDGE_HEAD_Y;
+
+        while (edge->fFirstY != SK_MaxS32)
+        {
+            edge->validate();
+            SkASSERT(y <= edge->fFirstY);
+
+            y = edge->fFirstY;
+            edge = edge->fNext;
+        }
+    }
+#else
+    #define validate_sort(edge)
+#endif
+
+static inline void remove_edge(SkEdge* edge)
+{
+    edge->fPrev->fNext = edge->fNext;
+    edge->fNext->fPrev = edge->fPrev;
+}
+
+static inline void swap_edges(SkEdge* prev, SkEdge* next)
+{
+    SkASSERT(prev->fNext == next && next->fPrev == prev);
+
+    // remove prev from the list
+    prev->fPrev->fNext = next;
+    next->fPrev = prev->fPrev;
+
+    // insert prev after next
+    prev->fNext = next->fNext;
+    next->fNext->fPrev = prev;
+    next->fNext = prev;
+    prev->fPrev = next;
+}
+
+static void backward_insert_edge_based_on_x(SkEdge* edge SkDECLAREPARAM(int, curr_y))
+{
+    SkFixed x = edge->fX;
+
+    for (;;)
+    {
+        SkEdge* prev = edge->fPrev;
+        
+        // add 1 to curr_y since we may have added new edges (built from curves)
+        // that start on the next scanline
+        SkASSERT(prev && prev->fFirstY <= curr_y + 1);
+
+        if (prev->fX <= x)
+            break;
+
+        swap_edges(prev, edge);
+    }
+}
+
+static void insert_new_edges(SkEdge* newEdge, int curr_y)
+{
+    SkASSERT(newEdge->fFirstY >= curr_y);
+
+    while (newEdge->fFirstY == curr_y)
+    {
+        SkEdge* next = newEdge->fNext;
+        backward_insert_edge_based_on_x(newEdge  SkPARAM(curr_y));
+        newEdge = next;
+    }
+}
+
+#ifdef SK_DEBUG
+static void validate_edges_for_y(const SkEdge* edge, int curr_y)
+{
+    while (edge->fFirstY <= curr_y)
+    {
+        SkASSERT(edge->fPrev && edge->fNext);
+        SkASSERT(edge->fPrev->fNext == edge);
+        SkASSERT(edge->fNext->fPrev == edge);
+        SkASSERT(edge->fFirstY <= edge->fLastY);
+
+        SkASSERT(edge->fPrev->fX <= edge->fX);
+        edge = edge->fNext;
+    }
+}
+#else
+    #define validate_edges_for_y(edge, curr_y)
+#endif
+
+#if defined _WIN32 && _MSC_VER >= 1300  // disable warning : local variable used without having been initialized
+#pragma warning ( push )
+#pragma warning ( disable : 4701 )
+#endif
+
+typedef void (*PrePostProc)(SkBlitter* blitter, int y, bool isStartOfScanline);
+#define PREPOST_START   true
+#define PREPOST_END     false
+
+static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType,
+                       SkBlitter* blitter, int stop_y, PrePostProc proc)
+{
+    validate_sort(prevHead->fNext);
+
+    int curr_y = prevHead->fNext->fFirstY;
+    // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
+    int windingMask = (fillType & 1) ? 1 : -1;
+
+    for (;;)
+    {
+        int     w = 0;
+        int     left SK_INIT_TO_AVOID_WARNING;
+        bool    in_interval = false;
+        SkEdge* currE = prevHead->fNext;
+        SkFixed prevX = prevHead->fX;
+
+        validate_edges_for_y(currE, curr_y);
+        
+        if (proc) {
+            proc(blitter, curr_y, PREPOST_START);    // pre-proc
+        }
+        
+        while (currE->fFirstY <= curr_y)
+        {
+            SkASSERT(currE->fLastY >= curr_y);
+
+            int x = (currE->fX + SK_Fixed1/2) >> 16;
+            w += currE->fWinding;
+            if ((w & windingMask) == 0) // we finished an interval
+            {
+                SkASSERT(in_interval);
+                int width = x - left;
+                SkASSERT(width >= 0);
+                if (width)
+                    blitter->blitH(left, curr_y, width);
+                in_interval = false;
+            }
+            else if (!in_interval)
+            {
+                left = x;
+                in_interval = true;
+            }
+
+            SkEdge* next = currE->fNext;
+            SkFixed newX;
+
+            if (currE->fLastY == curr_y)    // are we done with this edge?
+            {
+                if (currE->fCurveCount < 0)
+                {
+                    if (((SkCubicEdge*)currE)->updateCubic())
+                    {
+                        SkASSERT(currE->fFirstY == curr_y + 1);
+                        
+                        newX = currE->fX;
+                        goto NEXT_X;
+                    }
+                }
+                else if (currE->fCurveCount > 0)
+                {
+                    if (((SkQuadraticEdge*)currE)->updateQuadratic())
+                    {
+                        newX = currE->fX;
+                        goto NEXT_X;
+                    }
+                }
+                remove_edge(currE);
+            }
+            else
+            {
+                SkASSERT(currE->fLastY > curr_y);
+                newX = currE->fX + currE->fDX;
+                currE->fX = newX;
+            NEXT_X:
+                if (newX < prevX)   // ripple currE backwards until it is x-sorted
+                    backward_insert_edge_based_on_x(currE  SkPARAM(curr_y));
+                else
+                    prevX = newX;
+            }
+            currE = next;
+            SkASSERT(currE);
+        }
+        
+        if (proc) {
+            proc(blitter, curr_y, PREPOST_END);    // post-proc
+        }
+
+        curr_y += 1;
+        if (curr_y >= stop_y)
+            break;
+
+        // now currE points to the first edge with a Yint larger than curr_y
+        insert_new_edges(currE, curr_y);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// this guy overrides blitH, and will call its proxy blitter with the inverse
+// of the spans it is given (clipped to the left/right of the cliprect)
+//
+// used to implement inverse filltypes on paths
+//
+class InverseBlitter : public SkBlitter {
+public:
+    void setBlitter(SkBlitter* blitter, const SkIRect& clip, int shift) {
+        fBlitter = blitter;
+        fFirstX = clip.fLeft << shift;
+        fLastX = clip.fRight << shift;
+    }
+    void prepost(int y, bool isStart) {
+        if (isStart) {
+            fPrevX = fFirstX;
+        } else {
+            int invWidth = fLastX - fPrevX;
+            if (invWidth > 0) {
+                fBlitter->blitH(fPrevX, y, invWidth);
+            }
+        }
+    }
+
+    // overrides
+    virtual void blitH(int x, int y, int width) {
+        int invWidth = x - fPrevX;
+        if (invWidth > 0) {
+            fBlitter->blitH(fPrevX, y, invWidth);
+        }
+        fPrevX = x + width;
+    }
+    
+    // we do not expect to get called with these entrypoints
+    virtual void blitAntiH(int, int, const SkAlpha[], const int16_t runs[]) {
+        SkASSERT(!"blitAntiH unexpected");
+    }
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) {
+        SkASSERT(!"blitV unexpected");
+    }
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(!"blitRect unexpected");
+    }
+    virtual void blitMask(const SkMask&, const SkIRect& clip) {
+        SkASSERT(!"blitMask unexpected");
+    }
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) {
+        SkASSERT(!"justAnOpaqueColor unexpected");
+        return NULL;
+    }
+    
+private:
+    SkBlitter*  fBlitter;
+    int         fFirstX, fLastX, fPrevX;
+};
+
+static void PrePostInverseBlitterProc(SkBlitter* blitter, int y, bool isStart) {
+    ((InverseBlitter*)blitter)->prepost(y, isStart);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined _WIN32 && _MSC_VER >= 1300
+#pragma warning ( pop )
+#endif
+
+/*  Our line edge relies on the maximum span being <= 512, so that it can
+    use FDot6 and keep the dx,dy in 16bits (for much faster slope divide).
+    This function returns true if the specified line is too big.
+*/
+static inline bool line_too_big(const SkPoint pts[2])
+{
+    SkScalar dx = pts[1].fX - pts[0].fX;
+    SkScalar dy = pts[1].fY - pts[0].fY;
+
+    return  SkScalarAbs(dx) > SkIntToScalar(511) ||
+            SkScalarAbs(dy) > SkIntToScalar(511);
+}
+
+static int build_edges(SkEdge edge[], const SkPath& path,
+                       const SkIRect* clipRect, SkEdge* list[], int shiftUp) {
+    SkEdge**        start = list;
+    SkPath::Iter    iter(path, true);
+    SkPoint         pts[4];
+    SkPath::Verb    verb;
+
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kLine_Verb:
+                if (edge->setLine(pts[0], pts[1], clipRect, shiftUp)) {
+                    *list++ = edge;
+                    edge = (SkEdge*)((char*)edge + sizeof(SkEdge));
+                }
+                break;
+            case SkPath::kQuad_Verb: {
+                SkPoint tmp[5];
+                SkPoint* p = tmp;
+                int     count = SkChopQuadAtYExtrema(pts, tmp);
+
+                do {
+                    if (((SkQuadraticEdge*)edge)->setQuadratic(p, clipRect,
+                                                               shiftUp))
+                    {
+                        *list++ = edge;
+                        edge = (SkEdge*)((char*)edge + sizeof(SkQuadraticEdge));
+                    }
+                    p += 2;
+                } while (--count >= 0);
+                break;
+            }
+            case SkPath::kCubic_Verb: {
+                SkPoint tmp[10];
+                SkPoint* p = tmp;
+                int     count = SkChopCubicAtYExtrema(pts, tmp);                
+                SkASSERT(count >= 0 && count <= 2);
+
+                do {
+                    if (((SkCubicEdge*)edge)->setCubic(p, clipRect, shiftUp))
+                    {
+                        *list++ = edge;
+                        edge = (SkEdge*)((char*)edge + sizeof(SkCubicEdge));
+                    }
+                    p += 3;
+                } while (--count >= 0);
+                break;
+            }
+        default:
+            break;
+        }
+    }
+    return (int)(list - start);
+}
+
+extern "C" {
+    static int edge_compare(const void* a, const void* b)
+    {
+        const SkEdge* edgea = *(const SkEdge**)a;
+        const SkEdge* edgeb = *(const SkEdge**)b;
+
+        int valuea = edgea->fFirstY;
+        int valueb = edgeb->fFirstY;
+
+        if (valuea == valueb)
+        {
+            valuea = edgea->fX;
+            valueb = edgeb->fX;
+        }
+        return valuea - valueb;
+    }
+}
+
+static SkEdge* sort_edges(SkEdge* list[], int count, SkEdge** last)
+{
+    qsort(list, count, sizeof(SkEdge*), edge_compare);
+
+    // now make the edges linked in sorted order
+    for (int i = 1; i < count; i++)
+    {
+        list[i - 1]->fNext = list[i];
+        list[i]->fPrev = list[i - 1];
+    }
+
+    *last = list[count - 1];
+    return list[0];
+}
+
+/* 'quick' computation of the max sized needed to allocated for
+    our edgelist.
+*/
+static int worst_case_edge_count(const SkPath& path, size_t* storage)
+{
+    size_t  size = 0;
+    int     edgeCount = 0;
+
+    SkPath::Iter    iter(path, true);
+    SkPath::Verb    verb;
+
+    while ((verb = iter.next(NULL)) != SkPath::kDone_Verb)
+    {
+        switch (verb) {
+        case SkPath::kLine_Verb:
+            edgeCount += 1;
+            size += sizeof(SkQuadraticEdge);    // treat line like Quad (in case its > 512)
+            break;
+        case SkPath::kQuad_Verb:
+            edgeCount += 2;                     // might need 2 edges when we chop on Y extrema
+            size += 2 * sizeof(SkQuadraticEdge);
+            break;
+        case SkPath::kCubic_Verb:
+            edgeCount += 3;                     // might need 3 edges when we chop on Y extrema
+            size += 3 * sizeof(SkCubicEdge);
+            break;
+        default:
+            break;
+        }
+    }
+
+    SkASSERT(storage);
+    *storage = size;
+    return edgeCount;
+}
+
+/* Much faster than worst_case_edge_count, but over estimates even more
+*/
+static int cheap_worst_case_edge_count(const SkPath& path, size_t* storage)
+{
+    int ptCount = path.getPoints(NULL, 0);
+    int edgeCount = ptCount;
+    *storage = edgeCount * sizeof(SkCubicEdge);
+    return edgeCount;
+}
+
+// clipRect may be null, even though we always have a clip. This indicates that
+// the path is contained in the clip, and so we can ignore it during the blit
+//
+// clipRect (if no null) has already been shifted up
+//
+void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitter,
+                  int stop_y, int shiftEdgesUp, const SkRegion& clipRgn)
+{
+    SkASSERT(&path && blitter);
+
+    size_t  size;
+    int     maxCount = cheap_worst_case_edge_count(path, &size);
+
+#ifdef SK_DEBUG
+    {
+        size_t  size2;
+        int     maxCount2 = worst_case_edge_count(path, &size2);
+        
+        SkASSERT(maxCount >= maxCount2 && size >= size2);
+    }
+#endif
+
+    SkAutoMalloc    memory(maxCount * sizeof(SkEdge*) + size);
+    SkEdge**        list = (SkEdge**)memory.get();
+    SkEdge*         edge = (SkEdge*)(list + maxCount);
+    int             count = build_edges(edge, path, clipRect, list, shiftEdgesUp);
+    SkEdge          headEdge, tailEdge, *last;
+
+    SkASSERT(count <= maxCount);
+    if (count == 0) {
+        return;
+    }
+    SkASSERT(count > 1);
+
+    // this returns the first and last edge after they're sorted into a dlink list
+    edge = sort_edges(list, count, &last);
+
+    headEdge.fPrev = NULL;
+    headEdge.fNext = edge;
+    headEdge.fFirstY = kEDGE_HEAD_Y;
+    headEdge.fX = SK_MinS32;
+    edge->fPrev = &headEdge;
+
+    tailEdge.fPrev = last;
+    tailEdge.fNext = NULL;
+    tailEdge.fFirstY = kEDGE_TAIL_Y;
+    last->fNext = &tailEdge;
+
+    // now edge is the head of the sorted linklist
+
+    stop_y <<= shiftEdgesUp;
+    if (clipRect && stop_y > clipRect->fBottom) {
+        stop_y = clipRect->fBottom;
+    }
+
+    InverseBlitter  ib;
+    PrePostProc     proc = NULL;
+
+    if (path.isInverseFillType()) {
+        ib.setBlitter(blitter, clipRgn.getBounds(), shiftEdgesUp);
+        blitter = &ib;
+        proc = PrePostInverseBlitterProc;
+    }
+
+    walk_edges(&headEdge, path.getFillType(), blitter, stop_y, proc);
+}
+
+void sk_blit_above_and_below(SkBlitter* blitter, const SkIRect& ir,
+                             const SkRegion& clip) {
+    const SkIRect& cr = clip.getBounds();
+    SkIRect tmp;
+    
+    tmp.fLeft = cr.fLeft;
+    tmp.fRight = cr.fRight;
+
+    tmp.fTop = cr.fTop;
+    tmp.fBottom = ir.fTop;
+    if (!tmp.isEmpty()) {
+        blitter->blitRectRegion(tmp, clip);
+    }
+
+    tmp.fTop = ir.fBottom;
+    tmp.fBottom = cr.fBottom;
+    if (!tmp.isEmpty()) {
+        blitter->blitRectRegion(tmp, clip);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+SkScanClipper::SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& ir)
+{
+    fBlitter = NULL;     // null means blit nothing
+    fClipRect = NULL;
+
+    if (clip)
+    {
+        fClipRect = &clip->getBounds();
+        if (!SkIRect::Intersects(*fClipRect, ir))  // completely clipped out
+            return;
+
+        if (clip->isRect())
+        {
+            if (fClipRect->contains(ir))
+                fClipRect = NULL;
+            else
+            {
+                // only need a wrapper blitter if we're horizontally clipped
+                if (fClipRect->fLeft > ir.fLeft || fClipRect->fRight < ir.fRight)
+                {
+                    fRectBlitter.init(blitter, *fClipRect);
+                    blitter = &fRectBlitter;
+                }
+            }
+        }
+        else
+        {
+            fRgnBlitter.init(blitter, clip);
+            blitter = &fRgnBlitter;
+        }
+    }
+    fBlitter = blitter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkScan::FillPath(const SkPath& path, const SkRegion& clip,
+                      SkBlitter* blitter) {
+    if (clip.isEmpty()) {
+        return;
+    }
+
+    SkRect  r;
+    SkIRect ir;
+
+    path.computeBounds(&r, SkPath::kFast_BoundsType);
+    r.round(&ir);
+    if (ir.isEmpty()) {
+        if (path.isInverseFillType()) {
+            blitter->blitRegion(clip);
+        }
+        return;
+    }
+
+    SkScanClipper   clipper(blitter, &clip, ir);
+
+    blitter = clipper.getBlitter();
+    if (blitter) {
+        if (path.isInverseFillType()) {
+            sk_blit_above_and_below(blitter, ir, clip);
+        }
+        sk_fill_path(path, clipper.getClipRect(), blitter, ir.fBottom, 0, clip);
+    } else {
+        // what does it mean to not have a blitter if path.isInverseFillType???
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int build_tri_edges(SkEdge edge[], const SkPoint pts[],
+                           const SkIRect* clipRect, SkEdge* list[]) {
+    SkEdge** start = list;
+    
+    if (edge->setLine(pts[0], pts[1], clipRect, 0)) {
+        *list++ = edge;
+        edge = (SkEdge*)((char*)edge + sizeof(SkEdge));
+    }
+    if (edge->setLine(pts[1], pts[2], clipRect, 0)) {
+        *list++ = edge;
+        edge = (SkEdge*)((char*)edge + sizeof(SkEdge));
+    }
+    if (edge->setLine(pts[2], pts[0], clipRect, 0)) {
+        *list++ = edge;
+    }
+    return (int)(list - start);
+}
+
+
+void sk_fill_triangle(const SkPoint pts[], const SkIRect* clipRect,
+                      SkBlitter* blitter, const SkIRect& ir) {
+    SkASSERT(pts && blitter);
+    
+    SkEdge edgeStorage[3];
+    SkEdge* list[3];
+
+    int count = build_tri_edges(edgeStorage, pts, clipRect, list);
+    if (count < 2) {
+        return;
+    }
+
+    SkEdge headEdge, tailEdge, *last;
+
+    // this returns the first and last edge after they're sorted into a dlink list
+    SkEdge* edge = sort_edges(list, count, &last);
+    
+    headEdge.fPrev = NULL;
+    headEdge.fNext = edge;
+    headEdge.fFirstY = kEDGE_HEAD_Y;
+    headEdge.fX = SK_MinS32;
+    edge->fPrev = &headEdge;
+    
+    tailEdge.fPrev = last;
+    tailEdge.fNext = NULL;
+    tailEdge.fFirstY = kEDGE_TAIL_Y;
+    last->fNext = &tailEdge;
+    
+    // now edge is the head of the sorted linklist
+    int stop_y = ir.fBottom;
+    if (clipRect && stop_y > clipRect->fBottom) {
+        stop_y = clipRect->fBottom;
+    }
+    walk_edges(&headEdge, SkPath::kEvenOdd_FillType, blitter, stop_y, NULL);
+}
+
+void SkScan::FillTriangle(const SkPoint pts[], const SkRegion* clip,
+                          SkBlitter* blitter) {
+    if (clip && clip->isEmpty()) {
+        return;
+    }
+    
+    SkRect  r;
+    SkIRect ir;
+    r.set(pts, 3);
+    r.round(&ir);
+    if (ir.isEmpty()) {
+        return;
+    }
+    
+    SkScanClipper   clipper(blitter, clip, ir);
+    
+    blitter = clipper.getBlitter();
+    if (NULL != blitter) {
+        sk_fill_triangle(pts, clipper.getClipRect(), blitter, ir);
+    }
+}
+
diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp
new file mode 100644
index 0000000..dd9c859
--- /dev/null
+++ b/src/core/SkShader.cpp
@@ -0,0 +1,284 @@
+/* libs/graphics/sgl/SkShader.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 "SkShader.h"
+#include "SkPaint.h"
+
+SkShader::SkShader() : fLocalMatrix(NULL) {
+    SkDEBUGCODE(fInSession = false;)
+}
+
+SkShader::SkShader(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer), fLocalMatrix(NULL) {
+    if (buffer.readBool()) {
+        SkMatrix matrix;
+        buffer.read(&matrix, sizeof(matrix));
+        setLocalMatrix(matrix);
+    }
+    SkDEBUGCODE(fInSession = false;)
+}
+
+SkShader::~SkShader() {
+    SkASSERT(!fInSession);
+    sk_free(fLocalMatrix);
+}
+
+void SkShader::beginSession() {
+    SkASSERT(!fInSession);
+    SkDEBUGCODE(fInSession = true;)
+}
+
+void SkShader::endSession() {
+    SkASSERT(fInSession);
+    SkDEBUGCODE(fInSession = false;)
+}
+
+void SkShader::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+    buffer.writeBool(fLocalMatrix != NULL);
+    if (fLocalMatrix) {
+        buffer.writeMul4(fLocalMatrix, sizeof(SkMatrix));
+    }
+}
+
+bool SkShader::getLocalMatrix(SkMatrix* localM) const {
+    if (fLocalMatrix) {
+        if (localM) {
+            *localM = *fLocalMatrix;
+        }
+        return true;
+    } else {
+        if (localM) {
+            localM->reset();
+        }
+        return false;
+    }
+}
+
+void SkShader::setLocalMatrix(const SkMatrix& localM) {
+    if (localM.isIdentity()) {
+        this->resetLocalMatrix();
+    } else {
+        if (fLocalMatrix == NULL) {
+            fLocalMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix));
+        }
+        *fLocalMatrix = localM;
+    }
+}
+
+void SkShader::resetLocalMatrix() {
+    if (fLocalMatrix) {
+        sk_free(fLocalMatrix);
+        fLocalMatrix = NULL;
+    }
+}
+
+bool SkShader::setContext(const SkBitmap& device,
+                          const SkPaint& paint,
+                          const SkMatrix& matrix) {
+    const SkMatrix* m = &matrix;
+    SkMatrix        total;
+
+    fDeviceConfig = SkToU8(device.getConfig());
+    fPaintAlpha = paint.getAlpha();
+    if (fLocalMatrix) {
+        total.setConcat(matrix, *fLocalMatrix);
+        m = &total;
+    }
+    if (m->invert(&fTotalInverse)) {
+        fTotalInverseClass = (uint8_t)ComputeMatrixClass(fTotalInverse);
+        return true;
+    }
+    return false;
+}
+
+#include "SkColorPriv.h"
+
+void SkShader::shadeSpan16(int x, int y, uint16_t span16[], int count) {
+    SkASSERT(span16);
+    SkASSERT(count > 0);
+    SkASSERT(this->canCallShadeSpan16());
+
+    // basically, if we get here, the subclass screwed up
+    SkASSERT(!"kHasSpan16 flag is set, but shadeSpan16() not implemented");
+}
+
+#define kTempColorQuadCount 6   // balance between speed (larger) and saving stack-space
+#define kTempColorCount     (kTempColorQuadCount << 2)  
+
+#ifdef SK_CPU_BENDIAN
+    #define SkU32BitShiftToByteOffset(shift)    (3 - ((shift) >> 3))
+#else
+    #define SkU32BitShiftToByteOffset(shift)    ((shift) >> 3)
+#endif
+
+void SkShader::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
+    SkASSERT(count > 0);
+
+    SkPMColor   colors[kTempColorCount];
+
+    while ((count -= kTempColorCount) >= 0) {
+        this->shadeSpan(x, y, colors, kTempColorCount);
+        x += kTempColorCount;
+
+        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
+        int quads = kTempColorQuadCount;
+        do {
+            U8CPU a0 = srcA[0];
+            U8CPU a1 = srcA[4];
+            U8CPU a2 = srcA[8];
+            U8CPU a3 = srcA[12];
+            srcA += 4*4;
+            *alpha++ = SkToU8(a0);
+            *alpha++ = SkToU8(a1);
+            *alpha++ = SkToU8(a2);
+            *alpha++ = SkToU8(a3);
+        } while (--quads != 0);
+    }
+    SkASSERT(count < 0);
+    SkASSERT(count + kTempColorCount >= 0);
+    if (count += kTempColorCount) {
+        this->shadeSpan(x, y, colors, count);
+
+        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
+        do {
+            *alpha++ = *srcA;
+            srcA += 4;
+        } while (--count != 0);
+    }
+#if 0
+    do {
+        int n = count;
+        if (n > kTempColorCount)
+            n = kTempColorCount;
+        SkASSERT(n > 0);
+
+        this->shadeSpan(x, y, colors, n);
+        x += n;
+        count -= n;
+
+        const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT);
+        do {
+            *alpha++ = *srcA;
+            srcA += 4;
+        } while (--n != 0);
+    } while (count > 0);
+#endif
+}
+
+SkShader::MatrixClass SkShader::ComputeMatrixClass(const SkMatrix& mat) {
+    MatrixClass mc = kLinear_MatrixClass;
+
+    if (mat.getType() & SkMatrix::kPerspective_Mask) {
+        if (mat.fixedStepInX(0, NULL, NULL)) {
+            mc = kFixedStepInX_MatrixClass;
+        } else {
+            mc = kPerspective_MatrixClass;
+        }
+    }
+    return mc;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool SkShader::asABitmap(SkBitmap*, SkMatrix*, TileMode*) {
+    return false;
+}
+
+SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
+                                       TileMode tmx, TileMode tmy) {
+    return SkShader::CreateBitmapShader(src, tmx, tmy, NULL, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorShader.h"
+#include "SkUtils.h"
+
+SkColorShader::SkColorShader(SkFlattenableReadBuffer& b) : INHERITED(b) {
+    fInheritColor = b.readU8(); 
+    if (fInheritColor) {
+        return;
+    }
+    fColor = b.readU32();
+}
+
+void SkColorShader::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+    buffer.write8(fInheritColor);
+    if (fInheritColor) {
+        return;
+    }
+    buffer.write32(fColor);
+}
+
+uint32_t SkColorShader::getFlags() {
+    return (SkGetPackedA32(fPMColor) == 255 ? kOpaqueAlpha_Flag : 0) |
+            kHasSpan16_Flag;
+}
+
+uint8_t SkColorShader::getSpan16Alpha() const {
+    return SkGetPackedA32(fPMColor);
+}
+
+bool SkColorShader::setContext(const SkBitmap& device, const SkPaint& paint,
+                               const SkMatrix& matrix) {
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+
+    SkColor c;
+    unsigned a;
+    
+    if (fInheritColor) {
+        c = paint.getColor();
+        a = SkColorGetA(c);
+    } else {
+        c = fColor;
+        a = SkAlphaMul(SkColorGetA(c), SkAlpha255To256(paint.getAlpha()));
+    }
+
+    unsigned r = SkColorGetR(c);
+    unsigned g = SkColorGetG(c);
+    unsigned b = SkColorGetB(c);
+
+    // we want this before we apply any alpha
+    fColor16 = SkPack888ToRGB16(r, g, b);
+
+    if (a != 255) {
+        a = SkAlpha255To256(a);
+        r = SkAlphaMul(r, a);
+        g = SkAlphaMul(g, a);
+        b = SkAlphaMul(b, a);
+    }
+    fPMColor = SkPackARGB32(a, r, g, b);
+
+    return true;
+}
+
+void SkColorShader::shadeSpan(int x, int y, SkPMColor span[], int count) {
+    sk_memset32(span, fPMColor, count);
+}
+
+void SkColorShader::shadeSpan16(int x, int y, uint16_t span[], int count) {
+    sk_memset16(span, fColor16, count);
+}
+
+void SkColorShader::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
+    memset(alpha, SkGetPackedA32(fPMColor), count);
+}
+
diff --git a/src/core/SkSinTable.h b/src/core/SkSinTable.h
new file mode 100644
index 0000000..9b4477d
--- /dev/null
+++ b/src/core/SkSinTable.h
@@ -0,0 +1,285 @@
+/* libs/corecg/SkSinTable.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 SkSinTable_DEFINED
+#define SkSinTable_DEFINED
+
+#include "SkTypes.h"
+
+/* Fixed point values (low 16 bits) of sin(radians) for
+    radians in [0...PI/2)
+*/
+static const uint16_t gSkSinTable[256] = {
+    0x0000,
+    0x0192,
+    0x0324,
+    0x04B6,
+    0x0648,
+    0x07DA,
+    0x096C,
+    0x0AFE,
+    0x0C8F,
+    0x0E21,
+    0x0FB2,
+    0x1144,
+    0x12D5,
+    0x1466,
+    0x15F6,
+    0x1787,
+    0x1917,
+    0x1AA7,
+    0x1C37,
+    0x1DC7,
+    0x1F56,
+    0x20E5,
+    0x2273,
+    0x2402,
+    0x2590,
+    0x271D,
+    0x28AA,
+    0x2A37,
+    0x2BC4,
+    0x2D50,
+    0x2EDB,
+    0x3066,
+    0x31F1,
+    0x337B,
+    0x3505,
+    0x368E,
+    0x3817,
+    0x399F,
+    0x3B26,
+    0x3CAD,
+    0x3E33,
+    0x3FB9,
+    0x413E,
+    0x42C3,
+    0x4447,
+    0x45CA,
+    0x474D,
+    0x48CE,
+    0x4A50,
+    0x4BD0,
+    0x4D50,
+    0x4ECF,
+    0x504D,
+    0x51CA,
+    0x5347,
+    0x54C3,
+    0x563E,
+    0x57B8,
+    0x5931,
+    0x5AAA,
+    0x5C22,
+    0x5D98,
+    0x5F0E,
+    0x6083,
+    0x61F7,
+    0x636A,
+    0x64DC,
+    0x664D,
+    0x67BD,
+    0x692D,
+    0x6A9B,
+    0x6C08,
+    0x6D74,
+    0x6EDF,
+    0x7049,
+    0x71B1,
+    0x7319,
+    0x7480,
+    0x75E5,
+    0x774A,
+    0x78AD,
+    0x7A0F,
+    0x7B70,
+    0x7CD0,
+    0x7E2E,
+    0x7F8B,
+    0x80E7,
+    0x8242,
+    0x839C,
+    0x84F4,
+    0x864B,
+    0x87A1,
+    0x88F5,
+    0x8A48,
+    0x8B9A,
+    0x8CEA,
+    0x8E39,
+    0x8F87,
+    0x90D3,
+    0x921E,
+    0x9368,
+    0x94B0,
+    0x95F6,
+    0x973C,
+    0x987F,
+    0x99C2,
+    0x9B02,
+    0x9C42,
+    0x9D7F,
+    0x9EBC,
+    0x9FF6,
+    0xA12F,
+    0xA267,
+    0xA39D,
+    0xA4D2,
+    0xA605,
+    0xA736,
+    0xA866,
+    0xA994,
+    0xAAC0,
+    0xABEB,
+    0xAD14,
+    0xAE3B,
+    0xAF61,
+    0xB085,
+    0xB1A8,
+    0xB2C8,
+    0xB3E7,
+    0xB504,
+    0xB620,
+    0xB73A,
+    0xB852,
+    0xB968,
+    0xBA7C,
+    0xBB8F,
+    0xBCA0,
+    0xBDAE,
+    0xBEBC,
+    0xBFC7,
+    0xC0D0,
+    0xC1D8,
+    0xC2DE,
+    0xC3E2,
+    0xC4E3,
+    0xC5E4,
+    0xC6E2,
+    0xC7DE,
+    0xC8D8,
+    0xC9D1,
+    0xCAC7,
+    0xCBBB,
+    0xCCAE,
+    0xCD9F,
+    0xCE8D,
+    0xCF7A,
+    0xD064,
+    0xD14D,
+    0xD233,
+    0xD318,
+    0xD3FA,
+    0xD4DB,
+    0xD5B9,
+    0xD695,
+    0xD770,
+    0xD848,
+    0xD91E,
+    0xD9F2,
+    0xDAC4,
+    0xDB94,
+    0xDC61,
+    0xDD2D,
+    0xDDF6,
+    0xDEBE,
+    0xDF83,
+    0xE046,
+    0xE106,
+    0xE1C5,
+    0xE282,
+    0xE33C,
+    0xE3F4,
+    0xE4AA,
+    0xE55E,
+    0xE60F,
+    0xE6BE,
+    0xE76B,
+    0xE816,
+    0xE8BF,
+    0xE965,
+    0xEA09,
+    0xEAAB,
+    0xEB4B,
+    0xEBE8,
+    0xEC83,
+    0xED1C,
+    0xEDB2,
+    0xEE46,
+    0xEED8,
+    0xEF68,
+    0xEFF5,
+    0xF080,
+    0xF109,
+    0xF18F,
+    0xF213,
+    0xF294,
+    0xF314,
+    0xF391,
+    0xF40B,
+    0xF484,
+    0xF4FA,
+    0xF56D,
+    0xF5DE,
+    0xF64D,
+    0xF6BA,
+    0xF724,
+    0xF78B,
+    0xF7F1,
+    0xF853,
+    0xF8B4,
+    0xF912,
+    0xF96E,
+    0xF9C7,
+    0xFA1E,
+    0xFA73,
+    0xFAC5,
+    0xFB14,
+    0xFB61,
+    0xFBAC,
+    0xFBF5,
+    0xFC3B,
+    0xFC7E,
+    0xFCBF,
+    0xFCFE,
+    0xFD3A,
+    0xFD74,
+    0xFDAB,
+    0xFDE0,
+    0xFE13,
+    0xFE43,
+    0xFE70,
+    0xFE9B,
+    0xFEC4,
+    0xFEEA,
+    0xFF0E,
+    0xFF2F,
+    0xFF4E,
+    0xFF6A,
+    0xFF84,
+    0xFF9C,
+    0xFFB1,
+    0xFFC3,
+    0xFFD3,
+    0xFFE1,
+    0xFFEC,
+    0xFFF4,
+    0xFFFB,
+    0xFFFE
+};
+
+#endif
diff --git a/src/core/SkSpriteBlitter.h b/src/core/SkSpriteBlitter.h
new file mode 100644
index 0000000..9f7764d
--- /dev/null
+++ b/src/core/SkSpriteBlitter.h
@@ -0,0 +1,55 @@
+/* libs/graphics/sgl/SkSpriteBlitter.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 SkSpriteBlitter_DEFINED
+#define SkSpriteBlitter_DEFINED
+
+#include "SkBlitter.h"
+#include "SkBitmap.h"
+
+class SkPaint;
+
+class SkSpriteBlitter : public SkBlitter {
+public:
+            SkSpriteBlitter(const SkBitmap& source);
+    virtual ~SkSpriteBlitter();
+
+    virtual void setup(const SkBitmap& device, int left, int top,
+                       const SkPaint& paint);
+
+    // overrides
+#ifdef SK_DEBUG
+    virtual void    blitH(int x, int y, int width);
+    virtual void    blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+    virtual void    blitV(int x, int y, int height, SkAlpha alpha);
+    virtual void    blitMask(const SkMask&, const SkIRect& clip);
+#endif
+
+    static SkSpriteBlitter* ChooseD16(const SkBitmap& source, const SkPaint&,
+                                      void* storage, size_t storageSize);
+    static SkSpriteBlitter* ChooseD32(const SkBitmap& source, const SkPaint&,
+                                      void* storage, size_t storageSize);
+
+protected:
+    const SkBitmap* fDevice;
+    const SkBitmap* fSource;
+    int             fLeft, fTop;
+    const SkPaint*  fPaint;
+};
+
+#endif
+
diff --git a/src/core/SkSpriteBlitterTemplate.h b/src/core/SkSpriteBlitterTemplate.h
new file mode 100644
index 0000000..2ab2b3a
--- /dev/null
+++ b/src/core/SkSpriteBlitterTemplate.h
@@ -0,0 +1,91 @@
+/* libs/graphics/sgl/SkSpriteBlitterTemplate.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.
+*/
+
+
+class SkSPRITE_CLASSNAME : public SkSpriteBlitter {
+public:
+    SkSPRITE_CLASSNAME(const SkBitmap& source SkSPRITE_ARGS)
+        : SkSpriteBlitter(source) {
+        SkSPRITE_INIT
+    }
+
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        int srcX = x - fLeft;
+        int srcY = y - fTop;
+        SK_RESTRICT SkSPRITE_DST_TYPE* dst =fDevice->SkSPRITE_DST_GETADDR(x, y);
+        const SK_RESTRICT SkSPRITE_SRC_TYPE* src =
+                                      fSource->SkSPRITE_SRC_GETADDR(srcX, srcY);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+
+        SkDEBUGCODE((void)fDevice->SkSPRITE_DST_GETADDR(x + width - 1, y + height - 1);)
+        SkDEBUGCODE((void)fSource->SkSPRITE_SRC_GETADDR(srcX + width  - 1, srcY + height - 1);)
+
+        SkSPRITE_PREAMBLE((*fSource), srcX, srcY);
+
+        do {
+            SkSPRITE_DST_TYPE* d = dst;
+            const SkSPRITE_SRC_TYPE* s = src;
+#ifdef SkSPRITE_BEGIN_ROW
+            SkSPRITE_BEGIN_ROW
+#endif
+
+#ifdef SkSPRITE_ROW_PROC
+            SkSPRITE_ROW_PROC(d, s, width, x, y);
+#else
+            int w = width;
+            do {
+                SkSPRITE_SRC_TYPE sc = *s++;
+                SkSPRITE_BLIT_PIXEL(d, sc);
+                d += 1;
+            } while (--w != 0);
+#endif
+            dst = (SK_RESTRICT SkSPRITE_DST_TYPE*)((char*)dst + dstRB);
+            src = (const SK_RESTRICT SkSPRITE_SRC_TYPE*)
+                                            ((const char*)src + srcRB);
+            SkSPRITE_NEXT_ROW
+#ifdef SkSPRITE_ROW_PROC
+            y += 1;
+#endif
+        } while (--height != 0);
+
+        SkSPRITE_POSTAMBLE((*fSource));
+    }
+
+private:
+    SkSPRITE_FIELDS
+};
+
+#undef SkSPRITE_BLIT_PIXEL
+#undef SkSPRITE_CLASSNAME
+#undef SkSPRITE_DST_TYPE
+#undef SkSPRITE_SRC_TYPE
+#undef SkSPRITE_DST_GETADDR
+#undef SkSPRITE_SRC_GETADDR
+#undef SkSPRITE_PREAMBLE
+#undef SkSPRITE_POSTAMBLE
+#undef SkSPRITE_ARGS
+#undef SkSPRITE_FIELDS
+#undef SkSPRITE_INIT
+#undef SkSPRITE_NEXT_ROW
+#undef SkSPRITE_BEGIN_ROW
+
+#ifdef SkSPRITE_ROW_PROC
+    #undef SkSPRITE_ROW_PROC
+#endif
+
diff --git a/src/core/SkSpriteBlitter_ARGB32.cpp b/src/core/SkSpriteBlitter_ARGB32.cpp
new file mode 100644
index 0000000..6addde7
--- /dev/null
+++ b/src/core/SkSpriteBlitter_ARGB32.cpp
@@ -0,0 +1,314 @@
+/* libs/graphics/sgl/SkSpriteBlitter_ARGB32.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 "SkSpriteBlitter.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+
+#define D32_S32A_Opaque_Pixel(dst, sc) \
+do {                                                                  \
+    if (sc) {                                                         \
+        unsigned srcA = SkGetPackedA32(sc);                           \
+        uint32_t result = sc;                                         \
+        if (srcA != 0xFF) {                                           \
+            result += SkAlphaMulQ(*dst, SkAlpha255To256(255 - srcA)); \
+        }                                                             \
+        *dst = result;                                                \
+    }                                                                 \
+} while (0)
+
+#define SkSPRITE_CLASSNAME                  Sprite_D32_S32A_Opaque
+#define SkSPRITE_ARGS
+#define SkSPRITE_FIELDS
+#define SkSPRITE_INIT
+#define SkSPRITE_DST_TYPE                   uint32_t
+#define SkSPRITE_SRC_TYPE                   uint32_t
+#define SkSPRITE_DST_GETADDR                getAddr32
+#define SkSPRITE_SRC_GETADDR                getAddr32
+#define SkSPRITE_PREAMBLE(srcBM, x, y)
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D32_S32A_Opaque_Pixel(dst, src)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)
+#include "SkSpriteBlitterTemplate.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sprite_D32_S32_Opaque : public SkSpriteBlitter {
+public:
+    Sprite_D32_S32_Opaque(const SkBitmap& source) : SkSpriteBlitter(source) {}
+
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        SK_RESTRICT uint32_t* dst = fDevice->getAddr32(x, y);
+        const SK_RESTRICT uint32_t* src = fSource->getAddr32(x - fLeft,
+                                                             y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+        size_t size = width * sizeof(uint32_t);
+
+        do {
+            memcpy(dst, src, size);
+            dst = (SK_RESTRICT uint32_t*)((char*)dst + dstRB);
+            src = (const SK_RESTRICT uint32_t*)((const char*)src + srcRB);
+        } while (--height != 0);
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorFilter.h"
+#include "SkXfermode.h"
+
+class Sprite_D32_XferFilter : public SkSpriteBlitter {
+public:
+    Sprite_D32_XferFilter(const SkBitmap& source, const SkPaint& paint)
+        : SkSpriteBlitter(source) {
+        fColorFilter = paint.getColorFilter();
+        fColorFilter->safeRef();
+        
+        fXfermode = paint.getXfermode();
+        fXfermode->safeRef();
+        
+        fBufferSize = 0;
+        fBuffer = NULL;
+    }
+    
+    virtual ~Sprite_D32_XferFilter() {
+        delete[] fBuffer;
+        fXfermode->safeUnref();
+        fColorFilter->safeUnref();
+    }
+    
+    virtual void setup(const SkBitmap& device, int left, int top,
+                       const SkPaint& paint) {
+        this->INHERITED::setup(device, left, top, paint);
+        
+        int width = device.width();
+        if (width > fBufferSize) {
+            fBufferSize = width;
+            delete[] fBuffer;
+            fBuffer = new SkPMColor[width];
+        }
+    }
+
+protected:
+    SkColorFilter*  fColorFilter;
+    SkXfermode*     fXfermode;
+    int             fBufferSize;
+    SkPMColor*      fBuffer;
+    
+private:
+    typedef SkSpriteBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sprite_D32_S32A_XferFilter : public Sprite_D32_XferFilter {
+public:
+    Sprite_D32_S32A_XferFilter(const SkBitmap& source, const SkPaint& paint)
+        : Sprite_D32_XferFilter(source, paint) {}
+
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        SK_RESTRICT uint32_t* dst = fDevice->getAddr32(x, y);
+        const SK_RESTRICT uint32_t* src = fSource->getAddr32(x - fLeft,
+                                                             y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+        SkColorFilter* colorFilter = fColorFilter;
+        SkXfermode* xfermode = fXfermode;
+
+        do {
+            const SkPMColor* tmp = src;
+            
+            if (NULL != colorFilter) {
+                colorFilter->filterSpan(src, width, fBuffer);
+                tmp = fBuffer;
+            }
+            
+            if (NULL != xfermode) {
+                xfermode->xfer32(dst, tmp, width, NULL);
+            } else {
+                for (int i = 0; i < width; i++) {
+                    dst[i] = SkPMSrcOver(tmp[i], dst[i]);
+                }
+            }
+
+            dst = (SK_RESTRICT uint32_t*)((char*)dst + dstRB);
+            src = (const SK_RESTRICT uint32_t*)((const char*)src + srcRB);
+        } while (--height != 0);
+    }
+    
+private:
+    typedef Sprite_D32_XferFilter INHERITED;
+};
+
+static void fillbuffer(SK_RESTRICT SkPMColor dst[],
+                       const SK_RESTRICT SkPMColor16 src[], int count) {
+    SkASSERT(count > 0);
+    
+    do {
+        *dst++ = SkPixel4444ToPixel32(*src++);
+    } while (--count != 0);
+}
+
+class Sprite_D32_S4444_XferFilter : public Sprite_D32_XferFilter {
+public:
+    Sprite_D32_S4444_XferFilter(const SkBitmap& source, const SkPaint& paint)
+        : Sprite_D32_XferFilter(source, paint) {}
+    
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y);
+        const SK_RESTRICT SkPMColor16* src = fSource->getAddr16(x - fLeft,
+                                                                y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+        SK_RESTRICT SkPMColor* buffer = fBuffer;
+        SkColorFilter* colorFilter = fColorFilter;
+        SkXfermode* xfermode = fXfermode;
+
+        do {
+            fillbuffer(buffer, src, width);
+            
+            if (NULL != colorFilter) {
+                colorFilter->filterSpan(buffer, width, buffer);
+            }
+            if (NULL != xfermode) {
+                xfermode->xfer32(dst, buffer, width, NULL);
+            } else {
+                for (int i = 0; i < width; i++) {
+                    dst[i] = SkPMSrcOver(buffer[i], dst[i]);
+                }
+            }
+            
+            dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB);
+            src = (const SK_RESTRICT SkPMColor16*)((const char*)src + srcRB);
+        } while (--height != 0);
+    }
+    
+private:
+    typedef Sprite_D32_XferFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void src_row(SK_RESTRICT SkPMColor dst[],
+                    const SK_RESTRICT SkPMColor16 src[], int count) {
+    do {
+        *dst = SkPixel4444ToPixel32(*src);
+        src += 1;
+        dst += 1;
+    } while (--count != 0);
+}
+
+class Sprite_D32_S4444_Opaque : public SkSpriteBlitter {
+public:
+    Sprite_D32_S4444_Opaque(const SkBitmap& source) : SkSpriteBlitter(source) {}
+    
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y);
+        const SK_RESTRICT SkPMColor16* src = fSource->getAddr16(x - fLeft,
+                                                                y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+        
+        do {
+            src_row(dst, src, width);
+            dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB);
+            src = (const SK_RESTRICT SkPMColor16*)((const char*)src + srcRB);
+        } while (--height != 0);
+    }
+};
+
+static void srcover_row(SK_RESTRICT SkPMColor dst[],
+                        const SK_RESTRICT SkPMColor16 src[], int count) {
+    do {
+        *dst = SkPMSrcOver(SkPixel4444ToPixel32(*src), *dst);
+        src += 1;
+        dst += 1;
+    } while (--count != 0);
+}
+
+class Sprite_D32_S4444 : public SkSpriteBlitter {
+public:
+    Sprite_D32_S4444(const SkBitmap& source) : SkSpriteBlitter(source) {}
+    
+    virtual void blitRect(int x, int y, int width, int height) {
+        SkASSERT(width > 0 && height > 0);
+        SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y);
+        const SK_RESTRICT SkPMColor16* src = fSource->getAddr16(x - fLeft,
+                                                                y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+        
+        do {
+            srcover_row(dst, src, width);
+            dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB);
+            src = (const SK_RESTRICT SkPMColor16*)((const char*)src + srcRB);
+        } while (--height != 0);
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTemplatesPriv.h"
+
+SkSpriteBlitter* SkSpriteBlitter::ChooseD32(const SkBitmap& source,
+                                            const SkPaint& paint,
+                                            void* storage, size_t storageSize) {
+    if (paint.getMaskFilter() != NULL || paint.getAlpha() != 0xFF) {
+        return NULL;
+    }
+
+    SkXfermode* xfermode = paint.getXfermode();
+    SkColorFilter* filter = paint.getColorFilter();
+    SkSpriteBlitter* blitter = NULL;
+
+    switch (source.getConfig()) {
+        case SkBitmap::kARGB_4444_Config:
+            if (xfermode || filter) {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444_XferFilter,
+                                      storage, storageSize, (source, paint));
+            } else if (source.isOpaque()) {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444_Opaque,
+                                      storage, storageSize, (source));
+            } else {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444,
+                                      storage, storageSize, (source));
+            }
+            break;
+        case SkBitmap::kARGB_8888_Config:
+            if (xfermode || filter) {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32A_XferFilter,
+                                      storage, storageSize, (source, paint));
+            } else if (source.isOpaque()) {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32_Opaque,
+                                      storage, storageSize, (source));
+            } else {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32A_Opaque,
+                                      storage, storageSize, (source));
+            }
+            break;
+        default:
+            break;
+    }
+    return blitter;
+}
+
diff --git a/src/core/SkSpriteBlitter_RGB16.cpp b/src/core/SkSpriteBlitter_RGB16.cpp
new file mode 100644
index 0000000..a158637
--- /dev/null
+++ b/src/core/SkSpriteBlitter_RGB16.cpp
@@ -0,0 +1,320 @@
+/* libs/graphics/sgl/SkSpriteBlitter_RGB16.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 "SkSpriteBlitter.h"
+#include "SkBlitRow.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+
+#define D16_S32A_Opaque_Pixel(dst, sc)                                        \
+do {                                                                          \
+    if (sc) {                                                                 \
+        *dst = SkSrcOver32To16(sc, *dst);                                     \
+    }                                                                         \
+} while (0)
+
+static inline void D16_S32A_Blend_Pixel_helper(uint16_t* dst, SkPMColor sc,
+                                               unsigned src_scale) {
+    uint16_t dc = *dst;
+    unsigned sa = SkGetPackedA32(sc);
+    unsigned dr, dg, db;
+
+    if (255 == sa) {
+        dr = SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), src_scale);
+        dg = SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), src_scale);
+        db = SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), src_scale);
+    } else {
+        unsigned dst_scale = 255 - SkAlphaMul(sa, src_scale);
+        dr = (SkPacked32ToR16(sc) * src_scale +
+              SkGetPackedR16(dc) * dst_scale) >> 8;
+        dg = (SkPacked32ToG16(sc) * src_scale +
+              SkGetPackedG16(dc) * dst_scale) >> 8;
+        db = (SkPacked32ToB16(sc) * src_scale +
+              SkGetPackedB16(dc) * dst_scale) >> 8;
+    }
+    *dst = SkPackRGB16(dr, dg, db);
+}
+
+#define D16_S32A_Blend_Pixel(dst, sc, src_scale) \
+    do { if (sc) D16_S32A_Blend_Pixel_helper(dst, sc, src_scale); } while (0)
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sprite_D16_S16_Opaque : public SkSpriteBlitter {
+public:
+    Sprite_D16_S16_Opaque(const SkBitmap& source)
+        : SkSpriteBlitter(source) {}
+
+    // overrides
+    virtual void blitRect(int x, int y, int width, int height) {
+        SK_RESTRICT uint16_t* dst = fDevice->getAddr16(x, y);
+        const SK_RESTRICT uint16_t* src = fSource->getAddr16(x - fLeft,
+                                                             y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+
+        while (--height >= 0) {
+            memcpy(dst, src, width << 1);
+            dst = (uint16_t*)((char*)dst + dstRB);
+            src = (const uint16_t*)((const char*)src + srcRB);
+        }
+    }
+};
+
+#define D16_S16_Blend_Pixel(dst, sc, scale)     \
+    do {                                        \
+        uint16_t dc = *dst;                     \
+        *dst = SkBlendRGB16(sc, dc, scale);     \
+    } while (0)
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_S16_Blend
+#define SkSPRITE_ARGS                       , uint8_t alpha
+#define SkSPRITE_FIELDS                     uint8_t  fSrcAlpha;
+#define SkSPRITE_INIT                       fSrcAlpha = alpha;
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint16_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr16
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      int scale = SkAlpha255To256(fSrcAlpha);
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S16_Blend_Pixel(dst, src, scale)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)
+#include "SkSpriteBlitterTemplate.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define D16_S4444_Opaque(dst, sc)           \
+    do {                                    \
+        uint16_t dc = *dst;                 \
+        *dst = SkSrcOver4444To16(sc, dc);   \
+    } while (0)
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_S4444_Opaque
+#define SkSPRITE_ARGS                       
+#define SkSPRITE_FIELDS                     
+#define SkSPRITE_INIT                       
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   SkPMColor16
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr16
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S4444_Opaque(dst, src)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)
+#include "SkSpriteBlitterTemplate.h"
+
+#define D16_S4444_Blend(dst, sc, scale16)           \
+    do {                                            \
+        uint16_t dc = *dst;                         \
+        *dst = SkBlend4444To16(sc, dc, scale16);    \
+    } while (0)
+
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_S4444_Blend
+#define SkSPRITE_ARGS                       , uint8_t alpha
+#define SkSPRITE_FIELDS                     uint8_t  fSrcAlpha;
+#define SkSPRITE_INIT                       fSrcAlpha = alpha;
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint16_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr16
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      int scale = SkAlpha15To16(fSrcAlpha);
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S4444_Blend(dst, src, scale)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)
+#include "SkSpriteBlitterTemplate.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8A_Opaque
+#define SkSPRITE_ARGS
+#define SkSPRITE_FIELDS
+#define SkSPRITE_INIT
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint8_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr8
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      const SkPMColor* ctable = srcBM.getColorTable()->lockColors()
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S32A_Opaque_Pixel(dst, ctable[src])
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)           srcBM.getColorTable()->unlockColors(false)
+#include "SkSpriteBlitterTemplate.h"
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8A_Blend
+#define SkSPRITE_ARGS                       , uint8_t alpha
+#define SkSPRITE_FIELDS                     uint8_t fSrcAlpha;
+#define SkSPRITE_INIT                       fSrcAlpha = alpha;
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint8_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr8
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      const SkPMColor* ctable = srcBM.getColorTable()->lockColors(); unsigned src_scale = SkAlpha255To256(fSrcAlpha);
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S32A_Blend_Pixel(dst, ctable[src], src_scale)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)           srcBM.getColorTable()->unlockColors(false);
+#include "SkSpriteBlitterTemplate.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8_Opaque
+#define SkSPRITE_ARGS
+#define SkSPRITE_FIELDS
+#define SkSPRITE_INIT
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint8_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr8
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      const uint16_t* ctable = srcBM.getColorTable()->lock16BitCache()
+#define SkSPRITE_BLIT_PIXEL(dst, src)       *dst = ctable[src]
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)           srcBM.getColorTable()->unlock16BitCache()
+#include "SkSpriteBlitterTemplate.h"
+
+#define SkSPRITE_CLASSNAME                  Sprite_D16_SIndex8_Blend
+#define SkSPRITE_ARGS                       , uint8_t alpha
+#define SkSPRITE_FIELDS                     uint8_t fSrcAlpha;
+#define SkSPRITE_INIT                       fSrcAlpha = alpha;
+#define SkSPRITE_DST_TYPE                   uint16_t
+#define SkSPRITE_SRC_TYPE                   uint8_t
+#define SkSPRITE_DST_GETADDR                getAddr16
+#define SkSPRITE_SRC_GETADDR                getAddr8
+#define SkSPRITE_PREAMBLE(srcBM, x, y)      const uint16_t* ctable = srcBM.getColorTable()->lock16BitCache(); unsigned src_scale = SkAlpha255To256(fSrcAlpha);
+#define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S16_Blend_Pixel(dst, ctable[src], src_scale)
+#define SkSPRITE_NEXT_ROW
+#define SkSPRITE_POSTAMBLE(srcBM)           srcBM.getColorTable()->unlock16BitCache();
+#include "SkSpriteBlitterTemplate.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sprite_D16_S32_BlitRowProc : public SkSpriteBlitter {
+public:
+    Sprite_D16_S32_BlitRowProc(const SkBitmap& source)
+        : SkSpriteBlitter(source) {}
+    
+    // overrides
+    
+    virtual void setup(const SkBitmap& device, int left, int top,
+                       const SkPaint& paint) {
+        this->INHERITED::setup(device, left, top, paint);
+        
+        unsigned flags = 0;
+        
+        if (paint.getAlpha() < 0xFF) {
+            flags |= SkBlitRow::kGlobalAlpha_Flag;
+        }
+        if (!fSource->isOpaque()) {
+            flags |= SkBlitRow::kSrcPixelAlpha_Flag;
+        }
+        if (paint.isDither()) {
+            flags |= SkBlitRow::kDither_Flag;
+        }
+        fProc = SkBlitRow::Factory(flags, SkBitmap::kRGB_565_Config);
+    }
+    
+    virtual void blitRect(int x, int y, int width, int height) {
+        SK_RESTRICT uint16_t* dst = fDevice->getAddr16(x, y);
+        const SK_RESTRICT SkPMColor* src = fSource->getAddr32(x - fLeft,
+                                                              y - fTop);
+        unsigned dstRB = fDevice->rowBytes();
+        unsigned srcRB = fSource->rowBytes();
+        SkBlitRow::Proc proc = fProc;
+        U8CPU alpha = fPaint->getAlpha();
+        
+        while (--height >= 0) {
+            proc(dst, src, width, alpha, x, y);
+            y += 1;
+            dst = (SK_RESTRICT uint16_t*)((char*)dst + dstRB);
+            src = (const SK_RESTRICT SkPMColor*)((const char*)src + srcRB);
+        }
+    }
+    
+private:
+    SkBlitRow::Proc fProc;
+    
+    typedef SkSpriteBlitter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTemplatesPriv.h"
+
+SkSpriteBlitter* SkSpriteBlitter::ChooseD16(const SkBitmap& source,
+                                            const SkPaint& paint,
+                                            void* storage, size_t storageSize) {
+    if (paint.getMaskFilter() != NULL) { // may add cases for this
+        return NULL;
+    }
+    if (paint.getXfermode() != NULL) { // may add cases for this
+        return NULL;
+    }
+    if (paint.getColorFilter() != NULL) { // may add cases for this
+        return NULL;
+    }
+
+    SkSpriteBlitter* blitter = NULL;
+    unsigned alpha = paint.getAlpha();
+
+    switch (source.getConfig()) {
+        case SkBitmap::kARGB_8888_Config:
+            SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S32_BlitRowProc,
+                                  storage, storageSize, (source));
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            if (255 == alpha) {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S4444_Opaque,
+                                      storage, storageSize, (source));
+            } else {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S4444_Blend,
+                                    storage, storageSize, (source, alpha >> 4));
+            }
+            break;
+        case SkBitmap::kRGB_565_Config:
+            if (255 == alpha) {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S16_Opaque,
+                                      storage, storageSize, (source));
+            } else {
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_S16_Blend,
+                                      storage, storageSize, (source, alpha));
+            }
+            break;
+        case SkBitmap::kIndex8_Config:
+            if (source.isOpaque()) {
+                if (255 == alpha) {
+                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8_Opaque,
+                                          storage, storageSize, (source));
+                } else {
+                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8_Blend,
+                                         storage, storageSize, (source, alpha));
+                }
+            } else {
+                if (255 == alpha) {
+                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8A_Opaque,
+                                          storage, storageSize, (source));
+                } else {
+                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D16_SIndex8A_Blend,
+                                         storage, storageSize, (source, alpha));
+                }
+            }
+            break;
+        default:
+            break;
+    }
+    return blitter;
+}
+
diff --git a/src/core/SkString.cpp b/src/core/SkString.cpp
new file mode 100644
index 0000000..2e5d946
--- /dev/null
+++ b/src/core/SkString.cpp
@@ -0,0 +1,642 @@
+/* libs/graphics/sgl/SkString.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 "SkString.h"
+#include "SkFixed.h"
+#include "SkUtils.h"
+#include <stdarg.h>
+
+bool SkStrStartsWith(const char string[], const char prefix[])
+{
+    SkASSERT(string);
+    SkASSERT(prefix);
+    return !strncmp(string, prefix, strlen(prefix));
+}
+
+bool SkStrEndsWith(const char string[], const char suffix[])
+{
+    SkASSERT(string);
+    SkASSERT(suffix);
+    size_t  strLen = strlen(string);
+    size_t  suffixLen = strlen(suffix);
+    return  strLen >= suffixLen &&
+            !strncmp(string + strLen - suffixLen, suffix, suffixLen);
+}
+
+int SkStrStartsWithOneOf(const char string[], const char prefixes[])
+{
+    int index = 0;
+    do {
+        const char* limit = strchr(prefixes, '\0');
+        if (!strncmp(string, prefixes, limit - prefixes))
+            return index;
+        prefixes = limit + 1;
+        index++;
+    } while (prefixes[0]);
+    return -1;
+}
+
+char* SkStrAppendS32(char string[], int32_t dec)
+{
+    SkDEBUGCODE(char* start = string;)
+
+    char    buffer[SkStrAppendS32_MaxSize];
+    char*   p = buffer + sizeof(buffer);
+    bool    neg = false;
+
+    if (dec < 0)
+    {
+        neg = true;
+        dec = -dec;
+    }
+    do {
+        *--p = SkToU8('0' + dec % 10);
+        dec /= 10;
+    } while (dec != 0);
+    if (neg)
+        *--p = '-';
+
+    SkASSERT(p >= buffer);
+    char* stop = buffer + sizeof(buffer);
+    while (p < stop)
+        *string++ = *p++;
+
+    SkASSERT(string - start <= SkStrAppendS32_MaxSize);
+    return string;
+}
+
+char* SkStrAppendScalar(char string[], SkScalar value)
+{
+    SkDEBUGCODE(char* start = string;)
+
+    SkFixed x = SkScalarToFixed(value);
+
+    if (x < 0)
+    {
+        *string++ = '-';
+        x = -x;
+    }
+
+    unsigned frac = x & 0xFFFF;
+    x >>= 16;
+    if (frac == 0xFFFF) // need to do this to "round up", since 65535/65536 is closer to 1 than to .9999
+    {
+        x += 1;
+        frac = 0;
+    }
+    string = SkStrAppendS32(string, x);
+
+    // now handle the fractional part (if any)
+    if (frac)
+    {
+        static const uint16_t   gTens[] = { 1000, 100, 10, 1 };
+        const uint16_t*         tens = gTens;
+
+        x = SkFixedRound(frac * 10000);
+        SkASSERT(x <= 10000);
+        if (x == 10000) {
+            x -= 1;
+        }
+        *string++ = '.';
+        do {
+            unsigned powerOfTen = *tens++;
+            *string++ = SkToU8('0' + x / powerOfTen);
+            x %= powerOfTen;
+        } while (x != 0);
+    }
+    
+    SkASSERT(string - start <= SkStrAppendScalar_MaxSize);
+    return string;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+#define kMaxRefCnt_SkString     SK_MaxU16
+
+// the 3 values are [length] [refcnt] [terminating zero data]
+const SkString::Rec SkString::gEmptyRec = { 0, 0, 0 };
+
+#define SizeOfRec()     (gEmptyRec.data() - (const char*)&gEmptyRec)
+
+SkString::Rec* SkString::AllocRec(const char text[], U16CPU len)
+{
+    Rec* rec;
+
+    if (len == 0)
+        rec = const_cast<Rec*>(&gEmptyRec);
+    else
+    {
+        // add 1 for terminating 0, then align4 so we can have some slop when growing the string
+        rec = (Rec*)sk_malloc_throw(SizeOfRec() + SkAlign4(len + 1));
+        rec->fLength = SkToU16(len);
+        rec->fRefCnt = 1;
+        if (text)
+            memcpy(rec->data(), text, len);
+        rec->data()[len] = 0;
+    }
+    return rec;
+}
+
+SkString::Rec* SkString::RefRec(Rec* src)
+{
+    if (src != &gEmptyRec)
+    {
+        if (src->fRefCnt == kMaxRefCnt_SkString) {
+            src = AllocRec(src->data(), src->fLength);
+        } else
+            src->fRefCnt += 1;
+    }
+    return src;
+}
+
+#ifdef SK_DEBUG
+void SkString::validate() const
+{
+    // make sure know one has written over our global
+    SkASSERT(gEmptyRec.fLength == 0);
+    SkASSERT(gEmptyRec.fRefCnt == 0);
+    SkASSERT(gEmptyRec.data()[0] == 0);
+
+    if (fRec != &gEmptyRec)
+    {
+        SkASSERT(fRec->fLength > 0);
+        SkASSERT(fRec->fRefCnt > 0);
+        SkASSERT(fRec->data()[fRec->fLength] == 0);
+    }
+    SkASSERT(fStr == c_str());
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////
+
+SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+SkString::SkString(size_t len)
+{
+    SkASSERT(SkToU16(len) == len);  // can't handle larger than 64K
+
+    fRec = AllocRec(NULL, (U16CPU)len);
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+SkString::SkString(const char text[])
+{
+    size_t  len = text ? strlen(text) : 0;
+
+    fRec = AllocRec(text, (U16CPU)len);
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+SkString::SkString(const char text[], size_t len)
+{
+    fRec = AllocRec(text, (U16CPU)len);
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+SkString::SkString(const SkString& src)
+{
+    src.validate();
+
+    fRec = RefRec(src.fRec);
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+SkString::~SkString()
+{
+    this->validate();
+
+    if (fRec->fLength)
+    {
+        SkASSERT(fRec->fRefCnt > 0);
+        if (--fRec->fRefCnt == 0)
+            sk_free(fRec);
+    }
+}
+
+bool SkString::equals(const SkString& src) const
+{
+    return fRec == src.fRec || this->equals(src.c_str(), src.size());
+}
+
+bool SkString::equals(const char text[]) const
+{
+    return this->equals(text, text ? strlen(text) : 0);
+}
+
+bool SkString::equals(const char text[], size_t len) const
+{
+    SkASSERT(len == 0 || text != NULL);
+
+    return fRec->fLength == len && !memcmp(fRec->data(), text, len);
+}
+
+SkString& SkString::operator=(const SkString& src)
+{
+    this->validate();
+
+    if (fRec != src.fRec)
+    {
+        SkString    tmp(src);
+        this->swap(tmp);
+    }
+    return *this;
+}
+
+void SkString::reset()
+{
+    this->validate();
+
+    if (fRec->fLength)
+    {
+        SkASSERT(fRec->fRefCnt > 0);
+        if (--fRec->fRefCnt == 0)
+            sk_free(fRec);
+    }
+
+    fRec = const_cast<Rec*>(&gEmptyRec);
+#ifdef SK_DEBUG
+    fStr = fRec->data();
+#endif
+}
+
+char* SkString::writable_str()
+{
+    this->validate();
+
+    if (fRec->fLength)
+    {
+        if (fRec->fRefCnt > 1)
+        {
+            fRec->fRefCnt -= 1;
+            fRec = AllocRec(fRec->data(), fRec->fLength);
+        #ifdef SK_DEBUG
+            fStr = fRec->data();
+        #endif
+        }
+    }
+    return fRec->data();
+}
+
+void SkString::set(const char text[])
+{
+    this->set(text, text ? strlen(text) : 0);
+}
+
+void SkString::set(const char text[], size_t len)
+{
+    if (len == 0)
+        this->reset();
+    else if (fRec->fRefCnt == 1 && len <= fRec->fLength)    // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))
+    {
+        // just use less of the buffer without allocating a smaller one
+        char* p = this->writable_str();
+        if (text)
+            memcpy(p, text, len);
+        p[len] = 0;
+        fRec->fLength = SkToU16(len);
+    }
+    else if (fRec->fRefCnt == 1 && ((unsigned)fRec->fLength >> 2) == (len >> 2))
+    {
+        // we have spare room in the current allocation, so don't alloc a larger one
+        char* p = this->writable_str();
+        if (text)
+            memcpy(p, text, len);
+        p[len] = 0;
+        fRec->fLength = SkToU16(len);
+    }
+    else
+    {
+        SkString tmp(text, len);
+        this->swap(tmp);
+    }
+}
+
+void SkString::setUTF16(const uint16_t src[])
+{
+    int count = 0;
+
+    while (src[count])
+        count += 1;
+    setUTF16(src, count);
+}
+
+void SkString::setUTF16(const uint16_t src[], size_t count)
+{
+    if (count == 0)
+        this->reset();
+    else if (count <= fRec->fLength)    // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))
+    {
+        if (count < fRec->fLength)
+            this->resize(count);
+        char* p = this->writable_str();
+        for (size_t i = 0; i < count; i++)
+            p[i] = SkToU8(src[i]);
+        p[count] = 0;
+    }
+    else
+    {
+        SkString    tmp(count); // puts a null terminator at the end of the string
+        char*       p = tmp.writable_str();
+
+        for (size_t i = 0; i < count; i++)
+            p[i] = SkToU8(src[i]);
+
+        this->swap(tmp);
+    }
+}
+
+void SkString::insert(size_t offset, const char text[])
+{
+    this->insert(offset, text, text ? strlen(text) : 0);
+}
+
+void SkString::insert(size_t offset, const char text[], size_t len)
+{
+    if (len)
+    {
+        size_t length = fRec->fLength;
+        if (offset > length)
+            offset = length;
+
+        /*  If we're the only owner, and we have room in our allocation for the insert,
+            do it in place, rather than allocating a new buffer.
+
+            To know we have room, compare the allocated sizes
+            beforeAlloc = SkAlign4(length + 1)
+            afterAlloc  = SkAligh4(length + 1 + len)
+            but SkAlign4(x) is (x + 3) >> 2 << 2
+            which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
+            and we can then eliminate the +1+3 since that doesn't affec the answer
+        */
+        if (fRec->fRefCnt == 1 && (length >> 2) == ((length + len) >> 2))
+        {
+            char* dst = this->writable_str();
+
+            if (offset < length)
+                memmove(dst + offset + len, dst + offset, length - offset);
+            memcpy(dst + offset, text, len);
+
+            dst[length + len] = 0;
+            fRec->fLength = SkToU16(length + len);
+        }
+        else
+        {
+            /*  Seems we should use realloc here, since that is safe if it fails
+                (we have the original data), and might be faster than alloc/copy/free.
+            */
+            SkString    tmp(fRec->fLength + len);
+            char*       dst = tmp.writable_str();
+
+            if (offset > 0)
+                memcpy(dst, fRec->data(), offset);
+            memcpy(dst + offset, text, len);
+            if (offset < fRec->fLength)
+                memcpy(dst + offset + len, fRec->data() + offset, fRec->fLength - offset);
+
+            this->swap(tmp);
+        }
+    }
+}
+
+void SkString::insertUnichar(size_t offset, SkUnichar uni)
+{
+    char    buffer[kMaxBytesInUTF8Sequence];
+    size_t  len = SkUTF8_FromUnichar(uni, buffer);
+
+    if (len)
+        this->insert(offset, buffer, len);
+}
+
+void SkString::insertS32(size_t offset, int32_t dec)
+{
+    char    buffer[SkStrAppendS32_MaxSize];
+    char*   stop = SkStrAppendS32(buffer, dec);
+    this->insert(offset, buffer, stop - buffer);
+}
+
+void SkString::insertHex(size_t offset, uint32_t hex, int minDigits)
+{
+    minDigits = SkPin32(minDigits, 0, 8);
+    
+    static const char gHex[] = "0123456789ABCDEF";
+
+    char    buffer[8];
+    char*   p = buffer + sizeof(buffer);
+
+    do {
+        *--p = gHex[hex & 0xF];
+        hex >>= 4;
+        minDigits -= 1;
+    } while (hex != 0);
+    while (--minDigits >= 0)
+        *--p = '0';
+
+    SkASSERT(p >= buffer);
+    this->insert(offset, p, buffer + sizeof(buffer) - p);
+}
+
+void SkString::insertScalar(size_t offset, SkScalar value)
+{
+    char    buffer[SkStrAppendScalar_MaxSize];
+    char*   stop = SkStrAppendScalar(buffer, value);
+    this->insert(offset, buffer, stop - buffer);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+#include <stdio.h>
+
+// number of bytes (on the stack) to receive the printf result
+static const size_t kBufferSize = 256;
+
+#ifdef SK_BUILD_FOR_WIN
+    #define VSNPRINTF   _vsnprintf
+#else
+    #define VSNPRINTF   vsnprintf
+#endif
+
+#define ARGS_TO_BUFFER(format, buffer, size)        \
+    do {                                            \
+        va_list args;                               \
+        va_start(args, format);                     \
+        VSNPRINTF(buffer, size, format, args);      \
+        va_end(args);                               \
+    } while (0)
+
+void SkString::printf(const char format[], ...) {
+    char    buffer[kBufferSize];
+    ARGS_TO_BUFFER(format, buffer, kBufferSize);
+
+    this->set(buffer, strlen(buffer));
+}
+
+void SkString::appendf(const char format[], ...) {
+    char    buffer[kBufferSize];
+    ARGS_TO_BUFFER(format, buffer, kBufferSize);
+    
+    this->append(buffer, strlen(buffer));
+}
+
+void SkString::prependf(const char format[], ...) {
+    char    buffer[kBufferSize];
+    ARGS_TO_BUFFER(format, buffer, kBufferSize);
+    
+    this->prepend(buffer, strlen(buffer));
+}
+
+#undef VSNPRINTF
+
+///////////////////////////////////////////////////////////////////////////
+
+void SkString::remove(size_t offset, size_t length)
+{
+    size_t size = this->size();
+
+    if (offset < size)
+    {
+        if (offset + length > size)
+            length = size - offset;
+        if (length > 0)
+        {
+            SkASSERT(size > length);
+            SkString    tmp(size - length);
+            char*       dst = tmp.writable_str();
+            const char* src = this->c_str();
+
+            if (offset)
+            {
+                SkASSERT(offset <= tmp.size());
+                memcpy(dst, src, offset);
+            }
+            size_t tail = size - offset - length;
+            SkASSERT((int32_t)tail >= 0);
+            if (tail)
+            {
+        //      SkASSERT(offset + length <= tmp.size());
+                memcpy(dst + offset, src + offset + length, tail);
+            }
+            SkASSERT(dst[tmp.size()] == 0);
+            this->swap(tmp);
+        }
+    }
+}
+
+void SkString::swap(SkString& other)
+{
+    this->validate();
+    other.validate();
+
+    SkTSwap<Rec*>(fRec, other.fRec);
+#ifdef SK_DEBUG
+    SkTSwap<const char*>(fStr, other.fStr);
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+
+SkAutoUCS2::SkAutoUCS2(const char utf8[])
+{
+    size_t len = strlen(utf8);
+    fUCS2 = (uint16_t*)sk_malloc_throw((len + 1) * sizeof(uint16_t));
+
+    uint16_t* dst = fUCS2;
+    for (;;)
+    {
+        SkUnichar   uni = SkUTF8_NextUnichar(&utf8);
+        *dst++ = SkToU16(uni);
+        if (uni == 0)
+            break;
+    }
+    fCount = (int)(dst - fUCS2);
+}
+
+SkAutoUCS2::~SkAutoUCS2()
+{
+    delete[] fUCS2;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkString::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    SkString    a;
+    SkString    b((size_t)0);
+    SkString    c("");
+    SkString    d(NULL, 0);
+
+    SkASSERT(a.isEmpty());
+    SkASSERT(a == b && a == c && a == d);
+
+    a.set("hello");
+    b.set("hellox", 5);
+    c.set(a);
+    d.resize(5);
+    memcpy(d.writable_str(), "helloz", 5);
+
+    SkASSERT(!a.isEmpty());
+    SkASSERT(a.size() == 5);
+    SkASSERT(a == b && a == c && a == d);
+    SkASSERT(a.equals("hello", 5));
+    SkASSERT(a.equals("hello"));
+    SkASSERT(!a.equals("help"));
+
+    SkString    e(a);
+    SkString    f("hello");
+    SkString    g("helloz", 5);
+
+    SkASSERT(a == e && a == f && a == g);
+
+    b.set("world");
+    c = b;
+    SkASSERT(a != b && a != c && b == c);
+
+    a.append(" world");
+    e.append("worldz", 5);
+    e.insert(5, " ");
+    f.set("world");
+    f.prepend("hello ");
+    SkASSERT(a.equals("hello world") && a == e && a == f);
+
+    a.reset();
+    b.resize(0);
+    SkASSERT(a.isEmpty() && b.isEmpty() && a == b);
+
+    a.set("a");
+    a.set("ab");
+    a.set("abc");
+    a.set("abcd");
+#endif
+}
+
+#endif
+
diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
new file mode 100644
index 0000000..86dff48
--- /dev/null
+++ b/src/core/SkStroke.cpp
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkStrokerPriv.h"
+#include "SkGeometry.h"
+#include "SkPath.h"
+
+#define kMaxQuadSubdivide   5
+#define kMaxCubicSubdivide  4
+
+static inline bool degenerate_vector(const SkVector& v) {
+    return SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY);
+}
+
+static inline bool degenerate_line(const SkPoint& a, const SkPoint& b,
+                                   SkScalar tolerance = SK_ScalarNearlyZero) {
+    return SkScalarNearlyZero(a.fX - b.fX, tolerance) &&
+            SkScalarNearlyZero(a.fY - b.fY, tolerance);
+}
+
+static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
+    /*  root2/2 is a 45-degree angle
+        make this constant bigger for more subdivisions (but not >= 1)
+    */
+    static const SkScalar kFlatEnoughNormalDotProd =
+                                            SK_ScalarSqrt2/2 + SK_Scalar1/10;
+
+    SkASSERT(kFlatEnoughNormalDotProd > 0 &&
+             kFlatEnoughNormalDotProd < SK_Scalar1);
+
+    return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd;
+}
+
+static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) {
+    static const SkScalar kTooPinchyNormalDotProd = -SK_Scalar1 * 999 / 1000;
+
+    return SkPoint::DotProduct(norm0, norm1) <= kTooPinchyNormalDotProd;
+}
+
+static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after,
+                                  SkScalar radius,
+                                  SkVector* normal, SkVector* unitNormal) {
+    if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) {
+        return false;
+    }
+    unitNormal->rotateCCW();
+    unitNormal->scale(radius, normal);
+    return true;
+}
+
+static bool set_normal_unitnormal(const SkVector& vec,
+                                  SkScalar radius,
+                                  SkVector* normal, SkVector* unitNormal) {
+    if (!unitNormal->setNormalize(vec.fX, vec.fY)) {
+        return false;
+    }
+    unitNormal->rotateCCW();
+    unitNormal->scale(radius, normal);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkPathStroker {
+public:
+    SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap,
+                  SkPaint::Join join);
+
+    void moveTo(const SkPoint&);
+    void lineTo(const SkPoint&);
+    void quadTo(const SkPoint&, const SkPoint&);
+    void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
+    void close(bool isLine) { this->finishContour(true, isLine); }
+
+    void done(SkPath* dst, bool isLine) {
+        this->finishContour(false, isLine);
+        fOuter.addPath(fExtra);
+        dst->swap(fOuter);
+    }
+
+private:
+    SkScalar    fRadius;
+    SkScalar    fInvMiterLimit;
+
+    SkVector    fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
+    SkPoint     fFirstPt, fPrevPt;  // on original path
+    SkPoint     fFirstOuterPt;
+    int         fSegmentCount;
+    bool        fPrevIsLine;
+
+    SkStrokerPriv::CapProc  fCapper;
+    SkStrokerPriv::JoinProc fJoiner;
+
+    SkPath  fInner, fOuter; // outer is our working answer, inner is temp
+    SkPath  fExtra;         // added as extra complete contours
+
+    void    finishContour(bool close, bool isLine);
+    void    preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal,
+                      bool isLine);
+    void    postJoinTo(const SkPoint&, const SkVector& normal,
+                       const SkVector& unitNormal);
+
+    void    line_to(const SkPoint& currPt, const SkVector& normal);
+    void    quad_to(const SkPoint pts[3],
+                    const SkVector& normalAB, const SkVector& unitNormalAB,
+                    SkVector* normalBC, SkVector* unitNormalBC,
+                    int subDivide);
+    void    cubic_to(const SkPoint pts[4],
+                    const SkVector& normalAB, const SkVector& unitNormalAB,
+                    SkVector* normalCD, SkVector* unitNormalCD,
+                    int subDivide);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal,
+                              SkVector* unitNormal, bool currIsLine) {
+    SkASSERT(fSegmentCount >= 0);
+
+    SkScalar    prevX = fPrevPt.fX;
+    SkScalar    prevY = fPrevPt.fY;
+
+    SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal,
+                                         unitNormal));
+
+    if (fSegmentCount == 0) {
+        fFirstNormal = *normal;
+        fFirstUnitNormal = *unitNormal;
+        fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY);
+
+        fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY);
+        fInner.moveTo(prevX - normal->fX, prevY - normal->fY);
+    } else {    // we have a previous segment
+        fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
+                fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
+    }
+    fPrevIsLine = currIsLine;
+}
+
+void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal,
+                               const SkVector& unitNormal) {
+    fPrevPt = currPt;
+    fPrevUnitNormal = unitNormal;
+    fPrevNormal = normal;
+    fSegmentCount += 1;
+}
+
+void SkPathStroker::finishContour(bool close, bool currIsLine) {
+    if (fSegmentCount > 0) {
+        SkPoint pt;
+
+        if (close) {
+            fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
+                    fFirstUnitNormal, fRadius, fInvMiterLimit,
+                    fPrevIsLine, currIsLine);
+            fOuter.close();
+            // now add fInner as its own contour
+            fInner.getLastPt(&pt);
+            fOuter.moveTo(pt.fX, pt.fY);
+            fOuter.reversePathTo(fInner);
+            fOuter.close();
+        } else {    // add caps to start and end
+            // cap the end
+            fInner.getLastPt(&pt);
+            fCapper(&fOuter, fPrevPt, fPrevNormal, pt,
+                    currIsLine ? &fInner : NULL);
+            fOuter.reversePathTo(fInner);
+            // cap the start
+            fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
+                    fPrevIsLine ? &fInner : NULL);
+            fOuter.close();
+        }
+    }
+    fInner.reset();
+    fSegmentCount = -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit,
+                             SkPaint::Cap cap, SkPaint::Join join)
+        : fRadius(radius) {
+
+    /*  This is only used when join is miter_join, but we initialize it here
+        so that it is always defined, to fis valgrind warnings.
+    */
+    fInvMiterLimit = 0;
+
+    if (join == SkPaint::kMiter_Join) {
+        if (miterLimit <= SK_Scalar1) {
+            join = SkPaint::kBevel_Join;
+        } else {
+            fInvMiterLimit = SkScalarInvert(miterLimit);
+        }
+    }
+    fCapper = SkStrokerPriv::CapFactory(cap);
+    fJoiner = SkStrokerPriv::JoinFactory(join);
+    fSegmentCount = -1;
+    fPrevIsLine = false;
+}
+
+void SkPathStroker::moveTo(const SkPoint& pt) {
+    if (fSegmentCount > 0) {
+        this->finishContour(false, false);
+    }
+    fSegmentCount = 0;
+    fFirstPt = fPrevPt = pt;
+}
+
+void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) {
+    fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY);
+    fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY);
+}
+
+void SkPathStroker::lineTo(const SkPoint& currPt) {
+    if (degenerate_line(fPrevPt, currPt)) {
+        return;
+    }
+    SkVector    normal, unitNormal;
+
+    this->preJoinTo(currPt, &normal, &unitNormal, true);
+    this->line_to(currPt, normal);
+    this->postJoinTo(currPt, normal, unitNormal);
+}
+
+void SkPathStroker::quad_to(const SkPoint pts[3],
+                      const SkVector& normalAB, const SkVector& unitNormalAB,
+                      SkVector* normalBC, SkVector* unitNormalBC,
+                      int subDivide) {
+    if (!set_normal_unitnormal(pts[1], pts[2], fRadius,
+                               normalBC, unitNormalBC)) {
+        // pts[1] nearly equals pts[2], so just draw a line to pts[2]
+        this->line_to(pts[2], normalAB);
+        *normalBC = normalAB;
+        *unitNormalBC = unitNormalAB;
+        return;
+    }
+
+    if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) {
+        SkPoint     tmp[5];
+        SkVector    norm, unit;
+
+        SkChopQuadAtHalf(pts, tmp);
+        this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
+        this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
+    } else {
+        SkVector    normalB, unitB;
+        SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius,
+                                             &normalB, &unitB));
+
+        fOuter.quadTo(  pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
+                        pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
+        fInner.quadTo(  pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
+                        pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY);
+    }
+}
+
+void SkPathStroker::cubic_to(const SkPoint pts[4],
+                      const SkVector& normalAB, const SkVector& unitNormalAB,
+                      SkVector* normalCD, SkVector* unitNormalCD,
+                      int subDivide) {
+    SkVector    ab = pts[1] - pts[0];
+    SkVector    cd = pts[3] - pts[2];
+    SkVector    normalBC, unitNormalBC;
+
+    bool    degenerateAB = degenerate_vector(ab);
+    bool    degenerateCD = degenerate_vector(cd);
+
+    if (degenerateAB && degenerateCD) {
+DRAW_LINE:
+        this->line_to(pts[3], normalAB);
+        *normalCD = normalAB;
+        *unitNormalCD = unitNormalAB;
+        return;
+    }
+
+    if (degenerateAB) {
+        ab = pts[2] - pts[0];
+        degenerateAB = degenerate_vector(ab);
+    }
+    if (degenerateCD) {
+        cd = pts[3] - pts[1];
+        degenerateCD = degenerate_vector(cd);
+    }
+    if (degenerateAB || degenerateCD) {
+        goto DRAW_LINE;
+    }
+    SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
+    bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
+                                               &normalBC, &unitNormalBC);
+
+    if (--subDivide >= 0 &&
+            (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
+             normals_too_curvy(unitNormalBC, *unitNormalCD))) {
+        SkPoint     tmp[7];
+        SkVector    norm, unit, dummy, unitDummy;
+
+        SkChopCubicAtHalf(pts, tmp);
+        this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
+                       subDivide);
+        // we use dummys since we already have a valid (and more accurate)
+        // normals for CD
+        this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
+    } else {
+        SkVector    normalB, normalC;
+        
+        // need normals to inset/outset the off-curve pts B and C
+
+        if (0) {    // this is normal to the line between our adjacent pts
+            normalB = pts[2] - pts[0];
+            normalB.rotateCCW();
+            SkAssertResult(normalB.setLength(fRadius));
+
+            normalC = pts[3] - pts[1];
+            normalC.rotateCCW();
+            SkAssertResult(normalC.setLength(fRadius));
+        } else {    // miter-join
+            SkVector    unitBC = pts[2] - pts[1];
+            unitBC.normalize();
+            unitBC.rotateCCW();
+
+            normalB = unitNormalAB + unitBC;
+            normalC = *unitNormalCD + unitBC;
+
+            SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
+            SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
+                                        SkScalarSqrt((SK_Scalar1 + dot)/2))));
+            dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
+            SkAssertResult(normalC.setLength(SkScalarDiv(fRadius,
+                                        SkScalarSqrt((SK_Scalar1 + dot)/2))));
+        }
+
+        fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
+                        pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
+                        pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY);
+
+        fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
+                        pts[2].fX - normalC.fX, pts[2].fY - normalC.fY,
+                        pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY);
+    }
+}
+
+void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
+    bool    degenerateAB = degenerate_line(fPrevPt, pt1);
+    bool    degenerateBC = degenerate_line(pt1, pt2);
+
+    if (degenerateAB | degenerateBC) {
+        if (degenerateAB ^ degenerateBC) {
+            this->lineTo(pt2);
+        }
+        return;
+    }
+
+    SkVector    normalAB, unitAB, normalBC, unitBC;
+
+    this->preJoinTo(pt1, &normalAB, &unitAB, false);
+
+    {
+        SkPoint pts[3], tmp[5];
+        pts[0] = fPrevPt;
+        pts[1] = pt1;
+        pts[2] = pt2;
+
+        if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) {
+            unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY);
+            unitBC.rotateCCW();
+            if (normals_too_pinchy(unitAB, unitBC)) {
+                normalBC = unitBC;
+                normalBC.scale(fRadius);
+
+                fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY);
+                fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY);
+                fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY);
+
+                fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY);
+                fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY);
+                fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY);
+
+                fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius,
+                                 SkPath::kCW_Direction);
+            } else {
+                this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC,
+                              kMaxQuadSubdivide);
+                SkVector n = normalBC;
+                SkVector u = unitBC;
+                this->quad_to(&tmp[2], n, u, &normalBC, &unitBC,
+                              kMaxQuadSubdivide);
+            }
+        } else {
+            this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC,
+                          kMaxQuadSubdivide);
+        }
+    }
+
+    this->postJoinTo(pt2, normalBC, unitBC);
+}
+
+void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
+                            const SkPoint& pt3) {
+    bool    degenerateAB = degenerate_line(fPrevPt, pt1);
+    bool    degenerateBC = degenerate_line(pt1, pt2);
+    bool    degenerateCD = degenerate_line(pt2, pt3);
+
+    if (degenerateAB + degenerateBC + degenerateCD >= 2) {
+        this->lineTo(pt3);
+        return;
+    }
+
+    SkVector    normalAB, unitAB, normalCD, unitCD;
+
+    // find the first tangent (which might be pt1 or pt2
+    {
+        const SkPoint*  nextPt = &pt1;
+        if (degenerateAB)
+            nextPt = &pt2;
+        this->preJoinTo(*nextPt, &normalAB, &unitAB, false);
+    }
+
+    {
+        SkPoint pts[4], tmp[13];
+        int         i, count;
+        SkVector    n, u;
+        SkScalar    tValues[3];
+
+        pts[0] = fPrevPt;
+        pts[1] = pt1;
+        pts[2] = pt2;
+        pts[3] = pt3;
+
+#if 1
+        count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
+#else
+        count = 1;
+        memcpy(tmp, pts, 4 * sizeof(SkPoint));
+#endif
+        n = normalAB;
+        u = unitAB;
+        for (i = 0; i < count; i++) {
+            this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD,
+                           kMaxCubicSubdivide);
+            if (i == count - 1) {
+                break;
+            }
+            n = normalCD;
+            u = unitCD;
+
+        }
+
+        // check for too pinchy
+        for (i = 1; i < count; i++) {
+            SkPoint p;
+            SkVector    v, c;
+
+            SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c);
+
+            SkScalar    dot = SkPoint::DotProduct(c, c);
+            v.scale(SkScalarInvert(dot));
+
+            if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) {
+                fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction);
+            }
+        }
+
+    }
+
+    this->postJoinTo(pt3, normalCD, unitCD);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPaint.h"
+
+SkStroke::SkStroke() {
+    fWidth      = SK_DefaultStrokeWidth;
+    fMiterLimit = SK_DefaultMiterLimit;
+    fCap        = SkPaint::kDefault_Cap;
+    fJoin       = SkPaint::kDefault_Join;
+    fDoFill     = false;
+}
+
+SkStroke::SkStroke(const SkPaint& p) {
+    fWidth      = p.getStrokeWidth();
+    fMiterLimit = p.getStrokeMiter();
+    fCap        = (uint8_t)p.getStrokeCap();
+    fJoin       = (uint8_t)p.getStrokeJoin();
+    fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
+}
+
+SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
+    fWidth      = width;
+    fMiterLimit = p.getStrokeMiter();
+    fCap        = (uint8_t)p.getStrokeCap();
+    fJoin       = (uint8_t)p.getStrokeJoin();
+    fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
+}
+
+void SkStroke::setWidth(SkScalar width) {
+    SkASSERT(width >= 0);
+    fWidth = width;
+}
+
+void SkStroke::setMiterLimit(SkScalar miterLimit) {
+    SkASSERT(miterLimit >= 0);
+    fMiterLimit = miterLimit;
+}
+
+void SkStroke::setCap(SkPaint::Cap cap) {
+    SkASSERT((unsigned)cap < SkPaint::kCapCount);
+    fCap = SkToU8(cap);
+}
+
+void SkStroke::setJoin(SkPaint::Join join) {
+    SkASSERT((unsigned)join < SkPaint::kJoinCount);
+    fJoin = SkToU8(join);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SCALAR_IS_FIXED
+    /*  return non-zero if the path is too big, and should be shrunk to avoid
+        overflows during intermediate calculations. Note that we compute the
+        bounds for this. If we had a custom callback/walker for paths, we could
+        perhaps go faster by using that, and just perform the abs | in that
+        routine
+    */
+    static int needs_to_shrink(const SkPath& path) {
+        SkRect r;
+        path.computeBounds(&r, SkPath::kFast_BoundsType);
+        SkFixed mask = SkAbs32(r.fLeft);
+        mask |= SkAbs32(r.fTop);
+        mask |= SkAbs32(r.fRight);
+        mask |= SkAbs32(r.fBottom);
+        // we need the top 3 bits clear (after abs) to avoid overflow
+        return mask >> 29;
+    }
+
+    static void identity_proc(SkPoint pts[], int count) {}
+    static void shift_down_2_proc(SkPoint pts[], int count) {
+        for (int i = 0; i < count; i++) {
+            pts->fX >>= 2;
+            pts->fY >>= 2;
+            pts += 1;
+        }
+    }
+    #define APPLY_PROC(proc, pts, count)    proc(pts, count)
+#else   // float does need any of this
+    #define APPLY_PROC(proc, pts, count)
+#endif
+
+void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
+    SkASSERT(&src != NULL && dst != NULL);
+
+    SkScalar radius = SkScalarHalf(fWidth);
+
+    dst->reset();
+    if (radius <= 0) {
+        return;
+    }
+    
+#ifdef SK_SCALAR_IS_FIXED
+    void (*proc)(SkPoint pts[], int count) = identity_proc;
+    if (needs_to_shrink(src)) {
+        proc = shift_down_2_proc;
+        radius >>= 2;
+        if (radius == 0) {
+            return;
+        }
+    }
+#endif
+
+    SkPathStroker   stroker(radius, fMiterLimit, this->getCap(),
+                            this->getJoin());
+
+    SkPath::Iter    iter(src, false);
+    SkPoint         pts[4];
+    SkPath::Verb    verb, lastSegment = SkPath::kMove_Verb;
+
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                APPLY_PROC(proc, &pts[0], 1);
+                stroker.moveTo(pts[0]);
+                break;
+            case SkPath::kLine_Verb:
+                APPLY_PROC(proc, &pts[1], 1);
+                stroker.lineTo(pts[1]);
+                lastSegment = verb;
+                break;
+            case SkPath::kQuad_Verb:
+                APPLY_PROC(proc, &pts[1], 2);
+                stroker.quadTo(pts[1], pts[2]);
+                lastSegment = verb;
+                break;
+            case SkPath::kCubic_Verb:
+                APPLY_PROC(proc, &pts[1], 3);
+                stroker.cubicTo(pts[1], pts[2], pts[3]);
+                lastSegment = verb;
+                break;
+            case SkPath::kClose_Verb:
+                stroker.close(lastSegment == SkPath::kLine_Verb);
+                break;
+            default:
+                break;
+        }
+    }
+    stroker.done(dst, lastSegment == SkPath::kLine_Verb);
+
+#ifdef SK_SCALAR_IS_FIXED
+    // undo our previous down_shift
+    if (shift_down_2_proc == proc) {
+        // need a real shift methid on path. antialias paths could use this too
+        SkMatrix matrix;
+        matrix.setScale(SkIntToScalar(4), SkIntToScalar(4));
+        dst->transform(matrix);
+    }
+#endif
+
+    if (fDoFill) {
+        dst->addPath(src);
+    }
+}
+
+void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1,
+                          SkPath* dst) const {
+    SkPath  tmp;
+
+    tmp.moveTo(p0);
+    tmp.lineTo(p1);
+    this->strokePath(tmp, dst);
+}
+
diff --git a/src/core/SkStrokerPriv.cpp b/src/core/SkStrokerPriv.cpp
new file mode 100644
index 0000000..07833ca
--- /dev/null
+++ b/src/core/SkStrokerPriv.cpp
@@ -0,0 +1,275 @@
+/* libs/graphics/sgl/SkStrokerPriv.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 "SkStrokerPriv.h"
+#include "SkGeometry.h"
+#include "SkPath.h"
+
+static void ButtCapper(SkPath* path, const SkPoint& pivot,
+                       const SkVector& normal, const SkPoint& stop,
+                       SkPath*)
+{
+    path->lineTo(stop.fX, stop.fY);
+}
+
+static void RoundCapper(SkPath* path, const SkPoint& pivot,
+                        const SkVector& normal, const SkPoint& stop,
+                        SkPath*)
+{
+    SkScalar    px = pivot.fX;
+    SkScalar    py = pivot.fY;
+    SkScalar    nx = normal.fX;
+    SkScalar    ny = normal.fY;
+    SkScalar    sx = SkScalarMul(nx, CUBIC_ARC_FACTOR);
+    SkScalar    sy = SkScalarMul(ny, CUBIC_ARC_FACTOR);
+
+    path->cubicTo(px + nx + CWX(sx, sy), py + ny + CWY(sx, sy),
+                  px + CWX(nx, ny) + sx, py + CWY(nx, ny) + sy,
+                  px + CWX(nx, ny), py + CWY(nx, ny));
+    path->cubicTo(px + CWX(nx, ny) - sx, py + CWY(nx, ny) - sy,
+                  px - nx + CWX(sx, sy), py - ny + CWY(sx, sy),
+                  stop.fX, stop.fY);
+}
+
+static void SquareCapper(SkPath* path, const SkPoint& pivot,
+                         const SkVector& normal, const SkPoint& stop,
+                         SkPath* otherPath)
+{
+    SkVector parallel;
+    normal.rotateCW(&parallel);
+
+    if (otherPath)
+    {
+        path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
+        path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
+    }
+    else
+    {
+        path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
+        path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
+        path->lineTo(stop.fX, stop.fY);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static bool is_clockwise(const SkVector& before, const SkVector& after)
+{
+    return SkScalarMul(before.fX, after.fY) - SkScalarMul(before.fY, after.fX) > 0;
+}
+
+enum AngleType {
+    kNearly180_AngleType,
+    kSharp_AngleType,
+    kShallow_AngleType,
+    kNearlyLine_AngleType
+};
+
+static AngleType Dot2AngleType(SkScalar dot)
+{
+// need more precise fixed normalization
+//  SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero);
+
+    if (dot >= 0)   // shallow or line
+        return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType;
+    else            // sharp or 180
+        return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType;
+}
+
+static void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after)
+{
+#if 1
+    /*  In the degenerate case that the stroke radius is larger than our segments
+        just connecting the two inner segments may "show through" as a funny
+        diagonal. To pseudo-fix this, we go through the pivot point. This adds
+        an extra point/edge, but I can't see a cheap way to know when this is
+        not needed :(
+    */
+    inner->lineTo(pivot.fX, pivot.fY);
+#endif
+
+    inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY);
+}
+
+static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
+                        const SkPoint& pivot, const SkVector& afterUnitNormal,
+                        SkScalar radius, SkScalar invMiterLimit, bool, bool)
+{
+    SkVector    after;
+    afterUnitNormal.scale(radius, &after);
+
+    if (!is_clockwise(beforeUnitNormal, afterUnitNormal))
+    {
+        SkTSwap<SkPath*>(outer, inner);
+        after.negate();
+    }
+
+    outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
+    HandleInnerJoin(inner, pivot, after);
+}
+
+static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
+                        const SkPoint& pivot, const SkVector& afterUnitNormal,
+                        SkScalar radius, SkScalar invMiterLimit, bool, bool)
+{
+    SkScalar    dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
+    AngleType   angleType = Dot2AngleType(dotProd);
+
+    if (angleType == kNearlyLine_AngleType)
+        return;
+
+    SkVector            before = beforeUnitNormal;
+    SkVector            after = afterUnitNormal;
+    SkRotationDirection dir = kCW_SkRotationDirection;
+
+    if (!is_clockwise(before, after))
+    {
+        SkTSwap<SkPath*>(outer, inner);
+        before.negate();
+        after.negate();
+        dir = kCCW_SkRotationDirection;
+    }
+
+    SkPoint     pts[kSkBuildQuadArcStorage];
+    SkMatrix    matrix;
+    matrix.setScale(radius, radius);
+    matrix.postTranslate(pivot.fX, pivot.fY);
+    int count = SkBuildQuadArc(before, after, dir, &matrix, pts);
+    SkASSERT((count & 1) == 1);
+
+    if (count > 1)
+    {
+        for (int i = 1; i < count; i += 2)
+            outer->quadTo(pts[i].fX, pts[i].fY, pts[i+1].fX, pts[i+1].fY);
+
+        after.scale(radius);
+        HandleInnerJoin(inner, pivot, after);
+    }
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define kOneOverSqrt2   (0.707106781f)
+#else
+    #define kOneOverSqrt2   (46341)
+#endif
+
+static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
+                        const SkPoint& pivot, const SkVector& afterUnitNormal,
+                        SkScalar radius, SkScalar invMiterLimit,
+                        bool prevIsLine, bool currIsLine)
+{
+    // negate the dot since we're using normals instead of tangents
+    SkScalar    dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
+    AngleType   angleType = Dot2AngleType(dotProd);
+    SkVector    before = beforeUnitNormal;
+    SkVector    after = afterUnitNormal;
+    SkVector    mid;
+    SkScalar    sinHalfAngle;
+    bool        ccw;
+
+    if (angleType == kNearlyLine_AngleType)
+        return;
+    if (angleType == kNearly180_AngleType)
+    {
+        currIsLine = false;
+        goto DO_BLUNT;
+    }
+    
+    ccw = !is_clockwise(before, after);
+    if (ccw)
+    {
+        SkTSwap<SkPath*>(outer, inner);
+        before.negate();
+        after.negate();
+    }
+    
+    /*  Before we enter the world of square-roots and divides,
+        check if we're trying to join an upright right angle
+        (common case for stroking rectangles). If so, special case
+        that (for speed an accuracy).
+        Note: we only need to check one normal if dot==0
+    */
+    if (0 == dotProd && invMiterLimit <= kOneOverSqrt2)
+    {
+        mid.set(SkScalarMul(before.fX + after.fX, radius),
+                SkScalarMul(before.fY + after.fY, radius));
+        goto DO_MITER;
+    }
+
+    /*  midLength = radius / sinHalfAngle
+        if (midLength > miterLimit * radius) abort
+        if (radius / sinHalf > miterLimit * radius) abort
+        if (1 / sinHalf > miterLimit) abort
+        if (1 / miterLimit > sinHalf) abort
+        My dotProd is opposite sign, since it is built from normals and not tangents
+        hence 1 + dot instead of 1 - dot in the formula
+    */
+    sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd));
+    if (sinHalfAngle < invMiterLimit)
+    {
+        currIsLine = false;
+        goto DO_BLUNT;
+    }
+
+    // choose the most accurate way to form the initial mid-vector
+    if (angleType == kSharp_AngleType)
+    {
+        mid.set(after.fY - before.fY, before.fX - after.fX);
+        if (ccw)
+            mid.negate();
+    }
+    else
+        mid.set(before.fX + after.fX, before.fY + after.fY);
+
+    mid.setLength(SkScalarDiv(radius, sinHalfAngle));
+DO_MITER:
+    if (prevIsLine)
+        outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY);
+    else
+        outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY);
+
+DO_BLUNT:
+    after.scale(radius);
+    if (!currIsLine)
+        outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
+    HandleInnerJoin(inner, pivot, after);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+SkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap)
+{
+    static const SkStrokerPriv::CapProc gCappers[] = {
+        ButtCapper, RoundCapper, SquareCapper
+    };
+
+    SkASSERT((unsigned)cap < SkPaint::kCapCount);
+    return gCappers[cap];
+}
+
+SkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join)
+{
+    static const SkStrokerPriv::JoinProc gJoiners[] = {
+        MiterJoiner, RoundJoiner, BluntJoiner
+    };
+
+    SkASSERT((unsigned)join < SkPaint::kJoinCount);
+    return gJoiners[join];
+}
+
+
+
diff --git a/src/core/SkStrokerPriv.h b/src/core/SkStrokerPriv.h
new file mode 100644
index 0000000..ecb9bde
--- /dev/null
+++ b/src/core/SkStrokerPriv.h
@@ -0,0 +1,50 @@
+/* libs/graphics/sgl/SkStrokerPriv.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 SkStrokerPriv_DEFINED
+#define SkStrokerPriv_DEFINED
+
+#include "SkStroke.h"
+
+#define CWX(x, y)   (-y)
+#define CWY(x, y)   (x)
+#define CCWX(x, y)  (y)
+#define CCWY(x, y)  (-x)
+
+#define CUBIC_ARC_FACTOR    ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
+
+class SkStrokerPriv {
+public:
+    typedef void (*CapProc)(SkPath* path,
+                            const SkPoint& pivot,
+                            const SkVector& normal,
+                            const SkPoint& stop,
+                            SkPath* otherPath);
+
+    typedef void (*JoinProc)(SkPath* outer, SkPath* inner,
+                             const SkVector& beforeUnitNormal,
+                             const SkPoint& pivot,
+                             const SkVector& afterUnitNormal,
+                             SkScalar radius, SkScalar invMiterLimit,
+                             bool prevIsLine, bool currIsLine);
+
+    static CapProc  CapFactory(SkPaint::Cap);
+    static JoinProc JoinFactory(SkPaint::Join);
+};
+
+#endif
+
diff --git a/src/core/SkTSearch.cpp b/src/core/SkTSearch.cpp
new file mode 100644
index 0000000..bab348f
--- /dev/null
+++ b/src/core/SkTSearch.cpp
@@ -0,0 +1,219 @@
+/* libs/graphics/sgl/SkTSearch.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 "SkTSearch.h"
+#include <ctype.h>
+
+static inline const char* index_into_base(const char*const* base, int index,
+                                          size_t elemSize)
+{
+    return *(const char*const*)((const char*)base + index * elemSize);
+}
+
+int SkStrSearch(const char*const* base, int count, const char target[],
+                size_t target_len, size_t elemSize)
+{
+    if (count <= 0)
+        return ~0;
+
+    SkASSERT(base != NULL);
+
+    int lo = 0;
+    int hi = count - 1;
+
+    while (lo < hi)
+    {
+        int mid = (hi + lo) >> 1;
+        const char* elem = index_into_base(base, mid, elemSize);
+
+        int cmp = strncmp(elem, target, target_len);
+        if (cmp < 0)
+            lo = mid + 1;
+        else if (cmp > 0 || strlen(elem) > target_len)
+            hi = mid;
+        else
+            return mid;
+    }
+
+    const char* elem = index_into_base(base, hi, elemSize);
+    int cmp = strncmp(elem, target, target_len);
+    if (cmp || strlen(elem) > target_len)
+    {
+        if (cmp < 0)
+            hi += 1;
+        hi = ~hi;
+    }
+    return hi;
+}
+
+int SkStrSearch(const char*const* base, int count, const char target[],
+                size_t elemSize)
+{
+    return SkStrSearch(base, count, target, strlen(target), elemSize);
+}
+
+int SkStrLCSearch(const char*const* base, int count, const char target[],
+                  size_t len, size_t elemSize)
+{
+    SkASSERT(target);
+
+    SkAutoAsciiToLC tolc(target, len);
+
+    return SkStrSearch(base, count, tolc.lc(), len, elemSize);
+}
+
+int SkStrLCSearch(const char*const* base, int count, const char target[],
+                  size_t elemSize)
+{
+    return SkStrLCSearch(base, count, target, strlen(target), elemSize);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SkAutoAsciiToLC::SkAutoAsciiToLC(const char str[], size_t len)
+{
+    // see if we need to compute the length
+    if ((long)len < 0) {
+        len = strlen(str);
+    }
+    fLength = len;
+
+    // assign lc to our preallocated storage if len is small enough, or allocate
+    // it on the heap
+    char*   lc;
+    if (len <= STORAGE) {
+        lc = fStorage;
+    } else {
+        lc = (char*)sk_malloc_throw(len + 1);
+    }
+    fLC = lc;
+    
+    // convert any asii to lower-case. we let non-ascii (utf8) chars pass
+    // through unchanged
+    for (int i = (int)(len - 1); i >= 0; --i) {
+        int c = str[i];
+        if ((c & 0x80) == 0) {   // is just ascii
+            c = tolower(c);
+        }
+        lc[i] = c;
+    }
+    lc[len] = 0;
+}
+
+SkAutoAsciiToLC::~SkAutoAsciiToLC()
+{
+    if (fLC != fStorage) {
+        sk_free(fLC);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#define SK_QSortTempSize    16
+
+static inline void sk_qsort_swap(char a[], char b[], size_t elemSize)
+{
+    char    tmp[SK_QSortTempSize];
+
+    while (elemSize > 0)
+    {
+        size_t size = elemSize;
+        if (size > SK_QSortTempSize)
+            size = SK_QSortTempSize;
+        elemSize -= size;
+
+        memcpy(tmp, a, size);
+        memcpy(a, b, size);
+        memcpy(b, tmp, size);
+        a += size;
+        b += size;
+    }
+}
+
+static void SkQSort_Partition(char* first, char* last, size_t elemSize, SkQSortCompareProc compare)
+{
+    char*   left = first;
+    char*   rite = last;
+    char*   pivot = left;
+
+    while (left <= rite)
+    {
+        while (left < last && compare(left, pivot) < 0)
+            left += elemSize;
+        while (first < rite && compare(rite, pivot) > 0)
+            rite -= elemSize;
+        if (left <= rite)
+        {
+            if (left < rite)
+            {
+                SkASSERT(compare(left, rite) >= 0);
+                sk_qsort_swap(left, rite, elemSize);
+            }
+            left += elemSize;
+            rite -= elemSize;
+        }
+    }
+    if (first < rite)
+        SkQSort_Partition(first, rite, elemSize, compare);
+    if (left < last)
+        SkQSort_Partition(left, last, elemSize, compare);
+}
+
+void SkQSort(void* base, size_t count, size_t elemSize, SkQSortCompareProc compare)
+{
+    SkASSERT(base);
+    SkASSERT(compare);
+    SkASSERT(elemSize > 0);
+
+    if (count <= 1)
+        return;
+
+    SkQSort_Partition((char*)base, (char*)base + (count - 1) * elemSize, elemSize, compare);
+}
+
+#ifdef SK_DEBUG
+
+#include "SkRandom.h"
+
+#ifdef SK_SUPPORT_UNITTEST
+extern "C" {
+    int compare_int(const void* a, const void* b)
+    {
+        return *(const int*)a - *(const int*)b;
+    }
+}
+#endif
+
+void SkQSort_UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    int         array[100];
+    SkRandom    rand;
+
+    for (int i = 0; i < 1000; i++)
+    {
+        int j, count = rand.nextRangeU(1, SK_ARRAY_COUNT(array));
+        for (j = 0; j < count; j++)
+            array[j] = rand.nextS() & 0xFF;
+        SkQSort(array, count, sizeof(int), compare_int);
+        for (j = 1; j < count; j++)
+            SkASSERT(array[j-1] <= array[j]);
+    }
+#endif
+}
+
+#endif
diff --git a/src/core/SkTSort.h b/src/core/SkTSort.h
new file mode 100644
index 0000000..fba49e2
--- /dev/null
+++ b/src/core/SkTSort.h
@@ -0,0 +1,65 @@
+/* libs/graphics/sgl/SkTSort.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 SkTSort_DEFINED
+#define SkTSort_DEFINED
+
+#include "SkTypes.h"
+
+template <typename T>
+void SkTHeapSort_SiftDown(T array[], int root, int bottom)
+{
+    int root2 = root << 1;
+
+    while (root2 <= bottom)
+    {
+        int maxChild;
+
+        if (root2 == bottom)
+            maxChild = root2;
+        else if (array[root2] > array[root2 + 1])
+            maxChild = root2;
+        else
+            maxChild = root2 + 1;
+
+        if (array[root] < array[maxChild])
+        {
+            SkTSwap<T>(array[root], array[maxChild]);
+            root = maxChild;
+            root2 = root << 1;
+        }
+        else
+            break;
+    }
+}
+
+template <typename T>
+void SkTHeapSort(T array[], int count)
+{
+    int i;
+
+    for (i = count/2 - 1; i >= 0; --i)
+        SkTHeapSort_SiftDown<T>(array, i, count);
+
+    for (i = count - 2; i >= 0; --i)
+    {
+        SkTSwap<T>(array[0], array[i + 1]);
+        SkTHeapSort_SiftDown<T>(array, 0, i);
+    }
+}
+
+#endif
diff --git a/src/core/SkTemplatesPriv.h b/src/core/SkTemplatesPriv.h
new file mode 100644
index 0000000..b0a95a0
--- /dev/null
+++ b/src/core/SkTemplatesPriv.h
@@ -0,0 +1,84 @@
+/* libs/graphics/sgl/SkTemplatesPriv.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 SkTemplatesPriv_DEFINED
+#define SkTemplatesPriv_DEFINED
+
+#include "SkTemplates.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_BUILD_FOR_WIN32
+    #define SK_PLACEMENT_NEW(result, classname, storage, storageSize)   \
+        result = SkNEW(classname)
+
+    #define SK_PLACEMENT_NEW_ARGS(result, classname, storage, storageSize, args)    \
+        result = SkNEW_ARGS(classname, args)
+#else
+    #include <new>
+    #define SK_PLACEMENT_NEW(result, classname, storage, storagesize)       \
+    do {                                                                    \
+        if (storagesize)                                                    \
+        {                                                                   \
+            SkASSERT(storageSize >= sizeof(classname));                     \
+            result = new(storage) classname;                                \
+        }                                                                   \
+        else                                                                \
+            result = SkNEW(classname);                                      \
+    } while (0)
+
+    #define SK_PLACEMENT_NEW_ARGS(result, classname, storage, storagesize, args)        \
+    do {                                                                                \
+        if (storagesize)                                                                \
+        {                                                                               \
+            SkASSERT(storageSize >= sizeof(classname));                                 \
+            result = new(storage) classname args;                                       \
+        }                                                                               \
+        else                                                                            \
+            result = SkNEW_ARGS(classname, args);                                       \
+    } while (0)
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T> class SkAutoTPlacementDelete {
+public:
+    SkAutoTPlacementDelete(T* obj, void* storage) : fObj(obj), fStorage(storage)
+    {
+    }
+    ~SkAutoTPlacementDelete()
+    {
+        if (fObj)
+        {
+            if (fObj == fStorage)
+                fObj->~T();
+            else
+                delete fObj;
+        }
+    }
+    T* detach()
+    {
+        T*  obj = fObj;
+        fObj = NULL;
+        return obj;
+    }
+private:
+    T*      fObj;
+    void*   fStorage;
+};
+
+#endif
diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp
new file mode 100644
index 0000000..9821c51
--- /dev/null
+++ b/src/core/SkTypeface.cpp
@@ -0,0 +1,64 @@
+#include "SkTypeface.h"
+#include "SkFontHost.h"
+
+static const SkTypeface* resolve_null_typeface(const SkTypeface* face)
+{
+    if (NULL == face) {
+        face = SkFontHost::FindTypeface(NULL, NULL, SkTypeface::kNormal);
+        SkASSERT(face);
+    }
+    return face;
+}
+
+uint32_t SkTypeface::UniqueID(const SkTypeface* face)
+{
+    return resolve_null_typeface(face)->uniqueID();
+}
+
+bool SkTypeface::Equal(const SkTypeface* facea, const SkTypeface* faceb)
+{
+    return resolve_null_typeface(facea)->uniqueID() ==
+           resolve_null_typeface(faceb)->uniqueID();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkTypeface::Create(const char name[], Style style)
+{
+    SkTypeface* face = SkFontHost::FindTypeface(NULL, name, style);
+    face->ref();
+    return face;
+}
+
+SkTypeface* SkTypeface::CreateFromTypeface(const SkTypeface* family, Style s)
+{
+    family = resolve_null_typeface(family);
+    SkTypeface* face = SkFontHost::FindTypeface(family, NULL, s);
+    face->ref();
+    return face;
+}
+
+SkTypeface* SkTypeface::CreateFromStream(SkStream* stream)
+{
+    return SkFontHost::CreateTypeface(stream);
+}
+
+#include "SkMMapStream.h"
+SkTypeface* SkTypeface::CreateFromFile(const char path[])
+{
+    return SkFontHost::CreateTypeface(SkNEW_ARGS(SkMMAPStream, (path)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkTypeface::serialize(SkWStream* stream) const {
+    SkFontHost::Serialize(this, stream);
+}
+
+SkTypeface* SkTypeface::Deserialize(SkStream* stream) {
+    SkTypeface* face = SkFontHost::Deserialize(stream);
+    face->ref();
+    return face;
+}
+
+
diff --git a/src/core/SkUnPreMultiply.cpp b/src/core/SkUnPreMultiply.cpp
new file mode 100644
index 0000000..371af32
--- /dev/null
+++ b/src/core/SkUnPreMultiply.cpp
@@ -0,0 +1,73 @@
+#include "SkUnPreMultiply.h"
+#include "SkColorPriv.h"
+
+SkColor SkUnPreMultiply::PMColorToColor(SkPMColor c) {
+    const unsigned a = SkGetPackedA32(c);
+    const Scale scale = GetScale(a);
+    return SkColorSetARGB(a,
+                          ApplyScale(scale, SkGetPackedR32(c)),
+                          ApplyScale(scale, SkGetPackedG32(c)),
+                          ApplyScale(scale, SkGetPackedB32(c)));
+}
+
+const uint32_t SkUnPreMultiply::gTable[] = {
+    0x00000000, 0xFF000000, 0x7F800000, 0x55000000, 0x3FC00000, 0x33000000, 0x2A800000, 0x246DB6DB,
+    0x1FE00000, 0x1C555555, 0x19800000, 0x172E8BA3, 0x15400000, 0x139D89D9, 0x1236DB6E, 0x11000000,
+    0x0FF00000, 0x0F000000, 0x0E2AAAAB, 0x0D6BCA1B, 0x0CC00000, 0x0C249249, 0x0B9745D1, 0x0B1642C8,
+    0x0AA00000, 0x0A333333, 0x09CEC4EC, 0x0971C71C, 0x091B6DB7, 0x08CB08D4, 0x08800000, 0x0839CE74,
+    0x07F80000, 0x07BA2E8C, 0x07800000, 0x07492492, 0x07155555, 0x06E45307, 0x06B5E50D, 0x0689D89E,
+    0x06600000, 0x063831F4, 0x06124925, 0x05EE23B9, 0x05CBA2E9, 0x05AAAAAB, 0x058B2164, 0x056CEFA9,
+    0x05500000, 0x05343EB2, 0x0519999A, 0x05000000, 0x04E76276, 0x04CFB2B8, 0x04B8E38E, 0x04A2E8BA,
+    0x048DB6DB, 0x0479435E, 0x0465846A, 0x045270D0, 0x04400000, 0x042E29F8, 0x041CE73A, 0x040C30C3,
+    0x03FC0000, 0x03EC4EC5, 0x03DD1746, 0x03CE540F, 0x03C00000, 0x03B21643, 0x03A49249, 0x03976FC6,
+    0x038AAAAB, 0x037E3F20, 0x03722983, 0x03666666, 0x035AF287, 0x034FCACE, 0x0344EC4F, 0x033A5441,
+    0x03300000, 0x0325ED09, 0x031C18FA, 0x0312818B, 0x03092492, 0x03000000, 0x02F711DC, 0x02EE5847,
+    0x02E5D174, 0x02DD7BAF, 0x02D55555, 0x02CD5CD6, 0x02C590B2, 0x02BDEF7C, 0x02B677D4, 0x02AF286C,
+    0x02A80000, 0x02A0FD5C, 0x029A1F59, 0x029364D9, 0x028CCCCD, 0x0286562E, 0x02800000, 0x0279C952,
+    0x0273B13B, 0x026DB6DB, 0x0267D95C, 0x026217ED, 0x025C71C7, 0x0256E62A, 0x0251745D, 0x024C1BAD,
+    0x0246DB6E, 0x0241B2F9, 0x023CA1AF, 0x0237A6F5, 0x0232C235, 0x022DF2DF, 0x02293868, 0x02249249,
+    0x02200000, 0x021B810F, 0x021714FC, 0x0212BB51, 0x020E739D, 0x020A3D71, 0x02061862, 0x02020408,
+    0x01FE0000, 0x01FA0BE8, 0x01F62762, 0x01F25214, 0x01EE8BA3, 0x01EAD3BB, 0x01E72A08, 0x01E38E39,
+    0x01E00000, 0x01DC7F11, 0x01D90B21, 0x01D5A3EA, 0x01D24925, 0x01CEFA8E, 0x01CBB7E3, 0x01C880E5,
+    0x01C55555, 0x01C234F7, 0x01BF1F90, 0x01BC14E6, 0x01B914C2, 0x01B61EED, 0x01B33333, 0x01B05161,
+    0x01AD7943, 0x01AAAAAB, 0x01A7E567, 0x01A5294A, 0x01A27627, 0x019FCBD2, 0x019D2A20, 0x019A90E8,
+    0x01980000, 0x01957741, 0x0192F685, 0x01907DA5, 0x018E0C7D, 0x018BA2E9, 0x018940C5, 0x0186E5F1,
+    0x01849249, 0x018245AE, 0x01800000, 0x017DC11F, 0x017B88EE, 0x0179574E, 0x01772C23, 0x01750750,
+    0x0172E8BA, 0x0170D045, 0x016EBDD8, 0x016CB157, 0x016AAAAB, 0x0168A9B9, 0x0166AE6B, 0x0164B8A8,
+    0x0162C859, 0x0160DD68, 0x015EF7BE, 0x015D1746, 0x015B3BEA, 0x01596596, 0x01579436, 0x0155C7B5,
+    0x01540000, 0x01523D04, 0x01507EAE, 0x014EC4EC, 0x014D0FAC, 0x014B5EDD, 0x0149B26D, 0x01480A4B,
+    0x01466666, 0x0144C6B0, 0x01432B17, 0x0141938C, 0x01400000, 0x013E7064, 0x013CE4A9, 0x013B5CC1,
+    0x0139D89E, 0x01385831, 0x0136DB6E, 0x01356246, 0x0133ECAE, 0x01327A97, 0x01310BF6, 0x012FA0BF,
+    0x012E38E4, 0x012CD45A, 0x012B7315, 0x012A150B, 0x0128BA2F, 0x01276276, 0x01260DD6, 0x0124BC45,
+    0x01236DB7, 0x01222222, 0x0120D97D, 0x011F93BC, 0x011E50D8, 0x011D10C5, 0x011BD37A, 0x011A98EF,
+    0x0119611A, 0x01182BF3, 0x0116F970, 0x0115C988, 0x01149C34, 0x0113716B, 0x01124925, 0x01112359,
+    0x01100000, 0x010EDF12, 0x010DC087, 0x010CA458, 0x010B8A7E, 0x010A72F0, 0x01095DA9, 0x01084AA0,
+    0x010739CE, 0x01062B2E, 0x01051EB8, 0x01041466, 0x01030C31, 0x01020612, 0x01010204, 0x01000000
+};
+
+#ifdef BUILD_DIVIDE_TABLE
+void SkUnPreMultiply_BuildTable() {
+    for (unsigned i = 0; i <= 255; i++) {
+        uint32_t scale;
+        
+        if (0 == i) {
+            scale = 0;
+        } else {
+            scale = ((255 << 24) + (i >> 1)) / i;
+        }
+        
+        SkDebugf(" 0x%08X,", scale);
+        if ((i & 7) == 7) {
+            SkDebugf("\n");
+        }
+        
+        // test the result
+        for (int j = 1; j <= i; j++) {
+            uint32_t test = (j * scale + (1 << 23)) >> 24;
+            uint32_t div = roundf(j * 255.0f / i);
+            int diff = SkAbs32(test - div);
+            SkASSERT(diff <= 1 && test <= 255);
+        }
+    }
+}
+#endif
diff --git a/src/core/SkUtils.cpp b/src/core/SkUtils.cpp
new file mode 100644
index 0000000..4f5ba6e
--- /dev/null
+++ b/src/core/SkUtils.cpp
@@ -0,0 +1,574 @@
+/* libs/graphics/sgl/SkUtils.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 "SkUtils.h"
+
+#if 0
+#define assign_16_longs(dst, value)             \
+    do {                                        \
+        (dst)[0] = value;   (dst)[1] = value;   \
+        (dst)[2] = value;   (dst)[3] = value;   \
+        (dst)[4] = value;   (dst)[5] = value;   \
+        (dst)[6] = value;   (dst)[7] = value;   \
+        (dst)[8] = value;   (dst)[9] = value;   \
+        (dst)[10] = value;  (dst)[11] = value;  \
+        (dst)[12] = value;  (dst)[13] = value;  \
+        (dst)[14] = value;  (dst)[15] = value;  \
+    } while (0)
+#else
+#define assign_16_longs(dst, value)             \
+    do {                                        \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+        *(dst)++ = value;   *(dst)++ = value;   \
+    } while (0)
+#endif
+
+///////////////////////////////////////////////////////////////////////////
+
+void sk_memset16_portable(uint16_t dst[], uint16_t value, int count)
+{
+    SkASSERT(dst != NULL && count >= 0);
+
+    if (count <= 0)
+        return;
+
+    // not sure if this helps to short-circuit on small values of count
+    if (count < 8)
+    {
+        do {
+            *dst++ = (uint16_t)value;
+        } while (--count != 0);
+        return;
+    }
+
+    // ensure we're on a long boundary
+    if ((size_t)dst & 2)
+    {
+        *dst++ = (uint16_t)value;
+        count -= 1;
+    }
+
+    uint32_t value32 = ((uint32_t)value << 16) | value;
+
+    // handle the bulk with our unrolled macro
+    {
+        int sixteenlongs = count >> 5;
+        if (sixteenlongs)
+        {
+            uint32_t* dst32 = (uint32_t*)dst;
+            do {
+                assign_16_longs(dst32, value32);
+            } while (--sixteenlongs != 0);
+            dst = (uint16_t*)dst32;
+            count &= 31;
+        }
+    }
+
+    // handle (most) of the rest
+    {
+        int longs = count >> 1;
+        if (longs)
+        {
+            do {
+                *(uint32_t*)dst = value32;
+                dst += 2;
+            } while (--longs != 0);
+        }
+    }
+
+    // cleanup a possible trailing short
+    if (count & 1)
+        *dst = (uint16_t)value;
+}
+
+void sk_memset32_portable(uint32_t dst[], uint32_t value, int count)
+{
+    SkASSERT(dst != NULL && count >= 0);
+
+    {
+        int sixteenlongs = count >> 4;
+        if (sixteenlongs)
+        {
+            do {
+                assign_16_longs(dst, value);
+            } while (--sixteenlongs != 0);
+            count &= 15;
+        }
+    }
+
+    if (count)
+    {
+        do {
+            *dst++ = value;
+        } while (--count != 0);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+/*  0xxxxxxx    1 total
+    10xxxxxx    // never a leading byte
+    110xxxxx    2 total
+    1110xxxx    3 total
+    11110xxx    4 total
+
+    11 10 01 01 xx xx xx xx 0...
+    0xE5XX0000
+    0xE5 << 24
+*/
+
+#ifdef SK_DEBUG
+    static void assert_utf8_leadingbyte(unsigned c)
+    {
+        SkASSERT(c <= 0xF7);    // otherwise leading byte is too big (more than 4 bytes)
+        SkASSERT((c & 0xC0) != 0x80);   // can't begin with a middle char
+    }
+
+    int SkUTF8_LeadByteToCount(unsigned c)
+    {
+        assert_utf8_leadingbyte(c);
+        return (((0xE5 << 24) >> (c >> 4 << 1)) & 3) + 1;
+    }
+#else
+    #define assert_utf8_leadingbyte(c)
+#endif
+
+int SkUTF8_CountUnichars(const char utf8[])
+{
+    SkASSERT(utf8);
+
+    int count = 0;
+
+    for (;;)
+    {
+        int c = *(const uint8_t*)utf8;
+        if (c == 0)
+            break;
+
+        utf8 += SkUTF8_LeadByteToCount(c);
+        count += 1;
+    }
+    return count;
+}
+
+int SkUTF8_CountUnichars(const char utf8[], size_t byteLength)
+{
+    SkASSERT(NULL != utf8 || 0 == byteLength);
+
+    int         count = 0;
+    const char* stop = utf8 + byteLength;
+
+    while (utf8 < stop)
+    {
+        utf8 += SkUTF8_LeadByteToCount(*(const uint8_t*)utf8);
+        count += 1;
+    }
+    return count;
+}
+
+SkUnichar SkUTF8_ToUnichar(const char utf8[])
+{
+    SkASSERT(NULL != utf8);
+
+    const uint8_t*  p = (const uint8_t*)utf8;
+    int             c = *p;
+    int             hic = c << 24;
+
+    assert_utf8_leadingbyte(c);
+
+    if (hic < 0)
+    {
+        uint32_t mask = (uint32_t)~0x3F;
+        hic <<= 1;
+        do {
+            c = (c << 6) | (*++p & 0x3F);
+            mask <<= 5;
+        } while ((hic <<= 1) < 0);
+        c &= ~mask;
+    }
+    return c;
+}
+
+SkUnichar SkUTF8_NextUnichar(const char** ptr)
+{
+    SkASSERT(NULL != ptr && NULL != *ptr);
+
+    const uint8_t*  p = (const uint8_t*)*ptr;
+    int             c = *p;
+    int             hic = c << 24;
+    
+    assert_utf8_leadingbyte(c);
+
+    if (hic < 0)
+    {
+        uint32_t mask = (uint32_t)~0x3F;
+        hic <<= 1;
+        do {
+            c = (c << 6) | (*++p & 0x3F);
+            mask <<= 5;
+        } while ((hic <<= 1) < 0);
+        c &= ~mask;
+    }
+    *ptr = (char*)p + 1;
+    return c;
+}
+
+SkUnichar SkUTF8_PrevUnichar(const char** ptr)
+{
+    SkASSERT(NULL != ptr && NULL != *ptr);
+
+    const char* p = *ptr;
+    
+    if (*--p & 0x80)
+        while (*--p & 0x40)
+            ;
+
+    *ptr = (char*)p;
+    return SkUTF8_NextUnichar(&p);
+}
+
+size_t SkUTF8_FromUnichar(SkUnichar uni, char utf8[])
+{
+    if ((uint32_t)uni > 0x10FFFF)
+    {
+        SkASSERT(!"bad unichar");
+        return 0;
+    }
+
+    if (uni <= 127)
+    {
+        if (utf8)
+            *utf8 = (char)uni;
+        return 1;
+    }
+
+    char    tmp[4];
+    char*   p = tmp;
+    size_t  count = 1;
+
+    SkDEBUGCODE(SkUnichar orig = uni;)
+
+    while (uni > 0x3F)
+    {
+        *p++ = (char)(0x80 | (uni & 0x3F));
+        uni >>= 6;
+        count += 1;
+    }
+
+    if (utf8)
+    {
+        p = tmp;
+        utf8 += count;
+        while (p < tmp + count - 1)
+            *--utf8 = *p++;
+        *--utf8 = (char)(~(0xFF >> count) | uni);
+    }
+
+    SkASSERT(utf8 == NULL || orig == SkUTF8_ToUnichar(utf8));
+    return count;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+int SkUTF16_CountUnichars(const uint16_t src[])
+{
+    SkASSERT(src);
+
+    int count = 0;
+    unsigned c;
+    while ((c = *src++) != 0)
+    {
+        SkASSERT(!SkUTF16_IsLowSurrogate(c));
+        if (SkUTF16_IsHighSurrogate(c))
+        {
+            c = *src++;
+            SkASSERT(SkUTF16_IsLowSurrogate(c));
+        }
+        count += 1;
+    }
+    return count;
+}
+
+int SkUTF16_CountUnichars(const uint16_t src[], int numberOf16BitValues)
+{
+    SkASSERT(src);
+
+    const uint16_t* stop = src + numberOf16BitValues;
+    int count = 0;
+    while (src < stop)
+    {
+        unsigned c = *src++;
+        SkASSERT(!SkUTF16_IsLowSurrogate(c));
+        if (SkUTF16_IsHighSurrogate(c))
+        {
+            SkASSERT(src < stop);
+            c = *src++;
+            SkASSERT(SkUTF16_IsLowSurrogate(c));
+        }
+        count += 1;
+    }
+    return count;
+}
+
+SkUnichar SkUTF16_NextUnichar(const uint16_t** srcPtr)
+{
+    SkASSERT(srcPtr && *srcPtr);
+    
+    const uint16_t* src = *srcPtr;
+    SkUnichar       c = *src++;
+    
+    SkASSERT(!SkUTF16_IsLowSurrogate(c));
+    if (SkUTF16_IsHighSurrogate(c))
+    {
+        unsigned c2 = *src++;
+        SkASSERT(SkUTF16_IsLowSurrogate(c2));
+        
+        // c = ((c & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000
+        // c = (((c & 0x3FF) + 64) << 10) + (c2 & 0x3FF)
+        c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00);
+    }
+    *srcPtr = src;
+    return c;
+}
+
+SkUnichar SkUTF16_PrevUnichar(const uint16_t** srcPtr)
+{
+    SkASSERT(srcPtr && *srcPtr);
+    
+    const uint16_t* src = *srcPtr;
+    SkUnichar       c = *--src;
+    
+    SkASSERT(!SkUTF16_IsHighSurrogate(c));
+    if (SkUTF16_IsLowSurrogate(c))
+    {
+        unsigned c2 = *--src;
+        SkASSERT(SkUTF16_IsHighSurrogate(c2));
+        c = (c2 << 10) + c + (0x10000 - (0xD800 << 10) - 0xDC00);
+    }
+    *srcPtr = src;
+    return c;
+}
+
+size_t SkUTF16_FromUnichar(SkUnichar uni, uint16_t dst[])
+{
+    SkASSERT((unsigned)uni <= 0x10FFFF);
+
+    int extra = (uni > 0xFFFF);
+
+    if (dst)
+    {
+        if (extra)
+        {
+            // dst[0] = SkToU16(0xD800 | ((uni - 0x10000) >> 10));
+            // dst[0] = SkToU16(0xD800 | ((uni >> 10) - 64));
+            dst[0] = SkToU16((0xD800 - 64) + (uni >> 10));
+            dst[1] = SkToU16(0xDC00 | (uni & 0x3FF));
+            
+            SkASSERT(SkUTF16_IsHighSurrogate(dst[0]));
+            SkASSERT(SkUTF16_IsLowSurrogate(dst[1]));
+        }
+        else
+        {
+            dst[0] = SkToU16(uni);
+            SkASSERT(!SkUTF16_IsHighSurrogate(dst[0]));
+            SkASSERT(!SkUTF16_IsLowSurrogate(dst[0]));
+        }
+    }
+    return 1 + extra;
+}
+
+size_t SkUTF16_ToUTF8(const uint16_t utf16[], int numberOf16BitValues, char utf8[])
+{
+    SkASSERT(numberOf16BitValues >= 0);
+    if (numberOf16BitValues <= 0)
+        return 0;
+
+    SkASSERT(utf16 != NULL);
+    
+    const uint16_t* stop = utf16 + numberOf16BitValues;
+    size_t          size = 0;
+    
+    if (utf8 == NULL)    // just count
+    {
+        while (utf16 < stop)
+            size += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), NULL);
+    }
+    else
+    {
+        char* start = utf8;
+        while (utf16 < stop)
+            utf8 += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), utf8);
+        size = utf8 - start;
+    }
+    return size;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+#include <stdlib.h>
+
+static int round_to_K(size_t bytes)
+{
+    return (bytes + 512) >> 10;
+}
+
+SkAutoMemoryUsageProbe::SkAutoMemoryUsageProbe(const char label[])
+    : fLabel(label)
+{
+#if 0
+    struct mallinfo mi = mallinfo();
+
+    fBytesAllocated = mi.uordblks;
+#endif
+}
+
+SkAutoMemoryUsageProbe::~SkAutoMemoryUsageProbe()
+{
+#if 0
+    struct mallinfo mi = mallinfo();
+
+    printf("SkAutoMemoryUsageProbe ");
+    if (fLabel)
+        printf("<%s> ", fLabel);
+    printf("delta %dK, current total allocated %dK\n",
+            round_to_K(mi.uordblks - fBytesAllocated),
+            round_to_K(mi.uordblks));
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#include "SkRandom.h"
+#include "SkTSearch.h"
+#include "SkTSort.h"
+
+#define kSEARCH_COUNT   91
+
+#ifdef SK_SUPPORT_UNITTEST
+static void test_search()
+{
+    int         i, array[kSEARCH_COUNT];
+    SkRandom    rand;
+
+    for (i = 0; i < kSEARCH_COUNT; i++)
+        array[i] = rand.nextS();
+
+    SkTHeapSort<int>(array, kSEARCH_COUNT);
+    // make sure we got sorted properly
+    for (i = 1; i < kSEARCH_COUNT; i++)
+        SkASSERT(array[i-1] <= array[i]);
+
+    // make sure we can find all of our values
+    for (i = 0; i < kSEARCH_COUNT; i++)
+    {
+        int index = SkTSearch<int>(array, kSEARCH_COUNT, array[i], sizeof(int));
+        SkASSERT(index == i);
+    }
+
+    // make sure that random values are either found, or the correct
+    // insertion index is returned
+    for (i = 0; i < 10000; i++)
+    {
+        int value = rand.nextS();
+        int index = SkTSearch<int>(array, kSEARCH_COUNT, value, sizeof(int));
+
+        if (index >= 0)
+            SkASSERT(index < kSEARCH_COUNT && array[index] == value);
+        else
+        {
+            index = ~index;
+            SkASSERT(index <= kSEARCH_COUNT);
+            if (index < kSEARCH_COUNT)
+            {
+                SkASSERT(value < array[index]);
+                if (index > 0)
+                    SkASSERT(value > array[index - 1]);
+            }
+            else    // we should append the new value
+            {
+                SkASSERT(value > array[kSEARCH_COUNT - 1]);
+            }
+        }
+    }
+}
+
+static void test_utf16()
+{
+    static const SkUnichar gUni[] = {
+        0x10000, 0x18080, 0x20202, 0xFFFFF, 0x101234
+    };
+    
+    uint16_t buf[2];
+    
+    for (unsigned i = 0; i < SK_ARRAY_COUNT(gUni); i++)
+    {
+        size_t count = SkUTF16_FromUnichar(gUni[i], buf);
+        SkASSERT(count == 2);
+        size_t count2 = SkUTF16_CountUnichars(buf, 2);
+        SkASSERT(count2 == 1);
+        const uint16_t* ptr = buf;
+        SkUnichar c = SkUTF16_NextUnichar(&ptr);
+        SkASSERT(c == gUni[i]);
+        SkASSERT(ptr - buf == 2);
+    }
+}
+
+#endif
+
+void SkUtils::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    static const struct {
+        const char* fUtf8;
+        SkUnichar   fUni;
+    } gTest[] = {
+        { "a",                  'a' },
+        { "\xC3\x83",           (3 << 6) | 3    },
+        { "\xE3\x83\x83",       (3 << 12) | (3 << 6) | 3    },
+        { "\xF3\x83\x83\x83",   (3 << 18) | (3 << 12) | (3 << 6) | 3    }
+    };
+
+    for (unsigned i = 0; i < SK_ARRAY_COUNT(gTest); i++)
+    {
+        const char* p = gTest[i].fUtf8;
+        int         n = SkUTF8_CountUnichars(p);
+        SkUnichar   u0 = SkUTF8_ToUnichar(gTest[i].fUtf8);
+        SkUnichar   u1 = SkUTF8_NextUnichar(&p);
+
+        SkASSERT(n == 1);
+        SkASSERT(u0 == u1);
+        SkASSERT(u0 == gTest[i].fUni);
+        SkASSERT(p - gTest[i].fUtf8 == (int)strlen(gTest[i].fUtf8));
+    }
+    
+    test_utf16();
+
+    test_search();
+#endif
+}
+
+#endif
+
+
diff --git a/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
new file mode 100644
index 0000000..61d0051
--- /dev/null
+++ b/src/core/SkWriter32.cpp
@@ -0,0 +1,170 @@
+#include "SkWriter32.h"
+
+struct SkWriter32::Block {
+    Block*  fNext;
+    size_t  fSize;
+    size_t  fAllocated;
+    
+    size_t  available() const { return fSize - fAllocated; }
+    char*   base() { return (char*)(this + 1); }
+    const char* base() const { return (const char*)(this + 1); }
+    
+    uint32_t* alloc(size_t size)
+    {
+        SkASSERT(SkAlign4(size) == size);
+        SkASSERT(this->available() >= size);
+        void* ptr = this->base() + fAllocated;
+        fAllocated += size;
+        SkASSERT(fAllocated <= fSize);
+        return (uint32_t*)ptr;
+    }
+    
+    uint32_t* peek32(size_t offset)
+    {
+        SkASSERT(offset <= fAllocated + 4);
+        void* ptr = this->base() + offset;
+        return (uint32_t*)ptr;
+    }
+
+    static Block* Create(size_t size)
+    {
+        SkASSERT(SkAlign4(size) == size);
+        Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
+        block->fNext = NULL;
+        block->fSize = size;
+        block->fAllocated = 0;
+        return block;
+    }
+};
+
+static size_t compute_block_size(size_t currSize, size_t minSize)
+{
+    if (currSize < minSize)
+        currSize = minSize;
+    
+    currSize += (currSize >> 1);
+    return SkAlign4(currSize);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkWriter32::~SkWriter32()
+{
+    this->reset();
+}
+
+void SkWriter32::reset()
+{
+    Block* block = fHead;    
+    while (block)
+    {
+        Block* next = block->fNext;
+        sk_free(block);
+        block = next;
+    }
+    fHead = fTail = NULL;
+    fSize = 0;
+}
+
+uint32_t* SkWriter32::reserve(size_t size)
+{
+    SkASSERT(SkAlign4(size) == size);
+    
+    Block* block = fTail;
+
+    if (NULL == block)
+    {
+        SkASSERT(NULL == fHead);
+        fHead = fTail = block = Block::Create(SkMax32(size, fMinSize));
+    }
+    else if (block->available() < size)
+    {
+        fTail = Block::Create(SkMax32(size, fMinSize));
+        block->fNext = fTail;
+        block = fTail;
+    }
+    
+    fSize += size;
+
+    return block->alloc(size);
+}
+
+uint32_t* SkWriter32::peek32(size_t offset)
+{
+    SkASSERT(SkAlign4(offset) == offset);
+    SkASSERT(offset <= fSize);
+
+    Block* block = fHead;
+    SkASSERT(NULL != block);
+    
+    while (offset >= block->fAllocated)
+    {
+        offset -= block->fAllocated;
+        block = block->fNext;
+        SkASSERT(NULL != block);
+    }
+    return block->peek32(offset);
+}
+
+void SkWriter32::flatten(void* dst) const
+{
+    const Block* block = fHead;
+    SkDEBUGCODE(size_t total = 0;)
+
+    while (block)
+    {
+        size_t allocated = block->fAllocated;
+        memcpy(dst, block->base(), allocated);
+        dst = (char*)dst + allocated;
+        block = block->fNext;
+
+        SkDEBUGCODE(total += allocated;)
+        SkASSERT(total <= fSize);
+    }
+    SkASSERT(total == fSize);
+}
+
+void SkWriter32::writePad(const void* src, size_t size) {
+    size_t alignedSize = SkAlign4(size);
+    char* dst = (char*)this->reserve(alignedSize);
+    memcpy(dst, src, size);
+    dst += size;
+    int n = alignedSize - size;
+    while (--n >= 0) {
+        *dst++ = 0;
+    }
+}
+
+#include "SkStream.h"
+
+size_t SkWriter32::readFromStream(SkStream* stream, size_t length) {
+    char scratch[1024];
+    const size_t MAX = sizeof(scratch);
+    size_t remaining = length;
+    
+    while (remaining != 0) {
+        size_t n = remaining;
+        if (n > MAX) {
+            n = MAX;
+        }
+        size_t bytes = stream->read(scratch, n);
+        this->writePad(scratch, bytes);
+        remaining -= bytes;
+        if (bytes != n) {
+            break;
+        }
+    }
+    return length - remaining;
+}
+
+bool SkWriter32::writeToStream(SkWStream* stream) {
+    const Block* block = fHead;    
+    while (block) {
+        if (!stream->write(block->base(), block->fAllocated)) {
+            return false;
+        }
+        block = block->fNext;
+    }
+    return true;
+}
+
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
new file mode 100644
index 0000000..e8a202d
--- /dev/null
+++ b/src/core/SkXfermode.cpp
@@ -0,0 +1,978 @@
+/*
+ * 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.
+ */
+
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+
+#define SkAlphaMulAlpha(a, b)   SkMulDiv255Round(a, b)
+
+static SkPMColor SkFourByteInterp(SkPMColor src, SkPMColor dst, U8CPU alpha) {
+    unsigned scale = SkAlpha255To256(alpha);
+
+    unsigned a = SkAlphaBlend(SkGetPackedA32(src), SkGetPackedA32(dst), scale);
+    unsigned r = SkAlphaBlend(SkGetPackedR32(src), SkGetPackedR32(dst), scale);
+    unsigned g = SkAlphaBlend(SkGetPackedG32(src), SkGetPackedG32(dst), scale);
+    unsigned b = SkAlphaBlend(SkGetPackedB32(src), SkGetPackedB32(dst), scale);
+
+    return SkPackARGB32(a, r, g, b);
+}
+
+// idea for higher precision blends in xfer procs (and slightly faster)
+// see DstATop as a probable caller
+static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
+    SkASSERT(a <= 255);
+    SkASSERT(b <= 255);
+    SkASSERT(c <= 255);
+    SkASSERT(d <= 255);
+    unsigned prod = SkMulS16(a, b) + SkMulS16(c, d) + 128;
+    unsigned result = (prod + (prod >> 8)) >> 8;
+    SkASSERT(result <= 255);
+    return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) {
+    return false;
+}
+
+SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) {
+    // no-op. subclasses should override this
+    return dst;
+}
+
+void SkXfermode::xfer32(SK_RESTRICT SkPMColor dst[],
+                        const SK_RESTRICT SkPMColor src[], int count,
+                        const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            dst[i] = this->xferColor(src[i], dst[i]);
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkPMColor dstC = dst[i];
+                SkPMColor C = this->xferColor(src[i], dstC);
+                if (0xFF != a) {
+                    C = SkFourByteInterp(C, dstC, a);
+                }
+                dst[i] = C;
+            }
+        }
+    }
+}
+
+void SkXfermode::xfer16(SK_RESTRICT uint16_t dst[],
+                        const SK_RESTRICT SkPMColor src[], int count,
+                        const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+            dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC));
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+                SkPMColor C = this->xferColor(src[i], dstC);
+                if (0xFF != a) {
+                    C = SkFourByteInterp(C, dstC, a);
+                }
+                dst[i] = SkPixel32ToPixel16_ToU16(C);
+            }
+        }
+    }
+}
+
+void SkXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[],
+                          const SK_RESTRICT SkPMColor src[], int count,
+                          const SK_RESTRICT SkAlpha aa[])
+{
+    SkASSERT(dst && src && count >= 0);
+    
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+            dst[i] = SkPixel32ToPixel4444(this->xferColor(src[i], dstC));
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+                SkPMColor C = this->xferColor(src[i], dstC);
+                if (0xFF != a) {
+                    C = SkFourByteInterp(C, dstC, a);
+                }
+                dst[i] = SkPixel32ToPixel4444(C);
+            }
+        }
+    }
+}
+
+void SkXfermode::xferA8(SK_RESTRICT SkAlpha dst[],
+                        const SkPMColor src[], int count,
+                        const SK_RESTRICT SkAlpha aa[])
+{
+    SkASSERT(dst && src && count >= 0);
+
+    if (NULL == aa) {
+        for (int i = count - 1; i >= 0; --i) {
+            SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT));
+            dst[i] = SkToU8(SkGetPackedA32(res));
+        }
+    } else {
+        for (int i = count - 1; i >= 0; --i) {
+            unsigned a = aa[i];
+            if (0 != a) {
+                SkAlpha dstA = dst[i];
+                unsigned A = SkGetPackedA32(this->xferColor(src[i],
+                                            (SkPMColor)(dstA << SK_A32_SHIFT)));
+                if (0xFF != a) {
+                    A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
+                }
+                dst[i] = SkToU8(A);
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkProcXfermode::xfer32(SK_RESTRICT SkPMColor dst[],
+                            const SK_RESTRICT SkPMColor src[], int count,
+                            const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                dst[i] = proc(src[i], dst[i]);
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkPMColor dstC = dst[i];
+                    SkPMColor C = proc(src[i], dstC);
+                    if (a != 0xFF) {
+                        C = SkFourByteInterp(C, dstC, a);
+                    }
+                    dst[i] = C;
+                }
+            }
+        }
+    }
+}
+
+void SkProcXfermode::xfer16(SK_RESTRICT uint16_t dst[],
+                            const SK_RESTRICT SkPMColor src[], int count,
+                            const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+                dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC));
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+                    SkPMColor C = proc(src[i], dstC);
+                    if (0xFF != a) {
+                        C = SkFourByteInterp(C, dstC, a);
+                    }
+                    dst[i] = SkPixel32ToPixel16_ToU16(C);
+                }
+            }
+        }
+    }
+}
+
+void SkProcXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[],
+                              const SK_RESTRICT SkPMColor src[], int count,
+                              const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+    
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+                dst[i] = SkPixel32ToPixel4444(proc(src[i], dstC));
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkPMColor dstC = SkPixel4444ToPixel32(dst[i]);
+                    SkPMColor C = proc(src[i], dstC);
+                    if (0xFF != a) {
+                        C = SkFourByteInterp(C, dstC, a);
+                    }
+                    dst[i] = SkPixel32ToPixel4444(C);
+                }
+            }
+        }
+    }
+}
+
+void SkProcXfermode::xferA8(SK_RESTRICT SkAlpha dst[],
+                            const SK_RESTRICT SkPMColor src[], int count,
+                            const SK_RESTRICT SkAlpha aa[]) {
+    SkASSERT(dst && src && count >= 0);
+
+    SkXfermodeProc proc = fProc;
+
+    if (NULL != proc) {
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT);
+                dst[i] = SkToU8(SkGetPackedA32(res));
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    SkAlpha dstA = dst[i];
+                    SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT);
+                    unsigned A = SkGetPackedA32(res);
+                    if (0xFF != a) {
+                        A = SkAlphaBlend(A, dstA, SkAlpha255To256(a));
+                    }
+                    dst[i] = SkToU8(A);
+                }
+            }
+        }
+    }
+}
+
+SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer)
+        : SkXfermode(buffer) {
+    fProc = (SkXfermodeProc)buffer.readFunctionPtr();
+}
+
+void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) {
+    buffer.writeFunctionPtr((void*)fProc);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class SkProcCoeffXfermode : public SkProcXfermode {
+public:
+    SkProcCoeffXfermode(SkXfermodeProc proc, Coeff sc, Coeff dc)
+            : INHERITED(proc), fSrcCoeff(sc), fDstCoeff(dc) {
+    }
+    
+    virtual bool asCoeff(Coeff* sc, Coeff* dc) {
+        if (sc) {
+            *sc = fSrcCoeff;
+        }
+        if (dc) {
+            *dc = fDstCoeff;
+        }
+        return true;
+    }
+    
+    virtual Factory getFactory() { return CreateProc; }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+        this->INHERITED::flatten(buffer);
+        buffer.write32(fSrcCoeff);
+        buffer.write32(fDstCoeff);
+    }
+
+protected:
+    SkProcCoeffXfermode(SkFlattenableReadBuffer& buffer)
+            : INHERITED(buffer) {
+        fSrcCoeff = (Coeff)buffer.readU32();
+        fDstCoeff = (Coeff)buffer.readU32();
+    }
+    
+private:
+    Coeff   fSrcCoeff, fDstCoeff;
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+    return SkNEW_ARGS(SkProcCoeffXfermode, (buffer)); }
+
+    typedef SkProcXfermode INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+//  kClear_Mode,    //!< [0, 0]
+static SkPMColor clear_modeproc(SkPMColor src, SkPMColor dst) {
+    return 0;
+}
+
+//  kSrc_Mode,      //!< [Sa, Sc]
+static SkPMColor src_modeproc(SkPMColor src, SkPMColor dst) {
+    return src;
+}
+
+//  kDst_Mode,      //!< [Da, Dc]
+static SkPMColor dst_modeproc(SkPMColor src, SkPMColor dst) {
+    return dst;
+}
+
+//  kSrcOver_Mode,  //!< [Sa + (1 - Sa)*Da, Sc + (1 - Sa)*Dc] 
+static SkPMColor srcover_modeproc(SkPMColor src, SkPMColor dst) {
+    return src + SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
+}
+
+//  kDstOver_Mode,  //!< [Sa + (1 - Sa)*Da, Dc + (1 - Da)*Sc]
+static SkPMColor dstover_modeproc(SkPMColor src, SkPMColor dst) {
+    unsigned sa = SkGetPackedA32(src);
+    unsigned da = SkGetPackedA32(dst);
+    unsigned ida = 255 - da;
+    
+    return SkPackARGB32(sa + da - SkAlphaMulAlpha(sa, da),
+        SkGetPackedR32(dst) + SkAlphaMulAlpha(ida, SkGetPackedR32(src)),
+        SkGetPackedG32(dst) + SkAlphaMulAlpha(ida, SkGetPackedG32(src)),
+        SkGetPackedB32(dst) + SkAlphaMulAlpha(ida, SkGetPackedB32(src)));
+}
+
+//  kSrcIn_Mode,    //!< [Sa * Da, Sc * Da]
+static SkPMColor srcin_modeproc(SkPMColor src, SkPMColor dst) {
+    return SkAlphaMulQ(src, SkAlpha255To256(SkGetPackedA32(dst)));
+}
+
+//  kDstIn_Mode,    //!< [Sa * Da, Sa * Dc]
+static SkPMColor dstin_modeproc(SkPMColor src, SkPMColor dst) {
+    return SkAlphaMulQ(dst, SkAlpha255To256(SkGetPackedA32(src)));
+}
+
+//  kSrcOut_Mode,   //!< [Sa * (1 - Da), Sc * (1 - Da)]
+static SkPMColor srcout_modeproc(SkPMColor src, SkPMColor dst) {
+    return SkAlphaMulQ(src, SkAlpha255To256(255 - SkGetPackedA32(dst)));
+}
+
+//  kDstOut_Mode,   //!< [Da * (1 - Sa), Dc * (1 - Sa)]
+static SkPMColor dstout_modeproc(SkPMColor src, SkPMColor dst) {
+    return SkAlphaMulQ(dst, SkAlpha255To256(255 - SkGetPackedA32(src)));
+}
+
+//  kSrcATop_Mode,  //!< [Da, Sc * Da + (1 - Sa) * Dc]
+static SkPMColor srcatop_modeproc(SkPMColor src, SkPMColor dst) {
+    unsigned sa = SkGetPackedA32(src);
+    unsigned da = SkGetPackedA32(dst);
+    unsigned isa = 255 - sa;
+
+    return SkPackARGB32(da,
+                        SkAlphaMulAlpha(da, SkGetPackedR32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
+                        SkAlphaMulAlpha(da, SkGetPackedG32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
+                        SkAlphaMulAlpha(da, SkGetPackedB32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
+}
+
+//  kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
+static SkPMColor dstatop_modeproc(SkPMColor src, SkPMColor dst) {
+    unsigned sa = SkGetPackedA32(src);
+    unsigned da = SkGetPackedA32(dst);
+    unsigned ida = 255 - da;
+
+    return SkPackARGB32(sa,
+                        SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
+                            SkAlphaMulAlpha(sa, SkGetPackedR32(dst)),
+                        SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
+                            SkAlphaMulAlpha(sa, SkGetPackedG32(dst)),
+                        SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
+                            SkAlphaMulAlpha(sa, SkGetPackedB32(dst)));
+}
+
+//  kXor_Mode   [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
+static SkPMColor xor_modeproc(SkPMColor src, SkPMColor dst) {
+    unsigned sa = SkGetPackedA32(src);
+    unsigned da = SkGetPackedA32(dst);
+    unsigned isa = 255 - sa;
+    unsigned ida = 255 - da;
+
+    return SkPackARGB32(sa + da - (SkAlphaMulAlpha(sa, da) << 1),
+                        SkAlphaMulAlpha(ida, SkGetPackedR32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedR32(dst)),
+                        SkAlphaMulAlpha(ida, SkGetPackedG32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedG32(dst)),
+                        SkAlphaMulAlpha(ida, SkGetPackedB32(src)) +
+                            SkAlphaMulAlpha(isa, SkGetPackedB32(dst)));
+}
+
+
+// kDarken_Mode,   [Sa + Da - Sa·Da, Sc·(1 - Da) + Dc·(1 - Sa) + min(Sc, Dc)]
+
+static inline unsigned darken_p(unsigned src, unsigned dst,
+                                unsigned src_mul, unsigned dst_mul) {
+    return ((dst_mul * src + src_mul * dst) >> 8) + SkMin32(src, dst);
+}
+
+static SkPMColor darken_modeproc(SkPMColor src, SkPMColor dst) {
+    unsigned sa = SkGetPackedA32(src);
+    unsigned da = SkGetPackedA32(dst);
+    unsigned src_scale = SkAlpha255To256(255 - sa);
+    unsigned dst_scale = SkAlpha255To256(255 - da);
+
+    unsigned ra = sa + da - SkAlphaMulAlpha(sa, da);
+    unsigned rr = darken_p(SkGetPackedR32(src), SkGetPackedR32(dst),
+                           src_scale, dst_scale);
+    unsigned rg = darken_p(SkGetPackedG32(src), SkGetPackedG32(dst),
+                           src_scale, dst_scale);
+    unsigned rb = darken_p(SkGetPackedB32(src), SkGetPackedB32(dst),
+                           src_scale, dst_scale);
+
+    return SkPackARGB32(ra, SkFastMin32(rr, ra),
+                        SkFastMin32(rg, ra), SkFastMin32(rb, ra));
+}
+
+// kLighten_Mode,  [Sa + Da - Sa·Da, Sc·(1 - Da) + Dc·(1 - Sa) + max(Sc, Dc)]
+static inline unsigned lighten_p(unsigned src, unsigned dst,
+                                 unsigned src_mul, unsigned dst_mul) {
+    return ((dst_mul * src + src_mul * dst) >> 8) + SkMax32(src, dst);
+}
+
+static SkPMColor lighten_modeproc(SkPMColor src, SkPMColor dst) {
+    unsigned sa = SkGetPackedA32(src);
+    unsigned da = SkGetPackedA32(dst);
+    unsigned src_scale = SkAlpha255To256(255 - sa);
+    unsigned dst_scale = SkAlpha255To256(255 - da);
+    
+    unsigned ra = sa + da - SkAlphaMulAlpha(sa, da);
+    unsigned rr = lighten_p(SkGetPackedR32(src), SkGetPackedR32(dst),
+                            src_scale, dst_scale);
+    unsigned rg = lighten_p(SkGetPackedG32(src), SkGetPackedG32(dst),
+                            src_scale, dst_scale);
+    unsigned rb = lighten_p(SkGetPackedB32(src), SkGetPackedB32(dst),
+                            src_scale, dst_scale);
+
+    return SkPackARGB32(ra, SkFastMin32(rr, ra),
+                        SkFastMin32(rg, ra), SkFastMin32(rb, ra));
+}
+    
+static SkPMColor mult_modeproc(SkPMColor src, SkPMColor dst) {
+    int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
+    int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
+    int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
+    int b = SkAlphaMulAlpha(SkGetPackedB32(src), SkGetPackedB32(dst));
+    return SkPackARGB32(a, r, g, b);
+}
+
+static inline int screen_byte(int a, int b) {
+    return a + b - SkAlphaMulAlpha(a, b);
+}
+
+static SkPMColor screen_modeproc(SkPMColor src, SkPMColor dst) {
+    int a = screen_byte(SkGetPackedA32(src), SkGetPackedA32(dst));
+    int r = screen_byte(SkGetPackedR32(src), SkGetPackedR32(dst));
+    int g = screen_byte(SkGetPackedG32(src), SkGetPackedG32(dst));
+    int b = screen_byte(SkGetPackedB32(src), SkGetPackedB32(dst));
+    return SkPackARGB32(a, r, g, b);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkClearXfermode : public SkProcCoeffXfermode {
+public:
+    SkClearXfermode() : SkProcCoeffXfermode(clear_modeproc,
+                                            kZero_Coeff, kZero_Coeff) {}
+
+    virtual void xfer32(SK_RESTRICT SkPMColor dst[],
+                        const SK_RESTRICT SkPMColor[], int count,
+                        const SK_RESTRICT SkAlpha aa[]) {
+        SkASSERT(dst && count >= 0);
+
+        if (NULL == aa) {
+            memset(dst, 0, count << 2);
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0xFF == a) {
+                    dst[i] = 0;
+                } else if (a != 0) {
+                    dst[i] = SkAlphaMulQ(dst[i], SkAlpha255To256(255 - a));
+                }
+            }
+        }
+    }
+    virtual void xferA8(SK_RESTRICT SkAlpha dst[],
+                        const SK_RESTRICT SkPMColor[], int count,
+                        const SK_RESTRICT SkAlpha aa[]) {
+        SkASSERT(dst && count >= 0);
+
+        if (NULL == aa) {
+            memset(dst, 0, count);
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0xFF == a) {
+                    dst[i] = 0;
+                } else if (0 != a) {
+                    dst[i] = SkAlphaMulAlpha(dst[i], 255 - a);
+                }
+            }
+        }
+    }
+    
+    virtual Factory getFactory() { return CreateProc; }
+
+private:
+    SkClearXfermode(SkFlattenableReadBuffer& buffer)
+        : SkProcCoeffXfermode(buffer) {}
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkClearXfermode, (buffer));
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkSrcXfermode : public SkProcCoeffXfermode {
+public:
+    SkSrcXfermode() : SkProcCoeffXfermode(src_modeproc,
+                                          kOne_Coeff, kZero_Coeff) {}
+
+    virtual void xfer32(SK_RESTRICT SkPMColor dst[],
+                        const SK_RESTRICT SkPMColor src[], int count,
+                        const SK_RESTRICT SkAlpha aa[]) {
+        SkASSERT(dst && src && count >= 0);
+
+        if (NULL == aa) {
+            memcpy(dst, src, count << 2);
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (a == 0xFF) {
+                    dst[i] = src[i];
+                } else if (a != 0) {
+                    dst[i] = SkFourByteInterp(src[i], dst[i], a);
+                }
+            }
+        }
+    }
+
+    virtual void xferA8(SK_RESTRICT SkAlpha dst[],
+                        const SK_RESTRICT SkPMColor src[], int count,
+                        const SK_RESTRICT SkAlpha aa[]) {
+        SkASSERT(dst && src && count >= 0);
+
+        if (NULL == aa) {
+            for (int i = count - 1; i >= 0; --i) {
+                dst[i] = SkToU8(SkGetPackedA32(src[i]));
+            }
+        } else {
+            for (int i = count - 1; i >= 0; --i) {
+                unsigned a = aa[i];
+                if (0 != a) {
+                    unsigned srcA = SkGetPackedA32(src[i]);
+                    if (a == 0xFF) {
+                        dst[i] = SkToU8(srcA);
+                    } else {
+                        dst[i] = SkToU8(SkAlphaBlend(srcA, dst[i], a));
+                    }
+                }
+            }
+        }
+    }
+    
+    virtual Factory getFactory() { return CreateProc; }
+
+private:
+    SkSrcXfermode(SkFlattenableReadBuffer& buffer)
+        : SkProcCoeffXfermode(buffer) {}
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkSrcXfermode, (buffer));
+    }
+};
+
+class SkDstInXfermode : public SkProcCoeffXfermode {
+public:
+    SkDstInXfermode() : SkProcCoeffXfermode(dstin_modeproc,
+                                            kZero_Coeff, kSA_Coeff) {}
+    
+    virtual void xfer32(SK_RESTRICT SkPMColor dst[],
+                        const SK_RESTRICT SkPMColor src[], int count,
+                        const SK_RESTRICT SkAlpha aa[]) {
+        SkASSERT(dst && src);
+        
+        if (count <= 0) {
+            return;
+        }
+        if (NULL != aa) {
+            return this->INHERITED::xfer32(dst, src, count, aa);
+        }
+        
+        do {
+            unsigned a = SkGetPackedA32(*src);
+            *dst = SkAlphaMulQ(*dst, SkAlpha255To256(a));
+            dst++;
+            src++;
+        } while (--count != 0);
+    }
+    
+    virtual Factory getFactory() { return CreateProc; }
+    
+private:
+    SkDstInXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkDstInXfermode, (buffer));
+    }
+    
+    typedef SkProcCoeffXfermode INHERITED;
+};
+
+class SkDstOutXfermode : public SkProcCoeffXfermode {
+public:
+    SkDstOutXfermode() : SkProcCoeffXfermode(dstout_modeproc,
+                                             kZero_Coeff, kISA_Coeff) {}
+    
+    virtual void xfer32(SK_RESTRICT SkPMColor dst[],
+                        const SK_RESTRICT SkPMColor src[], int count,
+                        const SK_RESTRICT SkAlpha aa[]) {
+        SkASSERT(dst && src);
+        
+        if (count <= 0) {
+            return;
+        }
+        if (NULL != aa) {
+            return this->INHERITED::xfer32(dst, src, count, aa);
+        }
+        
+        do {
+            unsigned a = SkGetPackedA32(*src);
+            *dst = SkAlphaMulQ(*dst, SkAlpha255To256(255 - a));
+            dst++;
+            src++;
+        } while (--count != 0);
+    }
+    
+    virtual Factory getFactory() { return CreateProc; }
+    
+private:
+    SkDstOutXfermode(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {}
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkDstOutXfermode, (buffer));
+    }
+    
+    typedef SkProcCoeffXfermode INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPorterDuff.h"
+
+struct ProcCoeff {
+    SkXfermodeProc      fProc;
+    SkXfermode::Coeff   fSC;
+    SkXfermode::Coeff   fDC;
+};
+
+static const ProcCoeff gProcCoeffs[] = {
+    { clear_modeproc,   SkXfermode::kZero_Coeff,    SkXfermode::kZero_Coeff },
+    { src_modeproc,     SkXfermode::kOne_Coeff,     SkXfermode::kZero_Coeff },
+    { dst_modeproc,     SkXfermode::kZero_Coeff,    SkXfermode::kOne_Coeff },
+    { srcover_modeproc, SkXfermode::kOne_Coeff,     SkXfermode::kISA_Coeff },
+    { dstover_modeproc, SkXfermode::kIDA_Coeff,     SkXfermode::kOne_Coeff },
+    { srcin_modeproc,   SkXfermode::kDA_Coeff,      SkXfermode::kZero_Coeff },
+    { dstin_modeproc,   SkXfermode::kZero_Coeff,    SkXfermode::kSA_Coeff },
+    { srcout_modeproc,  SkXfermode::kIDA_Coeff,     SkXfermode::kZero_Coeff },
+    { dstout_modeproc,  SkXfermode::kZero_Coeff,    SkXfermode::kISA_Coeff },
+    { srcatop_modeproc, SkXfermode::kDA_Coeff,      SkXfermode::kISA_Coeff },
+    { dstatop_modeproc, SkXfermode::kIDA_Coeff,     SkXfermode::kSA_Coeff },
+    { xor_modeproc,     SkXfermode::kIDA_Coeff,     SkXfermode::kISA_Coeff },
+    // these two can't be represented as coefficients
+    { darken_modeproc,  SkXfermode::Coeff(-1),      SkXfermode::Coeff(-1) },
+    { lighten_modeproc, SkXfermode::Coeff(-1),      SkXfermode::Coeff(-1) },
+    // these can use coefficients
+    { mult_modeproc,    SkXfermode::kZero_Coeff,    SkXfermode::kSC_Coeff },
+    { screen_modeproc,  SkXfermode::kOne_Coeff,     SkXfermode::kISC_Coeff }
+};
+
+SkXfermode* SkPorterDuff::CreateXfermode(SkPorterDuff::Mode mode) {
+    SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == SkPorterDuff::kModeCount);
+    SkASSERT((unsigned)mode < SkPorterDuff::kModeCount);
+
+    switch (mode) {
+        case kClear_Mode:
+            return SkNEW(SkClearXfermode);
+        case kSrc_Mode:
+            return SkNEW(SkSrcXfermode);
+        case kSrcOver_Mode:
+            return NULL;
+        case kDstIn_Mode:
+            return SkNEW(SkDstInXfermode);
+        case kDstOut_Mode:
+            return SkNEW(SkDstOutXfermode);
+        // these two can't be represented with Coeff
+        case kDarken_Mode:
+            return SkNEW_ARGS(SkProcXfermode, (darken_modeproc));
+        case kLighten_Mode:
+            return SkNEW_ARGS(SkProcXfermode, (lighten_modeproc));
+        // use the table 
+        default: {
+            const ProcCoeff& rec = gProcCoeffs[mode];
+            SkASSERT((unsigned)rec.fSC < SkXfermode::kCoeffCount);
+            SkASSERT((unsigned)rec.fDC < SkXfermode::kCoeffCount);
+            return SkNEW_ARGS(SkProcCoeffXfermode, (rec.fProc,
+                                                    rec.fSC, rec.fDC));
+        }
+    }
+}
+
+bool SkPorterDuff::IsMode(SkXfermode* xfer, Mode* mode) {
+    if (NULL == xfer) {
+        if (mode) {
+            *mode = kSrcOver_Mode;
+        }
+        return true;
+    }
+
+    SkXfermode::Coeff sc, dc;
+    if (xfer->asCoeff(&sc, &dc)) {
+        SkASSERT((unsigned)sc < (unsigned)SkXfermode::kCoeffCount);
+        SkASSERT((unsigned)dc < (unsigned)SkXfermode::kCoeffCount);
+        
+        const ProcCoeff* rec = gProcCoeffs;
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gProcCoeffs); i++) {
+            if (rec[i].fSC == sc && rec[i].fDC == dc) {
+                if (mode) {
+                    *mode = SkPorterDuff::Mode(i);
+                }
+                return true;
+            }
+        }
+    }
+
+    // no coefficients, or not found in our table
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+static void unit_test() {
+    for (unsigned a = 0; a <= 255; a++) {
+        for (unsigned c = 0; c <= a; c++) {
+            SkPMColor pm = SkPackARGB32(a, c, c, c);
+            for (unsigned aa = 0; aa <= 255; aa++) {
+                for (unsigned cc = 0; cc <= aa; cc++) {
+                    SkPMColor pm2 = SkPackARGB32(aa, cc, cc, cc);
+                    
+                    const size_t N = SK_ARRAY_COUNT(gProcCoeffs);
+                    for (size_t i = 0; i < N; i++) {
+                        gProcCoeffs[i].fProc(pm, pm2);
+                    }
+                }
+            }
+        }
+    }            
+}
+#endif
+
+SkXfermodeProc SkPorterDuff::GetXfermodeProc(Mode mode) {
+#ifdef SK_DEBUGx
+    static bool gUnitTest;
+    if (!gUnitTest) {
+        gUnitTest = true;
+        unit_test();
+    }
+#endif
+
+    SkXfermodeProc  proc = NULL;
+
+    if ((unsigned)mode < SkPorterDuff::kModeCount) {
+        proc = gProcCoeffs[mode].fProc;
+    }
+    return proc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//////////// 16bit xfermode procs
+
+#ifdef SK_DEBUG
+static bool require_255(SkPMColor src) { return SkGetPackedA32(src) == 0xFF; }
+static bool require_0(SkPMColor src) { return SkGetPackedA32(src) == 0; }
+#endif
+
+static uint16_t src_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dst_modeproc16(SkPMColor src, uint16_t dst) {
+    return dst;
+}
+
+static uint16_t srcover_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return dst;
+}
+
+static uint16_t srcover_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dstover_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return dst;
+}
+
+static uint16_t dstover_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return dst;
+}
+
+static uint16_t srcin_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dstin_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return dst;
+}
+
+static uint16_t dstout_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return dst;
+}
+
+static uint16_t srcatop_modeproc16(SkPMColor src, uint16_t dst) {
+    unsigned isa = 255 - SkGetPackedA32(src);
+    
+    return SkPackRGB16(
+           SkPacked32ToR16(src) + SkAlphaMulAlpha(SkGetPackedR16(dst), isa),
+           SkPacked32ToG16(src) + SkAlphaMulAlpha(SkGetPackedG16(dst), isa),
+           SkPacked32ToB16(src) + SkAlphaMulAlpha(SkGetPackedB16(dst), isa));
+}
+
+static uint16_t srcatop_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return dst;
+}
+
+static uint16_t srcatop_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return SkPixel32ToPixel16(src);
+}
+
+static uint16_t dstatop_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    return dst;
+}
+
+/*********
+    darken and lighten boil down to this.
+
+    darken  = (1 - Sa) * Dc + min(Sc, Dc)
+    lighten = (1 - Sa) * Dc + max(Sc, Dc)
+
+    if (Sa == 0) these become
+        darken  = Dc + min(0, Dc) = 0
+        lighten = Dc + max(0, Dc) = Dc
+
+    if (Sa == 1) these become
+        darken  = min(Sc, Dc)
+        lighten = max(Sc, Dc)
+*/
+
+static uint16_t darken_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return 0;
+}
+
+static uint16_t darken_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    unsigned r = SkFastMin32(SkPacked32ToR16(src), SkGetPackedR16(dst));
+    unsigned g = SkFastMin32(SkPacked32ToG16(src), SkGetPackedG16(dst));
+    unsigned b = SkFastMin32(SkPacked32ToB16(src), SkGetPackedB16(dst));
+    return SkPackRGB16(r, g, b);
+}
+
+static uint16_t lighten_modeproc16_0(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_0(src));
+    return dst;
+}
+
+static uint16_t lighten_modeproc16_255(SkPMColor src, uint16_t dst) {
+    SkASSERT(require_255(src));
+    unsigned r = SkMax32(SkPacked32ToR16(src), SkGetPackedR16(dst));
+    unsigned g = SkMax32(SkPacked32ToG16(src), SkGetPackedG16(dst));
+    unsigned b = SkMax32(SkPacked32ToB16(src), SkGetPackedB16(dst));
+    return SkPackRGB16(r, g, b);
+}
+
+struct Proc16Rec {
+    SkXfermodeProc16    fProc16_0;
+    SkXfermodeProc16    fProc16_255;
+    SkXfermodeProc16    fProc16_General;
+};
+
+static const Proc16Rec gPorterDuffModeProcs16[] = {
+    { NULL,                 NULL,                   NULL            }, // CLEAR
+    { NULL,                 src_modeproc16_255,     NULL            },
+    { dst_modeproc16,       dst_modeproc16,         dst_modeproc16  },
+    { srcover_modeproc16_0, srcover_modeproc16_255, NULL            },
+    { dstover_modeproc16_0, dstover_modeproc16_255, NULL            },
+    { NULL,                 srcin_modeproc16_255,   NULL            },
+    { NULL,                 dstin_modeproc16_255,   NULL            },
+    { NULL,                 NULL,                   NULL            },// SRC_OUT
+    { dstout_modeproc16_0,  NULL,                   NULL            },
+    { srcatop_modeproc16_0, srcatop_modeproc16_255, srcatop_modeproc16  },
+    { NULL,                 dstatop_modeproc16_255, NULL            },
+    { NULL,                 NULL,                   NULL            }, // XOR
+    { darken_modeproc16_0,  darken_modeproc16_255,  NULL            },
+    { lighten_modeproc16_0, lighten_modeproc16_255, NULL            },
+    { NULL,                 NULL,                   NULL            },//multiply
+    { NULL,                 NULL,                   NULL            }// screen
+};
+
+SkXfermodeProc16 SkPorterDuff::GetXfermodeProc16(Mode mode, SkColor srcColor) {
+    SkXfermodeProc16  proc16 = NULL;
+
+    if ((unsigned)mode < SkPorterDuff::kModeCount) {
+        const Proc16Rec& rec = gPorterDuffModeProcs16[mode];
+        
+        unsigned a = SkColorGetA(srcColor);
+
+        if (0 == a) {
+            proc16 = rec.fProc16_0;
+        } else if (255 == a) {
+            proc16 = rec.fProc16_255;
+        } else {
+            proc16 = rec.fProc16_General;
+        }
+    }
+    return proc16;
+}
+
diff --git a/src/effects/Sk1DPathEffect.cpp b/src/effects/Sk1DPathEffect.cpp
new file mode 100644
index 0000000..fe299c9
--- /dev/null
+++ b/src/effects/Sk1DPathEffect.cpp
@@ -0,0 +1,206 @@
+/* libs/graphics/effects/Sk1DPathEffect.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 "Sk1DPathEffect.h"
+#include "SkPathMeasure.h"
+
+bool Sk1DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
+{
+    SkPathMeasure   meas(src, false);
+    do {
+        SkScalar    length = meas.getLength();
+        SkScalar    distance = this->begin(length);
+        while (distance < length)
+        {
+            SkScalar delta = this->next(dst, distance, meas);
+            if (delta <= 0)
+                break;
+            distance += delta;
+        }
+    } while (meas.nextContour());
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+SkPath1DPathEffect::SkPath1DPathEffect(const SkPath& path, SkScalar advance, 
+    SkScalar phase, Style style) : fPath(path)
+{
+    if (advance <= 0 || path.isEmpty())
+    {
+        SkDEBUGF(("SkPath1DPathEffect can't use advance <= 0\n"));
+        fAdvance = 0;   // signals we can't draw anything
+    }
+    else
+    {
+        // cleanup their phase parameter, inverting it so that it becomes an
+        // offset along the path (to match the interpretation in PostScript)
+        if (phase < 0)
+        {
+            phase = -phase;
+            if (phase > advance)
+                phase = SkScalarMod(phase, advance);
+        }
+        else
+        {
+            if (phase > advance)
+                phase = SkScalarMod(phase, advance);
+            phase = advance - phase;
+        }
+        // now catch the edge case where phase == advance (within epsilon)
+        if (phase >= advance)
+            phase = 0;
+        SkASSERT(phase >= 0);
+
+        fAdvance = advance;
+        fInitialOffset = phase;
+        
+        if ((unsigned)style >= kStyleCount) {
+            SkDEBUGF(("SkPath1DPathEffect style enum out of range %d\n", style));
+        }
+        fStyle = style;
+    }
+}
+
+bool SkPath1DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
+{
+    if (fAdvance > 0)
+    {
+        *width = -1;
+        return this->INHERITED::filterPath(dst, src, width);
+    }
+    return false;
+}
+
+static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
+                        SkPathMeasure& meas, SkScalar dist)
+{
+    for (int i = 0; i < count; i++)
+    {
+        SkPoint pos;
+        SkVector tangent;
+        
+        SkScalar sx = src[i].fX;
+        SkScalar sy = src[i].fY;
+        
+        meas.getPosTan(dist + sx, &pos, &tangent);
+        
+        SkMatrix    matrix;
+        SkPoint     pt;
+        
+        pt.set(sx, sy);
+        matrix.setSinCos(tangent.fY, tangent.fX, 0, 0);
+        matrix.preTranslate(-sx, 0);
+        matrix.postTranslate(pos.fX, pos.fY);
+        matrix.mapPoints(&dst[i], &pt, 1);
+    }
+}
+
+/*  TODO
+
+Need differentially more subdivisions when the follow-path is curvy. Not sure how to
+determine that, but we need it. I guess a cheap answer is let the caller tell us,
+but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
+*/
+static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, SkScalar dist)
+{
+    SkPath::Iter    iter(src, false);
+    SkPoint         srcP[4], dstP[3];
+    SkPath::Verb    verb;
+    
+    while ((verb = iter.next(srcP)) != SkPath::kDone_Verb)
+    {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                morphpoints(dstP, srcP, 1, meas, dist);
+                dst->moveTo(dstP[0]);
+                break;
+            case SkPath::kLine_Verb:
+                srcP[2] = srcP[1];
+                srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX),
+                            SkScalarAve(srcP[0].fY, srcP[2].fY));
+                // fall through to quad
+            case SkPath::kQuad_Verb:
+                morphpoints(dstP, &srcP[1], 2, meas, dist);
+                dst->quadTo(dstP[0], dstP[1]);
+                break;
+            case SkPath::kCubic_Verb:
+                morphpoints(dstP, &srcP[1], 3, meas, dist);
+                dst->cubicTo(dstP[0], dstP[1], dstP[2]);
+                break;
+            case SkPath::kClose_Verb:
+                dst->close();
+                break;
+            default:
+                SkASSERT(!"unknown verb");
+                break;
+        }
+    }
+}
+
+SkPath1DPathEffect::SkPath1DPathEffect(SkFlattenableReadBuffer& buffer)
+{
+    fAdvance = buffer.readScalar();
+    if (fAdvance > 0) {
+        fPath.unflatten(buffer);
+        fInitialOffset = buffer.readScalar();
+        fStyle = (Style) buffer.readU8();
+    }
+}
+
+SkScalar SkPath1DPathEffect::begin(SkScalar contourLength)
+{
+    return fInitialOffset;
+}
+
+void SkPath1DPathEffect::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    buffer.writeScalar(fAdvance);
+    if (fAdvance > 0) {
+        fPath.flatten(buffer);
+        buffer.writeScalar(fInitialOffset);
+        buffer.write8(fStyle);
+    }
+}
+
+SkScalar SkPath1DPathEffect::next(SkPath* dst, SkScalar distance, SkPathMeasure& meas)
+{
+    switch (fStyle) {
+        case kTranslate_Style:
+        {
+            SkPoint pos;
+            meas.getPosTan(distance, &pos, NULL);
+            dst->addPath(fPath, pos.fX, pos.fY);
+        }
+            break;
+        case kRotate_Style:
+        {
+            SkMatrix matrix;
+            meas.getMatrix(distance, &matrix);
+            dst->addPath(fPath, matrix);
+        }
+            break;
+        case kMorph_Style:
+            morphpath(dst, fPath, meas, distance);
+            break;
+        default:
+            SkASSERT(!"unknown Style enum");
+            break;
+    }
+    return fAdvance;
+}
+
diff --git a/src/effects/Sk2DPathEffect.cpp b/src/effects/Sk2DPathEffect.cpp
new file mode 100644
index 0000000..405b194
--- /dev/null
+++ b/src/effects/Sk2DPathEffect.cpp
@@ -0,0 +1,107 @@
+/* libs/graphics/effects/Sk2DPathEffect.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 "Sk2DPathEffect.h"
+#include "SkBlitter.h"
+#include "SkPath.h"
+#include "SkScan.h"
+
+class Sk2DPathEffectBlitter : public SkBlitter {
+public:
+    Sk2DPathEffectBlitter(Sk2DPathEffect* pe, SkPath* dst)
+        : fPE(pe), fDst(dst)
+    {}
+    virtual void blitH(int x, int y, int count)
+    {
+        fPE->nextSpan(x, y, count, fDst);
+    }
+private:
+    Sk2DPathEffect* fPE;
+    SkPath*         fDst;
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+
+Sk2DPathEffect::Sk2DPathEffect(const SkMatrix& mat) : fMatrix(mat)
+{
+    mat.invert(&fInverse);
+}
+
+bool Sk2DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
+{
+    Sk2DPathEffectBlitter   blitter(this, dst);
+    SkPath                  tmp;
+    SkRect                  bounds;
+    SkIRect                 ir;
+
+    src.transform(fInverse, &tmp);
+    tmp.computeBounds(&bounds, SkPath::kExact_BoundsType);
+    bounds.round(&ir);
+    if (!ir.isEmpty()) {
+        // need to pass a clip to fillpath, required for inverse filltypes,
+        // even though those do not make sense for this patheffect
+        SkRegion clip(ir);
+        
+        this->begin(ir, dst);
+        SkScan::FillPath(tmp, clip, &blitter);
+        this->end(dst);
+    }
+    return true;
+}
+
+void Sk2DPathEffect::nextSpan(int x, int y, int count, SkPath* path)
+{
+    const SkMatrix& mat = this->getMatrix();
+    SkPoint src, dst;
+
+    src.set(SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf);
+    do {
+        mat.mapPoints(&dst, &src, 1);
+        this->next(dst, x++, y, path);
+        src.fX += SK_Scalar1;
+    } while (--count > 0);
+}
+
+void Sk2DPathEffect::begin(const SkIRect& uvBounds, SkPath* dst) {}
+void Sk2DPathEffect::next(const SkPoint& loc, int u, int v, SkPath* dst) {}
+void Sk2DPathEffect::end(SkPath* dst) {}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void Sk2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    buffer.writeMul4(&fMatrix, sizeof(fMatrix));
+}
+
+Sk2DPathEffect::Sk2DPathEffect(SkFlattenableReadBuffer& buffer)
+{
+    buffer.read(&fMatrix, sizeof(fMatrix));
+    fMatrix.invert(&fInverse);
+}
+
+SkFlattenable::Factory Sk2DPathEffect::getFactory()
+{
+    return CreateProc;
+}
+
+SkFlattenable* Sk2DPathEffect::CreateProc(SkFlattenableReadBuffer& buffer)
+{
+    return SkNEW_ARGS(Sk2DPathEffect, (buffer));
+}
+
+
+
diff --git a/src/effects/SkAvoidXfermode.cpp b/src/effects/SkAvoidXfermode.cpp
new file mode 100644
index 0000000..eed4012
--- /dev/null
+++ b/src/effects/SkAvoidXfermode.cpp
@@ -0,0 +1,257 @@
+/* libs/graphics/effects/SkAvoidXfermode.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 "SkAvoidXfermode.h"
+#include "SkColorPriv.h"
+
+SkAvoidXfermode::SkAvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode)
+{    
+    if (tolerance > 255) {
+        tolerance = 255;
+    }
+
+    fOpColor = opColor;
+    fDistMul = (256 << 14) / (tolerance + 1);
+    fMode = mode;
+}
+
+SkAvoidXfermode::SkAvoidXfermode(SkFlattenableReadBuffer& buffer)
+    : INHERITED(buffer)
+{
+    fOpColor = buffer.readU32();
+    fDistMul = buffer.readU32();
+    fMode = (Mode)buffer.readU8();
+}
+
+void SkAvoidXfermode::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    this->INHERITED::flatten(buffer);
+
+    buffer.write32(fOpColor);
+    buffer.write32(fDistMul);
+    buffer.write8(fMode);
+}
+
+SkFlattenable* SkAvoidXfermode::Create(SkFlattenableReadBuffer& rb)
+{
+    return SkNEW_ARGS(SkAvoidXfermode, (rb));
+}
+
+SkFlattenable::Factory SkAvoidXfermode::getFactory()
+{
+    return Create;
+}
+
+// returns 0..31
+static unsigned color_dist16(uint16_t c, unsigned r, unsigned g, unsigned b)
+{
+    SkASSERT(r <= SK_R16_MASK);
+    SkASSERT(g <= SK_G16_MASK);
+    SkASSERT(b <= SK_B16_MASK);
+
+    unsigned dr = SkAbs32(SkGetPackedR16(c) - r);
+    unsigned dg = SkAbs32(SkGetPackedG16(c) - g) >> (SK_G16_BITS - SK_R16_BITS);
+    unsigned db = SkAbs32(SkGetPackedB16(c) - b);
+    
+    return SkMax32(dr, SkMax32(dg, db));
+}
+
+// returns 0..15
+static unsigned color_dist4444(uint16_t c, unsigned r, unsigned g, unsigned b)
+{
+    SkASSERT(r <= 0xF);
+    SkASSERT(g <= 0xF);
+    SkASSERT(b <= 0xF);
+    
+    unsigned dr = SkAbs32(SkGetPackedR4444(c) - r);
+    unsigned dg = SkAbs32(SkGetPackedG4444(c) - g);
+    unsigned db = SkAbs32(SkGetPackedB4444(c) - b);
+    
+    return SkMax32(dr, SkMax32(dg, db));
+}
+
+// returns 0..255
+static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b)
+{
+    SkASSERT(r <= 0xFF);
+    SkASSERT(g <= 0xFF);
+    SkASSERT(b <= 0xFF);
+
+    unsigned dr = SkAbs32(SkGetPackedR32(c) - r);
+    unsigned dg = SkAbs32(SkGetPackedG32(c) - g);
+    unsigned db = SkAbs32(SkGetPackedB32(c) - b);
+    
+    return SkMax32(dr, SkMax32(dg, db));
+}
+
+static int scale_dist_14(int dist, uint32_t mul, uint32_t sub)
+{
+    int tmp = dist * mul - sub;
+    int result = (tmp + (1 << 13)) >> 14;
+
+    return result;
+}
+
+static SkPMColor SkFourByteInterp(SkPMColor src, SkPMColor dst, unsigned scale)
+{
+    unsigned a = SkAlphaBlend(SkGetPackedA32(src), SkGetPackedA32(dst), scale);
+    unsigned r = SkAlphaBlend(SkGetPackedR32(src), SkGetPackedR32(dst), scale);
+    unsigned g = SkAlphaBlend(SkGetPackedG32(src), SkGetPackedG32(dst), scale);
+    unsigned b = SkAlphaBlend(SkGetPackedB32(src), SkGetPackedB32(dst), scale);
+
+    return SkPackARGB32(a, r, g, b);
+}
+
+void SkAvoidXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                             const SkAlpha aa[])
+{
+    unsigned    opR = SkColorGetR(fOpColor);
+    unsigned    opG = SkColorGetG(fOpColor);
+    unsigned    opB = SkColorGetB(fOpColor);
+    uint32_t    mul = fDistMul;
+    uint32_t    sub = (fDistMul - (1 << 14)) << 8;
+    
+    int MAX, mask;
+    
+    if (kTargetColor_Mode == fMode) {
+        mask = -1;
+        MAX = 255;
+    } else {
+        mask = 0;
+        MAX = 0;
+    }
+    
+    for (int i = 0; i < count; i++) {
+        int d = color_dist32(dst[i], opR, opG, opB);
+        // now reverse d if we need to
+        d = MAX + (d ^ mask) - mask;
+        SkASSERT((unsigned)d <= 255);
+        d = SkAlpha255To256(d);
+        
+        d = scale_dist_14(d, mul, sub);
+        SkASSERT(d <= 256);
+        
+        if (d > 0) {
+            if (NULL != aa) {
+                d = SkAlphaMul(d, SkAlpha255To256(*aa++));
+                if (0 == d) {
+                    continue;
+                }
+            }
+            dst[i] = SkFourByteInterp(src[i], dst[i], d);
+        }
+    }
+}
+
+static inline U16CPU SkBlend3216(SkPMColor src, U16CPU dst, unsigned scale)
+{
+    SkASSERT(scale <= 32);
+    scale <<= 3;
+
+    return SkPackRGB16( SkAlphaBlend(SkPacked32ToR16(src), SkGetPackedR16(dst), scale),
+                        SkAlphaBlend(SkPacked32ToG16(src), SkGetPackedG16(dst), scale),
+                        SkAlphaBlend(SkPacked32ToB16(src), SkGetPackedB16(dst), scale));
+}
+
+void SkAvoidXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                             const SkAlpha aa[])
+{
+    unsigned    opR = SkColorGetR(fOpColor) >> (8 - SK_R16_BITS);
+    unsigned    opG = SkColorGetG(fOpColor) >> (8 - SK_G16_BITS);
+    unsigned    opB = SkColorGetB(fOpColor) >> (8 - SK_R16_BITS);
+    uint32_t    mul = fDistMul;
+    uint32_t    sub = (fDistMul - (1 << 14)) << 8;
+
+    int MAX, mask;
+    
+    if (kTargetColor_Mode == fMode) {
+        mask = -1;
+        MAX = 31;
+    } else {
+        mask = 0;
+        MAX = 0;
+    }
+
+    for (int i = 0; i < count; i++) {
+        int d = color_dist16(dst[i], opR, opG, opB);
+        // now reverse d if we need to
+        d = MAX + (d ^ mask) - mask;
+        SkASSERT((unsigned)d <= 31);
+        // convert from 0..31 to 0..32
+        d += d >> 4;
+
+        d = scale_dist_14(d, mul, sub);
+        SkASSERT(d <= 32);
+
+        if (d > 0) {
+            if (NULL != aa) {
+                d = SkAlphaMul(d, SkAlpha255To256(*aa++));
+                if (0 == d) {
+                    continue;
+                }
+            }
+            dst[i] = SkBlend3216(src[i], dst[i], d);
+        }
+    }
+}
+
+void SkAvoidXfermode::xfer4444(uint16_t dst[], const SkPMColor src[], int count,
+                               const SkAlpha aa[])
+{
+    unsigned    opR = SkColorGetR(fOpColor) >> 4;
+    unsigned    opG = SkColorGetG(fOpColor) >> 4;
+    unsigned    opB = SkColorGetB(fOpColor) >> 4;
+    uint32_t    mul = fDistMul;
+    uint32_t    sub = (fDistMul - (1 << 14)) << 8;
+    
+    int MAX, mask;
+    
+    if (kTargetColor_Mode == fMode) {
+        mask = -1;
+        MAX = 15;
+    } else {
+        mask = 0;
+        MAX = 0;
+    }
+    
+    for (int i = 0; i < count; i++) {
+        int d = color_dist4444(dst[i], opR, opG, opB);
+        // now reverse d if we need to
+        d = MAX + (d ^ mask) - mask;
+        SkASSERT((unsigned)d <= 15);
+        d = SkAlpha255To256(d);
+        
+        d = scale_dist_14(d, mul, sub);
+        SkASSERT(d <= 16);
+        
+        if (d > 0) {
+            if (NULL != aa) {
+                d = SkAlphaMul(d, SkAlpha255To256(*aa++));
+                if (0 == d) {
+                    continue;
+                }
+            }
+            dst[i] = SkBlend4444(SkPixel32ToPixel4444(src[i]), dst[i], d);
+        }
+    }
+}
+
+void SkAvoidXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[])
+{
+    // override in subclass
+}
+
diff --git a/src/effects/SkBlurDrawLooper.cpp b/src/effects/SkBlurDrawLooper.cpp
new file mode 100644
index 0000000..6ad0136
--- /dev/null
+++ b/src/effects/SkBlurDrawLooper.cpp
@@ -0,0 +1,91 @@
+#include "SkBlurDrawLooper.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkMaskFilter.h"
+
+SkBlurDrawLooper::SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy,
+                                   SkColor color)
+    : fDx(dx), fDy(dy), fBlurColor(color)
+{
+    if (radius > 0)
+        fBlur = SkBlurMaskFilter::Create(radius,
+                                         SkBlurMaskFilter::kNormal_BlurStyle);
+    else
+        fBlur = NULL;
+}
+
+SkBlurDrawLooper::SkBlurDrawLooper(SkFlattenableReadBuffer& buffer)
+{
+    fDx = buffer.readScalar();
+    fDy = buffer.readScalar();
+    fBlurColor = buffer.readU32();
+    fBlur = static_cast<SkMaskFilter*>(buffer.readFlattenable());
+}
+
+SkBlurDrawLooper::~SkBlurDrawLooper()
+{
+    fBlur->safeUnref();
+}
+
+void SkBlurDrawLooper::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    buffer.writeScalar(fDx);
+    buffer.writeScalar(fDy);
+    buffer.write32(fBlurColor);
+    buffer.writeFlattenable(fBlur);
+}
+
+void SkBlurDrawLooper::init(SkCanvas* canvas, SkPaint* paint)
+{
+    // we do nothing if a maskfilter is already installed
+    if (paint->getMaskFilter() != NULL)
+        fState = kDone;
+    else
+    {
+        fState = kBeforeEdge;
+        fPaint = paint;
+        fCanvas = canvas;
+        fSaveCount = canvas->getSaveCount();
+    }
+}
+
+bool SkBlurDrawLooper::next()
+{
+    switch (fState) {
+    case kBeforeEdge:
+        fSavedColor = fPaint->getColor();
+        fPaint->setColor(fBlurColor);
+        fPaint->setMaskFilter(fBlur);
+        fCanvas->save(SkCanvas::kMatrix_SaveFlag);
+        fCanvas->translate(fDx, fDy);
+        fState = kAfterEdge;
+        return true;
+    case kAfterEdge:
+        fPaint->setColor(fSavedColor);
+        fPaint->setMaskFilter(NULL);
+        fCanvas->restore(); // to remove the translate we did earlier
+        fState = kDone;
+        return true;
+    default:
+        SkASSERT(kDone == fState);
+        return false;
+    }
+}
+
+void SkBlurDrawLooper::restore()
+{
+    if (kAfterEdge == fState)
+    {
+        fPaint->setColor(fSavedColor);
+        fPaint->setMaskFilter(NULL);
+        fCanvas->restore(); // to remove the translate we did earlier
+        fState = kDone;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkFlattenable::Registrar gReg("SkBlurDrawLooper",
+                                     SkBlurDrawLooper::CreateProc);
+
diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp
new file mode 100644
index 0000000..a7b3202
--- /dev/null
+++ b/src/effects/SkBlurMask.cpp
@@ -0,0 +1,332 @@
+/* libs/graphics/effects/SkBlurMask.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 "SkBlurMask.h"
+#include "SkTemplates.h"
+
+static void build_sum_buffer(uint32_t dst[], int w, int h, const uint8_t src[], int srcRB)
+{
+    SkASSERT(srcRB >= w);
+    // mod srcRB so we can apply it after each row
+    srcRB -= w;
+
+    int x, y;
+
+    // special case first row
+    uint32_t X = 0;
+    for (x = w - 1; x >= 0; --x)
+    {
+        X = *src++ + X;
+        *dst++ = X;
+    }
+    src += srcRB;
+
+    // now do the rest of the rows
+    for (y = h - 1; y > 0; --y)
+    {
+        uint32_t L = 0;
+        uint32_t C = 0;
+        for (x = w - 1; x >= 0; --x)
+        {
+            uint32_t T = dst[-w];
+            X = *src++ + L + T - C;
+            *dst++ = X;
+            L = X;
+            C = T;
+        }
+        src += srcRB;
+    }
+}
+
+static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t src[], int sw, int sh)
+{
+    uint32_t scale = (1 << 24) / ((2*rx + 1)*(2*ry + 1));
+
+    int rowBytes = sw;
+
+    int dw = sw + 2*rx;
+    int dh = sh + 2*ry;
+
+    sw -= 1;    // now it is max_x
+    sh -= 1;    // now it is max_y
+
+    int prev_y = -ry - 1    -ry;
+    int next_y = ry         -ry;
+
+    for (int y = 0; y < dh; y++)
+    {
+        int py = SkClampPos(prev_y) * rowBytes;
+        int ny = SkFastMin32(next_y, sh) * rowBytes;
+
+        int prev_x = -rx - 1    -rx;
+        int next_x = rx         -rx;
+
+        for (int x = 0; x < dw; x++)
+        {
+            int px = SkClampPos(prev_x);
+            int nx = SkFastMin32(next_x, sw);
+
+            uint32_t sum = src[px+py] + src[nx+ny] - src[nx+py] - src[px+ny];
+            *dst++ = SkToU8(sum * scale >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+        prev_y += 1;
+        next_y += 1;
+    }
+}
+
+static void apply_kernel_interp(uint8_t dst[], int rx, int ry, const uint32_t src[], int sw, int sh, U8CPU outer_weight)
+{
+    SkASSERT(rx > 0 && ry > 0);
+    SkASSERT(outer_weight <= 255);
+
+    int inner_weight = 255 - outer_weight;
+
+    // round these guys up if they're bigger than 127
+    outer_weight += outer_weight >> 7;
+    inner_weight += inner_weight >> 7;
+
+    uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1));
+    uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1));
+
+    int rowBytes = sw;
+
+    int dw = sw + 2*rx;
+    int dh = sh + 2*ry;
+
+    sw -= 1;    // now it is max_x
+    sh -= 1;    // now it is max_y
+
+    int prev_y = -ry - 1    -ry;
+    int next_y = ry         -ry;
+
+    for (int y = 0; y < dh; y++)
+    {
+        int py = SkClampPos(prev_y) * rowBytes;
+        int ny = SkFastMin32(next_y, sh) * rowBytes;
+
+        int ipy = SkClampPos(prev_y + 1) * rowBytes;
+        int iny = SkClampMax(next_y - 1, sh) * rowBytes;
+
+        int prev_x = -rx - 1    -rx;
+        int next_x = rx         -rx;
+
+        for (int x = 0; x < dw; x++)
+        {
+            int px = SkClampPos(prev_x);
+            int nx = SkFastMin32(next_x, sw);
+
+            int ipx = SkClampPos(prev_x + 1);
+            int inx = SkClampMax(next_x - 1, sw);
+
+            uint32_t outer_sum = src[px+py] + src[nx+ny] - src[nx+py] - src[px+ny];
+            uint32_t inner_sum = src[ipx+ipy] + src[inx+iny] - src[inx+ipy] - src[ipx+iny];
+            *dst++ = SkToU8((outer_sum * outer_scale + inner_sum * inner_scale) >> 24);
+
+            prev_x += 1;
+            next_x += 1;
+        }
+        prev_y += 1;
+        next_y += 1;
+    }
+}
+
+#include "SkColorPriv.h"
+
+static void merge_src_with_blur(uint8_t dst[],
+                                const uint8_t src[], int sw, int sh,
+                                const uint8_t blur[], int blurRowBytes)
+{
+    while (--sh >= 0)
+    {
+        for (int x = sw - 1; x >= 0; --x)
+        {
+            *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src)));
+            dst += 1;
+            src += 1;
+            blur += 1;
+        }
+        blur += blurRowBytes - sw;
+    }
+}
+
+static void clamp_with_orig(uint8_t dst[], int dstRowBytes,
+                            const uint8_t src[], int sw, int sh,
+                            SkBlurMask::Style style)
+{
+    int x;
+    while (--sh >= 0)
+    {
+        switch (style) {
+        case SkBlurMask::kSolid_Style:
+            for (x = sw - 1; x >= 0; --x)
+            {
+                *dst = SkToU8(*src + SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
+                dst += 1;
+                src += 1;
+            }
+            break;
+        case SkBlurMask::kOuter_Style:
+            for (x = sw - 1; x >= 0; --x)
+            {
+                if (*src)
+                    *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
+                dst += 1;
+                src += 1;
+            }
+            break;
+        default:
+            SkASSERT(!"Unexpected blur style here");
+            break;
+        }
+        dst += dstRowBytes - sw;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+// we use a local funciton to wrap the class static method to work around
+// a bug in gcc98
+void SkMask_FreeImage(uint8_t* image);
+void SkMask_FreeImage(uint8_t* image)
+{
+    SkMask::FreeImage(image);
+}
+
+bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
+                      SkScalar radius, Style style)
+{
+    if (src.fFormat != SkMask::kA8_Format)
+        return false;
+
+    int rx = SkScalarCeil(radius);
+    int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - radius) * 255);
+
+    SkASSERT(rx >= 0);
+    SkASSERT((unsigned)outer_weight <= 255);
+
+    if (rx == 0)
+        return false;
+
+    int ry = rx;    // only do square blur for now
+
+    dst->fBounds.set(src.fBounds.fLeft - rx, src.fBounds.fTop - ry,
+                        src.fBounds.fRight + rx, src.fBounds.fBottom + ry);
+    dst->fRowBytes = SkToU16(dst->fBounds.width());
+    dst->fFormat = SkMask::kA8_Format;
+    dst->fImage = NULL;
+
+    if (src.fImage)
+    {
+        int             sw = src.fBounds.width();
+        int             sh = src.fBounds.height();
+        const uint8_t*  sp = src.fImage;
+        uint8_t*        dp = SkMask::AllocImage(dst->computeImageSize());
+
+        SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
+
+        // build the blurry destination
+        {
+            SkAutoTMalloc<uint32_t> storage(sw * sh);
+            uint32_t*               sumBuffer = storage.get();
+
+            build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes);
+            if (outer_weight == 255)
+                apply_kernel(dp, rx, ry, sumBuffer, sw, sh);
+            else
+                apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight);
+        }
+
+        dst->fImage = dp;
+        // if need be, alloc the "real" dst (same size as src) and copy/merge
+        // the blur into it (applying the src)
+        if (style == kInner_Style)
+        {
+            dst->fImage = SkMask::AllocImage(src.computeImageSize());
+            merge_src_with_blur(dst->fImage, sp, sw, sh,
+                                dp + rx + ry*dst->fBounds.width(),
+                                dst->fBounds.width());
+            SkMask::FreeImage(dp);
+        }
+        else if (style != kNormal_Style)
+        {
+            clamp_with_orig(dp + rx + ry*dst->fBounds.width(),
+                            dst->fBounds.width(),
+                            sp, sw, sh,
+                            style);
+        }
+        (void)autoCall.detach();
+    }
+
+    if (style == kInner_Style)
+    {
+        dst->fBounds = src.fBounds; // restore trimmed bounds
+        dst->fRowBytes = SkToU16(dst->fBounds.width());
+    }
+
+#if 0
+    if (gamma && dst->fImage)
+    {
+        uint8_t*    image = dst->fImage;
+        uint8_t*    stop = image + dst->computeImageSize();
+
+        for (; image < stop; image += 1)
+            *image = gamma[*image];
+    }
+#endif
+    return true;
+}
+
+#if 0
+void SkBlurMask::BuildSqrtGamma(uint8_t gamma[256], SkScalar percent)
+{
+    SkASSERT(gamma);
+    SkASSERT(percent >= 0 && percent <= SK_Scalar1);
+
+    int scale = SkScalarRound(percent * 256);
+
+    for (int i = 0; i < 256; i++)
+    {
+        SkFixed n = i * 257;
+        n += n >> 15;
+        SkASSERT(n >= 0 && n <= SK_Fixed1);
+        n = SkFixedSqrt(n);
+        n = n * 255 >> 16;
+        n = SkAlphaBlend(n, i, scale);
+        gamma[i] = SkToU8(n);
+    }
+}
+
+void SkBlurMask::BuildSqrGamma(uint8_t gamma[256], SkScalar percent)
+{
+    SkASSERT(gamma);
+    SkASSERT(percent >= 0 && percent <= SK_Scalar1);
+
+    int     scale = SkScalarRound(percent * 256);
+    SkFixed div255 = SK_Fixed1 / 255;
+
+    for (int i = 0; i < 256; i++)
+    {
+        int square = i * i;
+        int linear = i * 255;
+        int n = SkAlphaBlend(square, linear, scale);
+        gamma[i] = SkToU8(n * div255 >> 16);
+    }
+}
+#endif
diff --git a/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h
new file mode 100644
index 0000000..8f61d54
--- /dev/null
+++ b/src/effects/SkBlurMask.h
@@ -0,0 +1,40 @@
+/* libs/graphics/effects/SkBlurMask.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 SkBlurMask_DEFINED
+#define SkBlurMask_DEFINED
+
+#include "SkShader.h"
+
+class SkBlurMask {
+public:
+    enum Style {
+        kNormal_Style,  //!< fuzzy inside and outside
+        kSolid_Style,   //!< solid inside, fuzzy outside
+        kOuter_Style,   //!< nothing inside, fuzzy outside
+        kInner_Style,   //!< fuzzy inside, nothing outside
+
+        kStyleCount
+    };
+
+    static bool Blur(SkMask* dst, const SkMask& src, SkScalar radius, Style);
+};
+
+#endif
+
+
+
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
new file mode 100644
index 0000000..2db60a5
--- /dev/null
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -0,0 +1,123 @@
+/* libs/graphics/effects/SkBlurMaskFilter.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 "SkBlurMaskFilter.h"
+#include "SkBlurMask.h"
+#include "SkBuffer.h"
+#include "SkMaskFilter.h"
+
+class SkBlurMaskFilterImpl : public SkMaskFilter {
+public:
+    SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle style);
+
+    // overrides from SkMaskFilter
+    virtual SkMask::Format getFormat();
+    virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin);
+
+    // overrides from SkFlattenable
+    // This method is not exported to java.
+    virtual Factory getFactory();
+    // This method is not exported to java.
+    virtual void flatten(SkFlattenableWriteBuffer&);
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+
+private:
+    SkScalar                    fRadius;
+    SkBlurMaskFilter::BlurStyle fBlurStyle;
+
+    SkBlurMaskFilterImpl(SkFlattenableReadBuffer&);
+    
+    typedef SkMaskFilter INHERITED;
+};
+
+SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius, SkBlurMaskFilter::BlurStyle style)
+{
+    if (radius <= 0 || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount)
+        return NULL;
+
+    return SkNEW_ARGS(SkBlurMaskFilterImpl, (radius, style));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle style)
+    : fRadius(radius), fBlurStyle(style)
+{
+#if 0
+    fGamma = NULL;
+    if (gammaScale)
+    {
+        fGamma = new U8[256];
+        if (gammaScale > 0)
+            SkBlurMask::BuildSqrGamma(fGamma, gammaScale);
+        else
+            SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale);
+    }
+#endif
+    SkASSERT(radius >= 0);
+    SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount);
+}
+
+SkMask::Format SkBlurMaskFilterImpl::getFormat()
+{
+    return SkMask::kA8_Format;
+}
+
+bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin)
+{
+    SkScalar radius = matrix.mapRadius(fRadius);
+
+    if (SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle))
+    {
+        if (margin)
+            margin->set(SkScalarCeil(radius), SkScalarCeil(radius));
+        return true;
+    }
+    return false;
+}
+
+SkFlattenable* SkBlurMaskFilterImpl::CreateProc(SkFlattenableReadBuffer& buffer)
+{
+    return SkNEW_ARGS(SkBlurMaskFilterImpl, (buffer));
+}
+
+SkFlattenable::Factory SkBlurMaskFilterImpl::getFactory()
+{
+    return CreateProc;
+}
+
+SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer) : SkMaskFilter(buffer)
+{
+    fRadius = buffer.readScalar();
+    fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readS32();
+    SkASSERT(fRadius >= 0);
+    SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount);
+}
+
+void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fRadius);
+    buffer.write32(fBlurStyle);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkFlattenable::Registrar gReg("SkBlurMaskFilter",
+                                     SkBlurMaskFilterImpl::CreateProc);
+
diff --git a/src/effects/SkColorFilters.cpp b/src/effects/SkColorFilters.cpp
new file mode 100644
index 0000000..50be1ad
--- /dev/null
+++ b/src/effects/SkColorFilters.cpp
@@ -0,0 +1,553 @@
+/* libs/graphics/effects/SkColorFilters.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 "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkPorterDuff.h"
+#include "SkUtils.h"
+
+//#define TRACE_CreatePorterDuffFilter
+
+// common baseclass
+class Sk_XfermodeColorFilter : public SkColorFilter {
+protected:
+    Sk_XfermodeColorFilter(SkColor color) : fColor(SkPreMultiplyColor(color)) {}
+
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) 
+    {
+        buffer.write32(fColor);
+    }
+    
+    Sk_XfermodeColorFilter(SkFlattenableReadBuffer& buffer) 
+    {
+        fColor = buffer.readU32();
+    }
+    
+    SkPMColor   fColor;
+};
+
+class SkSrc_XfermodeColorFilter : public Sk_XfermodeColorFilter {
+public:
+    SkSrc_XfermodeColorFilter(SkColor color) : INHERITED(color) {}
+
+    virtual uint32_t getFlags()
+    {
+        if (SkGetPackedA32(fColor) == 0xFF)
+            return kAlphaUnchanged_Flag | kHasFilter16_Flag;
+        else
+            return 0;
+    }
+
+    virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[])
+    {
+        sk_memset32(result, fColor, count);
+    }
+
+    virtual void filterSpan16(const uint16_t shader[], int count, uint16_t result[])
+    {
+        SkASSERT(this->getFlags() & kHasFilter16_Flag);
+        
+        sk_memset16(result, SkPixel32ToPixel16(fColor), count);
+    }
+
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+
+    SkSrc_XfermodeColorFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+    
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) 
+    {
+        return SkNEW_ARGS(SkSrc_XfermodeColorFilter, (buffer));
+    }
+    
+    typedef Sk_XfermodeColorFilter INHERITED;
+};
+
+class SkSrcOver_XfermodeColorFilter : public Sk_XfermodeColorFilter {
+public:
+    SkSrcOver_XfermodeColorFilter(SkColor color) : INHERITED(color) {}
+
+    virtual uint32_t getFlags()
+    {
+        if (SkGetPackedA32(fColor) == 0xFF)
+            return kAlphaUnchanged_Flag | kHasFilter16_Flag;
+        else
+            return 0;
+    }
+    
+    virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[])
+    {
+        SkPMColor   src = fColor;
+        unsigned    scale = SkAlpha255To256(255 - SkGetPackedA32(src));
+        
+        for (int i = 0; i < count; i++)
+            result[i] = src + SkAlphaMulQ(shader[i], scale);
+    }
+
+    virtual void filterSpan16(const uint16_t shader[], int count, uint16_t result[])
+    {
+        SkASSERT(this->getFlags() & kHasFilter16_Flag);
+
+        sk_memset16(result, SkPixel32ToPixel16(fColor), count);
+    }
+        
+protected:
+    virtual Factory getFactory() { return CreateProc;  }
+    
+    SkSrcOver_XfermodeColorFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+    
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) 
+    {
+        return SkNEW_ARGS(SkSrcOver_XfermodeColorFilter, (buffer));
+    }
+    
+    typedef Sk_XfermodeColorFilter INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class SkXfermodeColorFilter : public Sk_XfermodeColorFilter {
+public:
+    SkXfermodeColorFilter(SkColor color, SkXfermodeProc proc,
+                          SkXfermodeProc16 proc16) : INHERITED(color)
+    {
+        fProc = proc;
+        fProc16 = proc16;
+    }
+    
+    virtual uint32_t getFlags()
+    {
+        return fProc16 ? (kAlphaUnchanged_Flag | kHasFilter16_Flag) : 0;
+    }
+
+    virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[])
+    {
+        SkPMColor       color = fColor;
+        SkXfermodeProc  proc = fProc;
+        
+        for (int i = 0; i < count; i++)
+            result[i] = proc(color, shader[i]);
+    }
+    
+    virtual void filterSpan16(const uint16_t shader[], int count, uint16_t result[])
+    {
+        SkASSERT(this->getFlags() & kHasFilter16_Flag);
+        
+        SkPMColor        color = fColor;
+        SkXfermodeProc16 proc16 = fProc16;
+        
+        for (int i = 0; i < count; i++)
+            result[i] = proc16(color, shader[i]);
+    }
+    
+protected:
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+        this->INHERITED::flatten(buffer);
+        buffer.writeFunctionPtr((void*)fProc);
+        buffer.writeFunctionPtr((void*)fProc16);
+    }
+    
+    virtual Factory getFactory() { 
+        return CreateProc;
+    }
+
+    SkXfermodeColorFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+        fProc = (SkXfermodeProc) buffer.readFunctionPtr();
+        fProc16 = (SkXfermodeProc16) buffer.readFunctionPtr();
+    }
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(SkXfermodeColorFilter, (buffer));
+    }
+
+    SkXfermodeProc   fProc;
+    SkXfermodeProc16 fProc16;
+    
+    typedef Sk_XfermodeColorFilter INHERITED;
+};
+
+SkColorFilter* SkColorFilter::CreatXfermodeProcFilter(SkColor color,
+                                                      SkXfermodeProc proc,
+                                                      SkXfermodeProc16 proc16)
+{
+    return proc ?
+            SkNEW_ARGS(SkXfermodeColorFilter, (color, proc, proc16)) :
+            NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkColorFilter* SkColorFilter::CreatePorterDuffFilter(SkColor color,
+                                                     SkPorterDuff::Mode mode)
+{
+    unsigned alpha = SkColorGetA(color);
+
+    // first collaps some modes if possible
+
+    if (SkPorterDuff::kClear_Mode == mode)
+    {
+        color = 0;
+        mode = SkPorterDuff::kSrc_Mode;
+    }
+    else if (SkPorterDuff::kSrcOver_Mode == mode)
+    {
+        if (0 == alpha)
+        {
+            mode = SkPorterDuff::kDst_Mode;
+        }
+        else if (255 == alpha)
+        {
+            mode = SkPorterDuff::kSrc_Mode;
+        }
+        // else just stay srcover
+    }
+
+    // weed out combinations that are noops, and just return null
+    if (SkPorterDuff::kDst_Mode == mode ||
+        (0 == alpha && (SkPorterDuff::kSrcOver_Mode == mode ||
+                        SkPorterDuff::kDstOver_Mode == mode ||
+                        SkPorterDuff::kDstOut_Mode == mode ||
+                        SkPorterDuff::kSrcATop_Mode == mode ||
+                        SkPorterDuff::kXor_Mode == mode ||
+                        SkPorterDuff::kDarken_Mode == mode)) ||
+        (0xFF == alpha && SkPorterDuff::kDstIn_Mode == mode))
+    {
+        return NULL;
+    }
+        
+    switch (mode) {
+    case SkPorterDuff::kSrc_Mode:
+        return SkNEW_ARGS(SkSrc_XfermodeColorFilter, (color));
+    case SkPorterDuff::kSrcOver_Mode:
+        return SkNEW_ARGS(SkSrcOver_XfermodeColorFilter, (color));
+    default:
+        return SkColorFilter::CreatXfermodeProcFilter(color,
+                                SkPorterDuff::GetXfermodeProc(mode),
+                                SkPorterDuff::GetXfermodeProc16(mode, color));
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+static inline unsigned pin(unsigned value, unsigned max)
+{
+    if (value > max)
+        value = max;
+    return value;
+}
+
+static inline unsigned SkUClampMax(unsigned value, unsigned max)
+{
+    SkASSERT((int32_t)value >= 0);
+    SkASSERT((int32_t)max >= 0);
+
+    int diff = max - value;
+    // clear diff if diff is positive
+    diff &= diff >> 31;
+
+    return value + diff;
+}
+
+class SkLightingColorFilter : public SkColorFilter {
+public:
+    SkLightingColorFilter(SkColor mul, SkColor add) : fMul(mul), fAdd(add) {}
+
+    virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[])
+    {
+        unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul));
+        unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul));
+        unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul));
+        
+        unsigned addR = SkColorGetR(fAdd);
+        unsigned addG = SkColorGetG(fAdd);
+        unsigned addB = SkColorGetB(fAdd);
+
+        for (int i = 0; i < count; i++)
+        {
+            SkPMColor c = shader[i];
+            if (c)
+            {
+                unsigned a = SkGetPackedA32(c);
+                unsigned scaleA = SkAlpha255To256(a);                
+                unsigned r = pin(SkAlphaMul(SkGetPackedR32(c), scaleR) + SkAlphaMul(addR, scaleA), a);
+                unsigned g = pin(SkAlphaMul(SkGetPackedG32(c), scaleG) + SkAlphaMul(addG, scaleA), a);
+                unsigned b = pin(SkAlphaMul(SkGetPackedB32(c), scaleB) + SkAlphaMul(addB, scaleA), a);
+                c = SkPackARGB32(a, r, g, b);
+            }
+            result[i] = c;
+        }
+    }
+
+protected:
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) 
+    {
+        buffer.write32(fMul);
+        buffer.write32(fAdd);
+    }
+    
+    virtual Factory getFactory() 
+    { 
+        return CreateProc;
+    }
+
+    SkLightingColorFilter(SkFlattenableReadBuffer& buffer) 
+    {
+        fMul = buffer.readU32();
+        fAdd = buffer.readU32();
+    }
+    
+    SkColor fMul, fAdd;
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) 
+    {
+        return SkNEW_ARGS(SkLightingColorFilter, (buffer));
+    }
+};
+
+class SkLightingColorFilter_JustAdd : public SkLightingColorFilter {
+public:
+    SkLightingColorFilter_JustAdd(SkColor mul, SkColor add)
+        : INHERITED(mul, add) {}
+
+    virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[])
+    {
+        unsigned addR = SkColorGetR(fAdd);
+        unsigned addG = SkColorGetG(fAdd);
+        unsigned addB = SkColorGetB(fAdd);
+
+        for (int i = 0; i < count; i++)
+        {
+            SkPMColor c = shader[i];
+            if (c)
+            {
+                unsigned a = SkGetPackedA32(c);
+                unsigned scaleA = SkAlpha255To256(a);                
+                unsigned r = pin(SkGetPackedR32(c) + SkAlphaMul(addR, scaleA), a);
+                unsigned g = pin(SkGetPackedG32(c) + SkAlphaMul(addG, scaleA), a);
+                unsigned b = pin(SkGetPackedB32(c) + SkAlphaMul(addB, scaleA), a);
+                c = SkPackARGB32(a, r, g, b);
+            }
+            result[i] = c;
+        }
+    }
+
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+
+    SkLightingColorFilter_JustAdd(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {}
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) 
+    {
+        return SkNEW_ARGS(SkLightingColorFilter_JustAdd, (buffer));
+    }
+    typedef SkLightingColorFilter INHERITED;
+};
+
+class SkLightingColorFilter_JustMul : public SkLightingColorFilter {
+public:
+    SkLightingColorFilter_JustMul(SkColor mul, SkColor add)
+        : INHERITED(mul, add) {}
+
+    virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[])
+    {
+        unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul));
+        unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul));
+        unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul));
+        
+        for (int i = 0; i < count; i++)
+        {
+            SkPMColor c = shader[i];
+            if (c)
+            {
+                unsigned a = SkGetPackedA32(c);
+                unsigned r = SkAlphaMul(SkGetPackedR32(c), scaleR);
+                unsigned g = SkAlphaMul(SkGetPackedG32(c), scaleG);
+                unsigned b = SkAlphaMul(SkGetPackedB32(c), scaleB);
+                c = SkPackARGB32(a, r, g, b);
+            }
+            result[i] = c;
+        }
+    }
+
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+
+    SkLightingColorFilter_JustMul(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {}
+    
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) 
+    {
+        return SkNEW_ARGS(SkLightingColorFilter_JustMul, (buffer));
+    }
+
+    typedef SkLightingColorFilter INHERITED;
+};
+
+class SkLightingColorFilter_SingleMul : public SkLightingColorFilter {
+public:
+    SkLightingColorFilter_SingleMul(SkColor mul, SkColor add)
+        : INHERITED(mul, add)
+    {
+        SkASSERT(SkColorGetR(add) == 0);
+        SkASSERT(SkColorGetG(add) == 0);
+        SkASSERT(SkColorGetB(add) == 0);
+        SkASSERT(SkColorGetR(mul) == SkColorGetG(mul));
+        SkASSERT(SkColorGetR(mul) == SkColorGetB(mul));
+    }
+    
+    virtual uint32_t getFlags()
+    {
+        return this->INHERITED::getFlags() | (kAlphaUnchanged_Flag | kHasFilter16_Flag);
+    }
+
+    virtual void filterSpan16(const uint16_t shader[], int count, uint16_t result[])
+    {
+        // all mul components are the same
+        unsigned scale = SkAlpha255To256(SkColorGetR(fMul));
+
+        if (count > 0)
+            do {
+                *result++ = SkAlphaMulRGB16(*shader++, scale);
+            } while (--count > 0);
+    }
+
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+
+    SkLightingColorFilter_SingleMul(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {}
+
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) 
+    {
+        return SkNEW_ARGS(SkLightingColorFilter_SingleMul, (buffer));
+    }
+    
+    typedef SkLightingColorFilter INHERITED;
+};
+
+class SkLightingColorFilter_NoPin : public SkLightingColorFilter {
+public:
+    SkLightingColorFilter_NoPin(SkColor mul, SkColor add)
+    : INHERITED(mul, add) {}
+    
+    virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[])
+    {
+        unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul));
+        unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul));
+        unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul));
+        
+        unsigned addR = SkColorGetR(fAdd);
+        unsigned addG = SkColorGetG(fAdd);
+        unsigned addB = SkColorGetB(fAdd);
+        
+        for (int i = 0; i < count; i++)
+        {
+            SkPMColor c = shader[i];
+            if (c)
+            {
+                unsigned a = SkGetPackedA32(c);
+                unsigned scaleA = SkAlpha255To256(a);                
+                unsigned r = SkAlphaMul(SkGetPackedR32(c), scaleR) + SkAlphaMul(addR, scaleA);
+                unsigned g = SkAlphaMul(SkGetPackedG32(c), scaleG) + SkAlphaMul(addG, scaleA);
+                unsigned b = SkAlphaMul(SkGetPackedB32(c), scaleB) + SkAlphaMul(addB, scaleA);
+                c = SkPackARGB32(a, r, g, b);
+            }
+            result[i] = c;
+        }
+    }
+    
+protected:
+    virtual Factory getFactory() { return CreateProc; }
+    
+    SkLightingColorFilter_NoPin(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {}
+    
+private:
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) 
+    {
+        return SkNEW_ARGS(SkLightingColorFilter_NoPin, (buffer));
+    }
+    
+    typedef SkLightingColorFilter INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+class SkSimpleColorFilter : public SkColorFilter {
+protected:
+    void filterSpan(const SkPMColor src[], int count, SkPMColor result[])
+    {
+        if (result != src)
+            memcpy(result, src, count * sizeof(SkPMColor));
+    }
+
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) 
+    {
+    }
+    
+    virtual Factory getFactory() 
+    { 
+        return CreateProc;
+    }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) 
+    {
+        return SkNEW(SkSimpleColorFilter);
+    }
+};
+
+SkColorFilter* SkColorFilter::CreateLightingFilter(SkColor mul, SkColor add)
+{
+    mul &= 0x00FFFFFF;
+    add &= 0x00FFFFFF;
+
+    if (0xFFFFFF == mul)
+    {
+        if (0 == add)
+            return SkNEW(SkSimpleColorFilter);   // no change to the colors
+        else
+            return SkNEW_ARGS(SkLightingColorFilter_JustAdd, (mul, add));
+    }
+
+    if (0 == add)
+    {
+        if (SkColorGetR(mul) == SkColorGetG(mul) &&
+            SkColorGetR(mul) == SkColorGetB(mul))
+        {
+            return SkNEW_ARGS(SkLightingColorFilter_SingleMul, (mul, add));
+        }
+        else
+        {
+            return SkNEW_ARGS(SkLightingColorFilter_JustMul, (mul, add));
+        }
+    }
+
+    if (SkColorGetR(mul) + SkColorGetR(add) <= 255 &&
+        SkColorGetG(mul) + SkColorGetG(add) <= 255 &&
+        SkColorGetB(mul) + SkColorGetB(add) <= 255)
+        return SkNEW_ARGS(SkLightingColorFilter_NoPin, (mul, add));
+
+    return SkNEW_ARGS(SkLightingColorFilter, (mul, add));
+}
+
diff --git a/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp
new file mode 100644
index 0000000..07c8d2f
--- /dev/null
+++ b/src/effects/SkColorMatrixFilter.cpp
@@ -0,0 +1,330 @@
+#include "SkColorMatrixFilter.h"
+#include "SkColorMatrix.h"
+#include "SkColorPriv.h"
+#include "SkUnPreMultiply.h"
+
+static int32_t rowmul4(const int32_t array[], unsigned r, unsigned g,
+                          unsigned b, unsigned a) {
+    return array[0] * r + array[1] * g  + array[2] * b + array[3] * a + array[4];
+}
+
+static int32_t rowmul3(const int32_t array[], unsigned r, unsigned g,
+                       unsigned b) {
+    return array[0] * r + array[1] * g  + array[2] * b + array[4];
+}
+
+static void General(SkColorMatrixFilter::State* state,
+                    unsigned r, unsigned g, unsigned b, unsigned a) {
+    const int32_t* SK_RESTRICT array = state->fArray;
+    const int shift = state->fShift;
+    int32_t* SK_RESTRICT result = state->fResult;
+    
+    result[0] = rowmul4(&array[0], r, g, b, a) >> shift;
+    result[1] = rowmul4(&array[5], r, g, b, a) >> shift;
+    result[2] = rowmul4(&array[10], r, g, b, a) >> shift;
+    result[3] = rowmul4(&array[15], r, g, b, a) >> shift;
+}
+
+static void General16(SkColorMatrixFilter::State* state,
+                      unsigned r, unsigned g, unsigned b, unsigned a) {
+    const int32_t* SK_RESTRICT array = state->fArray;
+    int32_t* SK_RESTRICT result = state->fResult;
+    
+    result[0] = rowmul4(&array[0], r, g, b, a) >> 16;
+    result[1] = rowmul4(&array[5], r, g, b, a) >> 16;
+    result[2] = rowmul4(&array[10], r, g, b, a) >> 16;
+    result[3] = rowmul4(&array[15], r, g, b, a) >> 16;
+}
+
+static void AffineAdd(SkColorMatrixFilter::State* state,
+                      unsigned r, unsigned g, unsigned b, unsigned a) {
+    const int32_t* SK_RESTRICT array = state->fArray;
+    const int shift = state->fShift;
+    int32_t* SK_RESTRICT result = state->fResult;
+    
+    result[0] = rowmul3(&array[0], r, g, b) >> shift;
+    result[1] = rowmul3(&array[5], r, g, b) >> shift;
+    result[2] = rowmul3(&array[10], r, g, b) >> shift;
+    result[3] = a;
+}
+
+static void AffineAdd16(SkColorMatrixFilter::State* state,
+                        unsigned r, unsigned g, unsigned b, unsigned a) {
+    const int32_t* SK_RESTRICT array = state->fArray;
+    int32_t* SK_RESTRICT result = state->fResult;
+    
+    result[0] = rowmul3(&array[0], r, g, b) >> 16;
+    result[1] = rowmul3(&array[5], r, g, b) >> 16;
+    result[2] = rowmul3(&array[10], r, g, b) >> 16;
+    result[3] = a;
+}
+
+static void ScaleAdd(SkColorMatrixFilter::State* state,
+                     unsigned r, unsigned g, unsigned b, unsigned a) {
+    const int32_t* SK_RESTRICT array = state->fArray;
+    const int shift = state->fShift;
+    int32_t* SK_RESTRICT result = state->fResult;
+    
+    // cast to (int) to keep the expression signed for the shift
+    result[0] = (array[0] * (int)r + array[4]) >> shift;
+    result[1] = (array[6] * (int)g + array[9]) >> shift;
+    result[2] = (array[12] * (int)b + array[14]) >> shift;
+    result[3] = a;
+}
+
+static void ScaleAdd16(SkColorMatrixFilter::State* state,
+                       unsigned r, unsigned g, unsigned b, unsigned a) {
+    const int32_t* SK_RESTRICT array = state->fArray;
+    int32_t* SK_RESTRICT result = state->fResult;
+    
+    // cast to (int) to keep the expression signed for the shift
+    result[0] = (array[0] * (int)r + array[4]) >> 16;
+    result[1] = (array[6] * (int)g + array[9]) >> 16;
+    result[2] = (array[12] * (int)b + array[14]) >> 16;
+    result[3] = a;
+}
+
+static void Add(SkColorMatrixFilter::State* state,
+                unsigned r, unsigned g, unsigned b, unsigned a) {
+    const int32_t* SK_RESTRICT array = state->fArray;
+    const int shift = state->fShift;
+    int32_t* SK_RESTRICT result = state->fResult;
+    
+    result[0] = r + (array[4] >> shift);
+    result[1] = g + (array[9] >> shift);
+    result[2] = b + (array[14] >> shift);
+    result[3] = a;
+}
+
+static void Add16(SkColorMatrixFilter::State* state,
+                  unsigned r, unsigned g, unsigned b, unsigned a) {
+    const int32_t* SK_RESTRICT array = state->fArray;
+    int32_t* SK_RESTRICT result = state->fResult;
+    
+    result[0] = r + (array[4] >> 16);
+    result[1] = g + (array[9] >> 16);
+    result[2] = b + (array[14] >> 16);
+    result[3] = a;
+}
+
+#define kNO_ALPHA_FLAGS (SkColorFilter::kAlphaUnchanged_Flag |  \
+                         SkColorFilter::kHasFilter16_Flag)
+
+void SkColorMatrixFilter::setup(const SkScalar SK_RESTRICT src[20]) {
+    if (NULL == src) {
+        fProc = NULL;   // signals identity
+        fFlags  = kNO_ALPHA_FLAGS;
+        // fState is undefined, but that is OK, since we shouldn't look at it
+        return;
+    }
+    
+    int32_t* SK_RESTRICT array = fState.fArray;
+
+    int i;
+    SkFixed max = 0;
+
+    for (int i = 0; i < 20; i++) {
+        SkFixed value = SkScalarToFixed(src[i]);
+        array[i] = value;
+        value = SkAbs32(value);
+        max = SkMax32(max, value);
+    }
+    
+    /*  All of fArray[] values must fit in 23 bits, to safely allow me to
+        multiply them by 8bit unsigned values, and get a signed answer without
+        overflow. This means clz needs to be 9 or bigger
+    */
+    int bits = SkCLZ(max);
+    int32_t one = SK_Fixed1;
+
+    fState.fShift = 16; // we are starting out as fixed 16.16
+    if (bits < 9) {
+        bits = 9 - bits;
+        fState.fShift -= bits;
+        for (i = 0; i < 20; i++) {
+            array[i] >>= bits;
+        }
+        one >>= bits;
+    }
+    
+    // check if we have to munge Alpha
+    int32_t changesAlpha = (array[15] | array[16] | array[17] |
+                            (array[18] - one) | array[19]);
+    int32_t usesAlpha = (array[3] | array[8] | array[13]);
+    bool shiftIs16 = (16 == fState.fShift);
+
+    if (changesAlpha | usesAlpha) {
+        fProc = shiftIs16 ? General16 : General;
+        fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag;
+    } else {
+        fFlags = kNO_ALPHA_FLAGS;
+
+        int32_t needsScale = (array[0] - one) |       // red axis
+                             (array[6] - one) |       // green axis
+                             (array[12] - one);       // blue axis
+
+        int32_t needs3x3 =  array[1] | array[2] |     // red off-axis
+                            array[5] | array[7] |     // green off-axis
+                            array[10] | array[11];    // blue off-axis
+
+        if (needs3x3) {
+            fProc = shiftIs16 ? AffineAdd16 : AffineAdd;
+        } else if (needsScale) {
+            fProc = shiftIs16 ? ScaleAdd16 : ScaleAdd;
+        } else if (array[4] | array[9] | array[14]) {   // needs add
+            fProc = shiftIs16 ? Add16 : Add;
+        } else {
+            fProc = NULL;   // identity
+        }
+    }
+
+    /*  preround our add values so we get a rounded shift. We do this after we
+        analyze the array, so we don't miss the case where the caller has zeros
+        which could make us accidentally take the General or Add case.
+    */
+    if (NULL != fProc) {
+        int32_t add = 1 << (fState.fShift - 1);
+        array[4] += add;
+        array[9] += add;
+        array[14] += add;
+        array[19] += add;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int32_t pin(int32_t value, int32_t max) {
+    if (value < 0) {
+        value = 0;
+    }
+    if (value > max) {
+        value = max;
+    }
+    return value;
+}
+
+SkColorMatrixFilter::SkColorMatrixFilter() {
+    this->setup(NULL);
+}
+
+SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) {
+    this->setup(cm.fMat);
+}
+
+SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
+    this->setup(array);
+}
+
+uint32_t SkColorMatrixFilter::getFlags() {
+    return this->INHERITED::getFlags() | fFlags;
+}
+
+void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count,
+                                     SkPMColor dst[]) {
+    Proc proc = fProc;
+    State* state = &fState;
+    int32_t* SK_RESTRICT result = state->fResult;
+    
+    if (NULL == proc) {
+        if (src != dst) {
+            memcpy(dst, src, count * sizeof(SkPMColor));
+        }
+        return;
+    }
+    
+    const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
+
+    for (int i = 0; i < count; i++) {
+        SkPMColor c = src[i];
+        
+        unsigned r = SkGetPackedR32(c);
+        unsigned g = SkGetPackedG32(c);
+        unsigned b = SkGetPackedB32(c);
+        unsigned a = SkGetPackedA32(c);
+        
+        // need our components to be un-premultiplied
+        if (255 != a) {
+            SkUnPreMultiply::Scale scale = table[a];
+            r = SkUnPreMultiply::ApplyScale(scale, r);
+            g = SkUnPreMultiply::ApplyScale(scale, g);
+            b = SkUnPreMultiply::ApplyScale(scale, b);
+            
+            SkASSERT(r <= 255);
+            SkASSERT(g <= 255);
+            SkASSERT(b <= 255);
+        }
+        
+        proc(state, r, g, b, a);
+
+        r = pin(result[0], SK_R32_MASK);
+        g = pin(result[1], SK_G32_MASK);
+        b = pin(result[2], SK_B32_MASK);
+        a = pin(result[3], SK_A32_MASK);
+        // re-prepremultiply if needed
+        if (255 != a) {
+            int scale = SkAlpha255To256(a);
+            r = SkAlphaMul(r, scale);
+            g = SkAlphaMul(g, scale);
+            b = SkAlphaMul(b, scale);
+        }
+        dst[i] = SkPackARGB32(a, r, g, b);
+    }
+}
+
+void SkColorMatrixFilter::filterSpan16(const uint16_t src[], int count,
+                                       uint16_t dst[]) {
+    SkASSERT(fFlags & SkColorFilter::kHasFilter16_Flag);
+
+    Proc   proc = fProc;
+    State* state = &fState;
+    int32_t* SK_RESTRICT result = state->fResult;
+    
+    if (NULL == proc) {
+        if (src != dst) {
+            memcpy(dst, src, count * sizeof(uint16_t));
+        }
+        return;
+    }
+
+    for (int i = 0; i < count; i++) {
+        uint16_t c = src[i];
+        
+        // expand to 8bit components (since our matrix translate is 8bit biased
+        unsigned r = SkPacked16ToR32(c);
+        unsigned g = SkPacked16ToG32(c);
+        unsigned b = SkPacked16ToB32(c);
+        
+        proc(state, r, g, b, 0);
+        
+        r = pin(result[0], SK_R32_MASK);
+        g = pin(result[1], SK_G32_MASK);
+        b = pin(result[2], SK_B32_MASK);
+        
+        // now packed it back down to 16bits (hmmm, could dither...)
+        dst[i] = SkPack888ToRGB16(r, g, b);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkColorMatrixFilter::flatten(SkFlattenableWriteBuffer& buffer)  {
+    this->INHERITED::flatten(buffer);
+
+    buffer.writeFunctionPtr((void*)fProc);
+    buffer.writeMul4(&fState, sizeof(fState));
+    buffer.write32(fFlags);
+}
+    
+SkFlattenable::Factory SkColorMatrixFilter::getFactory() { return CreateProc;  }
+    
+SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {
+    fProc = (Proc)buffer.readFunctionPtr();
+    buffer.read(&fState, sizeof(fState));
+    fFlags = buffer.readU32();
+}
+    
+SkFlattenable* SkColorMatrixFilter::CreateProc(SkFlattenableReadBuffer& buf) {
+    return SkNEW_ARGS(SkColorMatrixFilter, (buf));
+}
+
diff --git a/src/effects/SkCornerPathEffect.cpp b/src/effects/SkCornerPathEffect.cpp
new file mode 100644
index 0000000..43d571a
--- /dev/null
+++ b/src/effects/SkCornerPathEffect.cpp
@@ -0,0 +1,157 @@
+/* libs/graphics/effects/SkCornerPathEffect.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 "SkCornerPathEffect.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkBuffer.h"
+
+SkCornerPathEffect::SkCornerPathEffect(SkScalar radius) : fRadius(radius)
+{
+}
+
+SkCornerPathEffect::~SkCornerPathEffect()
+{
+}
+
+static bool ComputeStep(const SkPoint& a, const SkPoint& b, SkScalar radius, SkPoint* step)
+{
+    SkScalar dist = SkPoint::Distance(a, b);
+
+    step->set(b.fX - a.fX, b.fY - a.fY);
+    
+    if (dist <= radius * 2) {
+        step->scale(SK_ScalarHalf);
+        return false;
+    }
+    else {
+        step->scale(SkScalarDiv(radius, dist));
+        return true;
+    }
+}
+
+bool SkCornerPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
+{
+    if (fRadius == 0)
+        return false;
+
+    SkPath::Iter    iter(src, false);
+    SkPath::Verb    verb, prevVerb = (SkPath::Verb)-1;
+    SkPoint         pts[4];
+
+    bool        closed;
+    SkPoint     moveTo, lastCorner;
+    SkVector    firstStep, step;
+    bool        prevIsValid = true;
+
+    // to avoid warnings
+    moveTo.set(0, 0);
+    firstStep.set(0, 0);
+    lastCorner.set(0, 0);
+
+    for (;;) {
+        switch (verb = iter.next(pts)) {
+        case SkPath::kMove_Verb:
+            closed = iter.isClosedContour();
+            if (closed) {
+                moveTo = pts[0];
+                prevIsValid = false;
+            }
+            else {
+                dst->moveTo(pts[0]);
+                prevIsValid = true;
+            }
+            break;
+        case SkPath::kLine_Verb:
+            {
+                bool drawSegment = ComputeStep(pts[0], pts[1], fRadius, &step);
+                // prev corner
+                if (!prevIsValid) {
+                    dst->moveTo(moveTo + step);
+                    prevIsValid = true;
+                }
+                else {
+                    dst->quadTo(pts[0].fX, pts[0].fY, pts[0].fX + step.fX, pts[0].fY + step.fY);
+                }
+                if (drawSegment) {
+                    dst->lineTo(pts[1].fX - step.fX, pts[1].fY - step.fY);
+                }
+                lastCorner = pts[1];
+                prevIsValid = true;
+            }
+            break;
+        case SkPath::kQuad_Verb:
+            // TBD - just replicate the curve for now
+            if (!prevIsValid)
+            {
+                dst->moveTo(pts[0]);
+                prevIsValid = true;
+            }
+            dst->quadTo(pts[1], pts[2]);
+            lastCorner = pts[2];
+            firstStep.set(0, 0);
+            break;
+        case SkPath::kCubic_Verb:
+            if (!prevIsValid)
+            {
+                dst->moveTo(pts[0]);
+                prevIsValid = true;
+            }
+            // TBD - just replicate the curve for now
+            dst->cubicTo(pts[1], pts[2], pts[3]);
+            lastCorner = pts[3];
+            firstStep.set(0, 0);
+            break;
+        case SkPath::kClose_Verb:
+            if (firstStep.fX || firstStep.fY)
+                dst->quadTo(lastCorner.fX, lastCorner.fY,
+                            lastCorner.fX + firstStep.fX,
+                            lastCorner.fY + firstStep.fY);
+            dst->close();
+            break;
+        case SkPath::kDone_Verb:
+            goto DONE;
+        }
+
+        if (SkPath::kMove_Verb == prevVerb)
+            firstStep = step;
+        prevVerb = verb;
+    }
+DONE:
+    return true;
+}
+
+SkFlattenable::Factory SkCornerPathEffect::getFactory()
+{
+    return CreateProc;
+}
+
+void SkCornerPathEffect::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    buffer.writeScalar(fRadius);
+}
+
+SkFlattenable* SkCornerPathEffect::CreateProc(SkFlattenableReadBuffer& buffer)
+{
+    return SkNEW_ARGS(SkCornerPathEffect, (buffer));
+}
+
+SkCornerPathEffect::SkCornerPathEffect(SkFlattenableReadBuffer& buffer)
+{
+    fRadius = buffer.readScalar();
+}
+
diff --git a/src/effects/SkDashPathEffect.cpp b/src/effects/SkDashPathEffect.cpp
new file mode 100644
index 0000000..48581b5
--- /dev/null
+++ b/src/effects/SkDashPathEffect.cpp
@@ -0,0 +1,178 @@
+/* libs/graphics/effects/SkDashPathEffect.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 "SkDashPathEffect.h"
+#include "SkBuffer.h"
+#include "SkPathMeasure.h"
+
+static inline int is_even(int x)
+{
+    return (~x) << 31;
+}
+
+static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase, int32_t* index)
+{
+    int i;
+
+    for (i = 0; phase > intervals[i]; i++)
+        phase -= intervals[i];
+    *index = i;
+    return intervals[i] - phase;
+}
+
+SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase, bool scaleToFit)
+    : fScaleToFit(scaleToFit)
+{
+    SkASSERT(intervals);
+    SkASSERT(count > 1 && SkAlign2(count) == count);
+
+    fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count);
+    fCount = count;
+
+    SkScalar len = 0;
+    for (int i = 0; i < count; i++)
+    {
+        SkASSERT(intervals[i] >= 0);
+        fIntervals[i] = intervals[i];
+        len += intervals[i];
+    }
+    fIntervalLength = len;
+
+    if (len > 0)    // we don't handle 0 length dash arrays
+    {
+        if (phase < 0)
+        {
+            phase = -phase;
+            if (phase > len)
+                phase = SkScalarMod(phase, len);
+            phase = len - phase;
+        }
+        else if (phase >= len)
+            phase = SkScalarMod(phase, len);
+
+        SkASSERT(phase >= 0 && phase < len);
+        fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex);
+
+        SkASSERT(fInitialDashLength >= 0);
+        SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
+    }
+    else
+        fInitialDashLength = -1;    // signal bad dash intervals
+}
+
+SkDashPathEffect::~SkDashPathEffect()
+{
+    sk_free(fIntervals);
+}
+
+bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
+{
+    // we do nothing if the src wants to be filled, or if our dashlength is 0
+    if (*width < 0 || fInitialDashLength < 0)
+        return false;
+
+    SkPathMeasure   meas(src, false);
+    const SkScalar* intervals = fIntervals;
+
+    do {
+        bool        skipFirstSegment = meas.isClosed();
+        bool        addedSegment = false;
+        SkScalar    length = meas.getLength();
+        int         index = fInitialDashIndex;
+        SkScalar    scale = SK_Scalar1;
+
+        if (fScaleToFit)
+        {
+            if (fIntervalLength >= length)
+                scale = SkScalarDiv(length, fIntervalLength);
+            else
+            {
+                SkScalar div = SkScalarDiv(length, fIntervalLength);
+                int n = SkScalarFloor(div);
+                scale = SkScalarDiv(length, n * fIntervalLength);
+            }
+        }
+
+        SkScalar    distance = 0;
+        SkScalar    dlen = SkScalarMul(fInitialDashLength, scale);
+
+        while (distance < length)
+        {
+            SkASSERT(dlen >= 0);
+            addedSegment = false;
+            if (is_even(index) && dlen > 0 && !skipFirstSegment)
+            {
+                addedSegment = true;
+                meas.getSegment(distance, distance + dlen, dst, true);
+            }
+            distance += dlen;
+
+            // clear this so we only respect it the first time around
+            skipFirstSegment = false;
+
+            // wrap around our intervals array if necessary
+            index += 1;
+            SkASSERT(index <= fCount);
+            if (index == fCount)
+                index = 0;
+
+            // fetch our next dlen
+            dlen = SkScalarMul(intervals[index], scale);
+        }
+
+        // extend if we ended on a segment and we need to join up with the (skipped) initial segment
+        if (meas.isClosed() && is_even(fInitialDashIndex) && fInitialDashLength > 0)
+            meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
+    } while (meas.nextContour());
+    return true;
+}
+
+SkFlattenable::Factory SkDashPathEffect::getFactory()
+{
+    return fInitialDashLength < 0 ? NULL : CreateProc;
+}
+
+void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    SkASSERT(fInitialDashLength >= 0);
+
+    buffer.write32(fCount);
+    buffer.write32(fInitialDashIndex);
+    buffer.writeScalar(fInitialDashLength);
+    buffer.writeScalar(fIntervalLength);
+    buffer.write32(fScaleToFit);
+    buffer.writeMul4(fIntervals, fCount * sizeof(fIntervals[0]));
+}
+
+SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer)
+{
+    return SkNEW_ARGS(SkDashPathEffect, (buffer));
+}
+
+SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer)
+{
+    fCount = buffer.readS32();
+    fInitialDashIndex = buffer.readS32();
+    fInitialDashLength = buffer.readScalar();
+    fIntervalLength = buffer.readScalar();
+    fScaleToFit = (buffer.readS32() != 0);
+    
+    fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
+    buffer.read(fIntervals, fCount * sizeof(fIntervals[0]));
+}
+
+
diff --git a/src/effects/SkDiscretePathEffect.cpp b/src/effects/SkDiscretePathEffect.cpp
new file mode 100644
index 0000000..6286045
--- /dev/null
+++ b/src/effects/SkDiscretePathEffect.cpp
@@ -0,0 +1,105 @@
+/* libs/graphics/effects/SkDiscretePathEffect.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 "SkDiscretePathEffect.h"
+#include "SkBuffer.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+
+static void Perterb(SkPoint* p, const SkVector& tangent, SkScalar scale)
+{
+    SkVector normal = tangent;
+    normal.rotateCCW();
+    normal.setLength(scale);
+    *p += normal;
+}
+
+
+SkDiscretePathEffect::SkDiscretePathEffect(SkScalar segLength, SkScalar deviation)
+    : fSegLength(segLength), fPerterb(deviation)
+{
+}
+
+bool SkDiscretePathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
+{
+    bool doFill = *width < 0;
+
+    SkPathMeasure   meas(src, doFill);
+    uint32_t        seed = SkScalarRound(meas.getLength());
+    SkRandom        rand(seed ^ ((seed << 16) | (seed >> 16)));
+    SkScalar        scale = fPerterb;
+    SkPoint         p;
+    SkVector        v;
+
+    do {
+        SkScalar    length = meas.getLength();
+
+        if (fSegLength * (2 + doFill) > length)
+        {
+            meas.getSegment(0, length, dst, true);  // to short for us to mangle
+        }
+        else
+        {
+            int         n = SkScalarRound(SkScalarDiv(length, fSegLength));
+            SkScalar    delta = length / n;
+            SkScalar    distance = 0;
+
+            if (meas.isClosed())
+            {
+                n -= 1;
+                distance += delta/2;
+            }
+            meas.getPosTan(distance, &p, &v);
+            Perterb(&p, v, SkScalarMul(rand.nextSScalar1(), scale));
+            dst->moveTo(p);
+            while (--n >= 0)
+            {
+                distance += delta;
+                meas.getPosTan(distance, &p, &v);
+                Perterb(&p, v, SkScalarMul(rand.nextSScalar1(), scale));
+                dst->lineTo(p);
+            }
+            if (meas.isClosed())
+                dst->close();
+        }
+    } while (meas.nextContour());
+    return true;
+}
+
+SkFlattenable::Factory SkDiscretePathEffect::getFactory()
+{
+    return CreateProc;
+}
+
+SkFlattenable* SkDiscretePathEffect::CreateProc(SkFlattenableReadBuffer& buffer)
+{
+    return SkNEW_ARGS(SkDiscretePathEffect, (buffer));
+}
+
+void SkDiscretePathEffect::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    buffer.writeScalar(fSegLength);
+    buffer.writeScalar(fPerterb);
+}
+
+SkDiscretePathEffect::SkDiscretePathEffect(SkFlattenableReadBuffer& buffer)
+{
+    fSegLength = buffer.readScalar();
+    fPerterb = buffer.readScalar();
+}
+
+
diff --git a/src/effects/SkEmbossMask.cpp b/src/effects/SkEmbossMask.cpp
new file mode 100644
index 0000000..28e38a1
--- /dev/null
+++ b/src/effects/SkEmbossMask.cpp
@@ -0,0 +1,182 @@
+/* libs/graphics/effects/SkEmbossMask.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 "SkEmbossMask.h"
+
+static inline int nonzero_to_one(int x)
+{
+#if 0
+    return x != 0;
+#else
+    return ((unsigned)(x | -x)) >> 31;
+#endif
+}
+
+static inline int neq_to_one(int x, int max)
+{
+#if 0
+    return x != max;
+#else
+    SkASSERT(x >= 0 && x <= max);
+    return ((unsigned)(x - max)) >> 31;
+#endif
+}
+
+static inline int neq_to_mask(int x, int max)
+{
+#if 0
+    return -(x != max);
+#else
+    SkASSERT(x >= 0 && x <= max);
+    return (x - max) >> 31;
+#endif
+}
+
+static inline unsigned div255(unsigned x)
+{
+    SkASSERT(x <= (255*255));
+    return x * ((1 << 24) / 255) >> 24;
+}
+
+#define kDelta  32  // small enough to show off angle differences
+
+#include "SkEmbossMask_Table.h"
+
+#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
+
+#include <stdio.h>
+
+void SkEmbossMask_BuildTable()
+{
+    // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
+
+    FILE* file = ::fopen("SkEmbossMask_Table.h", "w");
+    SkASSERT(file);
+    ::fprintf(file, "#include \"SkTypes.h\"\n\n");
+    ::fprintf(file, "static const U16 gInvSqrtTable[128 * 128] = {\n");
+    for (int dx = 0; dx <= 255/2; dx++)
+    {
+        for (int dy = 0; dy <= 255/2; dy++)
+        {
+            if ((dy & 15) == 0)
+                ::fprintf(file, "\t");
+
+            U16 value = SkToU16((1 << 15) / SkSqrt32(dx * dx + dy * dy + kDelta*kDelta/4));
+
+            ::fprintf(file, "0x%04X", value);
+            if (dx * 128 + dy < 128*128-1)
+                ::fprintf(file, ", ");
+            if ((dy & 15) == 15)
+                ::fprintf(file, "\n");
+        }
+    }
+    ::fprintf(file, "};\n#define kDeltaUsedToBuildTable\t%d\n", kDelta);
+    ::fclose(file);
+}
+
+#endif
+
+void SkEmbossMask::Emboss(SkMask* mask, const SkEmbossMaskFilter::Light& light)
+{
+    SkASSERT(kDelta == kDeltaUsedToBuildTable);
+
+    SkASSERT(mask->fFormat == SkMask::k3D_Format);
+
+    int     specular = light.fSpecular;
+    int     ambient = light.fAmbient;
+    SkFixed lx = SkScalarToFixed(light.fDirection[0]);
+    SkFixed ly = SkScalarToFixed(light.fDirection[1]);
+    SkFixed lz = SkScalarToFixed(light.fDirection[2]);
+    SkFixed lz_dot_nz = lz * kDelta;
+    int     lz_dot8 = lz >> 8;
+
+    size_t      planeSize = mask->computeImageSize();
+    uint8_t*    alpha = mask->fImage;
+    uint8_t*    multiply = (uint8_t*)alpha + planeSize;
+    uint8_t*    additive = multiply + planeSize;
+
+    int rowBytes = mask->fRowBytes;
+    int maxy = mask->fBounds.height() - 1;
+    int maxx = mask->fBounds.width() - 1;
+
+    int prev_row = 0;
+    for (int y = 0; y <= maxy; y++)
+    {
+        int next_row = neq_to_mask(y, maxy) & rowBytes;
+
+        for (int x = 0; x <= maxx; x++)
+        {
+            if (alpha[x])
+            {
+                int nx = alpha[x + neq_to_one(x, maxx)] - alpha[x - nonzero_to_one(x)];
+                int ny = alpha[x + next_row] - alpha[x - prev_row];
+
+                SkFixed numer = lx * nx + ly * ny + lz_dot_nz;
+                int     mul = ambient;
+                int     add = 0;
+
+                if (numer > 0)  // preflight when numer/denom will be <= 0
+                {
+#if 0
+                    int denom = SkSqrt32(nx * nx + ny * ny + kDelta*kDelta);
+                    SkFixed dot = numer / denom;
+                    dot >>= 8;  // now dot is 2^8 instead of 2^16
+#else
+                    // can use full numer, but then we need to call SkFixedMul, since
+                    // numer is 24 bits, and our table is 12 bits
+
+                    // SkFixed dot = SkFixedMul(numer, gTable[]) >> 8
+                    SkFixed dot = (unsigned)(numer >> 4) * gInvSqrtTable[(SkAbs32(nx) >> 1 << 7) | (SkAbs32(ny) >> 1)] >> 20;
+#endif
+                    mul = SkFastMin32(mul + dot, 255);
+
+                    // now for the reflection
+
+                    //  R = 2 (Light * Normal) Normal - Light
+                    //  hilite = R * Eye(0, 0, 1)
+
+                    int hilite = (2 * dot - lz_dot8) * lz_dot8 >> 8;
+                    if (hilite > 0)
+                    {
+                        // pin hilite to 255, since our fast math is also a little sloppy
+                        hilite = SkClampMax(hilite, 255);
+
+                        // specular is 4.4
+                        // would really like to compute the fractional part of this
+                        // and then possibly cache a 256 table for a given specular
+                        // value in the light, and just pass that in to this function.
+                        add = hilite;
+                        for (int i = specular >> 4; i > 0; --i)
+                            add = div255(add * hilite);
+                    }
+                }
+                multiply[x] = SkToU8(mul);
+                additive[x] = SkToU8(add);
+
+            //  multiply[x] = 0xFF;
+            //  additive[x] = 0;
+            //  ((uint8_t*)alpha)[x] = alpha[x] * multiply[x] >> 8;
+            }
+        }
+        alpha += rowBytes;
+        multiply += rowBytes;
+        additive += rowBytes;
+        prev_row = rowBytes;
+    }
+}
+
+
diff --git a/src/effects/SkEmbossMask.h b/src/effects/SkEmbossMask.h
new file mode 100644
index 0000000..8e56d6f
--- /dev/null
+++ b/src/effects/SkEmbossMask.h
@@ -0,0 +1,29 @@
+/* libs/graphics/effects/SkEmbossMask.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 SkEmbossMask_DEFINED
+#define SkEmbossMask_DEFINED
+
+#include "SkEmbossMaskFilter.h"
+
+class SkEmbossMask {
+public:
+    static void Emboss(SkMask* mask, const SkEmbossMaskFilter::Light&);
+};
+
+#endif
+
diff --git a/src/effects/SkEmbossMaskFilter.cpp b/src/effects/SkEmbossMaskFilter.cpp
new file mode 100644
index 0000000..fcfad9d
--- /dev/null
+++ b/src/effects/SkEmbossMaskFilter.cpp
@@ -0,0 +1,141 @@
+/* libs/graphics/effects/SkEmbossMaskFilter.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 "SkEmbossMaskFilter.h"
+#include "SkBlurMaskFilter.h"
+#include "SkBlurMask.h"
+#include "SkEmbossMask.h"
+#include "SkBuffer.h"
+
+SkMaskFilter* SkBlurMaskFilter::CreateEmboss(const SkScalar direction[3],
+                                             SkScalar ambient, SkScalar specular,
+                                             SkScalar blurRadius)
+{
+    if (direction == NULL)
+        return NULL;
+
+    // ambient should be 0...1 as a scalar
+    int am = SkScalarToFixed(ambient) >> 8;
+    if (am < 0) am = 0;
+    else if (am > 0xFF) am = 0xFF;
+
+    // specular should be 0..15.99 as a scalar
+    int sp = SkScalarToFixed(specular) >> 12;
+    if (sp < 0) sp = 0;
+    else if (sp > 0xFF) sp = 0xFF;
+
+    SkEmbossMaskFilter::Light   light;
+    
+    memcpy(light.fDirection, direction, sizeof(light.fDirection));
+    light.fAmbient = SkToU8(am);
+    light.fSpecular = SkToU8(sp);
+    
+    return SkNEW_ARGS(SkEmbossMaskFilter, (light, blurRadius));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+static void normalize(SkScalar v[3])
+{
+    SkScalar mag = SkScalarSquare(v[0]) + SkScalarSquare(v[1]) + SkScalarSquare(v[2]);
+    mag = SkScalarSqrt(mag);
+
+    for (int i = 0; i < 3; i++)
+        v[i] = SkScalarDiv(v[i], mag);
+}
+
+SkEmbossMaskFilter::SkEmbossMaskFilter(const Light& light, SkScalar blurRadius)
+        : fLight(light), fBlurRadius(blurRadius)
+{
+    normalize(fLight.fDirection);
+}
+
+SkMask::Format SkEmbossMaskFilter::getFormat()
+{
+    return SkMask::k3D_Format;
+}
+
+bool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin)
+{
+    SkScalar radius = matrix.mapRadius(fBlurRadius);
+
+    if (!SkBlurMask::Blur(dst, src, radius, SkBlurMask::kInner_Style))
+        return false;
+
+    dst->fFormat = SkMask::k3D_Format;
+    if (margin)
+        margin->set(SkScalarCeil(radius), SkScalarCeil(radius));
+
+    if (src.fImage == NULL)
+        return true;
+
+    // create a larger buffer for the other two channels (should force fBlur to do this for us)
+
+    {
+        uint8_t* alphaPlane = dst->fImage;
+        size_t   planeSize = dst->computeImageSize();
+
+        dst->fImage = SkMask::AllocImage(planeSize * 3);
+        memcpy(dst->fImage, alphaPlane, planeSize);
+        SkMask::FreeImage(alphaPlane);
+    }
+
+    // run the light direction through the matrix...
+    Light   light = fLight;
+    matrix.mapVectors((SkVector*)(void*)light.fDirection, (SkVector*)(void*)fLight.fDirection, 1);
+
+    // now restore the length of the XY component
+    // cast to SkVector so we can call setLength (this double cast silences alias warnings)
+    SkVector* vec = (SkVector*)(void*)light.fDirection;
+    vec->setLength(light.fDirection[0],
+                   light.fDirection[1],
+                   SkPoint::Length(fLight.fDirection[0], fLight.fDirection[1]));
+
+    SkEmbossMask::Emboss(dst, light);
+
+    // restore original alpha
+    memcpy(dst->fImage, src.fImage, src.computeImageSize());
+
+    return true;
+}
+
+SkFlattenable* SkEmbossMaskFilter::CreateProc(SkFlattenableReadBuffer& buffer)
+{
+    return SkNEW_ARGS(SkEmbossMaskFilter, (buffer));
+}
+
+SkFlattenable::Factory SkEmbossMaskFilter::getFactory()
+{
+    return CreateProc;
+}
+
+SkEmbossMaskFilter::SkEmbossMaskFilter(SkFlattenableReadBuffer& buffer) : SkMaskFilter(buffer)
+{
+    buffer.read(&fLight, sizeof(fLight));
+    SkASSERT(fLight.fPad == 0); // for the font-cache lookup to be clean
+    fBlurRadius = buffer.readScalar();
+}
+
+void SkEmbossMaskFilter::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    this->INHERITED::flatten(buffer);
+
+    fLight.fPad = 0;    // for the font-cache lookup to be clean
+    buffer.writeMul4(&fLight, sizeof(fLight));
+    buffer.writeScalar(fBlurRadius);
+}
+
diff --git a/src/effects/SkEmbossMask_Table.h b/src/effects/SkEmbossMask_Table.h
new file mode 100644
index 0000000..775fe25
--- /dev/null
+++ b/src/effects/SkEmbossMask_Table.h
@@ -0,0 +1,1046 @@
+/* libs/graphics/effects/SkEmbossMask_Table.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.
+*/
+
+#include "SkTypes.h"
+
+static const uint16_t gInvSqrtTable[128 * 128] = {
+    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 
+    0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 
+    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 
+    0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 
+    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1, 
+    0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 
+    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1, 
+    0x05D1, 0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 
+    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0618, 0x0618, 0x05D1, 
+    0x05D1, 0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 
+    0x038E, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 
+    0x0590, 0x0590, 0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 
+    0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1, 0x05D1, 
+    0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 
+    0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x0590, 
+    0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 
+    0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 
+    0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 
+    0x0555, 0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 
+    0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 
+    0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x071C, 0x071C, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x0590, 0x0590, 
+    0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 
+    0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 
+    0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
+    0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0555, 
+    0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 
+    0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 
+    0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555, 
+    0x051E, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 
+    0x0375, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 
+    0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555, 0x051E, 
+    0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x038E, 
+    0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 
+    0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 
+    0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0666, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E, 
+    0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x0375, 
+    0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 
+    0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 
+    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0618, 0x0618, 0x0618, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 
+    0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 
+    0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 
+    0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 
+    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
+    0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04EC, 
+    0x04BD, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x0375, 0x0375, 
+    0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 
+    0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 
+    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04BD, 
+    0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 
+    0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 
+    0x026A, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 
+    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 
+    0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
+    0x0590, 0x0590, 0x0590, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 
+    0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x0375, 0x035E, 0x035E, 
+    0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 
+    0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 
+    0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 
+    0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
+    0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x0492, 
+    0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x0348, 
+    0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276, 
+    0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 
+    0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 
+    0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
+    0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469, 
+    0x0469, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 
+    0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 
+    0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 
+    0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 
+    0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
+    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
+    0x051E, 0x051E, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0469, 
+    0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0333, 
+    0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 
+    0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 
+    0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 
+    0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
+    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
+    0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0444, 
+    0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333, 
+    0x031F, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 
+    0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 
+    0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 
+    0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
+    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 
+    0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
+    0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0444, 0x0444, 0x0421, 
+    0x0421, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333, 0x031F, 
+    0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 
+    0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 
+    0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 
+    0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
+    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 
+    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
+    0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 
+    0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x031F, 
+    0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E, 
+    0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 
+    0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 
+    0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 
+    0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 
+    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
+    0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x0400, 
+    0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 
+    0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 
+    0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 
+    0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 
+    0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 
+    0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 
+    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
+    0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x03E0, 
+    0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 
+    0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E, 0x0253, 
+    0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 
+    0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 
+    0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 
+    0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 
+    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
+    0x0444, 0x0444, 0x0444, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3, 
+    0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 
+    0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 
+    0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 
+    0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 
+    0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 
+    0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 
+    0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
+    0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 
+    0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 
+    0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 
+    0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 
+    0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 
+    0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 
+    0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 
+    0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
+    0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 
+    0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 
+    0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 
+    0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 
+    0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 
+    0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 
+    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 
+    0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 
+    0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x038E, 
+    0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 
+    0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 
+    0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 
+    0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 
+    0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 
+    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 
+    0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 
+    0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x0375, 
+    0x0375, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 
+    0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 
+    0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 
+    0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 
+    0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 
+    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 
+    0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 
+    0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375, 
+    0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 
+    0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 
+    0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 
+    0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 
+    0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 
+    0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 
+    0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 
+    0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E, 
+    0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 
+    0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 
+    0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 
+    0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 
+    0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 
+    0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 
+    0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 
+    0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 
+    0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 
+    0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 
+    0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 
+    0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 
+    0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 
+    0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 
+    0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 
+    0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333, 
+    0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 
+    0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 
+    0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 
+    0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 
+    0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 
+    0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 
+    0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 
+    0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F, 
+    0x031F, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 
+    0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 
+    0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 
+    0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 
+    0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 
+    0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 
+    0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 
+    0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x031F, 0x030C, 
+    0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 
+    0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 
+    0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 
+    0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 
+    0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 
+    0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 
+    0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 
+    0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x031F, 0x031F, 0x030C, 0x030C, 0x030C, 0x02FA, 
+    0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 
+    0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219, 
+    0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 
+    0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 
+    0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 
+    0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 
+    0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 
+    0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02FA, 
+    0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 
+    0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 
+    0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 
+    0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 
+    0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 
+    0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 
+    0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 
+    0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02E8, 
+    0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 
+    0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 
+    0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 
+    0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 
+    0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 
+    0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 
+    0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 
+    0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 
+    0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A, 
+    0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0208, 
+    0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 
+    0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 
+    0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 
+    0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 
+    0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 
+    0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 
+    0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 
+    0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 
+    0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 
+    0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 
+    0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 
+    0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 
+    0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 
+    0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 
+    0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 
+    0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 
+    0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 
+    0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 
+    0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 
+    0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 
+    0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 
+    0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 
+    0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 
+    0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 
+    0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 
+    0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 
+    0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 
+    0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 
+    0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 
+    0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C, 
+    0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 
+    0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 
+    0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 
+    0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 
+    0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 
+    0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 
+    0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 
+    0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F, 
+    0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 
+    0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 
+    0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 
+    0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 
+    0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 
+    0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 
+    0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 
+    0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x028F, 
+    0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x023E, 
+    0x0234, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 
+    0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 
+    0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 
+    0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 
+    0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 
+    0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 
+    0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0282, 
+    0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 
+    0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 
+    0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 
+    0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 
+    0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 
+    0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 
+    0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 
+    0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x0276, 0x0276, 
+    0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 
+    0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 
+    0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 
+    0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 
+    0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 
+    0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 
+    0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 
+    0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 
+    0x026A, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x0222, 
+    0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 
+    0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 
+    0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 
+    0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 
+    0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 
+    0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 
+    0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E, 
+    0x025E, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 
+    0x0219, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 
+    0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 
+    0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 
+    0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 
+    0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 
+    0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 
+    0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0253, 
+    0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 
+    0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 
+    0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 
+    0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 
+    0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 
+    0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 
+    0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 
+    0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249, 
+    0x0249, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 
+    0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 
+    0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 
+    0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 
+    0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 
+    0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 
+    0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 
+    0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 
+    0x023E, 0x023E, 0x0234, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 
+    0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 
+    0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 
+    0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 
+    0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 
+    0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 
+    0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 
+    0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 
+    0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 
+    0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 
+    0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 
+    0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 
+    0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 
+    0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 
+    0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x022B, 
+    0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 
+    0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 
+    0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 
+    0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 
+    0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 
+    0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 
+    0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 
+    0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x0222, 
+    0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 
+    0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 
+    0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 
+    0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 
+    0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 
+    0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 
+    0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0219, 
+    0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 
+    0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 
+    0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 
+    0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 
+    0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 
+    0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 
+    0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 
+    0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0219, 0x0210, 
+    0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 
+    0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 
+    0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 
+    0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 
+    0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 
+    0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 
+    0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 
+    0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 
+    0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 
+    0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 
+    0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 
+    0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 
+    0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 
+    0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 
+    0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 
+    0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 
+    0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 
+    0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 
+    0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 
+    0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 
+    0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 
+    0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 
+    0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 
+    0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 
+    0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 
+    0x019E, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 
+    0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 
+    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 
+    0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 
+    0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 
+    0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 
+    0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 
+    0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 
+    0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 
+    0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 
+    0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
+    0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 
+    0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 
+    0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 
+    0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 
+    0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 
+    0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 
+    0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 
+    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 
+    0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 
+    0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 
+    0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 
+    0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 
+    0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 
+    0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141, 
+    0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 
+    0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
+    0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 
+    0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 
+    0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 
+    0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 
+    0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 
+    0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E, 
+    0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 
+    0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 
+    0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 
+    0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 
+    0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 
+    0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 
+    0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 
+    0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 
+    0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 
+    0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
+    0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 
+    0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 
+    0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 
+    0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 
+    0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 
+    0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 
+    0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 
+    0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 
+    0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 
+    0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 
+    0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 
+    0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 
+    0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 
+    0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 
+    0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 
+    0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 
+    0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 
+    0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 
+    0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 
+    0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 
+    0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 
+    0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 
+    0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 
+    0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 
+    0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 
+    0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 
+    0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 
+    0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 
+    0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 
+    0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 
+    0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 
+    0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 
+    0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 
+    0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 
+    0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 
+    0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 
+    0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 
+    0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 
+    0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 
+    0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 
+    0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 
+    0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 
+    0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 
+    0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 
+    0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 
+    0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 
+    0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 
+    0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 
+    0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 
+    0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 
+    0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 
+    0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 
+    0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 
+    0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 
+    0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 
+    0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 
+    0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 
+    0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 
+    0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 
+    0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 
+    0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 
+    0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 
+    0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 
+    0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 
+    0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 
+    0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 
+    0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 
+    0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 
+    0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 
+    0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 
+    0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 
+    0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 
+    0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 
+    0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E, 
+    0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 
+    0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 
+    0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 
+    0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 
+    0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 
+    0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 
+    0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 
+    0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 
+    0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 
+    0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 
+    0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 
+    0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 
+    0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 
+    0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 
+    0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 
+    0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 
+    0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 
+    0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 
+    0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 
+    0x0141, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 
+    0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 
+    0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 
+    0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 
+    0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 
+    0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 
+    0x017D, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 
+    0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 
+    0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 
+    0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 
+    0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 
+    0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 
+    0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 
+    0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 
+    0x0178, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 
+    0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 
+    0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 
+    0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 
+    0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 
+    0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 
+    0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 
+    0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0174, 
+    0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 
+    0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 
+    0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 
+    0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 
+    0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 
+    0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 
+    0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0170, 
+    0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 
+    0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x013B, 
+    0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 
+    0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 
+    0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 
+    0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 
+    0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 
+    0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x016C, 
+    0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 
+    0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 
+    0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 
+    0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 
+    0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 
+    0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 
+    0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 
+    0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 
+    0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 
+    0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 
+    0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 
+    0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 
+    0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 
+    0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 
+    0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 
+    0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 
+    0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 
+    0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 
+    0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 
+    0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 
+    0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 
+    0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 
+    0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 
+    0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 
+    0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 
+    0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 
+    0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 
+    0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 
+    0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 
+    0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 
+    0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 
+    0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 
+    0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 
+    0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 
+    0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 
+    0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 
+    0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 
+    0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 
+    0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 
+    0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 
+    0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 
+    0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 
+    0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 
+    0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 
+    0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 
+    0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 
+    0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 
+    0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 
+    0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 
+    0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 
+    0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 
+    0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 
+    0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 
+    0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 
+    0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 
+    0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 
+    0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 
+    0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 
+    0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111, 
+    0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 
+    0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 
+    0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 
+    0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 
+    0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 
+    0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 
+    0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 
+    0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 
+    0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 
+    0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 
+    0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 
+    0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 
+    0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 
+    0x014E, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 
+    0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 
+    0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 
+    0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 
+    0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 
+    0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 
+    0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 
+    0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 
+    0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138, 
+    0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 
+    0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 
+    0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 
+    0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 
+    0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 
+    0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155, 
+    0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 
+    0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 
+    0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 
+    0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A, 
+    0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 
+    0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 
+    0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 
+    0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151, 
+    0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 
+    0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 
+    0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 
+    0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 
+    0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 
+    0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 
+    0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 
+    0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E, 
+    0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 
+    0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 
+    0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 
+    0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 
+    0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 
+    0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 
+    0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 
+    0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 
+    0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E, 
+    0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 
+    0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 
+    0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 
+    0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 
+    0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 
+    0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 
+    0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 
+    0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 
+    0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 
+    0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 
+    0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 
+    0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 
+    0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 
+    0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 
+    0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 
+    0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B, 
+    0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 
+    0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 
+    0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 
+    0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 
+    0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 
+    0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 
+    0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 
+    0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138, 
+    0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 
+    0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 
+    0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 
+    0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 
+    0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 
+    0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 
+    0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 
+    0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135, 
+    0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 
+    0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 
+    0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 
+    0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 
+    0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 
+    0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00C9, 
+    0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 
+    0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 
+    0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 
+    0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 
+    0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 
+    0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 
+    0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 
+    0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 
+    0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 
+    0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 
+    0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 
+    0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 
+    0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 
+    0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 
+    0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 
+    0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7, 
+    0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 
+    0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 
+    0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 
+    0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 
+    0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 
+    0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 
+    0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 
+    0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 
+    0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 
+    0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 
+    0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 
+    0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 
+    0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 
+    0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 
+    0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 
+    0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 
+    0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 
+    0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 
+    0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 
+    0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 
+    0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 
+    0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 
+    0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 
+    0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C5, 
+    0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 
+    0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 
+    0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 
+    0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 
+    0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 
+    0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 
+    0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 
+    0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 
+    0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 
+    0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 
+    0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 
+    0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 
+    0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 
+    0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 
+    0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 
+    0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 
+    0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 
+    0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 
+    0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 
+    0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 
+    0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 
+    0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 
+    0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 
+    0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C3, 
+    0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 
+    0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 
+    0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 
+    0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 
+    0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 
+    0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 
+    0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 
+    0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C3, 0x00C3, 
+    0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 
+    0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 
+    0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 
+    0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 
+    0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 
+    0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 
+    0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 
+    0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 
+    0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 
+    0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 
+    0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 
+    0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 
+    0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 
+    0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 
+    0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 
+    0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C0, 
+    0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 
+    0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 
+    0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 
+    0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 
+    0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 
+    0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 
+    0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 
+    0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C0, 0x00C0, 
+    0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 
+    0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 
+    0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 
+    0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 
+    0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 
+    0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 
+    0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 
+    0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 
+    0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 
+    0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 
+    0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 
+    0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 
+    0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 
+    0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 
+    0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 
+    0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BE, 
+    0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 
+    0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 
+    0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 
+    0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 
+    0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 
+    0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 
+    0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 
+    0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BE, 0x00BE, 
+    0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 
+    0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C, 
+    0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 
+    0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 
+    0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 
+    0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 
+    0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 
+    0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 
+    0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 
+    0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A, 
+    0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 
+    0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 
+    0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 
+    0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 
+    0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 
+    0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 
+    0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 
+    0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108, 
+    0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 
+    0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 
+    0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 
+    0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 
+    0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 
+    0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 
+    0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 
+    0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 
+    0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 
+    0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 
+    0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 
+    0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 
+    0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 
+    0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 
+    0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 
+    0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 
+    0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 
+    0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 
+    0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 
+    0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 
+    0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 
+    0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 
+    0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 
+    0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104, 
+    0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 
+    0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 
+    0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 
+    0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 
+    0x00D2, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 
+    0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 
+    0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 
+    0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0102, 
+    0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 
+    0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 
+    0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 
+    0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 
+    0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 
+    0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 
+    0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 
+    0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x0100, 
+    0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 
+    0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 
+    0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 
+    0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 
+    0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 
+    0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 
+    0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 
+    0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 
+    0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 
+    0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA, 
+    0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 
+    0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00D0, 
+    0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 
+    0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 0x00B8, 
+    0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 
+    0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 
+    0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 
+    0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 
+    0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 
+    0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 
+    0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C3, 
+    0x00C1, 0x00C0, 0x00C0, 0x00BF, 0x00BE, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 0x00B8, 0x00B7, 
+    0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 
+    0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00FA, 
+    0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 
+    0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 
+    0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 
+    0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 
+    0x00CC, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C3, 0x00C1, 
+    0x00C0, 0x00C0, 0x00BF, 0x00BE, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 0x00B8, 0x00B7, 0x00B6
+};
+#define kDeltaUsedToBuildTable  32
diff --git a/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp
new file mode 100644
index 0000000..a1674cb
--- /dev/null
+++ b/src/effects/SkGradientShader.cpp
@@ -0,0 +1,1562 @@
+/* libs/graphics/effects/SkGradientShader.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 "SkGradientShader.h"
+#include "SkColorPriv.h"
+#include "SkUnitMapper.h"
+#include "SkUtils.h"
+
+/*
+    ToDo
+
+    - not sure we still need the full Rec struct, now that we're using a cache
+    - detect const-alpha (but not opaque) in getFlags()
+*/
+
+/* dither seems to look better, but not stuningly yet, and it slows us down a little
+    so its not on by default yet.
+*/
+#define TEST_GRADIENT_DITHER
+
+///////////////////////////////////////////////////////////////////////////
+
+typedef SkFixed (*TileProc)(SkFixed);
+
+static SkFixed clamp_tileproc(SkFixed x)
+{
+    return SkClampMax(x, 0xFFFF);
+}
+
+static SkFixed repeat_tileproc(SkFixed x)
+{
+    return x & 0xFFFF;
+}
+
+static inline SkFixed mirror_tileproc(SkFixed x)
+{
+    int s = x << 15 >> 31;
+    return (x ^ s) & 0xFFFF;
+}
+
+static const TileProc gTileProcs[] = {
+    clamp_tileproc,
+    repeat_tileproc,
+    mirror_tileproc
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static inline int repeat_6bits(int x)
+{
+    return x & 63;
+}
+
+static inline int mirror_6bits(int x)
+{
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (x & 64)
+        x = ~x;
+    return x & 63;
+#else
+    int s = x << 25 >> 31;
+    return (x ^ s) & 63;
+#endif
+}
+
+static inline int repeat_8bits(int x)
+{
+    return x & 0xFF;
+}
+
+static inline int mirror_8bits(int x)
+{
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (x & 256)
+        x = ~x;
+    return x & 255;
+#else
+    int s = x << 23 >> 31;
+    return (x ^ s) & 0xFF;
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class Gradient_Shader : public SkShader {
+public:
+    Gradient_Shader(const SkColor colors[], const SkScalar pos[],
+                    int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
+    virtual ~Gradient_Shader();
+
+    // overrides
+    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
+    virtual uint32_t getFlags() { return fFlags; }
+
+protected:
+    Gradient_Shader(SkFlattenableReadBuffer& );
+    SkUnitMapper* fMapper;
+    SkMatrix    fPtsToUnit;     // set by subclass
+    SkMatrix    fDstToIndex;
+    SkMatrix::MapXYProc fDstToIndexProc;
+    SkPMColor*  fARGB32;
+    TileMode    fTileMode;
+    TileProc    fTileProc;
+    uint16_t    fColorCount;
+    uint8_t     fDstToIndexClass;
+    uint8_t     fFlags;
+
+    struct Rec {
+        SkFixed     fPos;   // 0...1
+        uint32_t    fScale; // (1 << 24) / range
+    };
+    Rec*        fRecs;
+
+    enum {
+        kCache16Bits    = 6,    // seems like enough for visual accuracy
+        kCache16Count   = 1 << kCache16Bits,
+        kCache32Bits    = 8,    // pretty much should always be 8
+        kCache32Count   = 1 << kCache32Bits
+    };
+    virtual void flatten(SkFlattenableWriteBuffer& );
+    const uint16_t*     getCache16();
+    const SkPMColor*    getCache32();
+
+private:
+    enum {
+        kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
+
+        kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec))
+    };
+    SkColor     fStorage[(kStorageSize + 3) >> 2];
+    SkColor*    fOrigColors;
+
+    uint16_t*   fCache16;   // working ptr. If this is NULL, we need to recompute the cache values
+    SkPMColor*  fCache32;   // working ptr. If this is NULL, we need to recompute the cache values
+
+    uint16_t*   fCache16Storage;    // storage for fCache16, allocated on demand
+    SkPMColor*  fCache32Storage;    // storage for fCache32, allocated on demand
+    unsigned    fCacheAlpha;        // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
+
+    typedef SkShader INHERITED;
+};
+
+static inline unsigned scalarToU16(SkScalar x)
+{
+    SkASSERT(x >= 0 && x <= SK_Scalar1);
+
+#ifdef SK_SCALAR_IS_FLOAT
+    return (unsigned)(x * 0xFFFF);
+#else
+    return x - (x >> 16);   // probably should be x - (x > 0x7FFF) but that is slower
+#endif
+}
+
+Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[], int colorCount,
+                      SkShader::TileMode mode, SkUnitMapper* mapper)
+{
+    SkASSERT(colorCount > 1);
+
+    fCacheAlpha = 256;  // init to a value that paint.getAlpha() can't return
+
+    fMapper = mapper;
+    mapper->safeRef();
+
+    fCache16 = fCache16Storage = NULL;
+    fCache32 = fCache32Storage = NULL;
+
+    fColorCount = SkToU16(colorCount);
+    if (colorCount > kColorStorageCount)
+        fOrigColors = (SkColor*)sk_malloc_throw((sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec)) * colorCount);
+    else
+        fOrigColors = fStorage;
+    memcpy(fOrigColors, colors, colorCount * sizeof(SkColor));
+    // our premul colors point to the 2nd half of the array
+    // these are assigned each time in setContext
+    fARGB32 = fOrigColors + colorCount;
+
+    SkASSERT((unsigned)mode < SkShader::kTileModeCount);
+    SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
+    fTileMode = mode;
+    fTileProc = gTileProcs[mode];
+
+    fRecs = (Rec*)(fARGB32 + colorCount);
+    if (colorCount > 2)
+    {
+        Rec* recs = fRecs;
+
+        recs[0].fPos = 0;
+    //  recs[0].fScale = 0; // unused;
+        if (pos)
+        {
+            /*  We need to convert the user's array of relative positions into
+                fixed-point positions and scale factors. We need these results
+                to be strictly monotonic (no two values equal or out of order).
+                Hence this complex loop that just jams a zero for the scale
+                value if it sees a segment out of order, and it assures that
+                we start at 0 and end at 1.0
+            */
+            SkFixed prev = 0;
+            for (int i = 1; i < colorCount; i++)
+            {
+                // force the last value to be 1.0
+                SkFixed curr;
+                if (i == colorCount - 1)
+                    curr = SK_Fixed1;
+                else
+                {
+                    curr = SkScalarToFixed(pos[i]);
+                    // pin curr withing range
+                    if (curr < 0)
+                        curr = 0;
+                    else if (curr > SK_Fixed1)
+                        curr = SK_Fixed1;
+                }
+                recs[i].fPos = curr;
+                if (curr > prev)
+                    recs[i].fScale = (1 << 24) / (curr - prev);
+                else
+                    recs[i].fScale = 0; // ignore this segment
+                // get ready for the next value
+                prev = curr;
+            }
+        }
+        else    // assume even distribution
+        {
+            SkFixed dp = SK_Fixed1 / (colorCount - 1);
+            SkFixed p = dp;
+            SkFixed scale = (colorCount - 1) << 8;  // (1 << 24) / dp
+            for (int i = 1; i < colorCount; i++)
+            {
+                recs[i].fPos   = p;
+                recs[i].fScale = scale;
+                p += dp;
+            }
+        }
+    }
+}
+
+Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
+    INHERITED(buffer)
+{
+    fCacheAlpha = 256;
+
+    fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
+
+    fCache16 = fCache16Storage = NULL;
+    fCache32 = fCache32Storage = NULL;
+
+    int colorCount = fColorCount = buffer.readU16();
+    if (colorCount > kColorStorageCount)
+        fOrigColors = (SkColor*)sk_malloc_throw((sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec)) * colorCount);
+    else
+        fOrigColors = fStorage;
+    buffer.read(fOrigColors, colorCount * sizeof(SkColor));
+    fARGB32 = fOrigColors + colorCount;
+
+    fTileMode = (TileMode)buffer.readU8();
+    fTileProc = gTileProcs[fTileMode];
+    fRecs = (Rec*)(fARGB32 + colorCount);
+    if (colorCount > 2) {
+        Rec* recs = fRecs;
+        recs[0].fPos = 0;
+        for (int i = 1; i < colorCount; i++) {
+            recs[i].fPos = buffer.readS32();
+            recs[i].fScale = buffer.readU32();
+        }
+    }
+    buffer.read(&fPtsToUnit, sizeof(SkMatrix));
+}
+
+Gradient_Shader::~Gradient_Shader()
+{
+    if (fCache16Storage)
+        sk_free(fCache16Storage);
+    if (fCache32Storage)
+        sk_free(fCache32Storage);
+    if (fOrigColors != fStorage)
+        sk_free(fOrigColors);
+    fMapper->safeUnref();
+}
+
+void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    this->INHERITED::flatten(buffer);
+    buffer.writeFlattenable(fMapper);
+    buffer.write16(fColorCount);
+    buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
+    buffer.write8(fTileMode);
+    if (fColorCount > 2) {
+        Rec* recs = fRecs;
+        for (int i = 1; i < fColorCount; i++) {
+            buffer.write32(recs[i].fPos);
+            buffer.write32(recs[i].fScale);
+        }
+    }
+    buffer.writeMul4(&fPtsToUnit, sizeof(SkMatrix));
+}
+
+bool Gradient_Shader::setContext(const SkBitmap& device,
+                                 const SkPaint& paint,
+                                 const SkMatrix& matrix)
+{
+    if (!this->INHERITED::setContext(device, paint, matrix))
+        return false;
+
+    const SkMatrix& inverse = this->getTotalInverse();
+
+    if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
+        return false;
+    }
+
+    fDstToIndexProc = fDstToIndex.getMapXYProc();
+    fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
+
+    // now convert our colors in to PMColors
+    unsigned paintAlpha = this->getPaintAlpha();
+    unsigned colorAlpha = 0xFF;
+
+    for (unsigned i = 0; i < fColorCount; i++) {
+        SkColor src = fOrigColors[i];
+        unsigned sa = SkColorGetA(src);
+        colorAlpha &= sa;
+        
+        // now modulate it by the paint for our resulting ARGB32 array
+        sa = SkMulDiv255Round(sa, paintAlpha);
+        fARGB32[i] = SkPreMultiplyARGB(sa, SkColorGetR(src), SkColorGetG(src),
+                                       SkColorGetB(src));
+    }
+
+    fFlags = this->INHERITED::getFlags();
+    if ((colorAlpha & paintAlpha) == 0xFF) {
+        fFlags |= kOpaqueAlpha_Flag;
+    }
+    // we can do span16 as long as our individual colors are opaque,
+    // regardless of the paint's alpha
+    if (0xFF == colorAlpha) {
+        fFlags |= kHasSpan16_Flag;
+    }
+
+    // if the new alpha differs from the previous time we were called, inval our cache
+    // this will trigger the cache to be rebuilt.
+    // we don't care about the first time, since the cache ptrs will already be NULL
+    if (fCacheAlpha != paintAlpha) {
+        fCache16 = NULL;                // inval the cache
+        fCache32 = NULL;                // inval the cache
+        fCacheAlpha = paintAlpha;       // record the new alpha
+    }
+    return true;
+}
+
+static inline int blend8(int a, int b, int scale)
+{
+    SkASSERT(a == SkToU8(a));
+    SkASSERT(b == SkToU8(b));
+    SkASSERT(scale >= 0 && scale <= 256);
+
+    return a + ((b - a) * scale >> 8);
+}
+
+static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1, int blend)
+{
+#if 0
+    int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
+    int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
+    int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
+    int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
+
+    return SkPackARGB32(a, r, g, b);
+#else
+    int otherBlend = 256 - blend;
+
+#if 0
+    U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
+    U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
+    SkASSERT((t0 & t1) == 0);
+    return t0 | t1;
+#else
+    return  ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
+            ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
+#endif
+
+#endif
+}
+
+#define Fixed_To_Dot8(x)        (((x) + 0x80) >> 8)
+
+/** We take the original colors, not our premultiplied PMColors, since we can build a 16bit table
+    as long as the original colors are opaque, even if the paint specifies a non-opaque alpha.
+*/
+static void build_16bit_cache(uint16_t cache[], SkColor c0, SkColor c1, int count)
+{
+    SkASSERT(count > 1);
+    SkASSERT(SkColorGetA(c0) == 0xFF);
+    SkASSERT(SkColorGetA(c1) == 0xFF);
+
+    SkFixed r = SkColorGetR(c0);
+    SkFixed g = SkColorGetG(c0);
+    SkFixed b = SkColorGetB(c0);
+
+    SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
+    SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
+    SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
+
+    r = SkIntToFixed(r) + 0x8000;
+    g = SkIntToFixed(g) + 0x8000;
+    b = SkIntToFixed(b) + 0x8000;
+
+    do {
+        unsigned rr = r >> 16;
+        unsigned gg = g >> 16;
+        unsigned bb = b >> 16;
+        cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
+        cache[64] = SkDitherPack888ToRGB16(rr, gg, bb);
+        cache += 1;
+        r += dr;
+        g += dg;
+        b += db;
+    } while (--count != 0);
+}
+
+static void build_32bit_cache(SkPMColor cache[], SkPMColor c0, SkPMColor c1, int count)
+{
+    SkASSERT(count > 1);
+
+    SkFixed a = SkGetPackedA32(c0);
+    SkFixed r = SkGetPackedR32(c0);
+    SkFixed g = SkGetPackedG32(c0);
+    SkFixed b = SkGetPackedB32(c0);
+
+    SkFixed da = SkIntToFixed(SkGetPackedA32(c1) - a) / (count - 1);
+    SkFixed dr = SkIntToFixed(SkGetPackedR32(c1) - r) / (count - 1);
+    SkFixed dg = SkIntToFixed(SkGetPackedG32(c1) - g) / (count - 1);
+    SkFixed db = SkIntToFixed(SkGetPackedB32(c1) - b) / (count - 1);
+
+    a = SkIntToFixed(a) + 0x8000;
+    r = SkIntToFixed(r) + 0x8000;
+    g = SkIntToFixed(g) + 0x8000;
+    b = SkIntToFixed(b) + 0x8000;
+
+    do {
+        *cache++ = SkPackARGB32(a >> 16, r >> 16, g >> 16, b >> 16);
+        a += da;
+        r += dr;
+        g += dg;
+        b += db;
+    } while (--count != 0);
+}
+
+static inline int SkFixedToFFFF(SkFixed x)
+{
+    SkASSERT((unsigned)x <= SK_Fixed1);
+    return x - (x >> 16);
+}
+
+static inline U16CPU dot6to16(unsigned x)
+{
+    SkASSERT(x < 64);
+    return (x << 10) | (x << 4) | (x >> 2);
+}
+
+const uint16_t* Gradient_Shader::getCache16()
+{
+    if (fCache16 == NULL)
+    {
+        if (fCache16Storage == NULL) // set the storage and our working ptr
+#ifdef TEST_GRADIENT_DITHER
+            fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
+#else
+            fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
+#endif
+        fCache16 = fCache16Storage;
+        if (fColorCount == 2)
+            build_16bit_cache(fCache16, fOrigColors[0], fOrigColors[1], kCache16Count);
+        else
+        {
+            Rec* rec = fRecs;
+            int prevIndex = 0;
+            for (unsigned i = 1; i < fColorCount; i++)
+            {
+                int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache16Bits);
+                SkASSERT(nextIndex < kCache16Count);
+
+                if (nextIndex > prevIndex)
+                    build_16bit_cache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
+                prevIndex = nextIndex;
+            }
+            SkASSERT(prevIndex == kCache16Count - 1);
+        }
+
+        if (fMapper)
+        {
+#ifdef TEST_GRADIENT_DITHER
+            fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2);
+#else
+            fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count);
+#endif
+            uint16_t* linear = fCache16;         // just computed linear data
+            uint16_t* mapped = fCache16Storage;  // storage for mapped data
+            SkUnitMapper* map = fMapper;
+            for (int i = 0; i < 64; i++)
+            {
+                int index = map->mapUnit16(dot6to16(i)) >> 10;
+                mapped[i] = linear[index];
+#ifdef TEST_GRADIENT_DITHER
+                mapped[i + 64] = linear[index + 64];
+#endif
+            }
+            sk_free(fCache16);
+            fCache16 = fCache16Storage;
+        }
+    }
+    return fCache16;
+}
+
+const SkPMColor* Gradient_Shader::getCache32()
+{
+    if (fCache32 == NULL)
+    {
+        if (fCache32Storage == NULL) // set the storage and our working ptr
+            fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
+
+        fCache32 = fCache32Storage;
+        if (fColorCount == 2)
+            build_32bit_cache(fCache32, fARGB32[0], fARGB32[1], kCache32Count);
+        else
+        {
+            Rec* rec = fRecs;
+            int prevIndex = 0;
+            for (unsigned i = 1; i < fColorCount; i++)
+            {
+                int nextIndex = SkFixedToFFFF(rec[i].fPos) >> (16 - kCache32Bits);
+                SkASSERT(nextIndex < kCache32Count);
+
+                if (nextIndex > prevIndex)
+                    build_32bit_cache(fCache32 + prevIndex, fARGB32[i-1], fARGB32[i], nextIndex - prevIndex + 1);
+                prevIndex = nextIndex;
+            }
+            SkASSERT(prevIndex == kCache32Count - 1);
+        }
+
+        if (fMapper)
+        {
+            fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count);
+            SkPMColor* linear = fCache32;           // just computed linear data
+            SkPMColor* mapped = fCache32Storage;    // storage for mapped data
+            SkUnitMapper* map = fMapper;
+            for (int i = 0; i < 256; i++)
+                mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8];
+            sk_free(fCache32);
+            fCache32 = fCache32Storage;
+        }
+    }
+    return fCache32;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix)
+{
+    SkVector    vec = pts[1] - pts[0];
+    SkScalar    mag = vec.length();
+    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
+
+    vec.scale(inv);
+    matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
+    matrix->postTranslate(-pts[0].fX, -pts[0].fY);
+    matrix->postScale(inv, inv);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Linear_Gradient : public Gradient_Shader {
+public:
+    Linear_Gradient(const SkPoint pts[2],
+                    const SkColor colors[], const SkScalar pos[], int colorCount,
+                    SkShader::TileMode mode, SkUnitMapper* mapper)
+        : Gradient_Shader(colors, pos, colorCount, mode, mapper)
+    {
+        pts_to_unit_matrix(pts, &fPtsToUnit);
+    }
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
+    virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { 
+        return SkNEW_ARGS(Linear_Gradient, (buffer));
+    }
+
+protected:
+    Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
+    virtual Factory getFactory() { return CreateProc; }
+
+private:
+    typedef Gradient_Shader INHERITED;
+};
+
+//  Return true if fx, fx+dx, fx+2*dx, ... is always in range
+static bool no_need_for_clamp(int fx, int dx, int count)
+{
+    SkASSERT(count > 0);
+    return (unsigned)((fx | (fx + (count - 1) * dx)) >> 8) <= 0xFF;
+}
+
+void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
+{
+    SkASSERT(count > 0);
+
+    SkPoint             srcPt;
+    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+    TileProc            proc = fTileProc;
+    const SkPMColor*    cache = this->getCache32();
+
+    if (fDstToIndexClass != kPerspective_MatrixClass)
+    {
+        dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
+        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass)
+        {
+            SkFixed dxStorage[1];
+            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
+            dx = dxStorage[0];
+        }
+        else
+        {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = SkScalarToFixed(fDstToIndex.getScaleX());
+        }
+
+        if (SkFixedNearlyZero(dx))  // we're a vertical gradient, so no change in a span
+        {
+            unsigned fi = proc(fx);
+            SkASSERT(fi <= 0xFFFF);
+            sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count);
+        }
+        else if (proc == clamp_tileproc)
+        {
+#if 0
+            if (no_need_for_clamp(fx, dx, count))
+            {
+                unsigned fi;
+                while ((count -= 4) >= 0)
+                {
+                    fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
+                    fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
+                    fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
+                    fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi];
+                }
+                SkASSERT(count <= -1 && count >= -4);
+                count += 4;
+                while (--count >= 0)
+                {
+                    fi = fx >> 8;
+                    SkASSERT(fi <= 0xFF);
+                    fx += dx;
+                    *dstC++ = cache[fi];
+                }
+            }
+            else
+#endif
+                do {
+                    unsigned fi = SkClampMax(fx >> 8, 0xFF);
+                    SkASSERT(fi <= 0xFF);
+                    fx += dx;
+                    *dstC++ = cache[fi];
+                } while (--count != 0);
+        }
+        else if (proc == mirror_tileproc)
+        {
+            do {
+                unsigned fi = mirror_8bits(fx >> 8);
+                SkASSERT(fi <= 0xFF);
+                fx += dx;
+                *dstC++ = cache[fi];
+            } while (--count != 0);
+        }
+        else
+        {
+            SkASSERT(proc == repeat_tileproc);
+            do {
+                unsigned fi = repeat_8bits(fx >> 8);
+                SkASSERT(fi <= 0xFF);
+                fx += dx;
+                *dstC++ = cache[fi];
+            } while (--count != 0);
+        }
+    }
+    else
+    {
+        SkScalar    dstX = SkIntToScalar(x);
+        SkScalar    dstY = SkIntToScalar(y);
+        do {
+            dstProc(fDstToIndex, dstX, dstY, &srcPt);
+            unsigned fi = proc(SkScalarToFixed(srcPt.fX));
+            SkASSERT(fi <= 0xFFFF);
+            *dstC++ = cache[fi >> (16 - kCache32Bits)];
+            dstX += SK_Scalar1;
+        } while (--count != 0);
+    }
+}
+
+bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
+                                TileMode xy[]) {
+    if (bitmap) {
+        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
+        bitmap->allocPixels();  // share with shader???
+        memcpy(bitmap->getPixels(), this->getCache32(), kCache32Count * 4);
+    }
+    if (matrix) {
+        matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1);
+        matrix->preConcat(fPtsToUnit);
+    }
+    if (xy) {
+        xy[0] = fTileMode;
+        xy[1] = kClamp_TileMode;
+    }
+    return true;
+}
+
+#ifdef TEST_GRADIENT_DITHER
+static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int count)
+{
+    if ((unsigned)dst & 2)
+    {
+        *dst++ = value;
+        count -= 1;
+        SkTSwap(value, other);
+    }
+
+    sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
+    
+    if (count & 1)
+        dst[count - 1] = value;
+}
+#endif
+
+void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
+{
+    SkASSERT(count > 0);
+
+    SkPoint             srcPt;
+    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+    TileProc            proc = fTileProc;
+    const uint16_t*     cache = this->getCache16();
+#ifdef TEST_GRADIENT_DITHER
+    int                 toggle = ((x ^ y) & 1) << kCache16Bits;
+#endif
+
+    if (fDstToIndexClass != kPerspective_MatrixClass)
+    {
+        dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
+        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass)
+        {
+            SkFixed dxStorage[1];
+            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
+            dx = dxStorage[0];
+        }
+        else
+        {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = SkScalarToFixed(fDstToIndex.getScaleX());
+        }
+
+        if (SkFixedNearlyZero(dx))  // we're a vertical gradient, so no change in a span
+        {
+            unsigned fi = proc(fx) >> 10;
+            SkASSERT(fi <= 63);
+#ifdef TEST_GRADIENT_DITHER
+            dither_memset16(dstC, cache[toggle + fi], cache[(toggle ^ (1 << kCache16Bits)) + fi], count);
+#else
+            sk_memset16(dstC, cache[fi], count);
+#endif
+        }
+        else if (proc == clamp_tileproc)
+        {
+            do {
+                unsigned fi = SkClampMax(fx >> 10, 63);
+                SkASSERT(fi <= 63);
+                fx += dx;
+#ifdef TEST_GRADIENT_DITHER
+                *dstC++ = cache[toggle + fi];
+                toggle ^= (1 << kCache16Bits);
+#else
+                *dstC++ = cache[fi];
+#endif
+            } while (--count != 0);
+        }
+        else if (proc == mirror_tileproc)
+        {
+            do {
+                unsigned fi = mirror_6bits(fx >> 10);
+                SkASSERT(fi <= 0x3F);
+                fx += dx;
+#ifdef TEST_GRADIENT_DITHER
+                *dstC++ = cache[toggle + fi];
+                toggle ^= (1 << kCache16Bits);
+#else
+                *dstC++ = cache[fi];
+#endif
+            } while (--count != 0);
+        }
+        else
+        {
+            SkASSERT(proc == repeat_tileproc);
+            do {
+                unsigned fi = repeat_6bits(fx >> 10);
+                SkASSERT(fi <= 0x3F);
+                fx += dx;
+#ifdef TEST_GRADIENT_DITHER
+                *dstC++ = cache[toggle + fi];
+                toggle ^= (1 << kCache16Bits);
+#else
+                *dstC++ = cache[fi];
+#endif
+            } while (--count != 0);
+        }
+    }
+    else
+    {
+        SkScalar    dstX = SkIntToScalar(x);
+        SkScalar    dstY = SkIntToScalar(y);
+        do {
+            dstProc(fDstToIndex, dstX, dstY, &srcPt);
+            unsigned fi = proc(SkScalarToFixed(srcPt.fX));
+            SkASSERT(fi <= 0xFFFF);
+
+            int index = fi >> (16 - kCache16Bits);
+#ifdef TEST_GRADIENT_DITHER
+            *dstC++ = cache[toggle + index];
+            toggle ^= (1 << kCache16Bits);
+#else
+            *dstC++ = cache[index];
+#endif
+
+            dstX += SK_Scalar1;
+        } while (--count != 0);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define kSQRT_TABLE_BITS    11
+#define kSQRT_TABLE_SIZE    (1 << kSQRT_TABLE_BITS)
+
+#include "SkRadialGradient_Table.h"
+
+#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
+
+#include <stdio.h>
+
+void SkRadialGradient_BuildTable()
+{
+    // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
+
+    FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
+    SkASSERT(file);
+    ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
+
+    for (int i = 0; i < kSQRT_TABLE_SIZE; i++)
+    {
+        if ((i & 15) == 0)
+            ::fprintf(file, "\t");
+
+        uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
+
+        ::fprintf(file, "0x%02X", value);
+        if (i < kSQRT_TABLE_SIZE-1)
+            ::fprintf(file, ", ");
+        if ((i & 15) == 15)
+            ::fprintf(file, "\n");
+    }
+    ::fprintf(file, "};\n");
+    ::fclose(file);
+}
+
+#endif
+
+
+static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius, SkMatrix* matrix)
+{
+    SkScalar    inv = SkScalarInvert(radius);
+
+    matrix->setTranslate(-center.fX, -center.fY);
+    matrix->postScale(inv, inv);
+}
+
+class Radial_Gradient : public Gradient_Shader {
+public:
+    Radial_Gradient(const SkPoint& center, SkScalar radius,
+                    const SkColor colors[], const SkScalar pos[], int colorCount,
+                    SkShader::TileMode mode, SkUnitMapper* mapper)
+        : Gradient_Shader(colors, pos, colorCount, mode, mapper)
+    {
+        // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
+        SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
+
+        rad_to_unit_matrix(center, radius, &fPtsToUnit);
+    }
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
+    {
+        SkASSERT(count > 0);
+
+        SkPoint             srcPt;
+        SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+        TileProc            proc = fTileProc;
+        const SkPMColor*    cache = this->getCache32();
+
+        if (fDstToIndexClass != kPerspective_MatrixClass)
+        {
+            dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
+            SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+            SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
+
+            if (fDstToIndexClass == kFixedStepInX_MatrixClass)
+            {
+                SkFixed storage[2];
+                (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
+                dx = storage[0];
+                dy = storage[1];
+            }
+            else
+            {
+                SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+                dx = SkScalarToFixed(fDstToIndex.getScaleX());
+                dy = SkScalarToFixed(fDstToIndex.getSkewY());
+            }
+
+            if (proc == clamp_tileproc)
+            {
+                const uint8_t* sqrt_table = gSqrt8Table;
+                fx >>= 1;
+                dx >>= 1;
+                fy >>= 1;
+                dy >>= 1;
+                do {
+                    unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
+                    unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
+                    fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
+                    fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
+                    *dstC++ = cache[sqrt_table[fi] >> (8 - kCache32Bits)];
+                    fx += dx;
+                    fy += dy;
+                } while (--count != 0);
+            }
+            else if (proc == mirror_tileproc)
+            {
+                do {
+                    SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
+                    unsigned fi = mirror_tileproc(dist);
+                    SkASSERT(fi <= 0xFFFF);
+                    *dstC++ = cache[fi >> (16 - kCache32Bits)];
+                    fx += dx;
+                    fy += dy;
+                } while (--count != 0);
+            }
+            else
+            {
+                SkASSERT(proc == repeat_tileproc);
+                do {
+                    SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
+                    unsigned fi = repeat_tileproc(dist);
+                    SkASSERT(fi <= 0xFFFF);
+                    *dstC++ = cache[fi >> (16 - kCache32Bits)];
+                    fx += dx;
+                    fy += dy;
+                } while (--count != 0);
+            }
+        }
+        else    // perspective case
+        {
+            SkScalar dstX = SkIntToScalar(x);
+            SkScalar dstY = SkIntToScalar(y);
+            do {
+                dstProc(fDstToIndex, dstX, dstY, &srcPt);
+                unsigned fi = proc(SkScalarToFixed(srcPt.length()));
+                SkASSERT(fi <= 0xFFFF);
+                *dstC++ = cache[fi >> (16 - kCache32Bits)];
+                dstX += SK_Scalar1;
+            } while (--count != 0);
+        }
+    }
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count)
+    {
+        SkASSERT(count > 0);
+
+        SkPoint             srcPt;
+        SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+        TileProc            proc = fTileProc;
+        const uint16_t*     cache = this->getCache16();
+#ifdef TEST_GRADIENT_DITHER
+        int                 toggle = ((x ^ y) & 1) << kCache16Bits;
+#endif
+
+        if (fDstToIndexClass != kPerspective_MatrixClass)
+        {
+            dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt);
+            SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+            SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
+
+            if (fDstToIndexClass == kFixedStepInX_MatrixClass)
+            {
+                SkFixed storage[2];
+                (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &storage[0], &storage[1]);
+                dx = storage[0];
+                dy = storage[1];
+            }
+            else
+            {
+                SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+                dx = SkScalarToFixed(fDstToIndex.getScaleX());
+                dy = SkScalarToFixed(fDstToIndex.getSkewY());
+            }
+
+            if (proc == clamp_tileproc)
+            {
+                const uint8_t* sqrt_table = gSqrt8Table;
+
+                /* knock these down so we can pin against +- 0x7FFF, which is an immediate load,
+                    rather than 0xFFFF which is slower. This is a compromise, since it reduces our
+                    precision, but that appears to be visually OK. If we decide this is OK for
+                    all of our cases, we could (it seems) put this scale-down into fDstToIndex,
+                    to avoid having to do these extra shifts each time.
+                */
+                fx >>= 1;
+                dx >>= 1;
+                fy >>= 1;
+                dy >>= 1;
+                if (dy == 0)    // might perform this check for the other modes, but the win will be a smaller % of the total
+                {
+                    fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
+                    fy *= fy;
+                    do {
+                        unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
+                        unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
+                        fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
+                        fx += dx;
+#ifdef TEST_GRADIENT_DITHER
+                        *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
+                        toggle ^= (1 << kCache16Bits);
+#else
+                        *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
+#endif
+                    } while (--count != 0);
+                }
+                else
+                {
+                    do {
+                        unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
+                        unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
+                        fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
+                        fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
+                        fx += dx;
+                        fy += dy;
+#ifdef TEST_GRADIENT_DITHER
+                        *dstC++ = cache[toggle + (sqrt_table[fi] >> (8 - kCache16Bits))];
+                        toggle ^= (1 << kCache16Bits);
+#else
+                        *dstC++ = cache[sqrt_table[fi] >> (8 - kCache16Bits)];
+#endif
+                    } while (--count != 0);
+                }
+            }
+            else if (proc == mirror_tileproc)
+            {
+                do {
+                    SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
+                    unsigned fi = mirror_tileproc(dist);
+                    SkASSERT(fi <= 0xFFFF);
+                    fx += dx;
+                    fy += dy;
+#ifdef TEST_GRADIENT_DITHER
+                    *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
+                    toggle ^= (1 << kCache16Bits);
+#else
+                    *dstC++ = cache[fi >> (16 - kCache16Bits)];
+#endif
+                } while (--count != 0);
+            }
+            else
+            {
+                SkASSERT(proc == repeat_tileproc);
+                do {
+                    SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
+                    unsigned fi = repeat_tileproc(dist);
+                    SkASSERT(fi <= 0xFFFF);
+                    fx += dx;
+                    fy += dy;
+#ifdef TEST_GRADIENT_DITHER
+                    *dstC++ = cache[toggle + (fi >> (16 - kCache16Bits))];
+                    toggle ^= (1 << kCache16Bits);
+#else
+                    *dstC++ = cache[fi >> (16 - kCache16Bits)];
+#endif
+                } while (--count != 0);
+            }
+        }
+        else    // perspective case
+        {
+            SkScalar dstX = SkIntToScalar(x);
+            SkScalar dstY = SkIntToScalar(y);
+            do {
+                dstProc(fDstToIndex, dstX, dstY, &srcPt);
+                unsigned fi = proc(SkScalarToFixed(srcPt.length()));
+                SkASSERT(fi <= 0xFFFF);
+
+                int index = fi >> (16 - kCache16Bits);
+#ifdef TEST_GRADIENT_DITHER
+                *dstC++ = cache[toggle + index];
+                toggle ^= (1 << kCache16Bits);
+#else
+                *dstC++ = cache[index];
+#endif
+
+                dstX += SK_Scalar1;
+            } while (--count != 0);
+        }
+    }
+
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { 
+        return SkNEW_ARGS(Radial_Gradient, (buffer));
+    }
+
+protected:
+    Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {};
+    virtual Factory getFactory() { return CreateProc; }
+
+private:
+    typedef Gradient_Shader INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Sweep_Gradient : public Gradient_Shader {
+public:
+    Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
+                   const SkScalar pos[], int count, SkUnitMapper* mapper)
+    : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper)
+    {
+        fPtsToUnit.setTranslate(-cx, -cy);
+    }
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
+    
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+        return SkNEW_ARGS(Sweep_Gradient, (buffer));
+    }
+
+protected:
+    Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}
+
+    virtual Factory getFactory() { return CreateProc; }
+
+private:
+    typedef Gradient_Shader INHERITED;
+};
+
+#ifdef COMPUTE_SWEEP_TABLE
+#define PI  3.14159265
+static bool gSweepTableReady;
+static uint8_t gSweepTable[65];
+
+/*  Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
+    We scale the results to [0..32]
+*/
+static const uint8_t* build_sweep_table()
+{
+    if (!gSweepTableReady)
+    {
+        const int N = 65;
+        const double DENOM = N - 1;
+        
+        for (int i = 0; i < N; i++)
+        {
+            double arg = i / DENOM;
+            double v = atan(arg);
+            int iv = (int)round(v * DENOM * 2 / PI);
+//            printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
+            printf("%d, ", iv);
+            gSweepTable[i] = iv;
+        }
+        gSweepTableReady = true;
+    }
+    return gSweepTable;
+}
+#else
+static const uint8_t gSweepTable[] = {
+    0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
+    10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
+    19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
+    26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
+    32
+};
+static const uint8_t* build_sweep_table() { return gSweepTable; }
+#endif
+
+// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
+// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
+// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
+
+//unsigned div_64(int numer, int denom);
+static unsigned div_64(int numer, int denom)
+{
+    SkASSERT(numer <= denom);
+    SkASSERT(numer > 0);
+    SkASSERT(denom > 0);
+        
+    int nbits = SkCLZ(numer);
+    int dbits = SkCLZ(denom);
+    int bits = 6 - nbits + dbits;
+    SkASSERT(bits <= 6);
+    
+    if (bits < 0)   // detect underflow
+        return 0;
+
+    denom <<= dbits - 1;
+    numer <<= nbits - 1;
+
+    unsigned result = 0;
+
+    // do the first one
+    if ((numer -= denom) >= 0)
+        result = 1;
+    else
+        numer += denom;
+    
+    // Now fall into our switch statement if there are more bits to compute
+    if (bits > 0)
+    {
+        // make room for the rest of the answer bits
+        result <<= bits;
+        switch (bits) {
+        case 6:
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 32;
+            else
+                numer += denom;
+        case 5:
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 16;
+            else
+                numer += denom;
+        case 4:
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 8;
+            else
+                numer += denom;
+        case 3:
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 4;
+            else
+                numer += denom;
+        case 2:
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 2;
+            else
+                numer += denom;
+        case 1:
+        default:    // not strictly need, but makes GCC make better ARM code
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 1;
+            else
+                numer += denom;
+        }
+    }
+    return result;
+}
+
+// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
+static unsigned atan_0_90(SkFixed y, SkFixed x)
+{
+#ifdef SK_DEBUG
+    {
+        static bool gOnce;
+        if (!gOnce)
+        {
+            gOnce = true;
+            SkASSERT(div_64(55, 55) == 64);
+            SkASSERT(div_64(128, 256) == 32);
+            SkASSERT(div_64(2326528, 4685824) == 31);
+            SkASSERT(div_64(753664, 5210112) == 9);
+            SkASSERT(div_64(229376, 4882432) == 3);
+            SkASSERT(div_64(2, 64) == 2);
+            SkASSERT(div_64(1, 64) == 1);
+            // test that we handle underflow correctly
+            SkASSERT(div_64(12345, 0x54321234) == 0);
+        }
+    }
+#endif
+
+    SkASSERT(y > 0 && x > 0);
+    const uint8_t* table = build_sweep_table();
+
+    unsigned result;
+    bool swap = (x < y);
+    if (swap)
+    {
+        // first part of the atan(v) = PI/2 - atan(1/v) identity
+        // since our div_64 and table want v <= 1, where v = y/x
+        SkTSwap<SkFixed>(x, y);
+    }
+
+    result = div_64(y, x);
+    
+#ifdef SK_DEBUG
+    {
+        unsigned result2 = SkDivBits(y, x, 6);
+        SkASSERT(result2 == result ||
+                 (result == 1 && result2 == 0));
+    }
+#endif
+
+    SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
+    result = table[result];
+
+    if (swap)
+    {
+        // complete the atan(v) = PI/2 - atan(1/v) identity
+        result = 64 - result;
+        // pin to 63
+        result -= result >> 6;
+    }
+
+    SkASSERT(result <= 63);
+    return result;
+}
+
+//  returns angle in a circle [0..2PI) -> [0..255]
+static unsigned SkATan2_255(SkFixed y, SkFixed x)
+{
+    if (x == 0)
+    {
+        if (y == 0)
+            return 0;
+        return y < 0 ? 192 : 64;
+    }
+    if (y == 0)
+        return x < 0 ? 128 : 0;
+    
+    /*  Find the right quadrant for x,y
+        Since atan_0_90 only handles the first quadrant, we rotate x,y
+        appropriately before calling it, and then add the right amount
+        to account for the real quadrant.
+        quadrant 0 : add 0                  | x > 0 && y > 0
+        quadrant 1 : add 64 (90 degrees)    | x < 0 && y > 0
+        quadrant 2 : add 128 (180 degrees)  | x < 0 && y < 0
+        quadrant 3 : add 192 (270 degrees)  | x > 0 && y < 0
+        
+        map x<0 to (1 << 6)
+        map y<0 to (3 << 6)
+        add = map_x ^ map_y
+    */
+    int xsign = x >> 31;
+    int ysign = y >> 31;
+    int add = ((-xsign) ^ (ysign & 3)) << 6;
+
+#ifdef SK_DEBUG
+    if (0 == add)
+        SkASSERT(x > 0 && y > 0);
+    else if (64 == add)
+        SkASSERT(x < 0 && y > 0);
+    else if (128 == add)
+        SkASSERT(x < 0 && y < 0);
+    else if (192 == add)
+        SkASSERT(x > 0 && y < 0);
+    else
+        SkASSERT(!"bad value for add");
+#endif
+    
+    /*  This ^ trick makes x, y positive, and the swap<> handles quadrants
+        where we need to rotate x,y by 90 or -90
+    */
+    x = (x ^ xsign) - xsign;
+    y = (y ^ ysign) - ysign;
+    if (add & 64)               // quads 1 or 3 need to swap x,y
+        SkTSwap<SkFixed>(x, y);
+
+    unsigned result = add + atan_0_90(y, x);
+    SkASSERT(result < 256);
+    return result;
+}
+
+void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count)
+{
+    SkMatrix::MapXYProc proc = fDstToIndexProc;
+    const SkMatrix&     matrix = fDstToIndex;
+    const SkPMColor*    cache = this->getCache32();
+    SkPoint             srcPt;
+    
+    if (fDstToIndexClass != kPerspective_MatrixClass)
+    {
+        proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                     SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+        SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
+        
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass)
+        {
+            SkFixed storage[2];
+            (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
+                                      &storage[0], &storage[1]);
+            dx = storage[0];
+            dy = storage[1];
+        }
+        else
+        {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = SkScalarToFixed(matrix.getScaleX());
+            dy = SkScalarToFixed(matrix.getSkewY());
+        }
+        
+        for (; count > 0; --count)
+        {
+            *dstC++ = cache[SkATan2_255(fy, fx)];
+            fx += dx;
+            fy += dy;
+        }
+    }
+    else    // perspective case
+    {
+        for (int stop = x + count; x < stop; x++)
+        {
+            proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+            
+            int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
+                                    SkScalarToFixed(srcPt.fX));
+            *dstC++ = cache[index];
+        }
+    }
+}
+
+void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count)
+{
+    SkMatrix::MapXYProc proc = fDstToIndexProc;
+    const SkMatrix&     matrix = fDstToIndex;
+    const uint16_t*     cache = this->getCache16();
+    int                 toggle = ((x ^ y) & 1) << kCache16Bits;
+    SkPoint             srcPt;
+
+    if (fDstToIndexClass != kPerspective_MatrixClass)
+    {
+        proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                     SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+        SkFixed dy, fy = SkScalarToFixed(srcPt.fY);
+        
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass)
+        {
+            SkFixed storage[2];
+            (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
+                                      &storage[0], &storage[1]);
+            dx = storage[0];
+            dy = storage[1];
+        }
+        else
+        {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = SkScalarToFixed(matrix.getScaleX());
+            dy = SkScalarToFixed(matrix.getSkewY());
+        }
+        
+        for (; count > 0; --count)
+        {
+            int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
+            *dstC++ = cache[toggle + index];
+            toggle ^= (1 << kCache16Bits);
+            fx += dx;
+            fy += dy;
+        }
+    }
+    else    // perspective case
+    {
+        for (int stop = x + count; x < stop; x++)
+        {
+            proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+            
+            int index = SkATan2_255(SkScalarToFixed(srcPt.fY),
+                                    SkScalarToFixed(srcPt.fX));
+            index >>= (8 - kCache16Bits);
+            *dstC++ = cache[toggle + index];
+            toggle ^= (1 << kCache16Bits);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+// assumes colors is SkColor* and pos is SkScalar*
+#define EXPAND_1_COLOR(count)               \
+    SkColor tmp[2];                         \
+    do {                                    \
+        if (1 == count) {                   \
+            tmp[0] = tmp[1] = colors[0];    \
+            colors = tmp;                   \
+            pos = NULL;                     \
+            count = 2;                      \
+        }                                   \
+    } while (0)
+
+SkShader* SkGradientShader::CreateLinear(   const SkPoint pts[2],
+                                            const SkColor colors[], const SkScalar pos[], int colorCount,
+                                            SkShader::TileMode mode, SkUnitMapper* mapper)
+{
+    if (NULL == pts || NULL == colors || colorCount < 1) {
+        return NULL;
+    }
+    EXPAND_1_COLOR(colorCount);
+
+    return SkNEW_ARGS(Linear_Gradient, (pts, colors, pos, colorCount, mode, mapper));
+}
+
+SkShader* SkGradientShader::CreateRadial(   const SkPoint& center, SkScalar radius,
+                                            const SkColor colors[], const SkScalar pos[], int colorCount,
+                                            SkShader::TileMode mode, SkUnitMapper* mapper)
+{
+    if (radius <= 0 || NULL == colors || colorCount < 1) {
+        return NULL;
+    }
+    EXPAND_1_COLOR(colorCount);
+
+    return SkNEW_ARGS(Radial_Gradient, (center, radius, colors, pos, colorCount, mode, mapper));
+}
+
+SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
+                                        const SkColor colors[],
+                                        const SkScalar pos[],
+                                        int count, SkUnitMapper* mapper)
+{
+    if (NULL == colors || count < 1) {
+        return NULL;
+    }
+    EXPAND_1_COLOR(count);
+
+    return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
+}
+
+static SkFlattenable::Registrar gLinearGradientReg("Linear_Gradient",
+                                                   Linear_Gradient::CreateProc);
+
+static SkFlattenable::Registrar gRadialGradientReg("Radial_Gradient",
+                                                   Radial_Gradient::CreateProc);
+
+static SkFlattenable::Registrar gSweepGradientReg("Sweep_Gradient",
+                                                   Sweep_Gradient::CreateProc);
+
diff --git a/src/effects/SkKernel33MaskFilter.cpp b/src/effects/SkKernel33MaskFilter.cpp
new file mode 100644
index 0000000..a30ea4a
--- /dev/null
+++ b/src/effects/SkKernel33MaskFilter.cpp
@@ -0,0 +1,122 @@
+#include "SkKernel33MaskFilter.h"
+#include "SkColorPriv.h"
+
+SkMask::Format SkKernel33ProcMaskFilter::getFormat()
+{
+    return SkMask::kA8_Format;
+}
+
+bool SkKernel33ProcMaskFilter::filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, SkIPoint* margin)
+{
+    // margin???
+    dst->fImage = NULL;
+    dst->fBounds = src.fBounds;
+    dst->fBounds.inset(-1, -1);
+    dst->fFormat = SkMask::kA8_Format;
+    
+    if (NULL == src.fImage)
+        return true;
+    
+    dst->fRowBytes = dst->fBounds.width();
+    size_t size = dst->computeImageSize();
+    dst->fImage = SkMask::AllocImage(size);
+    
+    const int h = src.fBounds.height();
+    const int w = src.fBounds.width();
+    const int srcRB = src.fRowBytes;
+    const uint8_t* srcImage = src.fImage;
+    uint8_t* dstImage = dst->fImage;
+
+    uint8_t* srcRows[3];
+    uint8_t storage[3][3];
+    
+    srcRows[0] = storage[0];
+    srcRows[1] = storage[1];
+    srcRows[2] = storage[2];
+
+    unsigned scale = fPercent256;
+    
+    for (int y = -1; y <= h; y++)
+    {
+        uint8_t* dstRow = dstImage;
+        for (int x = -1; x <= w; x++)
+        {
+            memset(storage, 0, sizeof(storage));
+            uint8_t* storagePtr = &storage[0][0];
+
+            for (int ky = y - 1; ky <= y + 1; ky++)
+            {
+                const uint8_t* srcRow = srcImage + ky * srcRB;  // may be out-of-range
+                for (int kx = x - 1; kx <= x + 1; kx++)
+                {
+                    if ((unsigned)ky < (unsigned)h && (unsigned)kx < (unsigned)w)
+                        *storagePtr = srcRow[kx];
+                    storagePtr++;
+                }
+            }            
+            int value = this->computeValue(srcRows);
+            
+            if (scale < 256)
+                value = SkAlphaBlend(value, srcRows[1][1], scale);
+            *dstRow++ = SkToU8(value);
+        }
+        dstImage += dst->fRowBytes;
+    }
+    return true;
+}
+
+void SkKernel33ProcMaskFilter::flatten(SkFlattenableWriteBuffer& wb)
+{
+    this->INHERITED::flatten(wb);
+    wb.write32(fPercent256);
+}
+
+SkKernel33ProcMaskFilter::SkKernel33ProcMaskFilter(SkFlattenableReadBuffer& rb)
+    : SkMaskFilter(rb)
+{
+    fPercent256 = rb.readS32();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint8_t SkKernel33MaskFilter::computeValue(uint8_t* const* srcRows)
+{
+    int value = 0;
+
+    for (int i = 0; i < 3; i++)
+        for (int j = 0; j < 3; j++)
+            value += fKernel[i][j] * srcRows[i][j];
+    
+    value >>= fShift;
+
+    if (value < 0)
+        value = 0;
+    else if (value > 255)
+        value = 255;
+    return (uint8_t)value;
+}
+
+void SkKernel33MaskFilter::flatten(SkFlattenableWriteBuffer& wb)
+{
+    this->INHERITED::flatten(wb);
+    wb.writeMul4(fKernel, 9 * sizeof(int));
+    wb.write32(fShift);
+}
+
+SkFlattenable::Factory SkKernel33MaskFilter::getFactory()
+{
+    return Create;
+}
+
+SkFlattenable* SkKernel33MaskFilter::Create(SkFlattenableReadBuffer& rb)
+{
+    return new SkKernel33MaskFilter(rb);
+}
+
+SkKernel33MaskFilter::SkKernel33MaskFilter(SkFlattenableReadBuffer& rb)
+    : SkKernel33ProcMaskFilter(rb)
+{
+    rb.read(fKernel, 9 * sizeof(int));
+    fShift = rb.readS32();
+}
+
diff --git a/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp
new file mode 100644
index 0000000..f2d8ba8
--- /dev/null
+++ b/src/effects/SkLayerDrawLooper.cpp
@@ -0,0 +1,130 @@
+#include "SkCanvas.h"
+#include "SkLayerDrawLooper.h"
+#include "SkPaint.h"
+
+SkLayerDrawLooper::SkLayerDrawLooper() {
+    fRecs = NULL;
+    fCount = 0;
+}
+
+SkLayerDrawLooper::~SkLayerDrawLooper() {
+    Rec* rec = fRecs;
+    while (rec) {
+        Rec* next = rec->fNext;
+        SkDELETE(rec);
+        rec = next;
+    }
+}
+    
+SkPaint* SkLayerDrawLooper::addLayer(SkScalar dx, SkScalar dy) {
+    fCount += 1;
+
+    Rec* rec = SkNEW(Rec);
+    rec->fNext = fRecs;
+    rec->fOffset.set(dx, dy);
+    fRecs = rec;
+
+    return &rec->fPaint;
+}
+
+void SkLayerDrawLooper::init(SkCanvas* canvas, SkPaint* paint) {
+    fIter.fSavedPaint = *paint;
+    fIter.fPaint = paint;
+    fIter.fCanvas = canvas;
+    fIter.fRec = fRecs;
+    canvas->save(SkCanvas::kMatrix_SaveFlag);
+}
+
+bool SkLayerDrawLooper::next() {
+    Rec* rec = fIter.fRec;
+    if (rec) {
+        *fIter.fPaint = rec->fPaint;
+        fIter.fCanvas->restore();
+        fIter.fCanvas->save(SkCanvas::kMatrix_SaveFlag);
+        fIter.fCanvas->translate(rec->fOffset.fX, rec->fOffset.fY);
+
+        fIter.fRec = rec->fNext;
+        return true;
+    }
+    return false;
+}
+
+void SkLayerDrawLooper::restore() {
+    fIter.fCanvas->restore();
+    *fIter.fPaint = fIter.fSavedPaint;
+}
+
+SkLayerDrawLooper::Rec* SkLayerDrawLooper::Rec::Reverse(Rec* head) {
+    Rec* rec = head;
+    Rec* prev = NULL;
+    while (rec) {
+        Rec* next = rec->fNext;
+        rec->fNext = prev;
+        prev = rec;
+        rec = next;
+    }
+    return prev;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkLayerDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) {
+    this->INHERITED::flatten(buffer);
+
+#ifdef SK_DEBUG
+    {
+        Rec* rec = fRecs;
+        int count = 0;
+        while (rec) {
+            rec = rec->fNext;
+            count += 1;
+        }
+        SkASSERT(count == fCount);
+    }
+#endif
+
+    buffer.writeInt(fCount);
+    
+    Rec* rec = fRecs;
+    for (int i = 0; i < fCount; i++) {
+        buffer.writeScalar(rec->fOffset.fX);
+        buffer.writeScalar(rec->fOffset.fY);
+        rec->fPaint.flatten(buffer);
+        rec = rec->fNext;
+    }
+}
+
+SkLayerDrawLooper::SkLayerDrawLooper(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {
+    fRecs = NULL;
+    fCount = 0;
+    
+    int count = buffer.readInt();
+
+    for (int i = 0; i < count; i++) {
+        SkScalar dx = buffer.readScalar();
+        SkScalar dy = buffer.readScalar();
+        this->addLayer(dx, dy)->unflatten(buffer);
+    }
+    SkASSERT(count == fCount);
+
+    // we're in reverse order, so fix it now
+    fRecs = Rec::Reverse(fRecs);
+    
+#ifdef SK_DEBUG
+    {
+        Rec* rec = fRecs;
+        int n = 0;
+        while (rec) {
+            rec = rec->fNext;
+            n += 1;
+        }
+        SkASSERT(count == n);
+    }
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkFlattenable::Registrar gReg("SkLayerDrawLooper",
+                                     SkLayerDrawLooper::CreateProc);
diff --git a/src/effects/SkLayerRasterizer.cpp b/src/effects/SkLayerRasterizer.cpp
new file mode 100644
index 0000000..390e3a3
--- /dev/null
+++ b/src/effects/SkLayerRasterizer.cpp
@@ -0,0 +1,242 @@
+/* libs/graphics/effects/SkLayerRasterizer.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 "SkLayerRasterizer.h"
+#include "SkBuffer.h"
+#include "SkDraw.h"
+#include "SkMask.h"
+#include "SkMaskFilter.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkXfermode.h"
+#include <new>
+
+struct SkLayerRasterizer_Rec {
+    SkPaint     fPaint;
+    SkVector    fOffset;
+};
+
+SkLayerRasterizer::SkLayerRasterizer() : fLayers(sizeof(SkLayerRasterizer_Rec))
+{
+}
+
+SkLayerRasterizer::~SkLayerRasterizer()
+{
+    SkDeque::Iter           iter(fLayers);
+    SkLayerRasterizer_Rec*  rec;
+
+    while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL)
+        rec->fPaint.~SkPaint();
+}
+
+void SkLayerRasterizer::addLayer(const SkPaint& paint, SkScalar dx, SkScalar dy)
+{
+    SkLayerRasterizer_Rec* rec = (SkLayerRasterizer_Rec*)fLayers.push_back();
+
+    new (&rec->fPaint) SkPaint(paint);
+    rec->fOffset.set(dx, dy);
+}
+
+static bool compute_bounds(const SkDeque& layers, const SkPath& path, const SkMatrix& matrix,
+                           const SkIRect* clipBounds, SkIRect* bounds)
+{
+    SkDeque::Iter           iter(layers);
+    SkLayerRasterizer_Rec*  rec;
+
+    bounds->set(SK_MaxS32, SK_MaxS32, SK_MinS32, SK_MinS32);
+    
+    while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL)
+    {
+        const SkPaint&  paint = rec->fPaint;
+        SkPath          fillPath, devPath;
+        const SkPath*   p = &path;
+
+        if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style)
+        {
+            paint.getFillPath(path, &fillPath);
+            p = &fillPath;
+        }
+        if (p->isEmpty())
+            continue;
+
+        // apply the matrix and offset
+        {
+            SkMatrix m = matrix;
+            m.preTranslate(rec->fOffset.fX, rec->fOffset.fY);
+            p->transform(m, &devPath);
+        }
+
+        SkMask  mask;
+        if (!SkDraw::DrawToMask(devPath, clipBounds, paint.getMaskFilter(), &matrix,
+                                &mask, SkMask::kJustComputeBounds_CreateMode))
+            return false;
+
+        bounds->join(mask.fBounds);
+    }
+    return true;
+}
+
+bool SkLayerRasterizer::onRasterize(const SkPath& path, const SkMatrix& matrix,
+                                    const SkIRect* clipBounds,
+                                    SkMask* mask, SkMask::CreateMode mode)
+{
+    if (fLayers.empty())
+        return false;
+
+    if (SkMask::kJustRenderImage_CreateMode != mode)
+    {
+        if (!compute_bounds(fLayers, path, matrix, clipBounds, &mask->fBounds))
+            return false;
+    }
+
+    if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode)
+    {
+        mask->fFormat   = SkMask::kA8_Format;
+        mask->fRowBytes = SkToU16(mask->fBounds.width());
+        mask->fImage = SkMask::AllocImage(mask->computeImageSize());
+        memset(mask->fImage, 0, mask->computeImageSize());
+    }
+
+    if (SkMask::kJustComputeBounds_CreateMode != mode)
+    {    
+        SkBitmap device;
+        SkDraw   draw;
+        SkMatrix translatedMatrix;  // this translates us to our local pixels
+        SkMatrix drawMatrix;        // this translates the path by each layer's offset
+        SkRegion rectClip;
+        
+        rectClip.setRect(0, 0, mask->fBounds.width(), mask->fBounds.height());
+
+        translatedMatrix = matrix;
+        translatedMatrix.postTranslate(-SkIntToScalar(mask->fBounds.fLeft),
+                                       -SkIntToScalar(mask->fBounds.fTop));
+
+        device.setConfig(SkBitmap::kA8_Config, mask->fBounds.width(), mask->fBounds.height(), mask->fRowBytes);
+        device.setPixels(mask->fImage);
+
+        draw.fBitmap    = &device;
+        draw.fMatrix    = &drawMatrix;
+        draw.fClip      = &rectClip;
+        // we set the matrixproc in the loop, as the matrix changes each time (potentially)
+        draw.fBounder   = NULL;
+        
+        SkDeque::Iter           iter(fLayers);
+        SkLayerRasterizer_Rec*  rec;
+
+        while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) {
+            drawMatrix = translatedMatrix;
+            drawMatrix.preTranslate(rec->fOffset.fX, rec->fOffset.fY);
+            draw.drawPath(path, rec->fPaint);
+        }
+    }
+    return true;
+}
+
+/////////// Routines for flattening /////////////////
+
+static void paint_read(SkPaint* paint, SkFlattenableReadBuffer& buffer)
+{
+    paint->setAntiAlias(buffer.readBool());
+    paint->setStyle((SkPaint::Style)buffer.readU8());
+    paint->setAlpha(buffer.readU8());
+    
+    if (paint->getStyle() != SkPaint::kFill_Style)
+    {
+        paint->setStrokeWidth(buffer.readScalar());
+        paint->setStrokeMiter(buffer.readScalar());
+        paint->setStrokeCap((SkPaint::Cap)buffer.readU8());
+        paint->setStrokeJoin((SkPaint::Join)buffer.readU8());
+    }
+
+    paint->setMaskFilter((SkMaskFilter*)buffer.readFlattenable())->safeUnref();
+    paint->setPathEffect((SkPathEffect*)buffer.readFlattenable())->safeUnref();
+    paint->setRasterizer((SkRasterizer*)buffer.readFlattenable())->safeUnref();
+    paint->setXfermode((SkXfermode*)buffer.readFlattenable())->safeUnref();
+}
+
+static void paint_write(const SkPaint& paint, SkFlattenableWriteBuffer& buffer)
+{
+    buffer.writeBool(paint.isAntiAlias());
+    buffer.write8(paint.getStyle());
+    buffer.write8(paint.getAlpha());
+    
+    if (paint.getStyle() != SkPaint::kFill_Style)
+    {
+        buffer.writeScalar(paint.getStrokeWidth());
+        buffer.writeScalar(paint.getStrokeMiter());
+        buffer.write8(paint.getStrokeCap());
+        buffer.write8(paint.getStrokeJoin());
+    }
+    
+    buffer.writeFlattenable(paint.getMaskFilter());
+    buffer.writeFlattenable(paint.getPathEffect());
+    buffer.writeFlattenable(paint.getRasterizer());
+    buffer.writeFlattenable(paint.getXfermode());
+}
+
+SkLayerRasterizer::SkLayerRasterizer(SkFlattenableReadBuffer& buffer)
+    : SkRasterizer(buffer), fLayers(sizeof(SkLayerRasterizer_Rec))
+{
+    int count = buffer.readS32();
+    
+    for (int i = 0; i < count; i++)
+    {
+        SkLayerRasterizer_Rec* rec = (SkLayerRasterizer_Rec*)fLayers.push_back();
+    
+#if 0
+        new (&rec->fPaint) SkPaint(buffer);
+#else
+        new (&rec->fPaint) SkPaint;
+        paint_read(&rec->fPaint, buffer);
+#endif
+        rec->fOffset.fX = buffer.readScalar();
+        rec->fOffset.fY = buffer.readScalar();
+    }
+}
+
+void SkLayerRasterizer::flatten(SkFlattenableWriteBuffer& buffer)
+{
+    this->INHERITED::flatten(buffer);
+
+    buffer.write32(fLayers.count());
+
+    SkDeque::Iter                   iter(fLayers);
+    const SkLayerRasterizer_Rec*    rec;
+
+    while ((rec = (const SkLayerRasterizer_Rec*)iter.next()) != NULL)
+    {
+#if 0
+        rec->fPaint.flatten(buffer);
+#else
+        paint_write(rec->fPaint, buffer);
+#endif
+        buffer.writeScalar(rec->fOffset.fX);
+        buffer.writeScalar(rec->fOffset.fY);
+    }
+}
+
+SkFlattenable* SkLayerRasterizer::CreateProc(SkFlattenableReadBuffer& buffer)
+{
+    return SkNEW_ARGS(SkLayerRasterizer, (buffer));
+}
+
+SkFlattenable::Factory SkLayerRasterizer::getFactory()
+{
+    return CreateProc;
+}
+
diff --git a/src/effects/SkPaintFlagsDrawFilter.cpp b/src/effects/SkPaintFlagsDrawFilter.cpp
new file mode 100644
index 0000000..ed2df88
--- /dev/null
+++ b/src/effects/SkPaintFlagsDrawFilter.cpp
@@ -0,0 +1,22 @@
+#include "SkPaintFlagsDrawFilter.h"
+#include "SkPaint.h"
+
+SkPaintFlagsDrawFilter::SkPaintFlagsDrawFilter(uint32_t clearFlags,
+                                               uint32_t setFlags)
+{
+    fClearFlags = SkToU16(clearFlags & SkPaint::kAllFlags);
+    fSetFlags = SkToU16(setFlags & SkPaint::kAllFlags);
+}
+
+bool SkPaintFlagsDrawFilter::filter(SkCanvas*, SkPaint* paint, Type)
+{
+    fPrevFlags = paint->getFlags();
+    paint->setFlags((fPrevFlags & ~fClearFlags) | fSetFlags);
+    return true;
+}
+
+void SkPaintFlagsDrawFilter::restore(SkCanvas*, SkPaint* paint, Type)
+{
+    paint->setFlags(fPrevFlags);
+}
+    
diff --git a/src/effects/SkPixelXorXfermode.cpp b/src/effects/SkPixelXorXfermode.cpp
new file mode 100644
index 0000000..a5599e2
--- /dev/null
+++ b/src/effects/SkPixelXorXfermode.cpp
@@ -0,0 +1,36 @@
+#include "SkPixelXorXfermode.h"
+#include "SkColorPriv.h"
+
+// we always return an opaque color, 'cause I don't know what to do with
+// the alpha-component and still return a valid premultiplied color.
+SkPMColor SkPixelXorXfermode::xferColor(SkPMColor src, SkPMColor dst)
+{
+    SkPMColor res = src ^ dst ^ fOpColor;
+    res |= (SK_A32_MASK << SK_A32_SHIFT);   // force it to be opaque
+    return res;
+}
+
+void SkPixelXorXfermode::flatten(SkFlattenableWriteBuffer& wb)
+{
+    this->INHERITED::flatten(wb);
+    wb.write32(fOpColor);
+}
+
+SkPixelXorXfermode::SkPixelXorXfermode(SkFlattenableReadBuffer& rb)
+    : SkXfermode(rb)
+{
+    fOpColor = rb.readU32();
+}
+
+SkFlattenable::Factory SkPixelXorXfermode::getFactory()
+{
+    return Create;
+}
+
+SkFlattenable* SkPixelXorXfermode::Create(SkFlattenableReadBuffer& rb)
+{
+    return SkNEW_ARGS(SkPixelXorXfermode, (rb));
+}
+
+
+
diff --git a/src/effects/SkRadialGradient_Table.h b/src/effects/SkRadialGradient_Table.h
new file mode 100644
index 0000000..2336237
--- /dev/null
+++ b/src/effects/SkRadialGradient_Table.h
@@ -0,0 +1,147 @@
+/* libs/graphics/effects/SkRadialGradient_Table.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.
+*/
+
+static const uint8_t gSqrt8Table[] = {
+    0x00, 0x05, 0x08, 0x09, 0x0B, 0x0C, 0x0D, 0x0E, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x15, 
+    0x16, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1B, 0x1B, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 
+    0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, 
+    0x27, 0x27, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2B, 0x2C, 0x2C, 0x2C, 
+    0x2D, 0x2D, 0x2D, 0x2E, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32, 
+    0x32, 0x32, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x37, 
+    0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x3A, 0x3A, 0x3A, 0x3B, 0x3B, 0x3B, 
+    0x3B, 0x3C, 0x3C, 0x3C, 0x3C, 0x3D, 0x3D, 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F, 
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 
+    0x43, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, 
+    0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x4A, 0x4A, 0x4A, 0x4A, 
+    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4E, 
+    0x4E, 0x4E, 0x4E, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 
+    0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 
+    0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 
+    0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5A, 0x5A, 
+    0x5A, 0x5A, 0x5A, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5D, 
+    0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 
+    0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 
+    0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x65, 
+    0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 
+    0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 
+    0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6C, 0x6C, 0x6C, 
+    0x6C, 0x6C, 0x6C, 0x6C, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6E, 0x6E, 0x6E, 0x6E, 0x6E, 
+    0x6E, 0x6E, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 
+    0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 
+    0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 
+    0x75, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, 0x77, 
+    0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 
+    0x79, 0x79, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 
+    0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 
+    0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 
+    0x81, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 
+    0x83, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 
+    0x85, 0x85, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 
+    0x87, 0x87, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x89, 
+    0x89, 0x89, 0x89, 0x89, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8B, 0x8B, 0x8B, 0x8B, 
+    0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8D, 0x8D, 
+    0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 
+    0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 
+    0x90, 0x90, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x92, 
+    0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 
+    0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 
+    0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 
+    0x97, 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, 
+    0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9B, 
+    0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 
+    0x9C, 0x9C, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9E, 0x9E, 0x9E, 
+    0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 
+    0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 
+    0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA3, 
+    0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 
+    0xA4, 0xA4, 0xA4, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA6, 0xA6, 
+    0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 
+    0xA7, 0xA7, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA9, 0xA9, 0xA9, 
+    0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 
+    0xAA, 0xAA, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAC, 0xAC, 0xAC, 
+    0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 
+    0xAD, 0xAD, 0xAD, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAF, 0xAF, 
+    0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 
+    0xB0, 0xB0, 0xB0, 0xB0, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB2, 
+    0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 
+    0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 
+    0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB6, 0xB6, 0xB6, 0xB6, 
+    0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 
+    0xB7, 0xB7, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB9, 0xB9, 
+    0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 
+    0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 
+    0xBB, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBD, 0xBD, 0xBD, 
+    0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 
+    0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 
+    0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 
+    0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 
+    0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 
+    0xC3, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC5, 0xC5, 0xC5, 
+    0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 
+    0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 
+    0xC7, 0xC7, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC9, 
+    0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xCA, 0xCA, 0xCA, 0xCA, 
+    0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 
+    0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 
+    0xCC, 0xCC, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCE, 
+    0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF, 
+    0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 
+    0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 
+    0xD1, 0xD1, 0xD1, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 
+    0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD4, 0xD4, 0xD4, 
+    0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 
+    0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 
+    0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 
+    0xD7, 0xD7, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 
+    0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xDA, 0xDA, 
+    0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 
+    0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 
+    0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 
+    0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 
+    0xDE, 0xDE, 0xDE, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 
+    0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE1, 
+    0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE2, 0xE2, 0xE2, 
+    0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 
+    0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 
+    0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 
+    0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 
+    0xE6, 0xE6, 0xE6, 0xE6, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 
+    0xE7, 0xE7, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 
+    0xE8, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 
+    0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEB, 0xEB, 
+    0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEC, 0xEC, 0xEC, 
+    0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xED, 0xED, 0xED, 0xED, 
+    0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 
+    0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 
+    0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 
+    0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 
+    0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 
+    0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 
+    0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 
+    0xF4, 0xF4, 0xF4, 0xF4, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 
+    0xF5, 0xF5, 0xF5, 0xF5, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 
+    0xF6, 0xF6, 0xF6, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 
+    0xF7, 0xF7, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 
+    0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 
+    0xF9, 0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 
+    0xFA, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 
+    0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 
+    0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 
+    0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 
+    0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
diff --git a/src/effects/SkTransparentShader.cpp b/src/effects/SkTransparentShader.cpp
new file mode 100644
index 0000000..6b79839
--- /dev/null
+++ b/src/effects/SkTransparentShader.cpp
@@ -0,0 +1,144 @@
+/* libs/graphics/effects/SkTransparentShader.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 "SkTransparentShader.h"
+#include "SkColorPriv.h"
+
+bool SkTransparentShader::setContext(const SkBitmap& device,
+                                     const SkPaint& paint,
+                                     const SkMatrix& matrix)
+{
+    fDevice = &device;
+    fAlpha = paint.getAlpha();
+
+    return this->INHERITED::setContext(device, paint, matrix);
+}
+
+uint32_t SkTransparentShader::getFlags()
+{
+    uint32_t flags = this->INHERITED::getFlags();
+
+    switch (fDevice->getConfig()) {
+    case SkBitmap::kRGB_565_Config:
+        flags |= kHasSpan16_Flag;
+        if (fAlpha == 255)
+            flags |= kOpaqueAlpha_Flag;
+        break;
+    case SkBitmap::kARGB_8888_Config:
+    case SkBitmap::kARGB_4444_Config:
+        if (fAlpha == 255 && fDevice->isOpaque())
+            flags |= kOpaqueAlpha_Flag;
+        break;
+    default:
+        break;
+    }
+    return flags;
+}
+
+void SkTransparentShader::shadeSpan(int x, int y, SkPMColor span[], int count)
+{
+    unsigned scale = SkAlpha255To256(fAlpha);
+
+    switch (fDevice->getConfig()) {
+    case SkBitmap::kARGB_8888_Config:
+        if (scale == 256)
+            memcpy(span, fDevice->getAddr32(x, y), count * sizeof(SkPMColor));
+        else
+        {
+            const SkPMColor* src = fDevice->getAddr32(x, y);
+            for (int i = count - 1; i >= 0; --i)
+                span[i] = SkAlphaMulQ(src[i], scale);
+        }
+        break;
+    case SkBitmap::kRGB_565_Config:
+        {
+            const uint16_t* src = fDevice->getAddr16(x, y);
+            if (scale == 256)
+            {
+                for (int i = count - 1; i >= 0; --i)
+                    span[i] = SkPixel16ToPixel32(src[i]);
+            }
+            else
+            {
+                unsigned alpha = fAlpha;
+                for (int i = count - 1; i >= 0; --i)
+                {
+                    uint16_t c = src[i];
+                    unsigned r = SkPacked16ToR32(c);
+                    unsigned g = SkPacked16ToG32(c);
+                    unsigned b = SkPacked16ToB32(c);
+
+                    span[i] = SkPackARGB32( alpha,
+                                            SkAlphaMul(r, scale),
+                                            SkAlphaMul(g, scale),
+                                            SkAlphaMul(b, scale));
+                }
+            }
+        }
+        break;
+    case SkBitmap::kARGB_4444_Config:
+        {
+            const uint16_t* src = fDevice->getAddr16(x, y);
+            if (scale == 256)
+            {
+                for (int i = count - 1; i >= 0; --i)
+                    span[i] = SkPixel4444ToPixel32(src[i]);
+            }
+            else
+            {
+                unsigned scale16 = scale >> 4;
+                for (int i = count - 1; i >= 0; --i)
+                {
+                    uint32_t c = SkExpand_4444(src[i]) * scale16;
+                    span[i] = SkCompact_8888(c);
+                }
+            }
+        }
+            break;
+            case SkBitmap::kIndex8_Config:
+        SkASSERT(!"index8 not supported as a destination device");
+        break;
+    case SkBitmap::kA8_Config:
+        {
+            const uint8_t* src = fDevice->getAddr8(x, y);
+            if (scale == 256)
+            {
+                for (int i = count - 1; i >= 0; --i)
+                    span[i] = SkPackARGB32(src[i], 0, 0, 0);
+            }
+            else
+            {
+                for (int i = count - 1; i >= 0; --i)
+                    span[i] = SkPackARGB32(SkAlphaMul(src[i], scale), 0, 0, 0);
+            }
+        }
+        break;
+    case SkBitmap::kA1_Config:
+        SkASSERT(!"kA1_Config umimplemented at this time");
+        break;
+    default:    // to avoid warnings
+        break;
+    }
+}
+
+void SkTransparentShader::shadeSpan16(int x, int y, uint16_t span[], int count)
+{
+    SkASSERT(fDevice->getConfig() == SkBitmap::kRGB_565_Config);
+
+    memcpy(span, fDevice->getAddr16(x, y), count << 1);
+}
+
diff --git a/src/gl/SkGL.cpp b/src/gl/SkGL.cpp
new file mode 100644
index 0000000..0634709
--- /dev/null
+++ b/src/gl/SkGL.cpp
@@ -0,0 +1,528 @@
+#include "SkGL.h"
+#include "SkColorPriv.h"
+#include "SkGeometry.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkTemplates.h"
+#include "SkXfermode.h"
+
+//#define TRACE_TEXTURE_CREATION
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_GL_HAS_COLOR4UB
+static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) {
+    glColor4ub(r, g, b, a);
+}
+
+void SkGL::SetAlpha(U8CPU alpha) {
+    glColor4ub(alpha, alpha, alpha, alpha);
+}
+#else
+static inline SkFixed byte2fixed(U8CPU value) {
+    return (value + (value >> 7)) << 8;
+}
+
+static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) {
+    glColor4x(byte2fixed(r), byte2fixed(g), byte2fixed(b), byte2fixed(a));
+}
+
+void SkGL::SetAlpha(U8CPU alpha) {
+    SkFixed fa = byte2fixed(alpha);
+    glColor4x(fa, fa, fa, fa);
+}
+#endif
+
+void SkGL::SetColor(SkColor c) {
+    SkPMColor pm = SkPreMultiplyColor(c);
+    gl_pmcolor(SkGetPackedR32(pm),
+               SkGetPackedG32(pm),
+               SkGetPackedB32(pm),
+               SkGetPackedA32(pm));
+}
+
+static const GLenum gXfermodeCoeff2Blend[] = {
+    GL_ZERO,
+    GL_ONE,
+    GL_SRC_COLOR,
+    GL_ONE_MINUS_SRC_COLOR,
+    GL_DST_COLOR,
+    GL_ONE_MINUS_DST_COLOR,
+    GL_SRC_ALPHA,
+    GL_ONE_MINUS_SRC_ALPHA,
+    GL_DST_ALPHA,
+    GL_ONE_MINUS_DST_ALPHA,
+};
+
+void SkGL::SetPaint(const SkPaint& paint, bool isPremul, bool justAlpha) {
+    if (justAlpha) {
+        SkGL::SetAlpha(paint.getAlpha());
+    } else {
+        SkGL::SetColor(paint.getColor());
+    }
+
+    GLenum sm = GL_ONE;
+    GLenum dm = GL_ONE_MINUS_SRC_ALPHA;
+
+    SkXfermode* mode = paint.getXfermode();
+    SkXfermode::Coeff sc, dc;
+    if (mode && mode->asCoeff(&sc, &dc)) {
+        sm = gXfermodeCoeff2Blend[sc];
+        dm = gXfermodeCoeff2Blend[dc];
+    }
+    
+    // hack for text, which is not-premul (afaik)
+    if (!isPremul) {
+        if (GL_ONE == sm) {
+            sm = GL_SRC_ALPHA;
+        }
+    }
+    
+    glEnable(GL_BLEND);
+    glBlendFunc(sm, dm);
+    
+    if (paint.isDither()) {
+        glEnable(GL_DITHER);
+    } else {
+        glDisable(GL_DITHER);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGL::DumpError(const char caller[]) {
+    GLenum err = glGetError();
+    if (err) {
+        SkDebugf("---- glGetError(%s) %d\n", caller, err);
+    }
+}
+
+void SkGL::SetRGBA(uint8_t rgba[], const SkColor src[], int count) {
+    for (int i = 0; i < count; i++) {
+        SkPMColor c = SkPreMultiplyColor(*src++);
+        *rgba++ = SkGetPackedR32(c);
+        *rgba++ = SkGetPackedG32(c);
+        *rgba++ = SkGetPackedB32(c);
+        *rgba++ = SkGetPackedA32(c);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGL::Scissor(const SkIRect& r, int viewportHeight) {
+    glScissor(r.fLeft, viewportHeight - r.fBottom, r.width(), r.height());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGL::Ortho(float left, float right, float bottom, float top,
+                 float near, float far) {
+    
+    float mat[16];
+    
+    bzero(mat, sizeof(mat));
+    
+    mat[0] = 2 / (right - left);
+    mat[5] = 2 / (top - bottom);
+    mat[10] = 2 / (near - far);
+    mat[15] = 1;
+    
+    mat[12] = (right + left) / (left - right);
+    mat[13] = (top + bottom) / (bottom - top);
+    mat[14] = (far + near) / (near - far);
+    
+    glMultMatrixf(mat);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool canBeTexture(const SkBitmap& bm, GLenum* format, GLenum* type) {
+    switch (bm.config()) {
+        case SkBitmap::kARGB_8888_Config:
+            *format = GL_RGBA;
+            *type = GL_UNSIGNED_BYTE;
+            break;
+        case SkBitmap::kRGB_565_Config:
+            *format = GL_RGB;
+            *type = GL_UNSIGNED_SHORT_5_6_5;
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            *format = GL_RGBA;
+            *type = GL_UNSIGNED_SHORT_4_4_4_4;
+            break;
+        case SkBitmap::kIndex8_Config:
+#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+            *format = GL_PALETTE8_RGBA8_OES;
+            *type = GL_UNSIGNED_BYTE;   // unused I think
+#else
+            // we promote index to argb32
+            *format = GL_RGBA;
+            *type = GL_UNSIGNED_BYTE;
+#endif
+            break;
+        case SkBitmap::kA8_Config:
+            *format = GL_ALPHA;
+            *type = GL_UNSIGNED_BYTE;
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+
+#define SK_GL_SIZE_OF_PALETTE   (256 * sizeof(SkPMColor))
+
+size_t SkGL::ComputeTextureMemorySize(const SkBitmap& bitmap) {
+    int shift = 0;
+    size_t adder = 0;
+    switch (bitmap.config()) {
+        case SkBitmap::kARGB_8888_Config:
+        case SkBitmap::kRGB_565_Config:
+        case SkBitmap::kARGB_4444_Config:
+        case SkBitmap::kA8_Config:
+            // we're good as is
+            break;
+        case SkBitmap::kIndex8_Config:
+#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+            // account for the colortable
+            adder = SK_GL_SIZE_OF_PALETTE;
+#else
+            // we promote index to argb32
+            shift = 2;
+#endif
+            break;
+        default:
+            return 0;
+    }
+    return (bitmap.getSize() << shift) + adder;
+}
+
+#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+/*  Fill out buffer with the compressed format GL expects from a colortable
+    based bitmap. [palette (colortable) + indices].
+
+    At the moment I always take the 8bit version, since that's what my data
+    is. I could detect that the colortable.count is <= 16, and then repack the
+    indices as nibbles to save RAM, but it would take more time (i.e. a lot
+    slower than memcpy), so I'm skipping that for now.
+ 
+    GL wants a full 256 palette entry, even though my ctable is only as big
+    as the colortable.count says it is. I presume it is OK to leave any
+    trailing entries uninitialized, since none of my indices should exceed
+    ctable->count().
+*/
+static void build_compressed_data(void* buffer, const SkBitmap& bitmap) {
+    SkASSERT(SkBitmap::kIndex8_Config == bitmap.config());
+
+    SkColorTable* ctable = bitmap.getColorTable();
+    uint8_t* dst = (uint8_t*)buffer;
+
+    memcpy(dst, ctable->lockColors(), ctable->count() * sizeof(SkPMColor));
+    ctable->unlockColors(false);
+
+    // always skip a full 256 number of entries, even if we memcpy'd fewer
+    dst += SK_GL_SIZE_OF_PALETTE;
+    memcpy(dst, bitmap.getPixels(), bitmap.getSize());
+}
+#endif
+
+/*  Return true if the bitmap cannot be supported in its current config as a
+    texture, and it needs to be promoted to ARGB32.
+ */
+static bool needToPromoteTo32bit(const SkBitmap& bitmap) {
+    if (bitmap.config() == SkBitmap::kIndex8_Config) {
+#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+        const int w = bitmap.width();
+        const int h = bitmap.height();
+        if (SkNextPow2(w) == w && SkNextPow2(h) == h) {
+            // we can handle Indx8 if we're a POW2
+            return false;
+        }
+#endif
+        return true;    // must promote to ARGB32
+    }
+    return false;
+}
+
+GLuint SkGL::BindNewTexture(const SkBitmap& origBitmap, SkPoint* max) {
+    SkBitmap tmpBitmap;
+    const SkBitmap* bitmap = &origBitmap;
+
+    if (needToPromoteTo32bit(origBitmap)) {
+        origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config);
+        // now bitmap points to our temp, which has been promoted to 32bits
+        bitmap = &tmpBitmap;
+    }
+    
+    GLenum format, type;
+    if (!canBeTexture(*bitmap, &format, &type)) {
+        return 0;
+    }
+    
+    SkAutoLockPixels alp(*bitmap);
+    if (!bitmap->readyToDraw()) {
+        return 0;
+    }
+    
+    GLuint  textureName;
+    glGenTextures(1, &textureName);
+    
+    glBindTexture(GL_TEXTURE_2D, textureName);
+    
+    // express rowbytes as a number of pixels for ow
+    int ow = bitmap->rowBytesAsPixels();
+    int oh = bitmap->height();
+    int nw = SkNextPow2(ow);
+    int nh = SkNextPow2(oh);
+    
+    glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
+    
+    // check if we need to scale to create power-of-2 dimensions
+#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+    if (SkBitmap::kIndex8_Config == bitmap->config()) {
+        size_t imagesize = bitmap->getSize() + SK_GL_SIZE_OF_PALETTE;
+        SkAutoMalloc storage(imagesize);
+
+        build_compressed_data(storage.get(), *bitmap);
+        // we only support POW2 here (GLES 1.0 restriction)
+        SkASSERT(ow == nw);
+        SkASSERT(oh == nh);
+        glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, ow, oh, 0,
+                               imagesize, storage.get());
+    } else  // fall through to non-compressed logic
+#endif
+    {
+        if (ow != nw || oh != nh) {
+            glTexImage2D(GL_TEXTURE_2D, 0, format, nw, nh, 0,
+                         format, type, NULL);
+            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ow, oh,
+                            format, type, bitmap->getPixels());
+        } else {
+            // easy case, the bitmap is already pow2
+            glTexImage2D(GL_TEXTURE_2D, 0, format, ow, oh, 0,
+                         format, type, bitmap->getPixels());
+        }
+    }
+    
+#ifdef TRACE_TEXTURE_CREATION
+    SkDebugf("--- new texture [%d] size=(%d %d) bpp=%d\n", textureName, ow, oh,
+             bitmap->bytesPerPixel());
+#endif
+
+    if (max) {
+        max->fX = SkFixedToScalar(bitmap->width() << (16 - SkNextLog2(nw)));
+        max->fY = SkFixedToScalar(oh << (16 - SkNextLog2(nh)));
+    }
+    return textureName;
+}
+
+static const GLenum gTileMode2GLWrap[] = {
+    GL_CLAMP_TO_EDGE,
+    GL_REPEAT,
+#if GL_VERSION_ES_CM_1_0
+    GL_REPEAT       // GLES doesn't support MIRROR
+#else
+    GL_MIRRORED_REPEAT
+#endif
+};
+
+void SkGL::SetTexParams(bool doFilter,
+                        SkShader::TileMode tx, SkShader::TileMode ty) {
+    SkASSERT((unsigned)tx < SK_ARRAY_COUNT(gTileMode2GLWrap));
+    SkASSERT((unsigned)ty < SK_ARRAY_COUNT(gTileMode2GLWrap));
+    
+    GLenum filter = doFilter ? GL_LINEAR : GL_NEAREST;
+
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileMode2GLWrap[tx]);
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileMode2GLWrap[ty]);
+}
+
+void SkGL::SetTexParamsClamp(bool doFilter) {
+    GLenum filter = doFilter ? GL_LINEAR : GL_NEAREST;
+
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGL::DrawVertices(int count, GLenum mode,
+                        const SkGLVertex* SK_RESTRICT vertex,
+                        const SkGLVertex* SK_RESTRICT texCoords,
+                        const uint8_t* SK_RESTRICT colorArray,
+                        const uint16_t* SK_RESTRICT indexArray,
+                        SkGLClipIter* iter) {
+    SkASSERT(NULL != vertex);
+    
+    if (NULL != texCoords) {
+        glEnable(GL_TEXTURE_2D);
+        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+        glTexCoordPointer(2, SK_GLType, 0, texCoords);
+    } else {
+        glDisable(GL_TEXTURE_2D);
+        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    }
+    
+    if (NULL != colorArray) {
+        glEnableClientState(GL_COLOR_ARRAY);
+        glColorPointer(4, GL_UNSIGNED_BYTE, 0, colorArray);
+        glShadeModel(GL_SMOOTH); 
+    } else {
+        glDisableClientState(GL_COLOR_ARRAY);
+        glShadeModel(GL_FLAT); 
+    }
+    
+    glVertexPointer(2, SK_GLType, 0, vertex);
+
+    if (NULL != indexArray) {
+        if (iter) {
+            while (!iter->done()) {
+                iter->scissor();
+                glDrawElements(mode, count, GL_UNSIGNED_SHORT, indexArray);
+                iter->next();
+            }
+        } else {
+            glDrawElements(mode, count, GL_UNSIGNED_SHORT, indexArray);
+        }
+    } else {
+        if (iter) {
+            while (!iter->done()) {
+                iter->scissor();
+                glDrawArrays(mode, 0, count);
+                iter->next();
+            }
+        } else {
+            glDrawArrays(mode, 0, count);
+        }
+    }
+}
+
+void SkGL::PrepareForFillPath(SkPaint* paint) {
+    if (paint->getStrokeWidth() <= 0) {
+        paint->setStrokeWidth(SK_Scalar1);
+    }
+}
+
+void SkGL::FillPath(const SkPath& path, const SkPaint& paint, bool useTex,
+                    SkGLClipIter* iter) {
+    SkPaint p(paint);
+    SkPath  fillPath;
+    
+    SkGL::PrepareForFillPath(&p);
+    p.getFillPath(path, &fillPath);
+    SkGL::DrawPath(fillPath, useTex, iter);
+}
+
+// should return max of all contours, rather than the sum (to save temp RAM)
+static int worst_case_edge_count(const SkPath& path) {
+    int edgeCount = 0;
+    
+    SkPath::Iter    iter(path, true);
+    SkPath::Verb    verb;
+    
+    while ((verb = iter.next(NULL)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kLine_Verb:
+                edgeCount += 1;
+                break;
+            case SkPath::kQuad_Verb:
+                edgeCount += 8;
+                break;
+            case SkPath::kCubic_Verb:
+                edgeCount += 16;
+                break;
+            default:
+                break;
+        }
+    }
+    return edgeCount;
+}
+
+void SkGL::DrawPath(const SkPath& path, bool useTex, SkGLClipIter* clipIter) {
+    SkRect  bounds;
+    
+    path.computeBounds(&bounds, SkPath::kFast_BoundsType);
+    if (bounds.isEmpty()) {
+        return;
+    }
+    
+    int maxPts = worst_case_edge_count(path);
+    // add 1 for center of fan, and 1 for closing edge
+    SkAutoSTMalloc<32, SkGLVertex>  storage(maxPts + 2);
+    SkGLVertex* base = storage.get();
+    SkGLVertex* vert = base;
+    SkGLVertex* texs = useTex ? base : NULL;
+
+    SkPath::Iter    pathIter(path, true);
+    SkPoint         pts[4];
+    
+    bool needEnd = false;
+    
+    for (;;) {
+        switch (pathIter.next(pts)) {
+            case SkPath::kMove_Verb:
+                if (needEnd) {
+                    SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN,
+                                       base, texs, NULL, NULL, clipIter);
+                    clipIter->safeRewind();
+                    vert = base;
+                }
+                needEnd = true;
+                // center of the FAN
+                vert->setScalars(bounds.centerX(), bounds.centerY());
+                vert++;
+                // add first edge point
+                vert->setPoint(pts[0]);
+                vert++;
+                break;
+                case SkPath::kLine_Verb:
+                vert->setPoint(pts[1]);
+                vert++;
+                break;
+                case SkPath::kQuad_Verb: {
+                    const int n = 8;
+                    const SkScalar dt = SK_Scalar1 / n;
+                    SkScalar t = dt;
+                    for (int i = 1; i < n; i++) {
+                        SkPoint loc;
+                        SkEvalQuadAt(pts, t, &loc, NULL);
+                        t += dt;
+                        vert->setPoint(loc);
+                        vert++;
+                    }
+                    vert->setPoint(pts[2]);
+                    vert++;
+                    break;
+                }
+                case SkPath::kCubic_Verb: {
+                    const int n = 16;
+                    const SkScalar dt = SK_Scalar1 / n;
+                    SkScalar t = dt;
+                    for (int i = 1; i < n; i++) {
+                        SkPoint loc;
+                        SkEvalCubicAt(pts, t, &loc, NULL, NULL);
+                        t += dt;
+                        vert->setPoint(loc);
+                        vert++;
+                    }
+                    vert->setPoint(pts[3]);
+                    vert++;
+                    break;
+                }
+                case SkPath::kClose_Verb:
+                break;
+                case SkPath::kDone_Verb:
+                goto FINISHED;
+        }
+    }
+FINISHED:
+    if (needEnd) {
+        SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN, base, texs,
+                           NULL, NULL, clipIter);
+    }
+}
+
diff --git a/src/gl/SkGL.h b/src/gl/SkGL.h
new file mode 100644
index 0000000..d4cd3b6
--- /dev/null
+++ b/src/gl/SkGL.h
@@ -0,0 +1,303 @@
+#ifndef SkGL_DEFINED
+#define SkGL_DEFINED
+
+#ifdef SK_BUILD_FOR_MAC
+    #include <OpenGL/gl.h>
+    #include <OpenGL/glext.h>
+    #include <AGL/agl.h>
+    // use FBOs for devices
+    #define SK_GL_DEVICE_FBO
+#elif defined(ANDROID)
+    #include <GLES/gl.h>
+    #include <GLES/egl.h>
+#endif
+
+#include "SkColor.h"
+#include "SkMatrix.h"
+#include "SkShader.h"
+
+class SkPaint;
+class SkPath;
+
+class SkGLClipIter;
+
+//#define TRACE_TEXTURE_CREATE
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GL_OES_compressed_paletted_texture
+    #define SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
+#endif
+
+#if GL_OES_fixed_point && defined(SK_SCALAR_IS_FIXED)
+    #define SK_GLType   GL_FIXED
+#else
+    #define SK_GLType   GL_FLOAT
+#endif
+
+#if SK_GLType == GL_FIXED
+    typedef SkFixed SkGLScalar;
+
+    #define SkIntToGL(n)        SkIntToFixed(n)
+    #define SkScalarToGL(x)     SkScalarToFixed(x)
+    #define SK_GLScalar1        SK_Fixed1
+    #define SkGLScalarMul(a, b) SkFixedMul(a, b)
+    #define MAKE_GL(name)       name ## x
+
+    #ifdef SK_SCALAR_IS_FIXED
+        #define GLSCALAR_IS_SCALAR  1
+        #define SkPerspToGL(x)      SkFractToFixed(x)
+    #else
+        #define GLSCALAR_IS_SCALAR  0
+        #define SkPerspToGL(x)      SkFractToFloat(x)
+    #endif
+#else
+    typedef float SkGLScalar;
+
+    #define SkIntToGL(n)        (n)
+    #define SkScalarToGL(x)     SkScalarToFloat(x)
+    #define SK_GLScalar1        (1.f)
+    #define SkGLScalarMul(a, b) ((a) * (b))
+    #define MAKE_GL(name)       name ## f
+
+    #ifdef SK_SCALAR_IS_FLOAT
+        #define GLSCALAR_IS_SCALAR  1
+        #define SkPerspToGL(x)      (x)
+    #else
+        #define GLSCALAR_IS_SCALAR  0
+        #define SkPerspToGL(x)      SkFractToFloat(x)
+    #endif
+#endif
+
+#if GL_OES_fixed_point
+    typedef SkFixed SkGLTextScalar;
+    #define SK_TextGLType       GL_FIXED
+
+    #define SkIntToTextGL(n)    SkIntToFixed(n)
+    #define SkFixedToTextGL(x)  (x)
+
+    #define SK_glTexParameteri(target, pname, param) \
+                glTexParameterx(target, pname, param)
+#else
+    typedef float SkGLTextScalar;
+    #define SK_TextGLType       SK_GLType
+    #define SK_GL_HAS_COLOR4UB
+
+    #define SkIntToTextGL(n)    SkIntToGL(n)
+    #define SkFixedToTextGL(x)  SkFixedToFloat(x)
+
+
+    #define SK_glTexParameteri(target, pname, param) \
+                glTexParameteri(target, pname, param)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+// text has its own vertex class, since it may want to be in fixed point (given)
+// that it starts with all integers) even when the default vertices are floats
+struct SkGLTextVertex {
+    SkGLTextScalar fX;
+    SkGLTextScalar fY;
+    
+    void setI(int x, int y) {
+        fX = SkIntToTextGL(x);
+        fY = SkIntToTextGL(y);
+    }
+    
+    void setX(SkFixed x, SkFixed y) {
+        fX = SkFixedToTextGL(x);
+        fY = SkFixedToTextGL(y);
+    }
+    
+    // counter-clockwise fan
+    void setIRectFan(int l, int t, int r, int b) {
+        SkGLTextVertex* SK_RESTRICT v = this;
+        v[0].setI(l, t);
+        v[1].setI(l, b);
+        v[2].setI(r, b);
+        v[3].setI(r, t);
+    }
+    
+    // counter-clockwise fan
+    void setXRectFan(SkFixed l, SkFixed t, SkFixed r, SkFixed b) {
+        SkGLTextVertex* SK_RESTRICT v = this;
+        v[0].setX(l, t);
+        v[1].setX(l, b);
+        v[2].setX(r, b);
+        v[3].setX(r, t);
+    }
+};
+
+struct SkGLVertex {
+    SkGLScalar  fX;
+    SkGLScalar  fY;
+    
+    void setGL(SkGLScalar x, SkGLScalar y) {
+        fX = x;
+        fY = y;
+    }
+
+    void setScalars(SkScalar x, SkScalar y) {
+        fX = SkScalarToGL(x);
+        fY = SkScalarToGL(y);
+    }
+    
+    void setPoint(const SkPoint& pt) {
+        fX = SkScalarToGL(pt.fX);
+        fY = SkScalarToGL(pt.fY);
+    }
+    
+    void setPoints(const SkPoint* SK_RESTRICT pts, int count) {
+        const SkScalar* SK_RESTRICT src = (const SkScalar*)pts;
+        SkGLScalar* SK_RESTRICT dst = (SkGLScalar*)this;
+        for (int i = 0; i < count; i++) {
+            *dst++ = SkScalarToGL(*src++);
+            *dst++ = SkScalarToGL(*src++);
+        }
+    }
+    
+    // counter-clockwise fan
+    void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
+        SkGLVertex* v = this;
+        v[0].setScalars(l, t);
+        v[1].setScalars(l, b);
+        v[2].setScalars(r, b);
+        v[3].setScalars(r, t);
+    }
+    
+    // counter-clockwise fan
+    void setIRectFan(int l, int t, int r, int b) {
+        SkGLVertex* v = this;
+        v[0].setGL(SkIntToGL(l), SkIntToGL(t));
+        v[1].setGL(SkIntToGL(l), SkIntToGL(b));
+        v[2].setGL(SkIntToGL(r), SkIntToGL(b));
+        v[3].setGL(SkIntToGL(r), SkIntToGL(t));
+    }
+    
+    // counter-clockwise fan
+    void setRectFan(const SkRect& r) {
+        this->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom);
+    }
+    
+    // counter-clockwise fan
+    void setIRectFan(const SkIRect& r) {
+        this->setIRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom);
+    }
+};
+
+struct SkGLMatrix {
+    SkGLScalar fMat[16];
+    
+    void reset() {
+        bzero(fMat, sizeof(fMat));
+        fMat[0] = fMat[5] = fMat[10] = fMat[15] = SK_GLScalar1;
+    }
+    
+    void set(const SkMatrix& m) {
+        bzero(fMat, sizeof(fMat));
+        fMat[0] = SkScalarToGL(m[SkMatrix::kMScaleX]);
+        fMat[4] = SkScalarToGL(m[SkMatrix::kMSkewX]);
+        fMat[12] = SkScalarToGL(m[SkMatrix::kMTransX]);
+        
+        fMat[1] = SkScalarToGL(m[SkMatrix::kMSkewY]);
+        fMat[5] = SkScalarToGL(m[SkMatrix::kMScaleY]);
+        fMat[13] = SkScalarToGL(m[SkMatrix::kMTransY]);
+                
+        fMat[3] = SkPerspToGL(m[SkMatrix::kMPersp0]);
+        fMat[7] = SkPerspToGL(m[SkMatrix::kMPersp1]);
+        fMat[15] = SkPerspToGL(m[SkMatrix::kMPersp2]);
+
+        fMat[10] = SK_GLScalar1;    // z-scale
+    }
+};
+
+class SkGL {
+public:
+    static void SetColor(SkColor c);
+    static void SetAlpha(U8CPU alpha);
+    static void SetPaint(const SkPaint&, bool isPremul = true,
+                         bool justAlpha = false);
+    static void SetPaintAlpha(const SkPaint& paint, bool isPremul = true) {
+        SetPaint(paint, isPremul, true);
+    }
+
+    static void SetRGBA(uint8_t rgba[], const SkColor src[], int count);
+    static void DumpError(const char caller[]);
+    
+    static void Ortho(float left, float right, float bottom, float top,
+                      float near, float far);
+
+    static inline void Translate(SkScalar dx, SkScalar dy) {
+        MAKE_GL(glTranslate)(SkScalarToGL(dx), SkScalarToGL(dy), 0);
+    }
+    
+    static inline void Scale(SkScalar sx, SkScalar sy) {
+        MAKE_GL(glScale)(SkScalarToGL(sx), SkScalarToGL(sy), SK_GLScalar1);
+    }
+
+    static inline void Rotate(SkScalar angle) {
+        MAKE_GL(glRotate)(SkScalarToGL(angle), 0, 0, SK_GLScalar1);
+    }
+
+    static inline void MultMatrix(const SkMatrix& m) {
+        SkGLMatrix glm;
+        glm.set(m);
+        MAKE_GL(glMultMatrix)(glm.fMat);
+    }
+    
+    static inline void LoadMatrix(const SkMatrix& m) {
+        SkGLMatrix glm;
+        glm.set(m);
+        MAKE_GL(glLoadMatrix)(glm.fMat);
+    }
+    
+    static void Scissor(const SkIRect&, int viewportHeight);
+    
+    // return the byte size for the associated texture memory. This doesn't
+    // always == bitmap.getSize(), since on a given port we may have to change
+    // the format when the bitmap's pixels are copied over to GL
+    static size_t ComputeTextureMemorySize(const SkBitmap&);
+    // return 0 on failure
+    static GLuint BindNewTexture(const SkBitmap&, SkPoint* dimension);
+
+    static void SetTexParams(bool filter,
+                             SkShader::TileMode tx, SkShader::TileMode ty);
+    static void SetTexParamsClamp(bool filter);
+
+    static void DrawVertices(int count, GLenum mode,
+                             const SkGLVertex* SK_RESTRICT vertex,
+                             const SkGLVertex* SK_RESTRICT texCoords,
+                             const uint8_t* SK_RESTRICT colorArray,
+                             const uint16_t* SK_RESTRICT indexArray,
+                             SkGLClipIter*);
+    
+    static void PrepareForFillPath(SkPaint* paint);
+    static void FillPath(const SkPath& path, const SkPaint& paint, bool useTex,
+                         SkGLClipIter*);
+    static void DrawPath(const SkPath& path, bool useTex, SkGLClipIter*);
+};
+
+#include "SkRegion.h"
+
+class SkGLClipIter : public SkRegion::Iterator {
+public:
+    SkGLClipIter(int viewportHeight) : fViewportHeight(viewportHeight) {}
+    
+    // call rewind only if this is non-null
+    void safeRewind() {
+        if (this) {
+            this->rewind();
+        }
+    }
+
+    void scissor() {
+        SkASSERT(!this->done());
+        SkGL::Scissor(this->rect(), fViewportHeight);
+    }
+    
+private:
+    const int fViewportHeight;
+};
+
+#endif
+
diff --git a/src/gl/SkGLCanvas.cpp b/src/gl/SkGLCanvas.cpp
new file mode 100644
index 0000000..2e93209
--- /dev/null
+++ b/src/gl/SkGLCanvas.cpp
@@ -0,0 +1,179 @@
+#include "SkGLCanvas.h"
+#include "SkGLDevice.h"
+#include "SkBlitter.h"
+#include "SkDraw.h"
+#include "SkDrawProcs.h"
+#include "SkGL.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+#ifdef SK_GL_DEVICE_FBO
+    #define USE_FBO_DEVICE
+    #include "SkGLDevice_FBO.h"
+#else
+    #define USE_SWLAYER_DEVICE
+    #include "SkGLDevice_SWLayer.h"
+#endif
+
+// maximum number of entries in our texture cache (before purging)
+#define kTexCountMax_Default    256
+// maximum number of bytes used (by gl) for the texture cache (before purging)
+#define kTexSizeMax_Default     (4 * 1024 * 1024)
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGLCanvas::SkGLCanvas() {
+    glEnable(GL_TEXTURE_2D);
+    glEnable(GL_SCISSOR_TEST);
+    glEnableClientState(GL_VERTEX_ARRAY);
+
+    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+    fViewportSize.set(0, 0);
+}
+
+SkGLCanvas::~SkGLCanvas() {
+    // call this now, while our override of restore() is in effect
+    this->restoreToCount(1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkGLCanvas::getViewport(SkIPoint* size) const {
+    if (size) {
+        *size = fViewportSize;
+    }
+    return true;
+}
+
+bool SkGLCanvas::setViewport(int width, int height) {
+    fViewportSize.set(width, height);
+
+    const bool isOpaque = false; // should this be a parameter to setViewport?
+    const bool isForLayer = false;   // viewport is the base layer
+    SkDevice* device = this->createDevice(SkBitmap::kARGB_8888_Config, width,
+                                          height, isOpaque, isForLayer);
+    this->setDevice(device)->unref();
+
+    return true;
+}
+
+SkDevice* SkGLCanvas::createDevice(SkBitmap::Config, int width, int height,
+                                   bool isOpaque, bool isForLayer) {
+    SkBitmap bitmap;
+    
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    bitmap.setIsOpaque(isOpaque);
+
+#ifdef USE_FBO_DEVICE
+    return SkNEW_ARGS(SkGLDevice_FBO, (bitmap, isForLayer));
+#elif defined(USE_SWLAYER_DEVICE)
+    if (isForLayer) {
+        bitmap.allocPixels();
+        if (!bitmap.isOpaque()) {
+            bitmap.eraseColor(0);
+        }
+        return SkNEW_ARGS(SkGLDevice_SWLayer, (bitmap));
+    } else {
+        return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer));
+    }
+#else
+    return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer));
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTextureCache.h"
+#include "SkThread.h"
+
+static SkMutex gTextureCacheMutex;
+static SkTextureCache gTextureCache(kTexCountMax_Default, kTexSizeMax_Default);
+
+SkGLDevice::TexCache* SkGLDevice::LockTexCache(const SkBitmap& bitmap,
+                                                 GLuint* name, SkPoint* size) {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+    
+    SkTextureCache::Entry* entry = gTextureCache.lock(bitmap);
+    if (NULL != entry) {
+        if (name) {
+            *name = entry->name();
+        }
+        if (size) {
+            *size = entry->texSize();
+        }
+    }
+    return (TexCache*)entry;
+}
+
+void SkGLDevice::UnlockTexCache(TexCache* cache) {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+    gTextureCache.unlock((SkTextureCache::Entry*)cache);
+}
+
+// public exposure of texture cache settings
+
+size_t SkGLCanvas::GetTextureCacheMaxCount() {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+    return gTextureCache.getMaxCount();
+}
+
+size_t SkGLCanvas::GetTextureCacheMaxSize() {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+    return gTextureCache.getMaxSize();
+}
+
+void SkGLCanvas::SetTextureCacheMaxCount(size_t count) {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+    gTextureCache.setMaxCount(count);
+}
+
+void SkGLCanvas::SetTextureCacheMaxSize(size_t size) {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+    gTextureCache.setMaxSize(size);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGLTextCache.h"
+
+static bool deleteCachesProc(SkGlyphCache* cache, void* texturesAreValid) {
+    void* auxData;
+    if (cache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
+        bool valid = texturesAreValid != NULL;
+        SkGLTextCache* textCache = static_cast<SkGLTextCache*>(auxData);
+        // call this before delete, in case valid is false
+        textCache->deleteAllStrikes(valid);
+        // now free the memory for the cache itself
+        SkDELETE(textCache);
+        // now remove the entry in the glyphcache (does not call the proc)
+        cache->removeAuxProc(SkGLDevice::GlyphCacheAuxProc);
+    }
+    return false;   // keep going
+}
+
+void SkGLCanvas::DeleteAllTextures() {
+    // free the textures in our cache
+
+    gTextureCacheMutex.acquire();
+    gTextureCache.deleteAllCaches(true);
+    gTextureCacheMutex.release();
+    
+    // now free the textures in the font cache
+    
+    SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(true));
+}
+
+void SkGLCanvas::AbandonAllTextures() {
+    // abandon the textures in our cache
+
+    gTextureCacheMutex.acquire();
+    gTextureCache.deleteAllCaches(false);
+    gTextureCacheMutex.release();
+
+    // abandon the textures in the font cache
+    
+    SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(false));
+}
+
diff --git a/src/gl/SkGLDevice.cpp b/src/gl/SkGLDevice.cpp
new file mode 100644
index 0000000..70968e2
--- /dev/null
+++ b/src/gl/SkGLDevice.cpp
@@ -0,0 +1,761 @@
+#include "SkGLDevice.h"
+#include "SkGL.h"
+#include "SkDrawProcs.h"
+#include "SkRegion.h"
+#include "SkThread.h"
+
+static void TRACE_DRAW(const char func[], SkGLDevice* device,
+                       const SkDraw& draw) {
+    //    SkDebugf("--- <%s> %p %p\n", func, canvas, draw.fDevice);
+}
+
+struct SkGLDrawProcs : public SkDrawProcs {
+public:
+    void init(const SkRegion* clip, int height) {
+        fCurrQuad = 0;
+        fCurrTexture = 0;
+        fClip = clip;
+        fViewportHeight = height;
+
+        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+        glTexCoordPointer(2, SK_TextGLType, 0, fTexs);
+        glDisableClientState(GL_COLOR_ARRAY);
+        glVertexPointer(2, SK_TextGLType, 0, fVerts);
+    }
+
+    GLenum texture() const { return fCurrTexture; }
+
+    void flush() {
+        if (fCurrQuad && fCurrTexture) {
+            this->drawQuads();
+        }
+        fCurrQuad = 0;
+    }
+
+    void addQuad(GLuint texture, int x, int y, const SkGlyph& glyph,
+                 SkFixed left, SkFixed right, SkFixed bottom) {
+        SkASSERT((size_t)fCurrQuad <= SK_ARRAY_COUNT(fVerts));
+        
+        if (fCurrTexture != texture || fCurrQuad == SK_ARRAY_COUNT(fVerts)) {
+            if (fCurrQuad && fCurrTexture) {
+                this->drawQuads();
+            }
+            fCurrQuad = 0;
+            fCurrTexture = texture;
+        }
+        
+        fVerts[fCurrQuad].setIRectFan(x, y,
+                                      x + glyph.fWidth, y + glyph.fHeight);
+        fTexs[fCurrQuad].setXRectFan(left, 0, right, bottom);
+        fCurrQuad += 4;
+    }
+    
+    void drawQuads();
+
+private:
+    enum {
+        MAX_QUADS = 32
+    };
+    
+    SkGLTextVertex fVerts[MAX_QUADS * 4];
+    SkGLTextVertex fTexs[MAX_QUADS * 4];
+    
+    // these are initialized in setupForText
+    GLuint          fCurrTexture;    
+    int             fCurrQuad;
+    int             fViewportHeight;
+    const SkRegion* fClip;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGLDevice::SkGLDevice(const SkBitmap& bitmap, bool offscreen)
+        : SkDevice(bitmap), fClipIter(bitmap.height()) {
+    fDrawProcs = NULL;
+}
+
+SkGLDevice::~SkGLDevice() {
+    if (fDrawProcs) {
+        SkDELETE(fDrawProcs);
+    }
+}
+
+void SkGLDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip) {
+    this->INHERITED::setMatrixClip(matrix, clip);
+    
+    fGLMatrix.set(matrix);
+    fMatrix = matrix;
+    fClip = clip;
+    fDirty = true;
+}
+
+SkGLDevice::TexOrientation SkGLDevice::bindDeviceAsTexture() {
+    return kNo_TexOrientation;
+}
+
+void SkGLDevice::gainFocus(SkCanvas* canvas) {
+    this->INHERITED::gainFocus(canvas);
+
+    const int w = this->width();
+    const int h = this->height();
+    glViewport(0, 0, w, h);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    SkGL::Ortho(0, w, h, 0, -1, 1);
+    glMatrixMode(GL_MODELVIEW);
+    fDirty = true;    
+}
+
+SkGLClipIter* SkGLDevice::updateMatrixClip() {
+    bool useIter = false;
+
+    // first handle the clip
+    if (fDirty || !fClip.isRect()) {
+        fClipIter.reset(fClip);
+        useIter = true;
+    } else if (fDirty) {
+        // no iter means caller is not respecting complex clips :(
+        SkGL::Scissor(fClip.getBounds(), this->height());
+    }
+    // else we're just a rect, and we've already call scissor
+
+    // now handle the matrix
+    if (fDirty) {
+        MAKE_GL(glLoadMatrix)(fGLMatrix.fMat);
+#if 0
+        SkDebugf("--- gldevice update matrix %p %p\n", this, fFBO);
+        for (int y = 0; y < 4; y++) {
+            SkDebugf(" [ ");
+            for (int x = 0; x < 4; x++) {
+                SkDebugf("%g ", fGLMatrix.fMat[y*4 + x]);
+            }
+            SkDebugf("]\n");
+        }
+#endif
+        fDirty = false;
+    }
+
+    return useIter ? &fClipIter : NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// must be in the same order as SkXfermode::Coeff in SkXfermode.h
+SkGLDevice::AutoPaintShader::AutoPaintShader(SkGLDevice* device,
+                                             const SkPaint& paint) {
+    fDevice = device;
+    fTexCache = device->setupGLPaintShader(paint);
+}
+
+SkGLDevice::AutoPaintShader::~AutoPaintShader() {
+    if (fTexCache) {
+        SkGLDevice::UnlockTexCache(fTexCache);
+    }
+}
+
+SkGLDevice::TexCache* SkGLDevice::setupGLPaintShader(const SkPaint& paint) {
+    SkGL::SetPaint(paint);
+    
+    SkShader* shader = paint.getShader();
+    if (NULL == shader) {
+        return NULL;
+    }
+    
+    if (!shader->setContext(this->accessBitmap(false), paint, this->matrix())) {
+        return NULL;
+    }
+    
+    SkBitmap bitmap;
+    SkMatrix matrix;
+    SkShader::TileMode tileModes[2];
+    if (!shader->asABitmap(&bitmap, &matrix, tileModes)) {
+        return NULL;
+    }
+    
+    bitmap.lockPixels();
+    if (!bitmap.readyToDraw()) {
+        return NULL;
+    }
+    
+    // see if we've already cached the bitmap from the shader
+    SkPoint max;
+    GLuint name;
+    TexCache* cache = SkGLDevice::LockTexCache(bitmap, &name, &max);
+    // the lock has already called glBindTexture for us
+    SkGL::SetTexParams(paint.isFilterBitmap(), tileModes[0], tileModes[1]);
+    
+    // since our texture coords will be in local space, we wack the texture
+    // matrix to map them back into 0...1 before we load it
+    SkMatrix localM;
+    if (shader->getLocalMatrix(&localM)) {
+        SkMatrix inverse;
+        if (localM.invert(&inverse)) {
+            matrix.preConcat(inverse);
+        }
+    }
+    
+    matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height());
+    glMatrixMode(GL_TEXTURE);
+    SkGL::LoadMatrix(matrix);
+    glMatrixMode(GL_MODELVIEW);
+    
+    // since we're going to use a shader/texture, we don't want the color,
+    // just its alpha
+    SkGL::SetAlpha(paint.getAlpha());
+    // report that we have setup the texture
+    return cache;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGLDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+    TRACE_DRAW("coreDrawPaint", this, draw);
+    
+    AutoPaintShader   shader(this, paint);
+    SkGLVertex        vertex[4];
+    const SkGLVertex* texs = shader.useTex() ? vertex : NULL;
+    
+    // set vert to be big enough to fill the space, but not super-huge, to we
+    // don't overflow fixed-point implementations
+    {
+        SkRect r;
+        r.set(this->clip().getBounds());
+        SkMatrix inverse;
+        if (draw.fMatrix->invert(&inverse)) {
+            inverse.mapRect(&r);
+        }
+        vertex->setRectFan(r);
+    }
+    
+    SkGL::DrawVertices(4, GL_TRIANGLE_FAN, vertex, texs, NULL, NULL,
+                       this->updateMatrixClip());
+}
+
+static const GLenum gPointMode2GL[] = {
+    GL_POINTS,
+    GL_LINES,
+    GL_LINE_STRIP
+};
+
+void SkGLDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
+                            size_t count, const SkPoint pts[], const SkPaint& paint) {
+    TRACE_DRAW("coreDrawPoints", this, draw);
+    
+    SkScalar width = paint.getStrokeWidth();
+    if (width < 0) {
+        return;
+    }
+    
+    /*  We should really only use drawverts for hairlines, since gl and skia
+     treat the thickness differently...
+     */
+    
+    AutoPaintShader shader(this, paint);
+    
+    if (width <= 0) {
+        width = SK_Scalar1;
+    }
+    
+    if (SkCanvas::kPoints_PointMode == mode) {
+        glPointSize(SkScalarToFloat(width));
+    } else {
+        glLineWidth(SkScalarToFloat(width));
+    }
+    
+    const SkGLVertex* verts;
+    
+#if GLSCALAR_IS_SCALAR
+    verts = (const SkGLVertex*)pts;
+#else
+    SkAutoSTMalloc<32, SkGLVertex> storage(count);
+    SkGLVertex* v = storage.get();
+    
+    v->setPoints(pts, count);
+    verts = v;
+#endif
+    
+    const SkGLVertex* texs = shader.useTex() ? verts : NULL;
+    
+    SkGL::DrawVertices(count, gPointMode2GL[mode], verts, texs, NULL, NULL,
+                       this->updateMatrixClip());
+}
+
+void SkGLDevice::drawRect(const SkDraw& draw, const SkRect& rect,
+                          const SkPaint& paint) {
+    TRACE_DRAW("coreDrawRect", this, draw);
+    
+    if (paint.getStyle() == SkPaint::kStroke_Style) {
+        return;
+    }
+    
+    if (paint.getStrokeJoin() != SkPaint::kMiter_Join) {
+        SkPath  path;
+        path.addRect(rect);
+        this->drawPath(draw, path, paint);
+        return;
+    }
+    
+    AutoPaintShader shader(this, paint);
+    
+    SkGLVertex vertex[4];
+    vertex->setRectFan(rect);
+    const SkGLVertex* texs = shader.useTex() ? vertex : NULL;
+    
+    SkGL::DrawVertices(4, GL_TRIANGLE_FAN, vertex, texs, NULL, NULL,
+                       this->updateMatrixClip());
+}
+
+void SkGLDevice::drawPath(const SkDraw& draw, const SkPath& path,
+                          const SkPaint& paint) {
+    TRACE_DRAW("coreDrawPath", this, draw);
+    if (paint.getStyle() == SkPaint::kStroke_Style) {
+        return;
+    }
+    
+    AutoPaintShader shader(this, paint);
+    
+    SkGL::FillPath(path, paint, shader.useTex(), this->updateMatrixClip());
+}
+
+void SkGLDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+                            const SkMatrix& m, const SkPaint& paint) {
+    TRACE_DRAW("coreDrawBitmap", this, draw);
+    
+    SkAutoLockPixels alp(bitmap);
+    if (!bitmap.readyToDraw()) {
+        return;
+    }
+    
+    SkGLClipIter* iter = this->updateMatrixClip();
+    
+    SkPoint max;
+    GLenum name;
+    SkAutoLockTexCache(bitmap, &name, &max);
+    // the lock has already called glBindTexture for us
+    SkGL::SetTexParamsClamp(paint.isFilterBitmap());
+    
+    glMatrixMode(GL_TEXTURE);
+    glLoadIdentity();
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    SkGL::MultMatrix(m);
+    
+    SkGLVertex  pts[4], tex[4];
+    
+    pts->setIRectFan(0, 0, bitmap.width(), bitmap.height());
+    tex->setRectFan(0, 0, max.fX, max.fY);
+    
+    // now draw the mesh
+    SkGL::SetPaintAlpha(paint);
+    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    
+    SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter);
+    
+    glPopMatrix();    
+}
+
+// move this guy into SkGL, so we can call it from SkGLDevice
+static void gl_drawSprite(int x, int y, int w, int h, const SkPoint& max,
+                          const SkPaint& paint, SkGLClipIter* iter) {
+    SkGL::SetTexParamsClamp(false);
+    
+    glMatrixMode(GL_TEXTURE);
+    glLoadIdentity();
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadIdentity();
+    
+    SkGLVertex  pts[4], tex[4];
+    
+    // if h < 0, then the texture is bottom-to-top, but since our projection
+    // matrix always inverts Y, we have to re-invert our texture coord here
+    if (h < 0) {
+        h = -h;
+        tex->setRectFan(0, max.fY, max.fX, 0);
+    } else {
+        tex->setRectFan(0, 0, max.fX, max.fY);
+    }
+    pts->setIRectFan(x, y, x + w, y + h);
+    
+    SkGL::SetPaintAlpha(paint);
+    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    
+    // should look to use glDrawTexi() has we do for text...
+    SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter);
+    
+    glPopMatrix();
+}
+
+void SkGLDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+                            int left, int top, const SkPaint& paint) {
+    TRACE_DRAW("coreDrawSprite", this, draw);
+    
+    SkAutoLockPixels alp(bitmap);
+    if (!bitmap.readyToDraw()) {
+        return;
+    }
+    
+    SkGLClipIter* iter = this->updateMatrixClip();
+    
+    SkPoint max;
+    GLuint name;
+    SkAutoLockTexCache(bitmap, &name, &max);    
+    
+    gl_drawSprite(left, top, bitmap.width(), bitmap.height(), max, paint, iter);
+}
+
+void SkGLDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
+                            int x, int y, const SkPaint& paint) {
+    TRACE_DRAW("coreDrawDevice", this, draw);
+    
+    SkGLDevice::TexOrientation to = ((SkGLDevice*)dev)->bindDeviceAsTexture();
+    if (SkGLDevice::kNo_TexOrientation != to) {
+        SkGLClipIter* iter = this->updateMatrixClip();
+        
+        const SkBitmap& bm = dev->accessBitmap(false);
+        int w = bm.width();
+        int h = bm.height();
+        SkPoint max;
+        
+        max.set(SkFixedToScalar(w << (16 - SkNextLog2(bm.rowBytesAsPixels()))),
+                SkFixedToScalar(h << (16 - SkNextLog2(h))));
+        
+        if (SkGLDevice::kBottomToTop_TexOrientation == to) {
+            h = -h;
+        }
+        gl_drawSprite(x, y, w, h, max, paint, iter);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const GLenum gVertexModeToGL[] = {
+    GL_TRIANGLES,       // kTriangles_VertexMode,
+    GL_TRIANGLE_STRIP,  // kTriangleStrip_VertexMode,
+    GL_TRIANGLE_FAN     // kTriangleFan_VertexMode
+};
+
+#include "SkShader.h"
+
+void SkGLDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+                              int vertexCount, const SkPoint vertices[],
+                              const SkPoint texs[], const SkColor colors[],
+                              SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint) {
+
+    if (false) {
+        SkRect bounds;
+        SkIRect ibounds;
+        
+        bounds.set(vertices, vertexCount);
+        bounds.round(&ibounds);
+        
+        SkDebugf("---- drawverts: %d pts, texs=%d colors=%d indices=%d bounds [%d %d]\n",
+                 vertexCount, texs!=0, colors!=0, indexCount, ibounds.width(), ibounds.height());
+    }
+    
+    SkGLClipIter* iter = this->updateMatrixClip();
+    
+    SkGL::SetPaint(paint);
+    
+    const SkGLVertex* glVerts;
+    const SkGLVertex* glTexs = NULL;
+    
+#if GLSCALAR_IS_SCALAR
+    glVerts = (const SkGLVertex*)vertices;
+#else
+    SkAutoSTMalloc<32, SkGLVertex> storage(vertexCount);
+    storage.get()->setPoints(vertices, vertexCount);
+    glVerts = storage.get();
+#endif
+    
+    uint8_t* colorArray = NULL;
+    if (colors) {
+        colorArray = (uint8_t*)sk_malloc_throw(vertexCount*4);
+        SkGL::SetRGBA(colorArray, colors, vertexCount);
+    }
+    SkAutoFree afca(colorArray);
+    
+    SkGLVertex* texArray = NULL;
+    TexCache* cache = NULL;
+
+    if (texs && paint.getShader()) {
+        SkShader* shader = paint.getShader();
+        
+        //        if (!shader->setContext(this->accessBitmap(), paint, *draw.fMatrix)) {
+        if (!shader->setContext(*draw.fBitmap, paint, *draw.fMatrix)) {
+            goto DONE;
+        }
+        
+        SkBitmap bitmap;
+        SkMatrix matrix;
+        SkShader::TileMode tileModes[2];
+        if (shader->asABitmap(&bitmap, &matrix, tileModes)) {
+            SkPoint max;
+            GLuint name;
+            cache = SkGLDevice::LockTexCache(bitmap, &name, &max);
+            if (NULL == cache) {
+                return;
+            }
+
+            matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height());
+            glMatrixMode(GL_TEXTURE);
+            SkGL::LoadMatrix(matrix);
+            glMatrixMode(GL_MODELVIEW);
+            
+#if GLSCALAR_IS_SCALAR
+            glTexs = (const SkGLVertex*)texs;
+#else
+            texArray = (SkGLVertex*)sk_malloc_throw(vertexCount * sizeof(SkGLVertex));
+            texArray->setPoints(texs, vertexCount);
+            glTexs = texArray;
+#endif
+            
+            SkGL::SetPaintAlpha(paint);
+            SkGL::SetTexParams(paint.isFilterBitmap(),
+                               tileModes[0], tileModes[1]);
+        }
+    }
+DONE:
+    SkAutoFree aftex(texArray);
+    
+    SkGL::DrawVertices(indices ? indexCount : vertexCount,
+                       gVertexModeToGL[vmode],
+                       glVerts, glTexs, colorArray, indices, iter);
+    
+    if (cache) {
+        SkGLDevice::UnlockTexCache(cache);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGlyphCache.h"
+#include "SkGLTextCache.h"
+
+void SkGLDevice::GlyphCacheAuxProc(void* data) {
+    SkDebugf("-------------- delete text texture cache\n");
+    SkDELETE((SkGLTextCache*)data);
+}
+
+#ifdef SK_SCALAR_IS_FIXED
+#define SkDiv16ToScalar(numer, denom)    (SkIntToFixed(numer) / (denom))
+#else
+#define SkDiv16ToScalar(numer, denom)    SkScalarDiv(numer, denom)
+#endif
+
+// stolen from SkDraw.cpp - D1G_NoBounder_RectClip
+static void SkGL_Draw1Glyph(const SkDraw1Glyph& state, const SkGlyph& glyph,
+                            int x, int y) {
+    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+
+    SkGLDrawProcs* procs = (SkGLDrawProcs*)state.fDraw->fProcs;
+    
+    x += glyph.fLeft;
+    y  += glyph.fTop;
+    
+    // check if we're clipped out (nothing to draw)
+	SkIRect bounds;
+	bounds.set(x, y, x + glyph.fWidth, y + glyph.fHeight);
+    if (!SkIRect::Intersects(state.fClip->getBounds(), bounds)) {
+        return;
+    }
+    
+    // now dig up our texture cache
+    
+    SkGlyphCache* gcache = state.fCache;
+    void* auxData;
+    SkGLTextCache* textCache = NULL;
+    
+    if (gcache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
+        textCache = (SkGLTextCache*)auxData;            
+    }
+    if (NULL == textCache) {
+        // need to create one
+        textCache = SkNEW(SkGLTextCache);
+        gcache->setAuxProc(SkGLDevice::GlyphCacheAuxProc, textCache);
+    }
+    
+    int offset;
+    SkGLTextCache::Strike* strike = textCache->findGlyph(glyph, &offset);
+    if (NULL == strike) {
+        // make sure the glyph has an image
+        uint8_t* aa = (uint8_t*)glyph.fImage;               
+        if (NULL == aa) {
+            aa = (uint8_t*)gcache->findImage(glyph);
+            if (NULL == aa) {
+                return; // can't rasterize glyph
+            }
+        }
+        strike = textCache->addGlyphAndBind(glyph, aa, &offset);
+        if (NULL == strike) {
+            // too big to cache, need to draw as is...
+            return;
+        }
+    }
+    
+    const int shiftW = strike->widthShift();
+    const int shiftH = strike->heightShift();
+    
+    SkFixed left = offset << (16 - shiftW);
+    SkFixed right = (offset + glyph.fWidth) << (16 - shiftW);
+    SkFixed bottom = glyph.fHeight << (16 - shiftH);
+
+    procs->addQuad(strike->texture(), x, y, glyph, left, right, bottom);
+}
+
+#if 1
+// matches the orientation used in SkGL::setRectFan. Too bad we can't rely on
+// QUADS in android's GL
+static const uint8_t gQuadIndices[] = {
+    0,   1,   2,   0,   2,   3,
+    4,   5,   6,   4,   6,   7,
+    8,   9,  10,   8,  10,  11,
+    12,  13,  14,  12,  14,  15,
+    16,  17,  18,  16,  18,  19,
+    20,  21,  22,  20,  22,  23,
+    24,  25,  26,  24,  26,  27,
+    28,  29,  30,  28,  30,  31,
+    32,  33,  34,  32,  34,  35,
+    36,  37,  38,  36,  38,  39,
+    40,  41,  42,  40,  42,  43,
+    44,  45,  46,  44,  46,  47,
+    48,  49,  50,  48,  50,  51,
+    52,  53,  54,  52,  54,  55,
+    56,  57,  58,  56,  58,  59,
+    60,  61,  62,  60,  62,  63,
+    64,  65,  66,  64,  66,  67,
+    68,  69,  70,  68,  70,  71,
+    72,  73,  74,  72,  74,  75,
+    76,  77,  78,  76,  78,  79,
+    80,  81,  82,  80,  82,  83,
+    84,  85,  86,  84,  86,  87,
+    88,  89,  90,  88,  90,  91,
+    92,  93,  94,  92,  94,  95,
+    96,  97,  98,  96,  98,  99,
+    100, 101, 102, 100, 102, 103,
+    104, 105, 106, 104, 106, 107,
+    108, 109, 110, 108, 110, 111,
+    112, 113, 114, 112, 114, 115,
+    116, 117, 118, 116, 118, 119,
+    120, 121, 122, 120, 122, 123,
+    124, 125, 126, 124, 126, 127
+};
+#else
+static void generateQuadIndices(int n) {
+    int index = 0;
+    for (int i = 0; i < n; i++) {
+        SkDebugf("    %3d, %3d, %3d, %3d, %3d, %3d,\n",
+                 index, index + 1, index + 2, index, index + 2, index + 3);
+        index += 4;
+    }
+}
+#endif
+
+void SkGLDrawProcs::drawQuads() {
+    SkASSERT(SK_ARRAY_COUNT(gQuadIndices) == MAX_QUADS * 6);
+
+    glBindTexture(GL_TEXTURE_2D, fCurrTexture);
+
+#if 0
+    static bool gOnce;
+    if (!gOnce) {
+        generateQuadIndices(MAX_QUADS);
+        gOnce = true;
+    }
+#endif
+
+    // convert from quad vertex count to triangle vertex count
+    // 6/4 * n == n + (n >> 1) since n is always a multiple of 4
+    SkASSERT((fCurrQuad & 3) == 0);
+    int count = fCurrQuad + (fCurrQuad >> 1);
+
+    if (fClip->isComplex()) {
+        SkGLClipIter iter(fViewportHeight);
+        iter.reset(*fClip);
+        while (!iter.done()) {
+            iter.scissor();
+            glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices);
+            iter.next();
+        }
+    } else {
+        glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices);
+    }
+}
+
+void SkGLDevice::setupForText(SkDraw* draw, const SkPaint& paint) {
+    // we handle complex clips in the SkDraw common code, so we don't check
+    // for it here
+    this->updateMatrixClip();
+    
+    SkGL::SetPaint(paint, false);
+    
+    glMatrixMode(GL_TEXTURE);
+    glLoadIdentity();
+    
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadIdentity();
+
+    // deferred allocation
+    if (NULL == fDrawProcs) {
+        fDrawProcs = SkNEW(SkGLDrawProcs);
+        fDrawProcs->fD1GProc = SkGL_Draw1Glyph;
+    }
+
+    // init our (and GL's) state
+    fDrawProcs->init(draw->fClip, this->height());
+    // assign to the caller's SkDraw
+    draw->fProcs = fDrawProcs;
+
+    glEnable(GL_TEXTURE_2D);
+    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    glShadeModel(GL_FLAT); 
+}
+
+void SkGLDevice::drawText(const SkDraw& draw, const void* text,
+                          size_t byteLength, SkScalar x, SkScalar y,
+                          const SkPaint& paint) {
+    /*  Currently, perspective text is draw via paths, invoked directly by
+     SkDraw. This can't work for us, since the bitmap that our draw points
+     to has no pixels, so we just abort if we're in perspective.
+     
+     Better fix would be to...
+     - have a callback inside draw to handle path drawing
+     - option to have draw call the font cache, which we could patch (?)
+     */
+    if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
+        return;
+    }
+    
+    SkDraw myDraw(draw);
+    this->setupForText(&myDraw, paint);
+    this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
+    fDrawProcs->flush();
+    glPopMatrix();  // GL_MODELVIEW
+}
+
+void SkGLDevice::drawPosText(const SkDraw& draw, const void* text,
+                             size_t byteLength, const SkScalar pos[],
+                             SkScalar constY, int scalarsPerPos,
+                             const SkPaint& paint) {
+    if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
+        return;
+    }
+    
+    SkDraw myDraw(draw);
+    this->setupForText(&myDraw, paint);
+    this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
+                                 scalarsPerPos, paint);
+    fDrawProcs->flush();
+    glPopMatrix();  // GL_MODELVIEW
+}
+
+void SkGLDevice::drawTextOnPath(const SkDraw& draw, const void* text,
+                                size_t byteLength, const SkPath& path,
+                                const SkMatrix* m, const SkPaint& paint) {
+    // not supported yet
+}
+
diff --git a/src/gl/SkGLDevice.h b/src/gl/SkGLDevice.h
new file mode 100644
index 0000000..0fc9e47
--- /dev/null
+++ b/src/gl/SkGLDevice.h
@@ -0,0 +1,124 @@
+#ifndef SkGLDevice_DEFINED
+#define SkGLDevice_DEFINED
+
+#include "SkDevice.h"
+#include "SkGL.h"
+#include "SkRegion.h"
+
+struct SkGLDrawProcs;
+
+class SkGLDevice : public SkDevice {
+public:
+    SkGLDevice(const SkBitmap& bitmap, bool offscreen);
+    virtual ~SkGLDevice();
+
+    // used to identify GLTextCache data in the glyphcache
+    static void GlyphCacheAuxProc(void* data);    
+    
+    enum TexOrientation {
+        kNo_TexOrientation,
+        kTopToBottom_TexOrientation,
+        kBottomToTop_TexOrientation
+    };
+
+    /** Called when this device is no longer a candidate for a render target,
+        but will instead be used as a texture to be drawn. Be sure to call
+        the base impl if you override, as it will compute size and max.
+    */
+    virtual TexOrientation bindDeviceAsTexture();
+
+    // returns true if complex
+    SkGLClipIter* updateMatrixClip();
+    // call to set the clip to the specified rect
+    void scissor(const SkIRect&);
+
+    // overrides from SkDevice
+    virtual void gainFocus(SkCanvas*);
+    virtual void setMatrixClip(const SkMatrix& matrix, const SkRegion& clip);
+
+    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& path,
+                          const SkPaint& paint);
+    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+                            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&);
+
+protected:
+    /** Return the current glmatrix, from a previous call to setMatrixClip */
+    const SkMatrix& matrix() const { return fMatrix; }
+    /** Return the current clip, from a previous call to setMatrixClip */
+    const SkRegion& clip() const { return fClip; }
+
+private:
+    SkGLMatrix  fGLMatrix;
+    SkMatrix    fMatrix;
+    SkRegion    fClip;
+    bool        fDirty;
+
+    SkGLClipIter fClipIter;
+    SkGLDrawProcs* fDrawProcs;
+
+    void setupForText(SkDraw* draw, const SkPaint& paint);
+
+    // global texture cache methods
+    class TexCache;
+    static TexCache* LockTexCache(const SkBitmap&, GLuint* name,
+                                    SkPoint* size);
+    static void UnlockTexCache(TexCache*);
+    class SkAutoLockTexCache {
+    public:
+        SkAutoLockTexCache(const SkBitmap& bitmap, GLuint* name,
+                       SkPoint* size) {
+            fTex = SkGLDevice::LockTexCache(bitmap, name, size);
+        }
+        ~SkAutoLockTexCache() {
+            if (fTex) {
+                SkGLDevice::UnlockTexCache(fTex);
+            }
+        }
+        TexCache* get() const { return fTex; }
+    private:
+        TexCache* fTex;
+    };
+    friend class SkAutoTexCache;
+    
+    // returns cache if the texture is bound for the shader
+    TexCache* setupGLPaintShader(const SkPaint& paint);
+    
+    class AutoPaintShader {
+    public:
+        AutoPaintShader(SkGLDevice*, const SkPaint& paint);
+        ~AutoPaintShader();
+        
+        bool useTex() const { return fTexCache != 0; }
+    private:
+        SkGLDevice* fDevice;
+        TexCache*   fTexCache;
+    };
+    friend class AutoPaintShader;
+        
+    typedef SkDevice INHERITED;
+};
+
+#endif
+
diff --git a/src/gl/SkGLDevice_FBO.cpp b/src/gl/SkGLDevice_FBO.cpp
new file mode 100644
index 0000000..552d619
--- /dev/null
+++ b/src/gl/SkGLDevice_FBO.cpp
@@ -0,0 +1,57 @@
+#include "SkGLDevice_FBO.h"
+#include "SkRegion.h"
+
+SkGLDevice_FBO::SkGLDevice_FBO(const SkBitmap& bitmap, bool offscreen)
+        : SkGLDevice(bitmap, offscreen) {
+    fFBO = 0;
+    fTextureID = 0;
+
+    if (offscreen) {
+        int nw = SkNextPow2(bitmap.rowBytesAsPixels());
+        int nh = SkNextPow2(bitmap.height());
+        
+        glGenFramebuffersEXT(1, &fFBO);
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFBO);
+
+        glGenTextures(1, &fTextureID);
+        glBindTexture(GL_TEXTURE_2D, fTextureID);
+        SkGL::SetTexParamsClamp(false);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nw, nh, 0,
+                     GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+                                  GL_TEXTURE_2D, fTextureID, 0);
+        GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+        if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+            SkDebugf("-- glCheckFramebufferStatusEXT %x\n", status);
+        }
+
+        // now reset back to "normal" drawing target
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+    }
+}
+
+SkGLDevice_FBO::~SkGLDevice_FBO() {
+    if (fTextureID) {
+        glDeleteTextures(1, &fTextureID);
+    }
+    if (fFBO) {
+        glDeleteFramebuffersEXT(1, &fFBO);
+    }
+}
+
+SkGLDevice::TexOrientation SkGLDevice_FBO::bindDeviceAsTexture() {
+    if (fTextureID) {
+        glBindTexture(GL_TEXTURE_2D, fTextureID);
+        return kBottomToTop_TexOrientation;
+    }
+    return kNo_TexOrientation;
+}
+
+void SkGLDevice_FBO::gainFocus(SkCanvas* canvas) {
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFBO);
+
+    // now we're ready for the viewport and projection matrix
+    this->INHERITED::gainFocus(canvas);
+}
+
diff --git a/src/gl/SkGLDevice_FBO.h b/src/gl/SkGLDevice_FBO.h
new file mode 100644
index 0000000..d695ff0
--- /dev/null
+++ b/src/gl/SkGLDevice_FBO.h
@@ -0,0 +1,23 @@
+#ifndef SkGLDevice_FBO_DEFINED
+#define SkGLDevice_FBO_DEFINED
+
+#include "SkGLDevice.h"
+
+class SkGLDevice_FBO : public SkGLDevice {
+public:
+    SkGLDevice_FBO(const SkBitmap& bitmap, bool offscreen);
+    virtual ~SkGLDevice_FBO();
+
+    // overrides from SkGLDevice
+    virtual void gainFocus(SkCanvas*);
+    virtual TexOrientation bindDeviceAsTexture();
+
+private:
+    GLuint  fFBO;
+    GLuint  fTextureID;
+
+    typedef SkGLDevice INHERITED;
+};
+
+#endif
+
diff --git a/src/gl/SkGLDevice_SWLayer.cpp b/src/gl/SkGLDevice_SWLayer.cpp
new file mode 100644
index 0000000..4b75d4c
--- /dev/null
+++ b/src/gl/SkGLDevice_SWLayer.cpp
@@ -0,0 +1,91 @@
+#include "SkGLDevice_SWLayer.h"
+#include "SkRegion.h"
+
+SkGLDevice_SWLayer::SkGLDevice_SWLayer(const SkBitmap& bitmap)
+        : SkGLDevice(bitmap, true) {
+    fTextureID = 0;
+
+    SkASSERT(bitmap.getPixels());
+}
+
+SkGLDevice_SWLayer::~SkGLDevice_SWLayer() {
+    if (fTextureID) {
+        glDeleteTextures(1, &fTextureID);
+    }
+}
+
+SkGLDevice::TexOrientation SkGLDevice_SWLayer::bindDeviceAsTexture() {
+    const SkBitmap& bitmap = this->accessBitmap(false);
+
+    if (0 == fTextureID) {
+        fTextureID = SkGL::BindNewTexture(bitmap, NULL);
+    }
+    return kTopToBottom_TexOrientation;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkDraw.h"
+
+void SkGLDevice_SWLayer::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+    draw.drawPaint(paint);
+}
+
+void SkGLDevice_SWLayer::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
+                          const SkPoint pts[], const SkPaint& paint) {
+    draw.drawPoints(mode, count, pts, paint);
+}
+
+void SkGLDevice_SWLayer::drawRect(const SkDraw& draw, const SkRect& r,
+                        const SkPaint& paint) {
+    draw.drawRect(r, paint);
+}
+
+void SkGLDevice_SWLayer::drawPath(const SkDraw& draw, const SkPath& path,
+                        const SkPaint& paint) {
+    draw.drawPath(path, paint);
+}
+
+void SkGLDevice_SWLayer::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+                          const SkMatrix& matrix, const SkPaint& paint) {
+    draw.drawBitmap(bitmap, matrix, paint);
+}
+
+void SkGLDevice_SWLayer::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+                          int x, int y, const SkPaint& paint) {
+    draw.drawSprite(bitmap, x, y, paint);
+}
+
+void SkGLDevice_SWLayer::drawText(const SkDraw& draw, const void* text, size_t len,
+                        SkScalar x, SkScalar y, const SkPaint& paint) {
+    draw.drawText((const char*)text, len, x, y, paint);
+}
+
+void SkGLDevice_SWLayer::drawPosText(const SkDraw& draw, const void* text, size_t len,
+                           const SkScalar xpos[], SkScalar y,
+                           int scalarsPerPos, const SkPaint& paint) {
+    draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint);
+}
+
+void SkGLDevice_SWLayer::drawTextOnPath(const SkDraw& draw, const void* text,
+                              size_t len, const SkPath& path,
+                              const SkMatrix* matrix,
+                              const SkPaint& paint) {
+    draw.drawTextOnPath((const char*)text, len, path, matrix, paint);
+}
+
+void SkGLDevice_SWLayer::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+                            int vertexCount,
+                            const SkPoint verts[], const SkPoint textures[],
+                            const SkColor colors[], SkXfermode* xmode,
+                            const uint16_t indices[], int indexCount,
+                            const SkPaint& paint) {
+    draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode,
+                      indices, indexCount, paint);
+}
+
+void SkGLDevice_SWLayer::drawDevice(const SkDraw& draw, SkDevice* dev,
+                                    int x, int y, const SkPaint& paint) {
+    this->SkDevice::drawDevice(draw, dev, x, y, paint);
+}
+
diff --git a/src/gl/SkGLDevice_SWLayer.h b/src/gl/SkGLDevice_SWLayer.h
new file mode 100644
index 0000000..7e61370
--- /dev/null
+++ b/src/gl/SkGLDevice_SWLayer.h
@@ -0,0 +1,49 @@
+#ifndef SkGLDevice_SWLayer_DEFINED
+#define SkGLDevice_SWLayer_DEFINED
+
+#include "SkGLDevice.h"
+
+class SkGLDevice_SWLayer : public SkGLDevice {
+public:
+    SkGLDevice_SWLayer(const SkBitmap& bitmap);
+    virtual ~SkGLDevice_SWLayer();
+
+    // overrides from SkGLDevice
+    virtual TexOrientation bindDeviceAsTexture();
+
+    // overrides from SkDevice
+    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& path,
+                          const SkPaint& paint);
+    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+                            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&);
+
+private:
+    GLuint  fTextureID;
+
+    typedef SkGLDevice INHERITED;
+};
+
+#endif
+
diff --git a/src/gl/SkGLTextCache.cpp b/src/gl/SkGLTextCache.cpp
new file mode 100644
index 0000000..141e100
--- /dev/null
+++ b/src/gl/SkGLTextCache.cpp
@@ -0,0 +1,191 @@
+#include "SkGLTextCache.h"
+#include "SkScalerContext.h"
+#include "SkTSearch.h"
+
+const GLenum gTextTextureFormat = GL_ALPHA;
+const GLenum gTextTextureType = GL_UNSIGNED_BYTE;
+
+SkGLTextCache::Strike::Strike(Strike* next, int width, int height) {
+    fStrikeWidth = SkNextPow2(SkMax32(kMinStrikeWidth, width));
+    fStrikeHeight = SkNextPow2(height);
+    fGlyphCount = 0;
+    fNextFreeOffsetX = 0;
+    fNext = next;
+
+    fStrikeWidthShift = SkNextLog2(fStrikeWidth);
+    fStrikeHeightShift = SkNextLog2(fStrikeHeight);
+    
+    if (next) {
+        SkASSERT(next->fStrikeHeight == fStrikeHeight);
+    }
+
+    // create an empty texture to receive glyphs
+    fTexName = 0;
+    glGenTextures(1, &fTexName);
+    glBindTexture(GL_TEXTURE_2D, fTexName);
+    glTexImage2D(GL_TEXTURE_2D, 0, gTextTextureFormat, 
+                 fStrikeWidth, fStrikeHeight, 0,
+                 gTextTextureFormat, gTextTextureType, NULL);
+    
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);    
+}
+
+SkGLTextCache::Strike::~Strike() {
+    if (fTexName != 0) {
+        glDeleteTextures(1, &fTexName);
+    }
+}
+
+SkGLTextCache::Strike*
+SkGLTextCache::Strike::findGlyph(const SkGlyph& glyph, int* offset) {
+    Strike* strike = this;
+    SkDEBUGCODE(const int height = SkNextPow2(glyph.fHeight);)
+    
+    do {
+        SkASSERT(height == strike->fStrikeHeight);
+        
+        int index = SkTSearch(strike->fGlyphIDArray, strike->fGlyphCount,
+                              glyph.fID, sizeof(strike->fGlyphIDArray[0]));
+        if (index >= 0) {
+            if (offset) {
+                *offset = strike->fGlyphOffsetX[index];
+            }
+            return strike;
+        }
+        strike = strike->fNext;
+    } while (NULL != strike);
+    return NULL;
+}
+
+static void make_a_whole(void* buffer, int index, int count, size_t elemSize) {
+    SkASSERT(index >= 0 && index <= count);
+    size_t offset = index * elemSize;
+    memmove((char*)buffer + offset + elemSize,
+            (const char*)buffer + offset,
+            (count - index) * elemSize);
+}
+
+SkGLTextCache::Strike*
+SkGLTextCache::Strike::addGlyphAndBind(const SkGlyph& glyph,
+                                       const uint8_t image[], int* offset) {
+#ifdef SK_DEBUG
+    SkASSERT(this->findGlyph(glyph, NULL) == NULL);
+    const int height = SkNextPow2(glyph.fHeight);
+    SkASSERT(height <= fStrikeHeight && height > (fStrikeHeight >> 1));
+#endif
+
+    int rowBytes = glyph.rowBytes();
+    SkASSERT(rowBytes >= glyph.fWidth);
+
+    Strike* strike;
+    if (fGlyphCount == kMaxGlyphCount ||
+            fNextFreeOffsetX + rowBytes >= fStrikeWidth) {
+        // this will bind the next texture for us
+//        SkDebugf("--- extend strike %p\n", this);
+        strike = SkNEW_ARGS(Strike, (this, rowBytes, glyph.fHeight));
+    } else {
+        glBindTexture(GL_TEXTURE_2D, fTexName);
+        strike = this;
+    }
+    
+    uint32_t* idArray = strike->fGlyphIDArray;
+    uint16_t* offsetArray = strike->fGlyphOffsetX;
+    const int glyphCount = strike->fGlyphCount;
+
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+    glTexSubImage2D(GL_TEXTURE_2D, 0, strike->fNextFreeOffsetX, 0, rowBytes,
+                    glyph.fHeight, gTextTextureFormat, gTextTextureType,
+                    image);
+
+    // need to insert the offset
+    int index = SkTSearch(idArray, glyphCount, glyph.fID, sizeof(idArray[0]));
+    SkASSERT(index < 0);
+    index = ~index; // this is where we should insert it
+    make_a_whole(idArray, index, glyphCount, sizeof(idArray));
+    make_a_whole(offsetArray, index, glyphCount, sizeof(offsetArray[0]));
+    idArray[index] = glyph.fID;
+    offsetArray[index] = strike->fNextFreeOffsetX;
+    if (offset) {
+        *offset = strike->fNextFreeOffsetX;
+    }
+
+#if 0
+    SkDebugf("--- strike %p glyph %x [%d %d] offset %d count %d\n",
+             strike, glyph.fID, glyph.fWidth, glyph.fHeight,
+             strike->fNextFreeOffsetX, glyphCount + 1);
+#endif
+
+    // now update our header
+    strike->fGlyphCount = glyphCount + 1;
+    strike->fNextFreeOffsetX += glyph.fWidth;
+    return strike;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGLTextCache::SkGLTextCache() {
+    bzero(fStrikeList, sizeof(fStrikeList));
+}
+
+SkGLTextCache::~SkGLTextCache() {
+    this->deleteAllStrikes(true);
+}
+
+void SkGLTextCache::deleteAllStrikes(bool texturesAreValid) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(fStrikeList); i++) {
+        Strike* strike = fStrikeList[i];
+        while (strike != NULL) {
+            Strike* next = strike->fNext;
+            if (!texturesAreValid) {
+                strike->abandonTexture();
+            }
+            SkDELETE(strike);
+            strike = next;
+        }
+    }
+    bzero(fStrikeList, sizeof(fStrikeList));
+}
+
+SkGLTextCache::Strike* SkGLTextCache::findGlyph(const SkGlyph& glyph,
+                                                int* offset) {
+    SkASSERT(glyph.fWidth != 0);
+    SkASSERT(glyph.fHeight != 0);
+
+    size_t index = SkNextLog2(glyph.fHeight);
+    if (index >= SK_ARRAY_COUNT(fStrikeList)) {
+        // too big for us to cache;
+        return NULL;
+    }
+
+    Strike* strike = fStrikeList[index];
+    if (strike) {
+        strike = strike->findGlyph(glyph, offset);
+    }
+    return strike;
+}
+
+SkGLTextCache::Strike* SkGLTextCache::addGlyphAndBind(const SkGlyph& glyph,
+                                        const uint8_t image[], int* offset) {
+    SkASSERT(image != NULL);
+    SkASSERT(glyph.fWidth != 0);
+    SkASSERT(glyph.fHeight != 0);
+    
+    size_t index = SkNextLog2(glyph.fHeight);
+    if (index >= SK_ARRAY_COUNT(fStrikeList)) {
+        // too big for us to cache;
+        return NULL;
+    }
+    
+    Strike* strike = fStrikeList[index];
+    if (NULL == strike) {
+        strike = SkNEW_ARGS(Strike, (NULL, glyph.rowBytes(), glyph.fHeight));
+//        SkDebugf("--- create strike [%d] %p cache %p\n", index, strike, this);
+    }
+    strike = strike->addGlyphAndBind(glyph, image, offset);
+    fStrikeList[index] = strike;
+    return strike;
+}
+
diff --git a/src/gl/SkGLTextCache.h b/src/gl/SkGLTextCache.h
new file mode 100644
index 0000000..eb552aa
--- /dev/null
+++ b/src/gl/SkGLTextCache.h
@@ -0,0 +1,86 @@
+#ifndef SkGLTextCache_DEFINED
+#define SkGLTextCache_DEFINED
+
+#include "SkGL.h"
+
+class SkGlyph;
+
+class SkGLTextCache {
+public:
+    SkGLTextCache();
+    ~SkGLTextCache();
+    
+    /** Delete all of the strikes in the cache. Pass true if the texture IDs are
+        still valid, in which case glDeleteTextures will be called. Pass false
+        if they are invalid (e.g. the gl-context has changed), in which case
+        they will just be abandoned.
+    */
+    void deleteAllStrikes(bool texturesAreValid);
+
+    class Strike {
+    public:
+        int width() const { return fStrikeWidth; }
+        int height() const { return fStrikeHeight; }
+        GLuint texture() const { return fTexName; }
+        int widthShift() const { return fStrikeWidthShift; }
+        int heightShift() const { return fStrikeHeightShift; }
+
+        // call this to force us to ignore the texture name in our destructor
+        // only call it right before our destructor
+        void abandonTexture() { fTexName = 0; }
+
+    private:
+        // if next is non-null, its height must match our height
+        Strike(Strike* next, int width, int height);
+        ~Strike();
+
+        Strike* findGlyph(const SkGlyph&, int* offset);
+        Strike* addGlyphAndBind(const SkGlyph&, const uint8_t*, int* offset);
+
+        enum {
+            kMinStrikeWidth = 1024,
+            kMaxGlyphCount = 256
+        };
+
+        Strike*     fNext;
+        GLuint      fTexName;
+        uint32_t    fGlyphIDArray[kMaxGlyphCount];  // stores glyphIDs
+        uint16_t    fGlyphOffsetX[kMaxGlyphCount];  // stores x-offsets
+        uint16_t    fGlyphCount;
+        uint16_t    fNextFreeOffsetX;
+        uint16_t    fStrikeWidth;
+        uint16_t    fStrikeHeight;
+        uint8_t     fStrikeWidthShift;      // pow2(fStrikeWidth)
+        uint8_t     fStrikeHeightShift;     // pow2(fStrikeHeight)
+
+        friend class SkGLTextCache;
+    };
+
+    /** If found, returns the exact strike containing it (there may be more than
+        one with a given height), and sets offset to the offset for that glyph
+        (if not null). Does NOT bind the texture.
+        If not found, returns null and ignores offset param.
+    */
+    Strike* findGlyph(const SkGlyph&, int* offset);
+
+    /** Adds the specified glyph to this list of strikes, returning the new
+        head of the list. If offset is not null, it is set to the offset
+        for this glyph within the strike. The associated texture is bound
+        to the gl context.
+     */
+    Strike* addGlyphAndBind(const SkGlyph&, const uint8_t image[], int* offset);
+
+private:
+    enum {
+        // greater than this we won't cache
+        kMaxGlyphHeightShift = 9,
+        
+        kMaxGlyphHeight = 1 << kMaxGlyphHeightShift,
+        kMaxStrikeListCount = kMaxGlyphHeightShift + 1
+    };
+
+    // heads of the N families, one for each pow2 height
+    Strike* fStrikeList[kMaxStrikeListCount];
+};
+
+#endif
diff --git a/src/gl/SkTextureCache.cpp b/src/gl/SkTextureCache.cpp
new file mode 100644
index 0000000..17b37ca
--- /dev/null
+++ b/src/gl/SkTextureCache.cpp
@@ -0,0 +1,363 @@
+#include "SkTextureCache.h"
+
+//#define TRACE_HASH_HITS
+//#define TRACE_TEXTURE_CACHE_PURGE
+
+SkTextureCache::Entry::Entry(const SkBitmap& bitmap)
+        : fName(0), fKey(bitmap), fPrev(NULL), fNext(NULL) {
+
+    fMemSize = SkGL::ComputeTextureMemorySize(bitmap);
+    fLockCount = 0;
+}
+
+SkTextureCache::Entry::~Entry() {
+    if (fName != 0) {
+        glDeleteTextures(1, &fName);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTextureCache::SkTextureCache(size_t countMax, size_t sizeMax)
+        : fHead(NULL), fTail(NULL),
+          fTexCountMax(countMax), fTexSizeMax(sizeMax),
+          fTexCount(0), fTexSize(0) {
+
+    bzero(fHash, sizeof(fHash));
+    this->validate();
+}
+
+SkTextureCache::~SkTextureCache() {
+#ifdef SK_DEBUG
+    Entry* entry = fHead;
+    while (entry) {
+        SkASSERT(entry->lockCount() == 0);
+        entry = entry->fNext;
+    }
+#endif
+    this->validate();
+}
+
+void SkTextureCache::deleteAllCaches(bool texturesAreValid) {
+    this->validate();
+    
+    Entry* entry = fHead;
+    while (entry) {
+        Entry* next = entry->fNext;
+        if (!texturesAreValid) {
+            entry->abandonTexture();
+        }
+        SkDELETE(entry);
+        entry = next;
+    }
+    
+    fSorted.reset();
+    bzero(fHash, sizeof(fHash));
+    
+    fTexCount = 0;
+    fTexSize = 0;
+    
+    fTail = fHead = NULL;
+    
+    this->validate();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkTextureCache::findInSorted(const Key& key) const {
+    int count = fSorted.count();
+    if (count == 0) {
+        return ~0;
+    }
+
+    Entry** sorted = fSorted.begin();
+    int lo = 0;
+    int hi = count - 1;
+    while (lo < hi) {
+        int mid = (hi + lo) >> 1;
+        if (sorted[mid]->getKey() < key) {
+            lo = mid + 1;
+        } else {
+            hi = mid;
+        }
+    }
+    
+    // hi is now our best guess
+    const Entry* entry = sorted[hi];
+    if (entry->getKey() == key) {
+        return hi;
+    }
+    
+    // return where to insert it
+    if (entry->getKey() < key) {
+        hi += 1;
+    }
+    return ~hi; // we twiddle to indicate not-found
+}
+
+#ifdef TRACE_HASH_HITS
+static int gHashHits;
+static int gSortedHits;
+#endif
+
+SkTextureCache::Entry* SkTextureCache::find(const Key& key, int* insert) const {
+    int count = fSorted.count();
+    if (count == 0) {
+        *insert = 0;
+        return NULL;
+    }
+
+    // check the hash first
+    int hashIndex = key.getHashIndex();
+    Entry* entry = fHash[hashIndex];    
+    if (NULL != entry && entry->getKey() == key) {
+#ifdef TRACE_HASH_HITS
+        gHashHits += 1;
+#endif
+        return entry;
+    }
+    
+    int index = this->findInSorted(key);
+    if (index >= 0) {
+#ifdef TRACE_HASH_HITS
+        gSortedHits += 1;
+#endif
+        entry = fSorted[index];
+        fHash[hashIndex] = entry;
+        return entry;
+    }
+    
+    // ~index is where to insert the entry
+    *insert = ~index;
+    return NULL;
+}
+
+SkTextureCache::Entry* SkTextureCache::lock(const SkBitmap& bitmap) {
+    this->validate();
+    
+    // call this before we call find(), so we don't reorder after find() and
+    // invalidate our index
+    this->purgeIfNecessary(SkGL::ComputeTextureMemorySize(bitmap));
+
+    Key key(bitmap);
+    int index;
+    Entry* entry = this->find(key, &index);
+
+    if (NULL == entry) {
+        entry = SkNEW_ARGS(Entry, (bitmap));
+        
+        entry->fName = SkGL::BindNewTexture(bitmap, &entry->fTexSize);
+        if (0 == entry->fName) {
+            SkDELETE(entry);
+            return NULL;
+        }
+        fHash[key.getHashIndex()] = entry;
+        *fSorted.insert(index) = entry;
+
+        fTexCount += 1;
+        fTexSize += entry->memSize();
+    } else {
+        // detach from our llist
+        Entry* prev = entry->fPrev;
+        Entry* next = entry->fNext;
+        if (prev) {
+            prev->fNext = next;
+        } else {
+            SkASSERT(fHead == entry);
+            fHead = next;
+        }
+        if (next) {
+            next->fPrev = prev;
+        } else {
+            SkASSERT(fTail == entry);
+            fTail = prev;
+        }
+        // now bind the texture
+        glBindTexture(GL_TEXTURE_2D, entry->fName);
+    }
+    
+    // add to head of llist for LRU
+    entry->fPrev = NULL;
+    entry->fNext = fHead;
+    if (NULL != fHead) {
+        SkASSERT(NULL == fHead->fPrev);
+        fHead->fPrev = entry;
+    }
+    fHead = entry;
+    if (NULL == fTail) {
+        fTail = entry;
+    }
+    
+    this->validate();
+    entry->lock();
+    
+#ifdef TRACE_HASH_HITS
+    SkDebugf("---- texture cache hash=%d sorted=%d\n", gHashHits, gSortedHits);
+#endif
+    return entry;
+}
+
+void SkTextureCache::unlock(Entry* entry) {
+    this->validate();
+
+#ifdef SK_DEBUG
+    SkASSERT(entry);
+    int index = this->findInSorted(entry->getKey());
+    SkASSERT(fSorted[index] == entry);
+#endif
+
+    SkASSERT(entry->fLockCount > 0);
+    entry->unlock();
+}
+
+void SkTextureCache::purgeIfNecessary(size_t extraSize) {
+    this->validate();
+
+    size_t countMax = fTexCountMax;
+    size_t sizeMax = fTexSizeMax;
+    
+    // take extraSize into account, but watch for underflow of size_t
+    if (extraSize > sizeMax) {
+        sizeMax = 0;
+    } else {
+        sizeMax -= extraSize;
+    }
+
+    Entry* entry = fTail;
+    while (entry) {
+        if (fTexCount <= countMax && fTexSize <= sizeMax) {
+            break;
+        }
+
+        Entry* prev = entry->fPrev;
+        // don't purge an entry that is locked
+        if (entry->isLocked()) {
+            entry = prev;
+            continue;
+        }
+
+        fTexCount -= 1;
+        fTexSize -= entry->memSize();
+
+        // remove from our sorted and hash arrays
+        int index = this->findInSorted(entry->getKey());
+        SkASSERT(index >= 0);
+        fSorted.remove(index);
+        index = entry->getKey().getHashIndex();
+        if (entry == fHash[index]) {
+            fHash[index] = NULL;
+        }
+
+        // now detach it from our llist
+        Entry* next = entry->fNext;
+        if (prev) {
+            prev->fNext = next;
+        } else {
+            fHead = next;
+        }
+        if (next) {
+            next->fPrev = prev;
+        } else {
+            fTail = prev;
+        }
+        
+        // now delete it
+#ifdef TRACE_TEXTURE_CACHE_PURGE
+        SkDebugf("---- purge texture cache %d size=%d\n",
+                 entry->name(), entry->memSize());
+#endif
+        SkDELETE(entry);
+        
+        // keep going
+        entry = prev;
+    }
+
+    this->validate();
+}
+
+void SkTextureCache::setMaxCount(size_t count) {
+    if (fTexCountMax != count) {
+        fTexCountMax = count;
+        this->purgeIfNecessary(0);
+    }
+}
+
+void SkTextureCache::setMaxSize(size_t size) {
+    if (fTexSizeMax != size) {
+        fTexSizeMax = size;
+        this->purgeIfNecessary(0);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+void SkTextureCache::validate() const {
+    if (0 == fTexCount) {
+        SkASSERT(0 == fTexSize);
+        SkASSERT(NULL == fHead);
+        SkASSERT(NULL == fTail);
+        return;
+    }
+
+    SkASSERT(fTexSize); // do we allow a zero-sized texture?
+    SkASSERT(fHead);
+    SkASSERT(fTail);
+    
+    SkASSERT(NULL == fHead->fPrev);
+    SkASSERT(NULL == fTail->fNext);
+    if (1 == fTexCount) {
+        SkASSERT(fHead == fTail);
+    }
+
+    const Entry* entry = fHead;
+    size_t count = 0;
+    size_t size = 0;
+    size_t i;
+
+    while (entry != NULL) {
+        SkASSERT(count < fTexCount);
+        SkASSERT(size < fTexSize);
+        size += entry->memSize();
+        count += 1;
+        if (NULL == entry->fNext) {
+            SkASSERT(fTail == entry);
+        }
+        entry = entry->fNext;
+    }
+    SkASSERT(count == fTexCount);
+    SkASSERT(size == fTexSize);
+
+    count = 0;
+    size = 0;
+    entry = fTail;
+    while (entry != NULL) {
+        SkASSERT(count < fTexCount);
+        SkASSERT(size < fTexSize);
+        size += entry->memSize();
+        count += 1;
+        if (NULL == entry->fPrev) {
+            SkASSERT(fHead == entry);
+        }
+        entry = entry->fPrev;
+    }
+    SkASSERT(count == fTexCount);
+    SkASSERT(size == fTexSize);
+    
+    SkASSERT(count == (size_t)fSorted.count());
+    for (i = 1; i < count; i++) {
+        SkASSERT(fSorted[i-1]->getKey() < fSorted[i]->getKey());
+    }
+    
+    for (i = 0; i < kHashCount; i++) {
+        if (fHash[i]) {
+            size_t index = fHash[i]->getKey().getHashIndex();
+            SkASSERT(index == i);
+            index = fSorted.find(fHash[i]);
+            SkASSERT((size_t)index < count);
+        }
+    }
+}
+#endif
+
+
diff --git a/src/gl/SkTextureCache.h b/src/gl/SkTextureCache.h
new file mode 100644
index 0000000..0bc3091
--- /dev/null
+++ b/src/gl/SkTextureCache.h
@@ -0,0 +1,161 @@
+#ifndef SkTextureCache_DEFINED
+#define SkTextureCache_DEFINED
+
+#include "SkBitmap.h"
+#include "SkPoint.h"
+#include "SkGL.h"
+#include "SkTDArray.h"
+
+class SkTextureCache {
+public:
+    SkTextureCache(size_t maxCount, size_t maxSize);
+    ~SkTextureCache();
+    
+    size_t getMaxCount() { return fTexCountMax; }
+    size_t getMaxSize() { return fTexSizeMax; }
+
+    void setMaxCount(size_t count);
+    void setMaxSize(size_t size);
+    
+    /** Deletes all the caches. Pass true if the texture IDs are still valid,
+        and if so, it will call glDeleteTextures. Pass false if the texture IDs
+        are invalid (e.g. the gl-context has changed), in which case they will
+        just be abandoned.
+    */
+    void deleteAllCaches(bool texturesAreValid);
+    
+    static int HashMask() { return kHashMask; }
+    
+    class Key {
+    public:
+        Key(const SkBitmap& bm) {
+            fGenID = bm.getGenerationID();
+            fOffset = bm.pixelRefOffset();
+            fWH = (bm.width() << 16) | bm.height();
+            this->computeHash();
+        }
+        
+        int getHashIndex() const { return fHashIndex; }
+        
+        friend bool operator==(const Key& a, const Key& b) {
+            return  a.fHash ==   b.fHash &&
+                    a.fGenID ==  b.fGenID &&
+                    a.fOffset == b.fOffset &&
+                    a.fWH ==     b.fWH;
+        }
+        
+        friend bool operator<(const Key& a, const Key& b) {
+            if (a.fHash < b.fHash) {
+                return true;
+            } else if (a.fHash > b.fHash) {
+                return false;
+            }
+            
+            if (a.fGenID < b.fGenID) {
+                return true;
+            } else if (a.fGenID > b.fGenID) {
+                return false;
+            }
+            
+            if (a.fOffset < b.fOffset) {
+                return true;
+            } else if (a.fOffset > b.fOffset) {
+                return false;
+            }
+            
+            return a.fWH < b.fWH;
+        }
+        
+    private:
+        void computeHash() {
+            uint32_t hash = fGenID ^ fOffset ^ fWH;
+            fHash = hash;
+            hash ^= hash >> 16;
+            fHashIndex = hash & SkTextureCache::HashMask();
+        }
+        
+        uint32_t    fHash;  // computed from the other fields
+        uint32_t    fGenID;
+        size_t      fOffset;
+        uint32_t    fWH;
+        // for indexing into the texturecache's fHash
+        int fHashIndex;
+    };
+
+    class Entry {
+    public:
+        GLuint name() const { return fName; }
+        SkPoint texSize() const { return fTexSize; }
+        size_t memSize() const { return fMemSize; }
+        const Key& getKey() const { return fKey; }
+
+        // call this to clear the texture name, in case the context has changed
+        // in which case we should't reference or delete this texture in GL
+        void abandonTexture() { fName = 0; }
+
+    private:
+        Entry(const SkBitmap& bitmap);
+        ~Entry();
+
+        int lockCount() const { return fLockCount; }
+        bool isLocked() const { return fLockCount > 0; }
+
+        void lock() { fLockCount += 1; }
+        void unlock() {
+            SkASSERT(fLockCount > 0);
+            fLockCount -= 1;
+        }
+
+    private:
+        GLuint  fName;
+        SkPoint fTexSize;
+        Key     fKey;
+        size_t  fMemSize;
+        int     fLockCount;
+        
+        Entry*  fPrev;
+        Entry*  fNext;
+        
+        friend class SkTextureCache;
+    };
+    
+    Entry* lock(const SkBitmap&);
+    void unlock(Entry*);
+    
+private:
+    void purgeIfNecessary(size_t extraSize);
+    
+#ifdef SK_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+    Entry* fHead;
+    Entry* fTail;
+    
+    // limits for the cache
+    size_t  fTexCountMax;
+    size_t  fTexSizeMax;
+    
+    // current values for the cache
+    size_t  fTexCount;
+    size_t  fTexSize;
+    
+    enum {
+        kHashBits = 6,
+        kHashCount = 1 << kHashBits,
+        kHashMask = kHashCount - 1
+    };
+    mutable Entry* fHash[kHashCount];
+    SkTDArray<Entry*> fSorted;
+    
+    /*  If we find the key, return the entry and ignore index. If we don't,
+        return NULL and set index to the place to insert the entry in fSorted
+    */
+    Entry* find(const Key&, int* index) const;
+    // returns index or <0 if not found. Does NOT update hash
+    int findInSorted(const Key& key) const;
+};
+
+#endif
diff --git a/src/images/SkBitmap_RLEPixels.h b/src/images/SkBitmap_RLEPixels.h
new file mode 100644
index 0000000..c83bc69
--- /dev/null
+++ b/src/images/SkBitmap_RLEPixels.h
@@ -0,0 +1,19 @@
+#ifndef SkBitmap_RLEPixels_DEFINED
+#define SkBitmap_RLEPixels_DEFINED
+
+#include "SkChunkAlloc.h"
+
+class SkBitmap_RLEPixels {
+public:
+    SkBitmap_RLEPixels(int width, int height);
+    ~SkBitmap_RLEPixels();
+    
+    uint8_t* yptrs() const { return fYPtrs; }
+    uint8_t* allocChunk(size_t chunk);
+    
+private:
+    SkChunkAlloc    fChunk;
+    uint8_t**       fYPtrs;
+};
+
+#endif
diff --git a/src/images/SkCreateRLEPixelRef.cpp b/src/images/SkCreateRLEPixelRef.cpp
new file mode 100644
index 0000000..5756237
--- /dev/null
+++ b/src/images/SkCreateRLEPixelRef.cpp
@@ -0,0 +1,120 @@
+#include "SkChunkAlloc.h"
+#include "SkPackBits.h"
+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+
+class RLEPixelRef : public SkPixelRef {
+public:
+    RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable);
+    virtual ~RLEPixelRef();
+    
+protected:
+    // overrides from SkPixelRef
+    virtual void* onLockPixels(SkColorTable**);
+    virtual void onUnlockPixels();
+    
+private:
+    SkBitmap::RLEPixels* fRLEPixels;
+    SkColorTable*        fCTable;
+};
+
+RLEPixelRef::RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable)
+        : SkPixelRef(NULL) {
+    fRLEPixels = rlep;  // we now own this ptr
+    fCTable = ctable;
+    ctable->safeRef();
+}
+
+RLEPixelRef::~RLEPixelRef() {
+    SkDELETE(fRLEPixels);
+    fCTable->safeUnref();
+}
+
+void* RLEPixelRef::onLockPixels(SkColorTable** ct) {
+    *ct = fCTable;
+    return fRLEPixels;
+}
+
+void RLEPixelRef::onUnlockPixels() {
+    // nothing to do
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class ChunkRLEPixels : public SkBitmap::RLEPixels {
+public:
+    ChunkRLEPixels(int width, int height, size_t chunkSize)
+        : SkBitmap::RLEPixels(width, height), fStorage(chunkSize) {
+    }
+    
+    SkChunkAlloc fStorage;
+};
+
+SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src);
+SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src) {
+    
+    if (SkBitmap::kIndex8_Config != src.config() &&
+            SkBitmap::kA8_Config != src.config()) {
+        return NULL;
+    }
+    
+    size_t maxPacked = SkPackBits::ComputeMaxSize8(src.width());
+
+    // estimate the rle size based on the original size
+    size_t size = src.getSize() >> 3;
+    if (size < maxPacked) {
+        size = maxPacked;
+    }
+
+    ChunkRLEPixels* rlePixels = SkNEW_ARGS(ChunkRLEPixels,
+                                           (src.width(), src.height(), size));
+
+    uint8_t* dstRow = NULL;
+    size_t free = 0;
+    size_t totalPacked = 0;
+
+    for (int y = 0; y < src.height(); y++) {
+        const uint8_t* srcRow = src.getAddr8(0, y);
+        
+        if (free < maxPacked) {
+            dstRow = (uint8_t*)rlePixels->fStorage.allocThrow(size);
+            free = size;
+        }
+        size_t packedSize = SkPackBits::Pack8(srcRow, src.width(), dstRow);
+        SkASSERT(packedSize <= free);
+        rlePixels->setPackedAtY(y, dstRow);
+        
+        dstRow += packedSize;
+        free -= packedSize;
+        
+        totalPacked += packedSize;
+    }
+    
+//#ifdef SK_DEBUG
+#if 0
+    // test
+    uint8_t* buffer = new uint8_t[src.width()];
+    for (int y = 0; y < src.height(); y++) {
+        const uint8_t* srcRow = src.getAddr8(0, y);
+        SkPackBits::Unpack8(buffer, 0, src.width(), rlePixels->packedAtY(y));
+        int n = memcmp(buffer, srcRow, src.width());
+        if (n) {
+            SkDebugf("----- memcmp returned %d on line %d\n", n, y);
+        }
+        SkASSERT(n == 0);
+    }
+    delete[] buffer;
+
+    size_t totalAlloc = src.height() * sizeof(uint8_t*) + totalPacked;
+    
+    SkDebugf("--- RLE: orig [%d %d] %d, rle %d %d savings %g\n",
+             src.width(), src.height(), src.getSize(),
+             src.height() * sizeof(uint8_t*), totalPacked,
+             (float)totalAlloc / src.getSize());
+
+#endif
+
+    // transfer ownership of rlePixels to our pixelref
+    return SkNEW_ARGS(RLEPixelRef, (rlePixels, src.getColorTable()));
+}
+        
diff --git a/src/images/SkFDStream.cpp b/src/images/SkFDStream.cpp
new file mode 100644
index 0000000..db4a51a
--- /dev/null
+++ b/src/images/SkFDStream.cpp
@@ -0,0 +1,85 @@
+#include "SkStream.h"
+#include <unistd.h>
+
+//#define TRACE_FDSTREAM
+
+SkFDStream::SkFDStream(int fileDesc, bool closeWhenDone)
+    : fFD(fileDesc), fCloseWhenDone(closeWhenDone) {
+}
+
+SkFDStream::~SkFDStream() {
+    if (fFD >= 0 && fCloseWhenDone) {
+        ::close(fFD);
+    }
+}
+
+bool SkFDStream::rewind() {
+    if (fFD >= 0) {
+        off_t value = ::lseek(fFD, 0, SEEK_SET);
+#ifdef TRACE_FDSTREAM
+        if (value) {
+            SkDebugf("xxxxxxxxxxxxxx rewind failed %d\n", value);
+        }
+#endif
+        return value == 0;
+    }
+    return false;
+}
+
+size_t SkFDStream::read(void* buffer, size_t size) {
+    if (fFD >= 0) {
+        if (buffer == NULL && size == 0) {  // request total size
+            off_t curr = ::lseek(fFD, 0, SEEK_CUR);
+            if (curr < 0) {
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx lseek failed 0 CURR\n");
+#endif
+                return 0;   // error
+            }
+            off_t size = ::lseek(fFD, 0, SEEK_END);
+            if (size < 0) {
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx lseek failed 0 END\n");
+#endif
+                size = 0;   // error
+            }
+            if (::lseek(fFD, curr, SEEK_SET) != curr) {
+                // can't restore, error
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx lseek failed %d SET\n", curr);
+#endif
+                return 0;
+            }
+            return size;
+        } else if (NULL == buffer) {        // skip
+            off_t oldCurr = ::lseek(fFD, 0, SEEK_CUR);
+            if (oldCurr < 0) {
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx lseek1 failed %d CUR\n", oldCurr);
+#endif
+                return 0;   // error;
+            }
+            off_t newCurr = ::lseek(fFD, size, SEEK_CUR);
+            if (newCurr < 0) {
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx lseek2 failed %d CUR\n", newCurr);
+#endif
+                return 0;   // error;
+            }
+            // return the actual amount we skipped
+            return newCurr - oldCurr;
+        } else {                            // read
+            ssize_t actual = ::read(fFD, buffer, size);
+            // our API can't return an error, so we return 0
+            if (actual < 0) {
+#ifdef TRACE_FDSTREAM
+                SkDebugf("xxxxxxxxxxxxx read failed %d actual %d\n", size, actual);
+#endif
+                actual = 0;
+            }
+            return actual;
+        }
+    }
+    return 0;
+}
+
diff --git a/src/images/SkFlipPixelRef.cpp b/src/images/SkFlipPixelRef.cpp
new file mode 100644
index 0000000..95403cc
--- /dev/null
+++ b/src/images/SkFlipPixelRef.cpp
@@ -0,0 +1,127 @@
+#include "SkFlipPixelRef.h"
+#include "SkFlattenable.h"
+#include "SkRegion.h"
+
+SkFlipPixelRef::SkFlipPixelRef(SkBitmap::Config config, int width, int height)
+: fFlipper(width, height) {
+    fConfig = config;
+    fSize = SkBitmap::ComputeSize(config, width, height);
+    fStorage = sk_malloc_throw(fSize << 1);
+    fPage0 = fStorage;
+    fPage1 = (char*)fStorage + fSize;
+}
+
+SkFlipPixelRef::~SkFlipPixelRef() {
+    sk_free(fStorage);
+}
+
+const SkRegion& SkFlipPixelRef::beginUpdate(SkBitmap* device) {
+    void*       writeAddr;
+    const void* readAddr;
+    this->getFrontBack(&readAddr, &writeAddr);
+
+    device->setConfig(fConfig, fFlipper.width(), fFlipper.height());
+    device->setPixels(writeAddr);
+
+    SkRegion    copyBits;
+    const SkRegion& dirty = fFlipper.update(&copyBits);
+
+    SkFlipPixelRef::CopyBitsFromAddr(*device, copyBits, readAddr);
+    return dirty;
+}
+
+void SkFlipPixelRef::endUpdate() {
+    this->swapPages();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void* SkFlipPixelRef::onLockPixels(SkColorTable** ct) {
+    fMutex.acquire();
+    *ct = NULL;
+    return fPage0;
+}
+
+void SkFlipPixelRef::onUnlockPixels() {
+    fMutex.release();
+}
+
+void SkFlipPixelRef::swapPages() {
+    fMutex.acquire();
+    SkTSwap<void*>(fPage0, fPage1);
+    fMutex.release();
+}
+
+void SkFlipPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    
+    buffer.write32(fSize);
+    // only need to write page0
+    buffer.writePad(fPage0, fSize);
+}
+
+SkFlipPixelRef::SkFlipPixelRef(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer, NULL) {
+    fSize = buffer.readU32();
+    fStorage = sk_malloc_throw(fSize << 1);
+    fPage0 = fStorage;
+    fPage1 = (char*)fStorage + fSize;
+    buffer.read(fPage0, fSize);
+}
+
+SkPixelRef* SkFlipPixelRef::Create(SkFlattenableReadBuffer& buffer) {
+    return SkNEW_ARGS(SkFlipPixelRef, (buffer));
+}
+
+static SkPixelRef::Registrar::Registrar reg("SkFlipPixelRef",
+                                            SkFlipPixelRef::Create);
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void copyRect(const SkBitmap& dst, const SkIRect& rect,
+                     const void* srcAddr, int shift) {
+    const size_t offset = rect.fTop * dst.rowBytes() + (rect.fLeft << shift);
+    char* dstP = static_cast<char*>(dst.getPixels()) + offset;
+    const char* srcP = static_cast<const char*>(srcAddr) + offset;
+    const size_t rb = dst.rowBytes();
+    const size_t bytes = rect.width() << shift;
+    
+    int height = rect.height();
+    while (--height >= 0) {
+        memcpy(dstP, srcP, bytes);
+        dstP += rb;
+        srcP += rb;
+    }
+}
+
+static int getShift(SkBitmap::Config config) {
+    switch (config) {
+        case SkBitmap::kARGB_8888_Config:
+            return 2;
+        case SkBitmap::kRGB_565_Config:
+        case SkBitmap::kARGB_4444_Config:
+            return 1;
+        case SkBitmap::kIndex8_Config:
+        case SkBitmap::kA8_Config:
+            return 0;
+        default:
+            return -1;  // signal not supported
+    }
+}
+
+void SkFlipPixelRef::CopyBitsFromAddr(const SkBitmap& dst, const SkRegion& clip,
+                                      const void* srcAddr) {
+    const int shift = getShift(dst.config());
+    if (shift < 0) {
+        return;
+    }
+    
+    const SkIRect bounds = {0, 0, dst.width(), dst.height()};
+    SkRegion::Cliperator iter(clip, bounds);
+    
+    while (!iter.done()) {
+        copyRect(dst, iter.rect(), srcAddr, shift);
+        iter.next();
+    }
+}
+
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
new file mode 100644
index 0000000..18b52d6
--- /dev/null
+++ b/src/images/SkImageDecoder.cpp
@@ -0,0 +1,190 @@
+/* libs/graphics/images/SkImageDecoder.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 "SkImageDecoder.h"
+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;
+
+SkBitmap::Config SkImageDecoder::GetDeviceConfig()
+{
+    return gDeviceConfig;
+}
+
+void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
+{
+    gDeviceConfig = config;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder::SkImageDecoder()
+    : fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1),
+      fDitherImage(true) {
+}
+
+SkImageDecoder::~SkImageDecoder() {
+    fPeeker->safeUnref();
+    fChooser->safeUnref();
+    fAllocator->safeUnref();
+}
+
+SkImageDecoder::Format SkImageDecoder::getFormat() const {
+    return kUnknown_Format;
+}
+
+SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
+    SkRefCnt_SafeAssign(fPeeker, peeker);
+    return peeker;
+}
+
+SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
+    SkRefCnt_SafeAssign(fChooser, chooser);
+    return chooser;
+}
+
+SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
+    SkRefCnt_SafeAssign(fAllocator, alloc);
+    return alloc;
+}
+
+void SkImageDecoder::setSampleSize(int size) {
+    if (size < 1) {
+        size = 1;
+    }
+    fSampleSize = size;
+}
+
+bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
+                                         int height) const {
+    Chooser* chooser = fChooser;
+
+    if (NULL == chooser) {    // no chooser, we just say YES to decoding :)
+        return true;
+    }
+    chooser->begin(1);
+    chooser->inspect(0, config, width, height);
+    return chooser->choose() == 0;
+}
+
+bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
+                                   SkColorTable* ctable) const {
+    return bitmap->allocPixels(fAllocator, ctable);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
+                            SkBitmap::Config pref, Mode mode) {
+    SkBitmap    tmp;
+
+    // we reset this to false before calling onDecode
+    fShouldCancelDecode = false;
+
+    // pass a temporary bitmap, so that if we return false, we are assured of
+    // leaving the caller's bitmap untouched.
+    if (this->onDecode(stream, &tmp, pref, mode)) {
+        /*  We operate on a tmp bitmap until we know we succeed. This way
+         we're sure we don't change the caller's bitmap and then later
+         return false. Returning false must mean that their parameter
+         is unchanged.
+         */
+        bm->swap(tmp);
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
+                                SkBitmap::Config pref,  Mode mode) {
+    SkASSERT(file);
+    SkASSERT(bm);
+
+    SkFILEStream    stream(file);
+    if (stream.isValid()) {
+        if (SkImageDecoder::DecodeStream(&stream, bm, pref, mode)) {
+            bm->pixelRef()->setURI(file);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
+                                  SkBitmap::Config pref, Mode mode) {
+    if (0 == size) {
+        return false;
+    }
+    SkASSERT(buffer);
+
+    SkMemoryStream  stream(buffer, size);
+    return SkImageDecoder::DecodeStream(&stream, bm, pref, mode);
+}
+
+bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
+                                  SkBitmap::Config pref, Mode mode) {
+    SkASSERT(stream);
+    SkASSERT(bm);
+
+    bool success = false;
+    SkImageDecoder* codec = SkImageDecoder::Factory(stream);
+
+    if (NULL != codec) {
+        success = codec->decode(stream, bm, pref, mode);
+        delete codec;
+    }
+    return success;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+
+SkImageEncoder::~SkImageEncoder() {}
+
+bool SkImageEncoder::encodeStream(SkWStream* stream, const SkBitmap& bm,
+                                  int quality) {
+    quality = SkMin32(100, SkMax32(0, quality));
+    return this->onEncode(stream, bm, quality);
+}
+
+bool SkImageEncoder::encodeFile(const char file[], const SkBitmap& bm,
+                                int quality) {
+    quality = SkMin32(100, SkMax32(0, quality));
+    SkFILEWStream   stream(file);
+    return this->onEncode(&stream, bm, quality);
+}
+
+bool SkImageEncoder::EncodeFile(const char file[], const SkBitmap& bm, Type t,
+                                int quality) {
+    SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
+    return enc.get() && enc.get()->encodeFile(file, bm, quality);
+}
+
+bool SkImageEncoder::EncodeStream(SkWStream* stream, const SkBitmap& bm, Type t,
+                                int quality) {
+    SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
+    return enc.get() && enc.get()->encodeStream(stream, bm, quality);
+}
+
+#endif
+
diff --git a/src/images/SkImageDecoder_fpdfemb.cpp b/src/images/SkImageDecoder_fpdfemb.cpp
new file mode 100644
index 0000000..7f37e3d
--- /dev/null
+++ b/src/images/SkImageDecoder_fpdfemb.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+ 
+#include "SkImageDecoder.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkColorPriv.h"
+#include "SkTDArray.h"
+
+#include "fpdfemb.h"
+
+class SkFPDFEMBImageDecoder : public SkImageDecoder {
+public:
+    SkFPDFEMBImageDecoder() {}
+    
+    virtual Format getFormat() const {
+        return kBMP_Format;
+    }
+
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+                          SkBitmap::Config pref, Mode mode);
+
+private:
+    bool render(FPDFEMB_PAGE page, const FPDFEMB_RECT& bounds, SkBitmap* bm,
+                SkBitmap::Config prefConfig, SkImageDecoder::Mode mode);
+};
+
+SkImageDecoder* SkImageDecoder_FPDFEMB_Factory(SkStream*);
+SkImageDecoder* SkImageDecoder_FPDFEMB_Factory(SkStream* stream) {
+    static const char kPDFSig[] = { '%', 'P', 'D', 'F' };
+    
+    size_t len = stream->getLength();
+    char buffer[sizeof(kPDFSig)];
+    
+    SkDebugf("---- SkImageDecoder_FPDFEMB_Factory len=%d\n", len);
+    
+    if (len != 12683) { return NULL; }
+
+    if (len > sizeof(kPDFSig) &&
+            stream->read(buffer, sizeof(kPDFSig)) == sizeof(kPDFSig) &&
+            !memcmp(buffer, kPDFSig, sizeof(kPDFSig))) {
+        return SkNEW(SkFPDFEMBImageDecoder);
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern "C" {
+    static void* pdf_alloc(FPDFEMB_MEMMGR* pMgr, unsigned int size) {
+        void* addr = sk_malloc_throw(size);
+ //       SkDebugf("---- pdf_alloc %d %p\n", size, addr);
+        return addr;
+    }
+
+    static void* pdf_alloc_nl(FPDFEMB_MEMMGR* pMgr, unsigned int size) {
+        void* addr = sk_malloc_flags(size, 0);
+ //       SkDebugf("---- pdf_alloc_nl %d %p\n", size, addr);
+        return addr;
+    }
+
+    static void* pdf_realloc(FPDFEMB_MEMMGR*, void* addr, unsigned int size) {
+        void* newaddr = sk_realloc_throw(addr, size);
+ //       SkDebugf("---- pdf_realloc %p %d %p\n", addr, size, newaddr);
+        return newaddr;
+    }
+
+    static void pdf_free(FPDFEMB_MEMMGR* pMgr, void* pointer) {
+ //       SkDebugf("---- pdf_free %p\n", pointer);
+        sk_free(pointer);
+    }
+
+    void FX_OUTPUT_LOG_FUNC(const char* format, ...) {
+        SkDebugf("---- LOG_FUNC %s\n", format);
+    }
+    
+    static unsigned int file_getsize(FPDFEMB_FILE_ACCESS* file) {
+        SkStream* stream = (SkStream*)file->user;
+        return stream->getLength();
+    }
+    
+    static FPDFEMB_RESULT file_readblock(FPDFEMB_FILE_ACCESS* file, void* dst,
+                                    unsigned int offset, unsigned int size) {
+        SkStream* stream = (SkStream*)file->user;
+//        SkDebugf("---- readblock %p %p %d %d\n", stream, dst, offset, size);
+        if (!stream->rewind()) {
+            SkDebugf("---- rewind failed\n");
+            return FPDFERR_ERROR;
+        }
+        if (stream->skip(offset) != offset) {
+            SkDebugf("---- skip failed\n");
+            return FPDFERR_ERROR;
+        }
+        if (stream->read(dst, size) != size) {
+            SkDebugf("---- read failed\n");
+            return FPDFERR_ERROR;
+        }
+        return FPDFERR_SUCCESS;
+    }
+
+    static void pdf_oom_handler(void* memory, int size) {
+        SkDebugf("======== pdf OOM %p %d\n", memory, size);
+    }
+}
+
+static inline int PDF2Pixels(int x) { return x / 100; }
+static inline SkScalar PDF2Scalar(int x) {
+    return SkScalarMulDiv(SK_Scalar1, x, 100);
+}
+
+bool SkFPDFEMBImageDecoder::render(FPDFEMB_PAGE page, const FPDFEMB_RECT& bounds, SkBitmap* bm,
+                   SkBitmap::Config prefConfig, SkImageDecoder::Mode mode) {
+    int width = PDF2Pixels(bounds.right - bounds.left);
+    int height = PDF2Pixels(bounds.top - bounds.bottom);
+
+    SkDebugf("----- bitmap size [%d %d], mode=%d\n", width, height, mode);
+    bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        return true;
+    }
+    
+    // USE THE CODEC TO ALLOCATE THE PIXELS!!!!
+    if (!this->allocPixelRef(bm, NULL)) {
+        SkDebugf("----- failed to alloc pixels\n");
+        return false;
+    }
+
+    bm->eraseColor(0);
+    
+    FPDFEMB_RESULT result;
+    FPDFEMB_BITMAP dib;
+
+    result = FPDFEMB_CreateDIB(width, height, FPDFDIB_BGRA, bm->getPixels(),
+                               bm->rowBytes(), &dib);
+    SkDebugf("---- createdib %d\n", result);
+
+    result = FPDFEMB_StartRender(dib, page, 0, 0, width, height, 0, 0, NULL, NULL);
+    SkDebugf("---- render %d\n", result);
+    
+    result = FPDFEMB_DestroyDIB(dib);
+    SkDebugf("---- destroydib %d\n", result);
+    
+    SkPMColor* dst = bm->getAddr32(0, 0);
+    const uint8_t* src = (uint8_t*)dst;
+    int n = bm->getSize() >> 2;
+    for (int i = 0; i < n; i++) {
+        int b = *src++;
+        int g = *src++;
+        int r = *src++;
+        int a = *src++;
+        *dst++ = SkPackARGB32(a, r, g, b);
+    }
+
+    return true;
+}
+
+#define USE_FIXED_MEM   (4 * 1024 * 1024)
+
+bool SkFPDFEMBImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
+                                 SkBitmap::Config prefConfig, Mode mode) {
+
+    FPDFEMB_RESULT result;
+#ifdef USE_FIXED_MEM
+    SkAutoMalloc storage(USE_FIXED_MEM);
+    result = FPDFEMB_InitFixedMemory(storage.get(), USE_FIXED_MEM,
+                                     pdf_oom_handler);
+#else
+    FPDFEMB_MEMMGR  memmgr;
+    memmgr.Alloc = pdf_alloc;
+    memmgr.AllocNL = pdf_alloc_nl;
+    memmgr.Realloc = pdf_realloc;
+    memmgr.Free = pdf_free;
+
+    result = FPDFEMB_Init(&memmgr);
+#endif
+    SkDebugf("----- SkImageDecoder_FPDFEMB_Factory init %d, streamLen = %d\n", result, stream->getLength());
+
+    FPDFEMB_FILE_ACCESS file;
+    file.GetSize = file_getsize;
+    file.ReadBlock = file_readblock;
+    file.user = stream;
+
+    FPDFEMB_DOCUMENT document;
+    result = FPDFEMB_StartLoadDocument(&file, NULL, &document, NULL);
+    SkDebugf("----- SkImageDecoder_FPDFEMB_Factory open %d %p\n", result, document);
+
+    int pageCount = FPDFEMB_GetPageCount(document);
+    SkDebugf("----- SkImageDecoder_FPDFEMB_Factory pageCount %d\n", pageCount);
+
+    if (pageCount > 0) {
+        FPDFEMB_PAGE page;
+        result = FPDFEMB_LoadPage(document, 0, &page);
+        SkDebugf("----- SkImageDecoder_FPDFEMB_Factory load page %d\n", result);
+
+        int width, height;
+        result = FPDFEMB_GetPageSize(page, &width, &height);
+        SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page size %d [%d %d]\n", result, width, height);
+
+        FPDFEMB_RECT rect;
+        result = FPDFEMB_GetPageBBox(page, &rect);
+        SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page rect %d [%d %d %d %d]\n", result,
+                 rect.left, rect.top, rect.right, rect.bottom);
+
+        SkDebugf("----- SkImageDecoder_FPDFEMB_Factory begin page parse...\n");
+        result = FPDFEMB_StartParse(page, false, NULL);
+        SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page parse %d\n", result);
+
+        if (0 == result) {
+            this->render(page, rect, bm, prefConfig, mode);
+        }
+
+        result = FPDFEMB_ClosePage(page);
+        SkDebugf("----- SkImageDecoder_FPDFEMB_Factory close page %d\n", result);
+    }
+    
+    result = FPDFEMB_CloseDocument(document);
+    SkDebugf("----- SkImageDecoder_FPDFEMB_Factory close %d\n", result);
+
+ //   FPDFEMB_Exit();
+
+    return true;    
+}
diff --git a/src/images/SkImageDecoder_libbmp.cpp b/src/images/SkImageDecoder_libbmp.cpp
new file mode 100644
index 0000000..32a7a6d
--- /dev/null
+++ b/src/images/SkImageDecoder_libbmp.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2007, 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 "bmpdecoderhelper.h"
+#include "SkImageDecoder.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkColorPriv.h"
+#include "SkTDArray.h"
+
+class SkBMPImageDecoder : public SkImageDecoder {
+public:
+    SkBMPImageDecoder() {}
+    
+    virtual Format getFormat() const {
+        return kBMP_Format;
+    }
+
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+                          SkBitmap::Config pref, Mode mode);
+};
+
+SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream*);
+SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream* stream) {
+    static const char kBmpMagic[] = { 'B', 'M' };
+    
+    size_t len = stream->getLength();
+    char buffer[sizeof(kBmpMagic)];
+    
+    if (len > sizeof(kBmpMagic) &&
+            stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) &&
+            !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) {
+        return SkNEW(SkBMPImageDecoder);
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
+public:
+    // we don't copy the bitmap, just remember the pointer
+    SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {}
+
+    // override from BmpDecoderCallback
+    virtual uint8* SetSize(int width, int height) {
+        fWidth = width;
+        fHeight = height;
+        if (fJustBounds) {
+            return NULL;
+        }
+        
+        fRGB.setCount(width * height * 3);  // 3 == r, g, b
+        return fRGB.begin();
+    }
+
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+    uint8_t* rgb() const { return fRGB.begin(); }
+
+private:
+    SkTDArray<uint8_t> fRGB;
+    int fWidth;
+    int fHeight;
+    bool fJustBounds;
+};
+
+bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
+                                 SkBitmap::Config prefConfig, Mode mode) {
+
+    size_t length = stream->getLength();
+    SkAutoMalloc storage(length);
+    
+    if (stream->read(storage.get(), length) != length) {
+        return false;
+    }
+    
+    const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;
+    SkBmpDecoderCallback callback(justBounds);
+
+    // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...]
+    {
+        image_codec::BmpDecoderHelper helper;
+        const int max_pixels = 16383*16383; // max width*height
+        if (!helper.DecodeImage((const char*)storage.get(), length,
+                                max_pixels, &callback)) {
+            return false;
+        }
+    }
+    
+    // we don't need this anymore, so free it now (before we try to allocate
+    // the bitmap's pixels) rather than waiting for its destructor
+    storage.free();
+    
+    int width = callback.width();
+    int height = callback.height();
+    SkBitmap::Config config = SkBitmap::kARGB_8888_Config;
+    
+    // only accept prefConfig if it makes sense for us
+    if (SkBitmap::kARGB_4444_Config == prefConfig ||
+            SkBitmap::kRGB_565_Config == config) {
+        config = prefConfig;
+    }
+
+    SkScaledBitmapSampler sampler(width, height, getSampleSize());
+
+    bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+    bm->setIsOpaque(true);
+    if (justBounds) {
+        return true;
+    }
+
+    if (!this->allocPixelRef(bm, NULL)) {
+        return false;
+    }
+    
+    SkAutoLockPixels alp(*bm);
+    
+    if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) {
+        return false;
+    }
+
+    const int srcRowBytes = width * 3;
+    const int dstHeight = sampler.scaledHeight();
+    const uint8_t* srcRow = callback.rgb();
+    
+    srcRow += sampler.srcY0() * srcRowBytes;
+    for (int y = 0; y < dstHeight; y++) {
+        sampler.next(srcRow);
+        srcRow += sampler.srcDY() * srcRowBytes;
+    }
+    return true;
+}
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
new file mode 100644
index 0000000..519366a
--- /dev/null
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -0,0 +1,340 @@
+/* libs/graphics/images/SkImageDecoder_libgif.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 "SkImageDecoder.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkPackBits.h"
+
+#include "gif_lib.h"
+
+class SkGIFImageDecoder : public SkImageDecoder {
+public:
+    virtual Format getFormat() const {
+        return kGIF_Format;
+    }
+    
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+                          SkBitmap::Config pref, Mode mode);
+};
+
+static const uint8_t gStartingIterlaceYValue[] = {
+    0, 4, 2, 1
+};
+static const uint8_t gDeltaIterlaceYValue[] = {
+    8, 8, 4, 2
+};
+
+/*  Implement the GIF interlace algorithm in an iterator.
+    1) grab every 8th line beginning at 0
+    2) grab every 8th line beginning at 4
+    3) grab every 4th line beginning at 2
+    4) grab every 2nd line beginning at 1
+*/
+class GifInterlaceIter {
+public:
+    GifInterlaceIter(int height) : fHeight(height) {
+        fStartYPtr = gStartingIterlaceYValue;
+        fDeltaYPtr = gDeltaIterlaceYValue;
+
+        fCurrY = *fStartYPtr++;
+        fDeltaY = *fDeltaYPtr++;
+    }
+    
+    int currY() const {
+        SkASSERT(fStartYPtr);
+        SkASSERT(fDeltaYPtr);
+        return fCurrY;
+    }
+
+    void next() {
+        SkASSERT(fStartYPtr);
+        SkASSERT(fDeltaYPtr);
+
+        int y = fCurrY + fDeltaY;
+        // We went from an if statement to a while loop so that we iterate
+        // through fStartYPtr until a valid row is found. This is so that images
+        // that are smaller than 5x5 will not trash memory.
+        while (y >= fHeight) {
+            if (gStartingIterlaceYValue +
+                    SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
+                // we done
+                SkDEBUGCODE(fStartYPtr = NULL;)
+                SkDEBUGCODE(fDeltaYPtr = NULL;)
+                y = 0;
+            } else {
+                y = *fStartYPtr++;
+                fDeltaY = *fDeltaYPtr++;
+            }
+        }
+        fCurrY = y;
+    }
+    
+private:
+    const int fHeight;
+    int fCurrY;
+    int fDeltaY;
+    const uint8_t* fStartYPtr;
+    const uint8_t* fDeltaYPtr;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+//#define GIF_STAMP       "GIF"    /* First chars in file - GIF stamp. */
+//#define GIF_STAMP_LEN   (sizeof(GIF_STAMP) - 1)
+
+static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
+                              int size) {
+    SkStream* stream = (SkStream*) fileType->UserData;
+    return (int) stream->read(out, size);
+}
+
+void CheckFreeExtension(SavedImage* Image) {
+    if (Image->ExtensionBlocks) {
+        FreeExtension(Image);
+    }
+}
+
+// return NULL on failure
+static const ColorMapObject* find_colormap(const GifFileType* gif) {
+    const ColorMapObject* cmap = gif->Image.ColorMap;
+    if (NULL == cmap) {
+        cmap = gif->SColorMap;
+    }
+    // some sanity checks
+    if ((unsigned)cmap->ColorCount > 256 ||
+            cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
+        cmap = NULL;
+    }
+    return cmap;
+}
+
+// return -1 if not found (i.e. we're completely opaque)
+static int find_transpIndex(const SavedImage& image, int colorCount) {
+    int transpIndex = -1;
+    for (int i = 0; i < image.ExtensionBlockCount; ++i) {
+        const ExtensionBlock* eb = image.ExtensionBlocks + i;
+        if (eb->Function == 0xF9 && eb->ByteCount == 4) {
+            if (eb->Bytes[0] & 1) {
+                transpIndex = (unsigned char)eb->Bytes[3];
+                // check for valid transpIndex
+                if (transpIndex >= colorCount) {
+                    transpIndex = -1;
+                }
+                break;
+            }
+        }
+    }
+    return transpIndex;
+}
+
+static bool error_return(GifFileType* gif, const SkBitmap& bm,
+                         const char msg[]) {
+#if 0
+    SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n",
+             msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable());
+#endif
+    return false;
+}
+
+bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm,
+                                 SkBitmap::Config prefConfig, Mode mode) {   
+    GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
+    if (NULL == gif) {
+        return error_return(gif, *bm, "DGifOpen");
+    }
+
+    SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);
+
+    SavedImage temp_save;
+    temp_save.ExtensionBlocks=NULL;
+    temp_save.ExtensionBlockCount=0;
+    SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);
+
+    int width, height;
+    GifRecordType recType;
+    GifByteType *extData;
+    
+    do {
+        if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
+            return error_return(gif, *bm, "DGifGetRecordType");
+        }
+        
+        switch (recType) {
+        case IMAGE_DESC_RECORD_TYPE: {
+            if (DGifGetImageDesc(gif) == GIF_ERROR) {
+                return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
+            }
+            
+            if (gif->ImageCount < 1) {    // sanity check
+                return error_return(gif, *bm, "ImageCount < 1");
+            }
+                
+            width = gif->SWidth;
+            height = gif->SHeight;
+            if (width <= 0 || height <= 0 ||
+                !this->chooseFromOneChoice(SkBitmap::kIndex8_Config,
+                                           width, height)) {
+                return error_return(gif, *bm, "chooseFromOneChoice");
+            }
+            
+            bm->setConfig(SkBitmap::kIndex8_Config, width, height);
+            if (SkImageDecoder::kDecodeBounds_Mode == mode)
+                return true;
+
+            SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
+            const GifImageDesc& desc = image->ImageDesc;
+            
+            // check for valid descriptor
+            if (   (desc.Top | desc.Left) < 0 ||
+                    desc.Left + desc.Width > width ||
+                    desc.Top + desc.Height > height) {
+                return error_return(gif, *bm, "TopLeft");
+            }
+            
+            // now we decode the colortable
+            int colorCount = 0;
+            {
+                const ColorMapObject* cmap = find_colormap(gif);
+                if (NULL == cmap) {
+                    return error_return(gif, *bm, "null cmap");
+                }
+
+                colorCount = cmap->ColorCount;
+                SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount));
+                SkPMColor* colorPtr = ctable->lockColors();
+                for (int index = 0; index < colorCount; index++)
+                    colorPtr[index] = SkPackARGB32(0xFF,
+                                                   cmap->Colors[index].Red, 
+                                                   cmap->Colors[index].Green,
+                                                   cmap->Colors[index].Blue);
+
+                int transpIndex = find_transpIndex(temp_save, colorCount);
+                if (transpIndex < 0)
+                    ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+                else
+                    colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor
+                ctable->unlockColors(true);
+
+                SkAutoUnref aurts(ctable);
+                if (!this->allocPixelRef(bm, ctable)) {
+                    return error_return(gif, *bm, "allocPixelRef");
+                }
+            }
+            
+            SkAutoLockPixels alp(*bm);
+
+            // time to decode the scanlines
+            //
+            uint8_t*  scanline = bm->getAddr8(0, 0);
+            const int rowBytes = bm->rowBytes();
+            const int innerWidth = desc.Width;
+            const int innerHeight = desc.Height;
+
+            // abort if either inner dimension is <= 0
+            if (innerWidth <= 0 || innerHeight <= 0) {
+                return error_return(gif, *bm, "non-pos inner width/height");
+            }
+
+            // are we only a subset of the total bounds?
+            if ((desc.Top | desc.Left) > 0 ||
+                 innerWidth < width || innerHeight < height)
+            {
+                uint8_t fill = (uint8_t)gif->SBackGroundColor;
+                // check for valid fill index/color
+                if (fill >= (unsigned)colorCount) {
+                    fill = 0;
+                }
+                memset(scanline, gif->SBackGroundColor, bm->getSize());
+                // bump our starting address
+                scanline += desc.Top * rowBytes + desc.Left;
+            }
+            
+            // now decode each scanline
+            if (gif->Image.Interlace)
+            {
+                GifInterlaceIter iter(innerHeight);
+                for (int y = 0; y < innerHeight; y++)
+                {
+                    uint8_t* row = scanline + iter.currY() * rowBytes;
+                    if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) {
+                        return error_return(gif, *bm, "interlace DGifGetLine");
+                    }
+                    iter.next();
+                }
+            }
+            else
+            {
+                // easy, non-interlace case
+                for (int y = 0; y < innerHeight; y++) {
+                    if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
+                        return error_return(gif, *bm, "DGifGetLine");
+                    }
+                    scanline += rowBytes;
+                }
+            }
+            goto DONE;
+            } break;
+            
+        case EXTENSION_RECORD_TYPE:
+            if (DGifGetExtension(gif, &temp_save.Function,
+                                 &extData) == GIF_ERROR) {
+                return error_return(gif, *bm, "DGifGetExtension");
+            }
+
+            while (extData != NULL) {
+                /* Create an extension block with our data */
+                if (AddExtensionBlock(&temp_save, extData[0],
+                                      &extData[1]) == GIF_ERROR) {
+                    return error_return(gif, *bm, "AddExtensionBlock");
+                }
+                if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
+                    return error_return(gif, *bm, "DGifGetExtensionNext");
+                }
+                temp_save.Function = 0;
+            }
+            break;
+            
+        case TERMINATE_RECORD_TYPE:
+            break;
+            
+        default:	/* Should be trapped by DGifGetRecordType */
+            break;
+        }
+    } while (recType != TERMINATE_RECORD_TYPE);
+
+DONE:
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream* stream) {
+    char buf[GIF_STAMP_LEN];
+    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
+        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
+            return SkNEW(SkGIFImageDecoder);
+        }
+    }
+    return NULL;
+}
+
diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
new file mode 100644
index 0000000..b179a6b
--- /dev/null
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -0,0 +1,388 @@
+/* libs/graphics/images/SkImageDecoder_libico.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 "SkImageDecoder.h"
+#include "SkStream.h"
+#include "SkColorPriv.h"
+#include "SkTypes.h"
+
+class SkICOImageDecoder : public SkImageDecoder {
+public:
+    SkICOImageDecoder();
+    
+    virtual Format getFormat() const {
+        return kICO_Format;
+    }
+
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+                          SkBitmap::Config pref, Mode);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+//read bytes starting from the begin-th index in the buffer
+//read in Intel order, and return an integer
+
+#define readByte(buffer,begin) buffer[begin]
+#define read2Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8)
+#define read4Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8)+(buffer[begin+2]<<16)+(buffer[begin+3]<<24)
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream*);
+SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream* stream)
+{
+    //i'm going to check if we basically have 0,0,1,0 (reserved = 0, type = 1)
+    //is that required and sufficient?
+    SkAutoMalloc autoMal(4);
+    unsigned char* buf = (unsigned char*)autoMal.get();
+    stream->read((void*)buf, 4);
+    int reserved = read2Bytes(buf, 0);
+    int type = read2Bytes(buf, 2);
+    if (reserved != 0 || type != 1) //it's not an ico
+        return NULL;
+    return SkNEW(SkICOImageDecoder);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+SkICOImageDecoder::SkICOImageDecoder()
+{
+}
+
+//helpers - my function pointer will call one of these, depending on the bitCount, each time through the inner loop
+static void editPixelBit1(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit4(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit8(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit24(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit32(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+            
+            
+static int calculateRowBytesFor8888(int w, int bitCount)
+{
+    //  Default rowBytes is w << 2 for kARGB_8888
+    //  In the case of a 4 bit image with an odd width, we need to add some
+    //  so we can go off the end of the drawn bitmap.
+    //  Add 4 to ensure that it is still a multiple of 4.
+    if (4 == bitCount && (w & 0x1)) {
+        return (w + 1) << 2;
+    }
+    //  Otherwise return 0, which will allow it to be calculated automatically.
+    return 0;
+}
+
+bool SkICOImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
+                                 SkBitmap::Config pref, Mode mode)
+{
+    size_t length = stream->read(NULL, 0);
+    SkAutoMalloc autoMal(length);
+    unsigned char* buf = (unsigned char*)autoMal.get();
+    if (stream->read((void*)buf, length) != length) {
+        return false;
+    }
+    
+    //these should always be the same - should i use for error checking? - what about files that have some
+    //incorrect values, but still decode properly?
+    int reserved = read2Bytes(buf, 0);    // 0
+    int type = read2Bytes(buf, 2);        // 1
+    if (reserved != 0 || type != 1)
+        return false;
+    int count = read2Bytes(buf, 4);
+    
+    //need to at least have enough space to hold the initial table of info
+    if (length < (size_t)(6 + count*16))
+        return false;
+        
+    int choice;
+    Chooser* chooser = this->getChooser();
+    //FIXME:if no chooser, consider providing the largest color image
+    //what are the odds that the largest image would be monochrome?
+    if (NULL == chooser) {
+        choice = 0;
+    } else {
+        chooser->begin(count);
+        for (int i = 0; i < count; i++)
+        {
+            //need to find out the config, width, and height from the stream
+            int width = readByte(buf, 6 + i*16);
+            int height = readByte(buf, 7 + i*16);
+            int offset = read4Bytes(buf, 18 + i*16);
+            int bitCount = read2Bytes(buf, offset+14);
+            SkBitmap::Config c;
+            //currently only provide ARGB_8888_, but maybe we want kIndex8_Config for 1 and 4, and possibly 8?
+            //or maybe we'll determine this based on the provided config
+            switch (bitCount)
+            {
+                case 1:
+                case 4:
+                    // In reality, at least for the moment, these will be decoded into kARGB_8888 bitmaps.
+                    // However, this will be used to distinguish between the lower quality 1bpp and 4 bpp 
+                    // images and the higher quality images.
+                    c = SkBitmap::kIndex8_Config;
+                    break;
+                case 8:
+                case 24:
+                case 32:
+                    c = SkBitmap::kARGB_8888_Config;
+                    break;
+                default:
+                    SkDEBUGF(("Image with %ibpp not supported\n", bitCount));
+                    continue;
+            }
+            chooser->inspect(i, c, width, height);
+        }
+        choice = chooser->choose();
+    }
+    
+    //you never know what the chooser is going to supply
+    if (choice >= count || choice < 0)       
+        return false;
+    
+    //skip ahead to the correct header
+    //commented out lines are not used, but if i switch to other read method, need to know how many to skip
+    //otherwise, they could be used for error checking
+    int w = readByte(buf, 6 + choice*16);
+    int h = readByte(buf, 7 + choice*16);
+    int colorCount = readByte(buf, 8 + choice*16);
+    //int reservedToo = readByte(buf, 9 + choice*16);   //0
+    //int planes = read2Bytes(buf, 10 + choice*16);       //1 - but often 0
+    //int fakeBitCount = read2Bytes(buf, 12 + choice*16); //should be real - usually 0
+    int size = read4Bytes(buf, 14 + choice*16);           //matters?
+    int offset = read4Bytes(buf, 18 + choice*16);
+    if ((size_t)(offset + size) > length)
+        return false;
+    //int infoSize = read4Bytes(buf, offset);             //40
+    //int width = read4Bytes(buf, offset+4);              //should == w
+    //int height = read4Bytes(buf, offset+8);             //should == 2*h
+    //int planesToo = read2Bytes(buf, offset+12);         //should == 1 (does it?)
+    int bitCount = read2Bytes(buf, offset+14);
+    
+    void (*placePixel)(const int pixelNo, const unsigned char* buf, 
+        const int xorOffset, int& x, int y, const int w, 
+        SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) = NULL;
+    switch (bitCount)
+    {
+        case 1:
+            placePixel = &editPixelBit1;
+            colorCount = 2;
+            break;
+        case 4:
+            placePixel = &editPixelBit4;
+            colorCount = 16;
+            break;
+        case 8:
+            placePixel = &editPixelBit8;
+            colorCount = 256;
+            break;
+        case 24:
+            placePixel = &editPixelBit24;
+            colorCount = 0;
+            break;
+        case 32:
+            placePixel = &editPixelBit32;
+            colorCount = 0;
+            break;
+        default:
+            SkDEBUGF(("Decoding %ibpp is unimplemented\n", bitCount));
+            return false;
+    }
+        
+    //these should all be zero, but perhaps are not - need to check
+    //int compression = read4Bytes(buf, offset+16);       //0
+    //int imageSize = read4Bytes(buf, offset+20);         //0 - sometimes has a value
+    //int xPixels = read4Bytes(buf, offset+24);           //0
+    //int yPixels = read4Bytes(buf, offset+28);           //0
+    //int colorsUsed = read4Bytes(buf, offset+32)         //0 - might have an actual value though
+    //int colorsImportant = read4Bytes(buf, offset+36);   //0
+        
+    int begin = offset + 40;
+    //this array represents the colortable
+    //if i allow other types of bitmaps, it may actually be used as a part of the bitmap
+    SkPMColor* colors = NULL;
+    int blue, green, red;
+    if (colorCount) 
+    {
+        colors = new SkPMColor[colorCount];
+        for (int j = 0; j < colorCount; j++)
+        {
+            //should this be a function - maybe a #define?
+            blue = readByte(buf, begin + 4*j);
+            green = readByte(buf, begin + 4*j + 1);
+            red = readByte(buf, begin + 4*j + 2);
+            colors[j] = SkPackARGB32(0xFF, red & 0xFF, green & 0xFF, blue & 0xFF);
+        }
+    }
+    int bitWidth = w*bitCount;
+    int test = bitWidth & 0x1F;
+    int mask = -(((test >> 4) | (test >> 3) | (test >> 2) | (test >> 1) | test) & 0x1);    //either 0xFFFFFFFF or 0
+    int lineBitWidth = (bitWidth & 0xFFFFFFE0) + (0x20 & mask);
+    int lineWidth = lineBitWidth/bitCount;
+    
+    int xorOffset = begin + colorCount*4;   //beginning of the color bitmap
+                                            //other read method means we will just be here already
+    int andOffset = xorOffset + ((lineWidth*h*bitCount) >> 3);
+    
+    /*int */test = w & 0x1F;   //the low 5 bits - we are rounding up to the next 32 (2^5)
+    /*int */mask = -(((test >> 4) | (test >> 3) | (test >> 2) | (test >> 1) | test) & 0x1);    //either 0xFFFFFFFF or 0
+    int andLineWidth = (w & 0xFFFFFFE0) + (0x20 & mask);
+    //if we allow different Configs, everything is the same til here
+    //change the config, and use different address getter, and place index vs color, and add the color table
+    //FIXME: what is the tradeoff in size?
+    //if the andbitmap (mask) is all zeroes, then we can easily do an index bitmap
+    //however, with small images with large colortables, maybe it's better to still do argb_8888
+
+    bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
+    
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        delete[] colors;
+        return true;
+    }
+
+    if (!this->allocPixelRef(bm, NULL))
+    {
+        delete[] colors;
+        return false;
+    }
+    
+    SkAutoLockPixels alp(*bm);
+
+    for (int y = 0; y < h; y++)
+    {
+        for (int x = 0; x < w; x++)
+        {
+            //U32* address = bm->getAddr32(x, y);
+            
+            //check the alpha bit first, but pass it along to the function to figure out how to deal with it
+            int andPixelNo = andLineWidth*(h-y-1)+x;
+            //only need to get a new alphaByte when x %8 == 0
+            //but that introduces an if and a mod - probably much slower
+            //that's ok, it's just a read of an array, not a stream
+            int alphaByte = readByte(buf, andOffset + (andPixelNo >> 3));
+            int shift = 7 - (andPixelNo & 0x7);
+            int m = 1 << shift;
+            
+            int pixelNo = lineWidth*(h-y-1)+x;
+            placePixel(pixelNo, buf, xorOffset, x, y, w, bm, alphaByte, m, shift, colors);
+
+        }
+    }
+
+    delete [] colors;
+    //ensure we haven't read off the end?
+    //of course this doesn't help us if the andOffset was a lie...
+    //return andOffset + (andLineWidth >> 3) <= length;
+    return true;
+}   //onDecode
+
+//function to place the pixel, determined by the bitCount
+static void editPixelBit1(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+    // note that this should be the same as/similar to the AND bitmap
+    SkPMColor* address = bm->getAddr32(x,y);
+    int byte = readByte(buf, xorOffset + (pixelNo >> 3));
+    int colorBit;
+    int alphaBit;
+    // Read all of the bits in this byte.  
+    int i = x + 8;
+    // Pin to the width so we do not write outside the bounds of 
+    // our color table.
+    i = i > w ? w : i;
+    // While loop to check all 8 bits individually.
+    while (x < i)
+    {
+        
+        colorBit = (byte & m) >> shift;
+        alphaBit = (alphaByte & m) >> shift;
+        *address = (alphaBit-1)&(colors[colorBit]);
+        x++;
+        // setup for the next pixel
+        address = address + 1;
+        m = m >> 1;
+        shift -= 1;
+    }
+    x--;
+}
+static void editPixelBit4(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+    SkPMColor* address = bm->getAddr32(x, y);
+    int byte = readByte(buf, xorOffset + (pixelNo >> 1));
+    int pixel = (byte >> 4) & 0xF;
+    int alphaBit = (alphaByte & m) >> shift;
+    *address = (alphaBit-1)&(colors[pixel]);
+    x++;
+    //if w is odd, x may be the same as w, which means we are writing to an unused portion of the bitmap
+    //but that's okay, since i've added an extra rowByte for just this purpose
+    address = address + 1;
+    pixel = byte & 0xF;
+    m = m >> 1;
+    alphaBit = (alphaByte & m) >> (shift-1);
+    //speed up trick here
+    *address = (alphaBit-1)&(colors[pixel]);
+}
+
+static void editPixelBit8(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+    SkPMColor* address = bm->getAddr32(x, y);
+    int pixel = readByte(buf, xorOffset + pixelNo);
+    int alphaBit = (alphaByte & m) >> shift;
+    *address = (alphaBit-1)&(colors[pixel]);
+}            
+
+static void editPixelBit24(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+    SkPMColor* address = bm->getAddr32(x, y);
+    int blue = readByte(buf, xorOffset + 3*pixelNo);
+    int green = readByte(buf, xorOffset + 3*pixelNo + 1);
+    int red = readByte(buf, xorOffset + 3*pixelNo + 2);
+    int alphaBit = (alphaByte & m) >> shift;
+    //alphaBit == 1 => alpha = 0
+    int alpha = (alphaBit-1) & 0xFF;
+    *address = SkPackARGB32(alpha, red & alpha, green & alpha, blue & alpha);    
+}
+
+static void editPixelBit32(const int pixelNo, const unsigned char* buf, 
+            const int xorOffset, int& x, int y, const int w, 
+            SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+    SkPMColor* address = bm->getAddr32(x, y);
+    int blue = readByte(buf, xorOffset + 4*pixelNo);
+    int green = readByte(buf, xorOffset + 4*pixelNo + 1);
+    int red = readByte(buf, xorOffset + 4*pixelNo + 2);
+    int alphaBit = (alphaByte & m) >> shift;
+    int alpha = readByte(buf, xorOffset + 4*pixelNo + 3) & ((alphaBit-1)&0xFF);
+    *address = SkPackARGB32(alpha, red & alpha, green & alpha, blue & alpha);
+}
+
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
new file mode 100644
index 0000000..492de23
--- /dev/null
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -0,0 +1,811 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkImageDecoder.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+#include <stdio.h>
+extern "C" {
+    #include "jpeglib.h"
+    #include "jerror.h"
+}
+
+// this enables timing code to report milliseconds for an encode
+//#define TIME_ENCODE
+//#define TIME_DECODE
+
+// this enables our rgb->yuv code, which is faster than libjpeg on ARM
+// disable for the moment, as we have some glitches when width != multiple of 4
+#define WE_CONVERT_TO_YUV
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+class SkJPEGImageDecoder : public SkImageDecoder {
+public:
+    virtual Format getFormat() const {
+        return kJPEG_Format;
+    }
+
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+                          SkBitmap::Config pref, Mode);
+};
+
+SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream* stream) {
+    static const char gHeader[] = { 0xFF, 0xD8, 0xFF };
+    static const size_t HEADER_SIZE = sizeof(gHeader);
+
+    char buffer[HEADER_SIZE];
+    size_t len = stream->read(buffer, HEADER_SIZE);
+
+    if (len != HEADER_SIZE) {
+        return NULL;   // can't read enough
+    }
+    
+    if (memcmp(buffer, gHeader, HEADER_SIZE)) {
+        return NULL;
+    }
+
+    return SkNEW(SkJPEGImageDecoder);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+#include "SkTime.h"
+
+class AutoTimeMillis {
+public:
+    AutoTimeMillis(const char label[]) : fLabel(label) {
+        if (!fLabel) {
+            fLabel = "";
+        }
+        fNow = SkTime::GetMSecs();
+    }
+    ~AutoTimeMillis() {
+        SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
+    }
+private:
+    const char* fLabel;
+    SkMSec      fNow;
+};
+
+/* our source struct for directing jpeg to our stream object
+*/
+struct sk_source_mgr : jpeg_source_mgr {
+    sk_source_mgr(SkStream* stream, SkImageDecoder* decoder);
+
+    SkStream*   fStream;
+    const void* fMemoryBase;
+    size_t      fMemoryBaseSize;
+    SkImageDecoder* fDecoder;
+    enum {
+        kBufferSize = 1024
+    };
+    char    fBuffer[kBufferSize];
+};
+
+/* Automatically clean up after throwing an exception */
+class JPEGAutoClean {
+public:
+    JPEGAutoClean(): cinfo_ptr(NULL) {}
+    ~JPEGAutoClean() {
+        if (cinfo_ptr) {
+            jpeg_destroy_decompress(cinfo_ptr);
+        }
+    }
+    void set(jpeg_decompress_struct* info) {
+        cinfo_ptr = info;
+    }
+private:
+    jpeg_decompress_struct* cinfo_ptr;
+};
+
+static void sk_init_source(j_decompress_ptr cinfo) {
+    sk_source_mgr*  src = (sk_source_mgr*)cinfo->src;
+    src->next_input_byte = (const JOCTET*)src->fBuffer;
+    src->bytes_in_buffer = 0;
+}
+
+static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) {
+    sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
+    if (src->fDecoder != NULL && src->fDecoder->shouldCancelDecode()) {
+        return FALSE;
+    }
+    size_t bytes = src->fStream->read(src->fBuffer, sk_source_mgr::kBufferSize);
+    // note that JPEG is happy with less than the full read,
+    // as long as the result is non-zero
+    if (bytes == 0) {
+        return FALSE;
+    }
+
+    src->next_input_byte = (const JOCTET*)src->fBuffer;
+    src->bytes_in_buffer = bytes;
+    return TRUE;
+}
+
+static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+    SkASSERT(num_bytes > 0);
+
+    sk_source_mgr*  src = (sk_source_mgr*)cinfo->src;
+
+    long bytesToSkip = num_bytes - src->bytes_in_buffer;
+
+    // check if the skip amount exceeds the current buffer
+    if (bytesToSkip > 0) {
+        size_t bytes = src->fStream->skip(bytesToSkip);
+        if (bytes != (size_t)bytesToSkip) {
+//            SkDebugf("xxxxxxxxxxxxxx failure to skip request %d actual %d\n", bytesToSkip, bytes);
+            cinfo->err->error_exit((j_common_ptr)cinfo);
+        }
+        src->next_input_byte = (const JOCTET*)src->fBuffer;
+        src->bytes_in_buffer = 0;
+    } else {
+        src->next_input_byte += num_bytes;
+        src->bytes_in_buffer -= num_bytes;
+    }
+}
+
+static boolean sk_resync_to_restart(j_decompress_ptr cinfo, int desired) {
+    sk_source_mgr*  src = (sk_source_mgr*)cinfo->src;
+
+    // what is the desired param for???
+
+    if (!src->fStream->rewind()) {
+        SkDebugf("xxxxxxxxxxxxxx failure to rewind\n");
+        cinfo->err->error_exit((j_common_ptr)cinfo);
+        return FALSE;
+    }
+    src->next_input_byte = (const JOCTET*)src->fBuffer;
+    src->bytes_in_buffer = 0;
+    return TRUE;
+}
+
+static void sk_term_source(j_decompress_ptr /*cinfo*/) {}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void skmem_init_source(j_decompress_ptr cinfo) {
+    sk_source_mgr*  src = (sk_source_mgr*)cinfo->src;
+    src->next_input_byte = (const JOCTET*)src->fMemoryBase;
+    src->bytes_in_buffer = src->fMemoryBaseSize;
+}
+
+static boolean skmem_fill_input_buffer(j_decompress_ptr cinfo) {
+    SkDebugf("xxxxxxxxxxxxxx skmem_fill_input_buffer called\n");
+    return FALSE;
+}
+
+static void skmem_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+    sk_source_mgr*  src = (sk_source_mgr*)cinfo->src;
+//    SkDebugf("xxxxxxxxxxxxxx skmem_skip_input_data called %d\n", num_bytes);
+    src->next_input_byte = (const JOCTET*)((const char*)src->next_input_byte + num_bytes);
+    src->bytes_in_buffer -= num_bytes;
+}
+
+static boolean skmem_resync_to_restart(j_decompress_ptr cinfo, int desired) {
+    SkDebugf("xxxxxxxxxxxxxx skmem_resync_to_restart called\n");
+    return TRUE;
+}
+
+static void skmem_term_source(j_decompress_ptr /*cinfo*/) {}
+
+///////////////////////////////////////////////////////////////////////////////
+
+sk_source_mgr::sk_source_mgr(SkStream* stream, SkImageDecoder* decoder) : fStream(stream) {
+    fDecoder = decoder;
+    const void* baseAddr = stream->getMemoryBase();
+    if (baseAddr && false) {
+        fMemoryBase = baseAddr;
+        fMemoryBaseSize = stream->getLength();
+        
+        init_source = skmem_init_source;
+        fill_input_buffer = skmem_fill_input_buffer;
+        skip_input_data = skmem_skip_input_data;
+        resync_to_restart = skmem_resync_to_restart;
+        term_source = skmem_term_source;
+    } else {
+        fMemoryBase = NULL;
+        fMemoryBaseSize = 0;
+
+        init_source = sk_init_source;
+        fill_input_buffer = sk_fill_input_buffer;
+        skip_input_data = sk_skip_input_data;
+        resync_to_restart = sk_resync_to_restart;
+        term_source = sk_term_source;
+    }
+//    SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize);
+}
+
+#include <setjmp.h>
+
+struct sk_error_mgr : jpeg_error_mgr {
+    jmp_buf fJmpBuf;
+};
+
+static void sk_error_exit(j_common_ptr cinfo) {
+    sk_error_mgr* error = (sk_error_mgr*)cinfo->err;
+
+    (*error->output_message) (cinfo);
+
+    /* Let the memory manager delete any temp files before we die */
+    jpeg_destroy(cinfo);
+
+    longjmp(error->fJmpBuf, -1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer,
+                          int count) {
+    for (int i = 0; i < count; i++) {
+        JSAMPLE* rowptr = (JSAMPLE*)buffer;
+        int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
+        if (row_count != 1) {
+            return false;
+        }
+    }
+    return true;
+}
+
+// This guy exists just to aid in debugging, as it allows debuggers to just
+// set a break-point in one place to see all error exists.
+static bool return_false(const jpeg_decompress_struct& cinfo,
+                         const SkBitmap& bm, const char msg[]) {
+#if 0
+    SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
+             cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
+             bm.width(), bm.height());
+#endif
+    return false;   // must always return false
+}
+
+bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
+                                  SkBitmap::Config prefConfig, Mode mode) {
+#ifdef TIME_DECODE
+    AutoTimeMillis atm("JPEG Decode");
+#endif
+
+    SkAutoMalloc  srcStorage;
+    JPEGAutoClean autoClean;
+
+    jpeg_decompress_struct  cinfo;
+    sk_error_mgr            sk_err;
+    sk_source_mgr           sk_stream(stream, this);
+
+    cinfo.err = jpeg_std_error(&sk_err);
+    sk_err.error_exit = sk_error_exit;
+
+    // All objects need to be instantiated before this setjmp call so that
+    // they will be cleaned up properly if an error occurs.
+    if (setjmp(sk_err.fJmpBuf)) {
+        return return_false(cinfo, *bm, "setjmp");
+    }
+
+    jpeg_create_decompress(&cinfo);
+    autoClean.set(&cinfo);
+
+    //jpeg_stdio_src(&cinfo, file);
+    cinfo.src = &sk_stream;
+
+    int status = jpeg_read_header(&cinfo, true);
+    if (status != JPEG_HEADER_OK) {
+        return return_false(cinfo, *bm, "read_header");
+    }
+
+    /*  Try to fulfill the requested sampleSize. Since jpeg can do it (when it
+        can) much faster that we, just use their num/denom api to approximate
+        the size.
+    */
+    int sampleSize = this->getSampleSize();
+
+    cinfo.dct_method = JDCT_IFAST;
+    cinfo.scale_num = 1;
+    cinfo.scale_denom = sampleSize;
+
+    /* this gives about 30% performance improvement. In theory it may
+       reduce the visual quality, in practice I'm not seeing a difference
+     */
+    cinfo.do_fancy_upsampling = 0;
+
+    /* this gives another few percents */
+    cinfo.do_block_smoothing = 0;
+
+    /* default format is RGB */
+    cinfo.out_color_space = JCS_RGB;
+
+    SkBitmap::Config config = prefConfig;
+    // if no user preference, see what the device recommends
+    if (config == SkBitmap::kNo_Config)
+        config = SkImageDecoder::GetDeviceConfig();
+
+    // only these make sense for jpegs
+    if (config != SkBitmap::kARGB_8888_Config &&
+        config != SkBitmap::kARGB_4444_Config &&
+        config != SkBitmap::kRGB_565_Config) {
+        config = SkBitmap::kARGB_8888_Config;
+    }
+
+#ifdef ANDROID_RGB
+    cinfo.dither_mode = JDITHER_NONE;
+    if (config == SkBitmap::kARGB_8888_Config) {
+        cinfo.out_color_space = JCS_RGBA_8888;
+    } else if (config == SkBitmap::kRGB_565_Config) {
+        if (sampleSize == 1) {
+            // SkScaledBitmapSampler can't handle RGB_565 yet,
+            // so don't even try.
+            cinfo.out_color_space = JCS_RGB_565;
+            if (this->getDitherImage()) {
+                cinfo.dither_mode = JDITHER_ORDERED;
+            }
+        }
+    }
+#endif
+
+    /*  image_width and image_height are the original dimensions, available
+        after jpeg_read_header(). To see the scaled dimensions, we have to call
+        jpeg_start_decompress(), and then read output_width and output_height.
+    */
+    if (!jpeg_start_decompress(&cinfo)) {
+        return return_false(cinfo, *bm, "start_decompress");
+    }
+
+    /*  If we need to better match the request, we might examine the image and
+        output dimensions, and determine if the downsampling jpeg provided is
+        not sufficient. If so, we can recompute a modified sampleSize value to
+        make up the difference.
+
+        To skip this additional scaling, just set sampleSize = 1; below.
+    */
+    sampleSize = sampleSize * cinfo.output_width / cinfo.image_width;
+
+
+    // should we allow the Chooser (if present) to pick a config for us???
+    if (!this->chooseFromOneChoice(config, cinfo.output_width,
+                                   cinfo.output_height)) {
+        return return_false(cinfo, *bm, "chooseFromOneChoice");
+    }
+
+#ifdef ANDROID_RGB
+    /* short-circuit the SkScaledBitmapSampler when possible, as this gives
+       a significant performance boost.
+    */
+    if (sampleSize == 1 &&
+        ((config == SkBitmap::kARGB_8888_Config && 
+                cinfo.out_color_space == JCS_RGBA_8888) ||
+        (config == SkBitmap::kRGB_565_Config && 
+                cinfo.out_color_space == JCS_RGB_565)))
+    {
+        bm->setConfig(config, cinfo.output_width, cinfo.output_height);
+        bm->setIsOpaque(true);
+        if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+            return true;
+        }
+        if (!this->allocPixelRef(bm, NULL)) {
+            return return_false(cinfo, *bm, "allocPixelRef");
+        }
+        SkAutoLockPixels alp(*bm);
+        JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
+        INT32 const bpr =  bm->rowBytes();
+        
+        while (cinfo.output_scanline < cinfo.output_height) {
+            int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
+            // if row_count == 0, then we didn't get a scanline, so abort.
+            // if we supported partial images, we might return true in this case
+            if (0 == row_count) {
+                return return_false(cinfo, *bm, "read_scanlines");
+            }
+            if (this->shouldCancelDecode()) {
+                return return_false(cinfo, *bm, "shouldCancelDecode");
+            }
+            rowptr += bpr;
+        }
+        jpeg_finish_decompress(&cinfo);
+        return true;
+    }
+#endif
+    
+    // check for supported formats
+    SkScaledBitmapSampler::SrcConfig sc;
+    if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
+        sc = SkScaledBitmapSampler::kRGB;
+#ifdef ANDROID_RGB
+    } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
+        sc = SkScaledBitmapSampler::kRGBX;
+    //} else if (JCS_RGB_565 == cinfo.out_color_space) {
+    //    sc = SkScaledBitmapSampler::kRGB_565;
+#endif
+    } else if (1 == cinfo.out_color_components &&
+               JCS_GRAYSCALE == cinfo.out_color_space) {
+        sc = SkScaledBitmapSampler::kGray;
+    } else {
+        return return_false(cinfo, *bm, "jpeg colorspace");
+    }
+
+    SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height,
+                                  sampleSize);
+
+    bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+    // jpegs are always opauqe (i.e. have no per-pixel alpha)
+    bm->setIsOpaque(true);
+
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        return true;
+    }
+    if (!this->allocPixelRef(bm, NULL)) {
+        return return_false(cinfo, *bm, "allocPixelRef");
+    }
+
+    SkAutoLockPixels alp(*bm);                          
+    if (!sampler.begin(bm, sc, this->getDitherImage())) {
+        return return_false(cinfo, *bm, "sampler.begin");
+    }
+
+    uint8_t* srcRow = (uint8_t*)srcStorage.alloc(cinfo.output_width * 4);
+
+    //  Possibly skip initial rows [sampler.srcY0]
+    if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
+        return return_false(cinfo, *bm, "skip rows");
+    }
+
+    // now loop through scanlines until y == bm->height() - 1
+    for (int y = 0;; y++) {
+        JSAMPLE* rowptr = (JSAMPLE*)srcRow;
+        int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
+        if (0 == row_count) {
+            return return_false(cinfo, *bm, "read_scanlines");
+        }
+        if (this->shouldCancelDecode()) {
+            return return_false(cinfo, *bm, "shouldCancelDecode");
+        }
+        
+        sampler.next(srcRow);
+        if (bm->height() - 1 == y) {
+            // we're done
+            break;
+        }
+
+        if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
+            return return_false(cinfo, *bm, "skip rows");
+        }
+    }
+
+    // we formally skip the rest, so we don't get a complaint from libjpeg
+    if (!skip_src_rows(&cinfo, srcRow,
+                       cinfo.output_height - cinfo.output_scanline)) {
+        return return_false(cinfo, *bm, "skip rows");
+    }
+    jpeg_finish_decompress(&cinfo);
+
+//    SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+
+#include "SkColorPriv.h"
+
+// taken from jcolor.c in libjpeg
+#if 0   // 16bit - precise but slow
+    #define CYR     19595   // 0.299
+    #define CYG     38470   // 0.587
+    #define CYB      7471   // 0.114
+
+    #define CUR    -11059   // -0.16874
+    #define CUG    -21709   // -0.33126
+    #define CUB     32768   // 0.5
+
+    #define CVR     32768   // 0.5
+    #define CVG    -27439   // -0.41869
+    #define CVB     -5329   // -0.08131
+
+    #define CSHIFT  16
+#else      // 8bit - fast, slightly less precise
+    #define CYR     77    // 0.299
+    #define CYG     150    // 0.587
+    #define CYB      29    // 0.114
+
+    #define CUR     -43    // -0.16874
+    #define CUG    -85    // -0.33126
+    #define CUB     128    // 0.5
+
+    #define CVR      128   // 0.5
+    #define CVG     -107   // -0.41869
+    #define CVB      -21   // -0.08131
+
+    #define CSHIFT  8
+#endif
+
+static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
+    int r = SkGetPackedR32(c);
+    int g = SkGetPackedG32(c);
+    int b = SkGetPackedB32(c);
+
+    int  y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
+    int  u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
+    int  v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
+
+    dst[0] = SkToU8(y);
+    dst[1] = SkToU8(u + 128);
+    dst[2] = SkToU8(v + 128);
+}
+
+static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
+    int r = SkGetPackedR4444(c);
+    int g = SkGetPackedG4444(c);
+    int b = SkGetPackedB4444(c);
+
+    int  y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
+    int  u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
+    int  v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
+
+    dst[0] = SkToU8(y);
+    dst[1] = SkToU8(u + 128);
+    dst[2] = SkToU8(v + 128);
+}
+
+static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
+    int r = SkGetPackedR16(c);
+    int g = SkGetPackedG16(c);
+    int b = SkGetPackedB16(c);
+
+    int  y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
+    int  u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
+    int  v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
+
+    dst[0] = SkToU8(y);
+    dst[1] = SkToU8(u + 128);
+    dst[2] = SkToU8(v + 128);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
+                              const void* SK_RESTRICT src, int width,
+                              const SkPMColor* SK_RESTRICT ctable);
+
+static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
+                         const void* SK_RESTRICT srcRow, int width,
+                         const SkPMColor*) {
+    const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
+    while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+        rgb2yuv_32(dst, *src++);
+#else
+        uint32_t c = *src++;
+        dst[0] = SkGetPackedR32(c);
+        dst[1] = SkGetPackedG32(c);
+        dst[2] = SkGetPackedB32(c);
+#endif
+        dst += 3;
+    }
+}
+
+static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
+                           const void* SK_RESTRICT srcRow, int width,
+                           const SkPMColor*) {
+    const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
+    while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+        rgb2yuv_4444(dst, *src++);
+#else
+        SkPMColor16 c = *src++;
+        dst[0] = SkPacked4444ToR32(c);
+        dst[1] = SkPacked4444ToG32(c);
+        dst[2] = SkPacked4444ToB32(c);
+#endif
+        dst += 3;
+    }
+}
+
+static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
+                         const void* SK_RESTRICT srcRow, int width,
+                         const SkPMColor*) {
+    const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
+    while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+        rgb2yuv_16(dst, *src++);
+#else
+        uint16_t c = *src++;
+        dst[0] = SkPacked16ToR32(c);
+        dst[1] = SkPacked16ToG32(c);
+        dst[2] = SkPacked16ToB32(c);
+#endif
+        dst += 3;
+    }
+}
+
+static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
+                            const void* SK_RESTRICT srcRow, int width,
+                            const SkPMColor* SK_RESTRICT ctable) {
+    const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
+    while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+        rgb2yuv_32(dst, ctable[*src++]);
+#else
+        uint32_t c = ctable[*src++];
+        dst[0] = SkGetPackedR32(c);
+        dst[1] = SkGetPackedG32(c);
+        dst[2] = SkGetPackedB32(c);
+#endif
+        dst += 3;
+    }
+}
+
+static WriteScanline ChooseWriter(const SkBitmap& bm) {
+    switch (bm.config()) {
+        case SkBitmap::kARGB_8888_Config:
+            return Write_32_YUV;
+        case SkBitmap::kRGB_565_Config:
+            return Write_16_YUV;
+        case SkBitmap::kARGB_4444_Config:
+            return Write_4444_YUV;
+        case SkBitmap::kIndex8_Config:
+            return Write_Index_YUV;
+        default:
+            return NULL;
+    }
+}
+
+struct sk_destination_mgr : jpeg_destination_mgr {
+    sk_destination_mgr(SkWStream* stream);
+
+    SkWStream*  fStream;
+
+    enum {
+        kBufferSize = 1024
+    };
+    uint8_t fBuffer[kBufferSize];
+};
+
+static void sk_init_destination(j_compress_ptr cinfo) {
+    sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
+
+    dest->next_output_byte = dest->fBuffer;
+    dest->free_in_buffer = sk_destination_mgr::kBufferSize;
+}
+
+static boolean sk_empty_output_buffer(j_compress_ptr cinfo) {
+    sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
+
+//  if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize - dest->free_in_buffer))
+    if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize)) {
+        ERREXIT(cinfo, JERR_FILE_WRITE);
+        return false;
+    }
+
+    dest->next_output_byte = dest->fBuffer;
+    dest->free_in_buffer = sk_destination_mgr::kBufferSize;
+    return TRUE;
+}
+
+static void sk_term_destination (j_compress_ptr cinfo) {
+    sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
+
+    size_t size = sk_destination_mgr::kBufferSize - dest->free_in_buffer;
+    if (size > 0) {
+        if (!dest->fStream->write(dest->fBuffer, size)) {
+            ERREXIT(cinfo, JERR_FILE_WRITE);
+            return;
+        }
+    }
+    dest->fStream->flush();
+}
+
+sk_destination_mgr::sk_destination_mgr(SkWStream* stream)
+        : fStream(stream) {
+    this->init_destination = sk_init_destination;
+    this->empty_output_buffer = sk_empty_output_buffer;
+    this->term_destination = sk_term_destination;
+}
+
+class SkJPEGImageEncoder : public SkImageEncoder {
+protected:
+    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
+#ifdef TIME_ENCODE
+        AutoTimeMillis atm("JPEG Encode");
+#endif
+
+        const WriteScanline writer = ChooseWriter(bm);
+        if (NULL == writer) {
+            return false;
+        }
+
+        SkAutoLockPixels alp(bm);
+        if (NULL == bm.getPixels()) {
+            return false;
+        }
+
+        jpeg_compress_struct    cinfo;
+        sk_error_mgr            sk_err;
+        sk_destination_mgr      sk_wstream(stream);
+
+        // allocate these before set call setjmp
+        SkAutoMalloc    oneRow;
+        SkAutoLockColors ctLocker;
+
+        cinfo.err = jpeg_std_error(&sk_err);
+        sk_err.error_exit = sk_error_exit;
+        if (setjmp(sk_err.fJmpBuf)) {
+            return false;
+        }
+        jpeg_create_compress(&cinfo);
+
+        cinfo.dest = &sk_wstream;
+        cinfo.image_width = bm.width();
+        cinfo.image_height = bm.height();
+        cinfo.input_components = 3;
+#ifdef WE_CONVERT_TO_YUV
+        cinfo.in_color_space = JCS_YCbCr;
+#else
+        cinfo.in_color_space = JCS_RGB;
+#endif
+        cinfo.input_gamma = 1;
+
+        jpeg_set_defaults(&cinfo);
+        jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
+        cinfo.dct_method = JDCT_IFAST;
+
+        jpeg_start_compress(&cinfo, TRUE);
+
+        const int       width = bm.width();
+        uint8_t*        oneRowP = (uint8_t*)oneRow.alloc(width * 3);
+
+        const SkPMColor* colors = ctLocker.lockColors(bm);
+        const void*      srcRow = bm.getPixels();
+
+        while (cinfo.next_scanline < cinfo.image_height) {
+            JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s] */
+
+            writer(oneRowP, srcRow, width, colors);
+            row_pointer[0] = oneRowP;
+            (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
+            srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
+        }
+
+        jpeg_finish_compress(&cinfo);
+        jpeg_destroy_compress(&cinfo);
+
+        return true;
+    }
+};
+
+SkImageEncoder* SkImageEncoder_JPEG_Factory();
+SkImageEncoder* SkImageEncoder_JPEG_Factory() {
+    return SkNEW(SkJPEGImageEncoder);
+}
+
+#endif /* SK_SUPPORT_IMAGE_ENCODE */
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkImageDecoder::UnitTest() {
+    SkBitmap    bm;
+
+    (void)SkImageDecoder::DecodeFile("logo.jpg", &bm);
+}
+
+#endif
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
new file mode 100644
index 0000000..862ebf1
--- /dev/null
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -0,0 +1,795 @@
+/* libs/graphics/images/SkImageDecoder_libpng.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 "SkImageDecoder.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkMath.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+extern "C" {
+#include "png.h"
+}
+
+class SkPNGImageDecoder : public SkImageDecoder {
+public:
+    virtual Format getFormat() const {
+        return kPNG_Format;
+    }
+    
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+                          SkBitmap::Config pref, Mode);
+};
+
+#ifndef png_jmpbuf
+#  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
+#endif
+
+#define PNG_BYTES_TO_CHECK 4
+
+/* Automatically clean up after throwing an exception */
+struct PNGAutoClean {
+    PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
+    ~PNGAutoClean() {
+        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+    }
+private:
+    png_structp png_ptr;
+    png_infop info_ptr;
+};
+
+SkImageDecoder* SkImageDecoder_PNG_Factory(SkStream* stream) {
+    char buf[PNG_BYTES_TO_CHECK];
+    if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
+            !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
+        return SkNEW(SkPNGImageDecoder);
+    }
+    return NULL;
+}
+
+static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
+    SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
+    size_t bytes = sk_stream->read(data, length);
+    if (bytes != length) {
+        png_error(png_ptr, "Read Error!");
+    }
+}
+
+static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
+    SkImageDecoder::Peeker* peeker =
+                    (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
+    // peek() returning true means continue decoding
+    return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ?
+            1 : -1;
+}
+
+static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
+#if 0
+    SkDebugf("------ png error %s\n", msg);
+#endif
+    longjmp(png_jmpbuf(png_ptr), 1);
+}
+
+static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
+    for (int i = 0; i < count; i++) {
+        uint8_t* tmp = storage;
+        png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
+    }
+}
+
+static bool pos_le(int value, int max) {
+    return value > 0 && value <= max;
+}
+
+static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
+    SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
+    
+    bool reallyHasAlpha = false;
+
+    for (int y = bm->height() - 1; y >= 0; --y) {
+        SkPMColor* p = bm->getAddr32(0, y);
+        for (int x = bm->width() - 1; x >= 0; --x) {
+            if (match == *p) {
+                *p = 0;
+                reallyHasAlpha = true;
+            }
+            p += 1;
+        }
+    }
+    return reallyHasAlpha;
+}
+
+bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
+                                 SkBitmap::Config prefConfig, Mode mode) {
+//    SkAutoTrace    apr("SkPNGImageDecoder::onDecode");
+
+    /* Create and initialize the png_struct with the desired error handler
+    * functions.  If you want to use the default stderr and longjump method,
+    * you can supply NULL for the last three parameters.  We also supply the
+    * the compiler header file version, so that we know if the application
+    * was compiled with a compatible version of the library.  */
+    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+        NULL, sk_error_fn, NULL);
+    //   png_voidp user_error_ptr, user_error_fn, user_warning_fn);
+    if (png_ptr == NULL) {
+        return false;
+    }
+
+    /* Allocate/initialize the memory for image information. */
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (info_ptr == NULL) {
+        png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
+        return false;
+    }
+
+    PNGAutoClean autoClean(png_ptr, info_ptr);
+
+    /* Set error handling if you are using the setjmp/longjmp method (this is
+    * the normal method of doing things with libpng).  REQUIRED unless you
+    * set up your own error handlers in the png_create_read_struct() earlier.
+    */
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        return false;
+    }
+
+    /* If you are using replacement read functions, instead of calling
+    * png_init_io() here you would call:
+    */
+    png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
+    /* where user_io_ptr is a structure you want available to the callbacks */
+    /* If we have already read some of the signature */
+//  png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
+
+    // hookup our peeker so we can see any user-chunks the caller may be interested in
+    png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
+    if (this->getPeeker()) {
+        png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);
+    }
+
+    /* The call to png_read_info() gives us all of the information from the
+    * PNG file before the first IDAT (image data chunk). */
+    png_read_info(png_ptr, info_ptr);
+    png_uint_32 origWidth, origHeight;
+    int bit_depth, color_type, interlace_type;
+    png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type,
+        &interlace_type, int_p_NULL, int_p_NULL);
+
+    /* tell libpng to strip 16 bit/color files down to 8 bits/color */
+    if (bit_depth == 16) {
+        png_set_strip_16(png_ptr);
+    }
+    /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
+     * byte into separate bytes (useful for paletted and grayscale images). */
+    if (bit_depth < 8) {
+        png_set_packing(png_ptr);
+    }
+    /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
+    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+        png_set_gray_1_2_4_to_8(png_ptr);
+    }
+    
+    /* Make a grayscale image into RGB. */
+    if (color_type == PNG_COLOR_TYPE_GRAY ||
+        color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+        png_set_gray_to_rgb(png_ptr);
+    }
+        
+    SkBitmap::Config    config;
+    bool                hasAlpha = false;
+    bool                doDither = this->getDitherImage();
+    SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
+    
+    // check for sBIT chunk data, in case we should disable dithering because
+    // our data is not truely 8bits per component
+    if (doDither) {
+#if 0
+        SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red,
+                 info_ptr->sig_bit.green, info_ptr->sig_bit.blue,
+                 info_ptr->sig_bit.alpha);
+#endif
+        // 0 seems to indicate no information available
+        if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) &&
+                pos_le(info_ptr->sig_bit.green, SK_G16_BITS) &&
+                pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) {
+            doDither = false;
+        }
+    }
+    
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        config = SkBitmap::kIndex8_Config;  // defer sniffing for hasAlpha
+    } else {
+        png_color_16p   transpColor = NULL;
+        int             numTransp = 0;
+        
+        png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
+        
+        bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
+        
+        if (valid && numTransp == 1 && transpColor != NULL) {
+            /*  Compute our transparent color, which we'll match against later.
+                We don't really handle 16bit components properly here, since we
+                do our compare *after* the values have been knocked down to 8bit
+                which means we will find more matches than we should. The real
+                fix seems to be to see the actual 16bit components, do the
+                compare, and then knock it down to 8bits ourselves.
+            */
+            if (color_type & PNG_COLOR_MASK_COLOR) {
+                if (16 == bit_depth) {
+                    theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8,
+                              transpColor->green >> 8, transpColor->blue >> 8);
+                } else {
+                    theTranspColor = SkPackARGB32(0xFF, transpColor->red,
+                                      transpColor->green, transpColor->blue);
+                }
+            } else {    // gray
+                if (16 == bit_depth) {
+                    theTranspColor = SkPackARGB32(0xFF, transpColor->gray >> 8,
+                              transpColor->gray >> 8, transpColor->gray >> 8);
+                } else {
+                    theTranspColor = SkPackARGB32(0xFF, transpColor->gray,
+                                          transpColor->gray, transpColor->gray);
+                }
+            }
+        }
+
+        if (valid ||
+                PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
+                PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
+            hasAlpha = true;
+            config = SkBitmap::kARGB_8888_Config;
+        } else {    // we get to choose the config
+            config = prefConfig;
+            if (config == SkBitmap::kNo_Config) {
+                config = SkImageDecoder::GetDeviceConfig();
+            }
+            if (config != SkBitmap::kRGB_565_Config &&
+                    config != SkBitmap::kARGB_4444_Config) {
+                config = SkBitmap::kARGB_8888_Config;
+            }
+        }
+    }
+    
+    if (!this->chooseFromOneChoice(config, origWidth, origHeight)) {
+        return false;
+    }
+    
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
+
+    decodedBitmap->setConfig(config, sampler.scaledWidth(),
+                             sampler.scaledHeight(), 0);
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        return true;
+    }
+    
+    // from here down we are concerned with colortables and pixels
+
+    // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
+    // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
+    // draw lots faster if we can flag the bitmap has being opaque
+    bool reallyHasAlpha = false;
+
+    SkColorTable* colorTable = NULL;
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        int num_palette;
+        png_colorp palette;
+        png_bytep trans;
+        int num_trans;
+
+        png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
+        
+        /*  BUGGY IMAGE WORKAROUND
+            
+            We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
+            which is a problem since we use the byte as an index. To work around this we grow
+            the colortable by 1 (if its < 256) and duplicate the last color into that slot.
+        */
+        int colorCount = num_palette + (num_palette < 256);
+
+        colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
+
+        SkPMColor* colorPtr = colorTable->lockColors();
+        if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+            png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
+            hasAlpha = (num_trans > 0);
+        } else {
+            num_trans = 0;
+            colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+        }        
+        // check for bad images that might make us crash
+        if (num_trans > num_palette) {
+            num_trans = num_palette;
+        }
+
+        int index = 0;
+        int transLessThanFF = 0;
+
+        for (; index < num_trans; index++) {
+            transLessThanFF |= (int)*trans - 0xFF;
+            *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
+            palette++;
+        }
+        reallyHasAlpha |= (transLessThanFF < 0);
+
+        for (; index < num_palette; index++) {
+            *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
+            palette++;
+        }
+
+        // see BUGGY IMAGE WORKAROUND comment above
+        if (num_palette < 256) {
+            *colorPtr = colorPtr[-1];
+        }
+        colorTable->unlockColors(true);
+    }
+    
+    SkAutoUnref aur(colorTable);
+
+    if (!this->allocPixelRef(decodedBitmap, colorTable)) {
+        return false;
+    }
+    
+    SkAutoLockPixels alp(*decodedBitmap);
+
+    /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
+//  if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+//      ; // png_set_swap_alpha(png_ptr);
+
+    /* swap bytes of 16 bit files to least significant byte first */
+    //   png_set_swap(png_ptr);
+
+    /* Add filler (or alpha) byte (before/after each RGB triplet) */
+    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
+        png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+    }
+
+    /* Turn on interlace handling.  REQUIRED if you are not using
+    * png_read_image().  To see how to handle interlacing passes,
+    * see the png_read_row() method below:
+    */
+    const int number_passes = interlace_type != PNG_INTERLACE_NONE ? 
+                        png_set_interlace_handling(png_ptr) : 1;
+
+    /* Optional call to gamma correct and add the background to the palette
+    * and update info structure.  REQUIRED if you are expecting libpng to
+    * update the palette for you (ie you selected such a transform above).
+    */
+    png_read_update_info(png_ptr, info_ptr);
+
+    if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
+        for (int i = 0; i < number_passes; i++) {
+            for (png_uint_32 y = 0; y < origHeight; y++) {
+                uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
+                png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+            }
+        }
+    } else {
+        SkScaledBitmapSampler::SrcConfig sc;
+        int srcBytesPerPixel = 4;
+        
+        if (SkBitmap::kIndex8_Config == config) {
+            sc = SkScaledBitmapSampler::kIndex;
+            srcBytesPerPixel = 1;
+        } else if (hasAlpha) {
+            sc = SkScaledBitmapSampler::kRGBA;
+        } else {
+            sc = SkScaledBitmapSampler::kRGBX;
+        }
+
+        SkAutoMalloc storage(origWidth * srcBytesPerPixel);
+        const int height = decodedBitmap->height();
+
+        for (int i = 0; i < number_passes; i++) {
+            if (!sampler.begin(decodedBitmap, sc, doDither)) {
+                return false;
+            }
+
+            uint8_t* srcRow = (uint8_t*)storage.get();
+            skip_src_rows(png_ptr, srcRow, sampler.srcY0());
+
+            for (int y = 0; y < height; y++) {
+                uint8_t* tmp = srcRow;
+                png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
+                reallyHasAlpha |= sampler.next(srcRow);
+                if (y < height - 1) {
+                    skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
+                }
+            }
+            
+            // skip the rest of the rows (if any)
+            png_uint_32 read = (height - 1) * sampler.srcDY() +
+                               sampler.srcY0() + 1;
+            SkASSERT(read <= origHeight);
+            skip_src_rows(png_ptr, srcRow, origHeight - read);
+        }
+
+        if (hasAlpha && !reallyHasAlpha) {
+#if 0
+            SkDEBUGF(("Image doesn't really have alpha [%d %d]\n",
+                      origWidth, origHeight));
+#endif
+        }
+    }
+
+    /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
+    png_read_end(png_ptr, info_ptr);
+
+    if (0 != theTranspColor) {
+        reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
+    }
+    decodedBitmap->setIsOpaque(!reallyHasAlpha);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+
+#include "SkColorPriv.h"
+#include "SkUnPreMultiply.h"
+
+static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
+    SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
+    if (!sk_stream->write(data, len)) {
+        png_error(png_ptr, "sk_write_fn Error!");
+    }
+}
+
+typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src,
+                                        int width, char* SK_RESTRICT dst);
+
+static void transform_scanline_565(const char* SK_RESTRICT src, int width,
+                                   char* SK_RESTRICT dst) {
+    const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src;    
+    for (int i = 0; i < width; i++) {
+        unsigned c = *srcP++;
+        *dst++ = SkPacked16ToR32(c);
+        *dst++ = SkPacked16ToG32(c);
+        *dst++ = SkPacked16ToB32(c);
+    }
+}
+
+static void transform_scanline_888(const char* SK_RESTRICT src, int width,
+                                   char* SK_RESTRICT dst) {
+    const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;    
+    for (int i = 0; i < width; i++) {
+        SkPMColor c = *srcP++;
+        *dst++ = SkGetPackedR32(c);
+        *dst++ = SkGetPackedG32(c);
+        *dst++ = SkGetPackedB32(c);
+    }
+}
+
+static void transform_scanline_444(const char* SK_RESTRICT src, int width,
+                                   char* SK_RESTRICT dst) {
+    const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;    
+    for (int i = 0; i < width; i++) {
+        SkPMColor16 c = *srcP++;
+        *dst++ = SkPacked4444ToR32(c);
+        *dst++ = SkPacked4444ToG32(c);
+        *dst++ = SkPacked4444ToB32(c);
+    }
+}
+
+static void transform_scanline_8888(const char* SK_RESTRICT src, int width,
+                                    char* SK_RESTRICT dst) {
+    const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
+    const SkUnPreMultiply::Scale* SK_RESTRICT table = 
+                                              SkUnPreMultiply::GetScaleTable();
+
+    for (int i = 0; i < width; i++) {
+        SkPMColor c = *srcP++;
+        unsigned a = SkGetPackedA32(c);
+        unsigned r = SkGetPackedR32(c);
+        unsigned g = SkGetPackedG32(c);
+        unsigned b = SkGetPackedB32(c);
+
+        if (0 != a && 255 != a) {
+            SkUnPreMultiply::Scale scale = table[a];
+            r = SkUnPreMultiply::ApplyScale(scale, r);
+            g = SkUnPreMultiply::ApplyScale(scale, g);
+            b = SkUnPreMultiply::ApplyScale(scale, b);
+        }
+        *dst++ = r;
+        *dst++ = g;
+        *dst++ = b;
+        *dst++ = a;
+    }
+}
+
+static void transform_scanline_4444(const char* SK_RESTRICT src, int width,
+                                    char* SK_RESTRICT dst) {
+    const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
+    const SkUnPreMultiply::Scale* SK_RESTRICT table = 
+                                              SkUnPreMultiply::GetScaleTable();
+
+    for (int i = 0; i < width; i++) {
+        SkPMColor16 c = *srcP++;
+        unsigned a = SkPacked4444ToA32(c);
+        unsigned r = SkPacked4444ToR32(c);
+        unsigned g = SkPacked4444ToG32(c);
+        unsigned b = SkPacked4444ToB32(c);
+
+        if (0 != a && 255 != a) {
+            SkUnPreMultiply::Scale scale = table[a];
+            r = SkUnPreMultiply::ApplyScale(scale, r);
+            g = SkUnPreMultiply::ApplyScale(scale, g);
+            b = SkUnPreMultiply::ApplyScale(scale, b);
+        }
+        *dst++ = r;
+        *dst++ = g;
+        *dst++ = b;
+        *dst++ = a;
+    }
+}
+
+static void transform_scanline_index8(const char* SK_RESTRICT src, int width,
+                                      char* SK_RESTRICT dst) {
+    memcpy(dst, src, width);
+}
+
+static transform_scanline_proc choose_proc(SkBitmap::Config config,
+                                           bool hasAlpha) {
+    // we don't care about search on alpha if we're kIndex8, since only the
+    // colortable packing cares about that distinction, not the pixels
+    if (SkBitmap::kIndex8_Config == config) {
+        hasAlpha = false;   // we store false in the table entries for kIndex8
+    }
+    
+    static const struct {
+        SkBitmap::Config        fConfig;
+        bool                    fHasAlpha;
+        transform_scanline_proc fProc;
+    } gMap[] = {
+        { SkBitmap::kRGB_565_Config,    false,  transform_scanline_565 },
+        { SkBitmap::kARGB_8888_Config,  false,  transform_scanline_888 },
+        { SkBitmap::kARGB_8888_Config,  true,   transform_scanline_8888 },
+        { SkBitmap::kARGB_4444_Config,  false,  transform_scanline_444 },
+        { SkBitmap::kARGB_4444_Config,  true,   transform_scanline_4444 },
+        { SkBitmap::kIndex8_Config,     false,   transform_scanline_index8 },
+    };
+
+    for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
+        if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
+            return gMap[i].fProc;
+        }
+    }
+    sk_throw();
+    return NULL;
+}
+
+// return the minimum legal bitdepth (by png standards) for this many colortable
+// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
+// we can use fewer bits per in png
+static int computeBitDepth(int colorCount) {
+#if 0
+    int bits = SkNextLog2(colorCount);
+    SkASSERT(bits >= 1 && bits <= 8);
+    // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
+    return SkNextPow2(bits);
+#else
+    // for the moment, we don't know how to pack bitdepth < 8
+    return 8;
+#endif
+}
+
+/*  Pack palette[] with the corresponding colors, and if hasAlpha is true, also
+    pack trans[] and return the number of trans[] entries written. If hasAlpha
+    is false, the return value will always be 0.
+ 
+    Note: this routine takes care of unpremultiplying the RGB values when we
+    have alpha in the colortable, since png doesn't support premul colors
+*/
+static int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette,
+                        png_byte* SK_RESTRICT trans, bool hasAlpha) {
+    SkAutoLockColors alc(ctable);
+    const SkPMColor* SK_RESTRICT colors = alc.colors();
+    const int ctCount = ctable->count();
+    int i, num_trans = 0;
+
+    if (hasAlpha) {
+        /*  first see if we have some number of fully opaque at the end of the
+            ctable. PNG allows num_trans < num_palette, but all of the trans
+            entries must come first in the palette. If I was smarter, I'd
+            reorder the indices and ctable so that all non-opaque colors came
+            first in the palette. But, since that would slow down the encode,
+            I'm leaving the indices and ctable order as is, and just looking
+            at the tail of the ctable for opaqueness.
+        */
+        num_trans = ctCount;
+        for (i = ctCount - 1; i >= 0; --i) {
+            if (SkGetPackedA32(colors[i]) != 0xFF) {
+                break;
+            }
+            num_trans -= 1;
+        }
+        
+        const SkUnPreMultiply::Scale* SK_RESTRICT table =
+                                            SkUnPreMultiply::GetScaleTable();
+
+        for (i = 0; i < num_trans; i++) {
+            const SkPMColor c = *colors++;
+            const unsigned a = SkGetPackedA32(c);
+            const SkUnPreMultiply::Scale s = table[a];
+            trans[i] = a;
+            palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
+            palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
+            palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
+        }        
+        // now fall out of this if-block to use common code for the trailing
+        // opaque entries
+    }
+    
+    // these (remaining) entries are opaque
+    for (i = num_trans; i < ctCount; i++) {
+        SkPMColor c = *colors++;
+        palette[i].red = SkGetPackedR32(c);
+        palette[i].green = SkGetPackedG32(c);
+        palette[i].blue = SkGetPackedB32(c);
+    }
+    return num_trans;
+}
+
+class SkPNGImageEncoder : public SkImageEncoder {
+protected:
+    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
+};
+
+bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
+                                 int /*quality*/) {
+    SkBitmap::Config config = bitmap.getConfig();
+
+    const bool hasAlpha = !bitmap.isOpaque();
+    int colorType = PNG_COLOR_MASK_COLOR;
+    int bitDepth = 8;   // default for color
+    png_color_8 sig_bit;
+
+    switch (config) {
+        case SkBitmap::kIndex8_Config:
+            colorType |= PNG_COLOR_MASK_PALETTE;
+            // fall through to the ARGB_8888 case
+        case SkBitmap::kARGB_8888_Config:
+            sig_bit.red = 8;
+            sig_bit.green = 8;
+            sig_bit.blue = 8;
+            sig_bit.alpha = 8;
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            sig_bit.red = 4;
+            sig_bit.green = 4;
+            sig_bit.blue = 4;
+            sig_bit.alpha = 4;
+            break;
+        case SkBitmap::kRGB_565_Config:
+            sig_bit.red = 5;
+            sig_bit.green = 6;
+            sig_bit.blue = 5;
+            sig_bit.alpha = 0;
+            break;
+        default:
+            return false;
+    }
+    
+    if (hasAlpha) {
+        // don't specify alpha if we're a palette, even if our ctable has alpha
+        if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
+            colorType |= PNG_COLOR_MASK_ALPHA;
+        }
+    } else {
+        sig_bit.alpha = 0;
+    }
+    
+    SkAutoLockPixels alp(bitmap);
+    // readyToDraw checks for pixels (and colortable if that is required)
+    if (!bitmap.readyToDraw()) {
+        return false;
+    }
+
+    // we must do this after we have locked the pixels
+    SkColorTable* ctable = bitmap.getColorTable();
+    if (NULL != ctable) {
+        if (ctable->count() == 0) {
+            return false;
+        }
+        // check if we can store in fewer than 8 bits
+        bitDepth = computeBitDepth(ctable->count());
+    }
+
+    png_structp png_ptr;
+    png_infop info_ptr;
+
+    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
+                                      NULL);
+    if (NULL == png_ptr) {
+        return false;
+    }
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (NULL == info_ptr) {
+        png_destroy_write_struct(&png_ptr,  png_infopp_NULL);
+        return false;
+    }
+
+    /* Set error handling.  REQUIRED if you aren't supplying your own
+    * error handling functions in the png_create_write_struct() call.
+    */
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+        return false;
+    }
+
+    png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
+
+    /* Set the image information here.  Width and height are up to 2^31,
+    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
+    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
+    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
+    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
+    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
+    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
+    */
+
+    png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
+                 bitDepth, colorType,
+                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
+                 PNG_FILTER_TYPE_BASE);
+
+#if 0   // need to support this some day
+    /* set the palette if there is one.  REQUIRED for indexed-color images */
+    palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH
+             * png_sizeof (png_color));
+    /* ... set palette colors ... */
+    png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
+    /* You must not free palette here, because png_set_PLTE only makes a link to
+      the palette that you malloced.  Wait until you are about to destroy
+      the png structure. */
+#endif
+
+    png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+    png_write_info(png_ptr, info_ptr);
+
+    const char* srcImage = (const char*)bitmap.getPixels();
+    SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
+    char* storage = (char*)rowStorage.get();
+    transform_scanline_proc proc = choose_proc(config, hasAlpha);
+
+    for (int y = 0; y < bitmap.height(); y++) {
+        png_bytep row_ptr = (png_bytep)storage;
+        proc(srcImage, bitmap.width(), storage);
+        png_write_rows(png_ptr, &row_ptr, 1);
+        srcImage += bitmap.rowBytes();
+    }
+
+    png_write_end(png_ptr, info_ptr);
+
+    /* clean up after the write, and free any memory allocated */
+    png_destroy_write_struct(&png_ptr, &info_ptr);
+    return true;
+}
+
+SkImageEncoder* SkImageEncoder_PNG_Factory();
+SkImageEncoder* SkImageEncoder_PNG_Factory() {
+    return SkNEW(SkPNGImageEncoder);
+}
+
+#endif /* SK_SUPPORT_IMAGE_ENCODE */
diff --git a/src/images/SkImageDecoder_libpvjpeg.cpp b/src/images/SkImageDecoder_libpvjpeg.cpp
new file mode 100644
index 0000000..9177741
--- /dev/null
+++ b/src/images/SkImageDecoder_libpvjpeg.cpp
@@ -0,0 +1,206 @@
+#include "SkImageDecoder.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkMath.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+extern void ValidateHeap();
+
+class SkPVJPEGImageDecoder : public SkImageDecoder {
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+                          SkBitmap::Config pref, Mode);
+
+private:
+    enum {
+        STORAGE_SIZE = 8 * 1024
+    };
+    char    fStorage[STORAGE_SIZE];
+};
+
+SkImageDecoder* SkImageDecoder_PVJPEG_Factory(SkStream* stream)
+{
+    return SkNEW(SkPVJPEGImageDecoder);
+}
+
+#include "pvjpgdecoderinterface.h"
+#include "pvjpgdecoder_factory.h"
+
+class AutoPVDelete {
+public:
+    AutoPVDelete(PVJpgDecoderInterface* codec) : fCodec(codec) {}
+    ~AutoPVDelete() {
+        fCodec->Reset();
+        PVJpgDecoderFactory::DeletePVJpgDecoder(fCodec);
+    }
+private:    
+    PVJpgDecoderInterface* fCodec;
+};
+
+class MyObserver : public MPVJpegDecObserver {
+public:
+    MyObserver() : fCount(0) {}
+    ~MyObserver() {
+        if (fCount != 0) {
+            SkDebugf("--- pvjpeg left %d allocations\n", fCount);
+        }
+    }
+
+	virtual void allocateBuffer(uint8* &buffer, int32 buffersize) {
+        ++fCount;
+        // we double the allocation to work around bug when height is odd
+        buffer = (uint8*)sk_malloc_throw(buffersize << 1);
+        SkDebugf("---  pvjpeg alloc [%d] %d addr=%p\n", fCount, buffersize, buffer);
+    }
+    
+	virtual void deallocateBuffer(uint8 *buffer) {
+        SkDebugf("--- pvjpeg free [%d] addr=%p\n", fCount, buffer);
+        --fCount;
+        sk_free(buffer);
+    }
+
+private:
+    int fCount;
+};
+
+static void check_status(TPvJpgDecStatus status) {
+    if (TPVJPGDEC_SUCCESS != status) {
+        SkDEBUGF(("--- pvjpeg status %d\n", status));
+    }
+}
+
+static bool getFrame(PVJpgDecoderInterface* codec, SkBitmap* bitmap,
+                     SkBitmap::Config prefConfig, SkImageDecoder::Mode mode) {
+    TPvJpgDecInfo info;
+    TPvJpgDecStatus status = codec->GetInfo(&info);
+    if (status != TPVJPGDEC_SUCCESS)
+        return false;
+
+    int width = info.iWidth[0];
+    int height = info.iHeight[0];
+
+    bitmap->setConfig(SkBitmap::kRGB_565_Config, width, height);
+    bitmap->setIsOpaque(true);
+
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        return true;
+    }
+    
+    SkASSERT(info.iNumComponent == 3);
+
+    TPvJpgDecOutputFmt  format;
+    format.iColorFormat = TPV_COLORFMT_RGB16;
+    format.iCropped.topLeftX = 0;
+    format.iCropped.topLeftY = 0;
+    format.iCropped.bottomRightX = width - 1;
+    format.iCropped.bottomRightY = height - 1;
+    format.iOutputPitch = bitmap->rowBytes() >> 1;
+    status = codec->SetOutput(&format);
+    if (status != TPVJPGDEC_SUCCESS) {
+        SkDebugf("--- PV SetOutput failed %d\n", status);
+        return false;
+    }
+
+    TPvJpgDecFrame frame;
+    uint8*         ptrs[3];
+    int32          widths[3], heights[3];
+    bzero(ptrs, sizeof(ptrs));
+    frame.ptr = ptrs;
+    frame.iWidth = widths;
+    frame.iHeight = heights;
+    
+    status = codec->GetFrame(&frame);
+    if (status != TPVJPGDEC_SUCCESS) {
+        SkDebugf("--- PV GetFrame failed %d\n", status);
+        return false;
+    }
+
+    bitmap->allocPixels();
+    memcpy(bitmap->getPixels(), ptrs[0], bitmap->getSize());
+    return true;
+}
+
+class OsclCleanupper {
+public:
+    OsclCleanupper() {
+        OsclBase::Init();
+        OsclErrorTrap::Init();
+        OsclMem::Init();
+    }
+    ~OsclCleanupper() {
+        OsclMem::Cleanup();
+        OsclErrorTrap::Cleanup();
+        OsclBase::Cleanup();
+    }
+};
+
+bool SkPVJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
+                                    SkBitmap::Config prefConfig, Mode mode)
+{
+    // do I need this guy?
+    OsclCleanupper oc;
+    
+    PVJpgDecoderInterface*  codec = PVJpgDecoderFactory::CreatePVJpgDecoder();
+    TPvJpgDecStatus         status = codec->Init();
+    check_status(status);
+
+    MyObserver      observer;   // must create before autopvdelete
+    AutoPVDelete    ad(codec);
+    
+    status = codec->SetObserver(&observer);
+    check_status(status);
+    
+    char*   storage = fStorage;
+    int32   bytesInStorage = 0;
+    for (;;)
+    {
+        int32 bytesRead = stream->read(storage + bytesInStorage,
+                                       STORAGE_SIZE - bytesInStorage);
+        if (bytesRead <= 0) {
+            SkDEBUGF(("SkPVJPEGImageDecoder: stream read returned %d\n", bytesRead));
+            return false;
+        }
+        
+        // update bytesInStorage to account for the read()
+        bytesInStorage += bytesRead;
+        SkASSERT(bytesInStorage <= STORAGE_SIZE);
+        
+        // now call Decode to eat some of the bytes
+        int32 consumed = bytesInStorage;
+        status = codec->Decode((uint8*)storage, &consumed);
+
+        SkASSERT(bytesInStorage >= consumed);
+        bytesInStorage -= consumed;
+        // now bytesInStorage is the remaining unread bytes
+        if (bytesInStorage > 0) { // slide the leftovers to the beginning
+            SkASSERT(storage == fStorage);
+            SkASSERT(consumed >= 0 && bytesInStorage >= 0);
+            SkASSERT((size_t)(consumed + bytesInStorage) <= sizeof(fStorage));
+            SkASSERT(sizeof(fStorage) == STORAGE_SIZE);
+       //     SkDebugf("-- memmov srcOffset=%d, numBytes=%d\n", consumed, bytesInStorage);
+            memmove(storage, storage + consumed, bytesInStorage);
+        }
+        
+        switch (status) {
+        case TPVJPGDEC_SUCCESS:
+            SkDEBUGF(("SkPVJPEGImageDecoder::Decode returned success?\n");)
+            return false;
+        case TPVJPGDEC_FRAME_READY:
+        case TPVJPGDEC_DONE:
+            return getFrame(codec, decodedBitmap, prefConfig, mode);
+        case TPVJPGDEC_FAIL:
+        case TPVJPGDEC_INVALID_MEMORY:
+        case TPVJPGDEC_INVALID_PARAMS:
+        case TPVJPGDEC_NO_IMAGE_DATA:
+            SkDEBUGF(("SkPVJPEGImageDecoder: failed to decode err=%d\n", status);)
+            return false;
+        case TPVJPGDEC_WAITING_FOR_INPUT:
+            break;  // loop around and eat more from the stream
+        }
+    }
+    return false;
+}
+
diff --git a/src/images/SkImageDecoder_wbmp.cpp b/src/images/SkImageDecoder_wbmp.cpp
new file mode 100644
index 0000000..9d188f6
--- /dev/null
+++ b/src/images/SkImageDecoder_wbmp.cpp
@@ -0,0 +1,167 @@
+/**
+** 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 "SkImageDecoder.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkMath.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+class SkWBMPImageDecoder : public SkImageDecoder {
+public:
+    virtual Format getFormat() const {
+        return kWBMP_Format;
+    }
+    
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+                          SkBitmap::Config pref, Mode);
+};
+
+static bool read_byte(SkStream* stream, uint8_t* data)
+{
+    return stream->read(data, 1) == 1;
+}
+
+static bool read_mbf(SkStream* stream, int* value)
+{
+    int n = 0;
+    uint8_t data;
+    do {
+        if (!read_byte(stream, &data)) {
+            return false;
+        }
+        n = (n << 7) | (data & 0x7F);
+    } while (data & 0x80);
+    
+    *value = n;
+    return true;
+}
+
+struct wbmp_head {
+    int fWidth;
+    int fHeight;
+    
+    bool init(SkStream* stream)
+    {
+        uint8_t data;
+        
+        if (!read_byte(stream, &data) || data != 0) { // unknown type
+            return false;
+        }
+        if (!read_byte(stream, &data) || (data & 0x9F)) { // skip fixed header
+            return false;
+        }
+        if (!read_mbf(stream, &fWidth) || (unsigned)fWidth > 0xFFFF) {
+            return false;
+        }
+        if (!read_mbf(stream, &fHeight) || (unsigned)fHeight > 0xFFFF) {
+            return false;
+        }
+        return fWidth != 0 && fHeight != 0;
+    }
+};
+    
+SkImageDecoder* SkImageDecoder_WBMP_Factory(SkStream* stream)
+{
+    wbmp_head   head;
+
+    if (head.init(stream)) {
+        return SkNEW(SkWBMPImageDecoder);
+    }
+    return NULL;
+}
+
+static void expand_bits_to_bytes(uint8_t dst[], const uint8_t src[], int bits)
+{
+    int bytes = bits >> 3;
+    
+    for (int i = 0; i < bytes; i++) {
+        unsigned mask = *src++;
+        dst[0] = (mask >> 7) & 1;
+        dst[1] = (mask >> 6) & 1;
+        dst[2] = (mask >> 5) & 1;
+        dst[3] = (mask >> 4) & 1;
+        dst[4] = (mask >> 3) & 1;
+        dst[5] = (mask >> 2) & 1;
+        dst[6] = (mask >> 1) & 1;
+        dst[7] = (mask >> 0) & 1;
+        dst += 8;
+    }
+    
+    bits &= 7;
+    if (bits > 0) {
+        unsigned mask = *src;
+        do {
+            *dst++ = (mask >> 7) & 1;;
+            mask <<= 1;
+        } while (--bits != 0);    
+    }
+}
+
+#define SkAlign8(x)     (((x) + 7) & ~7)
+
+bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
+                                  SkBitmap::Config prefConfig, Mode mode)
+{
+    wbmp_head   head;
+    
+    if (!head.init(stream)) {
+        return false;
+    }
+        
+    int width = head.fWidth;
+    int height = head.fHeight;
+    
+    // assign these directly, in case we return kDimensions_Result
+    decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
+    decodedBitmap->setIsOpaque(true);
+    
+    if (SkImageDecoder::kDecodeBounds_Mode == mode)
+        return true;
+    
+    const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
+    SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2));
+    SkAutoUnref   aur(ct);
+
+    if (!this->allocPixelRef(decodedBitmap, ct)) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(*decodedBitmap);
+
+    uint8_t* dst = decodedBitmap->getAddr8(0, 0);
+    // store the 1-bit valuess at the end of our pixels, so we won't stomp
+    // on them before we're read them. Just trying to avoid a temp allocation
+    size_t srcRB = SkAlign8(width) >> 3;
+    size_t srcSize = height * srcRB;
+    uint8_t* src = dst + decodedBitmap->getSize() - srcSize;
+    if (stream->read(src, srcSize) != srcSize) {
+        return false;
+    }
+
+    for (int y = 0; y < height; y++)
+    {
+        expand_bits_to_bytes(dst, src, width);
+        dst += decodedBitmap->rowBytes();
+        src += srcRB;
+    }
+
+    return true;
+}
+
diff --git a/src/images/SkImageRef.cpp b/src/images/SkImageRef.cpp
new file mode 100644
index 0000000..90c37b6
--- /dev/null
+++ b/src/images/SkImageRef.cpp
@@ -0,0 +1,165 @@
+#include "SkImageRef.h"
+#include "SkBitmap.h"
+#include "SkFlattenable.h"
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+
+// can't be static, as SkImageRef_Pool needs to see it
+SkMutex gImageRefMutex;
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageRef::SkImageRef(SkStream* stream, SkBitmap::Config config,
+                       int sampleSize)
+        : SkPixelRef(&gImageRefMutex), fErrorInDecoding(false) {
+    SkASSERT(stream);
+    SkASSERT(1 == stream->getRefCnt());
+
+    fStream = stream;
+    fConfig = config;
+    fSampleSize = sampleSize;
+    fPrev = fNext = NULL;
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+    SkDebugf("add ImageRef %p [%d] data=%d\n",
+              this, config, (int)stream->getLength());
+#endif
+}
+
+SkImageRef::~SkImageRef() {
+    SkASSERT(&gImageRefMutex == this->mutex());
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+    SkDebugf("delete ImageRef %p [%d] data=%d\n",
+              this, fConfig, (int)fStream->getLength());
+#endif
+
+    delete fStream;
+}
+
+bool SkImageRef::getInfo(SkBitmap* bitmap) {
+    SkAutoMutexAcquire ac(gImageRefMutex);
+    
+    if (!this->prepareBitmap(SkImageDecoder::kDecodeBounds_Mode)) {
+        return false;
+    }
+    
+    SkASSERT(SkBitmap::kNo_Config != fBitmap.config());
+    if (bitmap) {
+        bitmap->setConfig(fBitmap.config(), fBitmap.width(), fBitmap.height());
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkImageRef::onDecode(SkImageDecoder* codec, SkStream* stream,
+                          SkBitmap* bitmap, SkBitmap::Config config,
+                          SkImageDecoder::Mode mode) {
+    return codec->decode(stream, bitmap, config, mode);
+}
+
+bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) {
+    SkASSERT(&gImageRefMutex == this->mutex());
+
+    if (fErrorInDecoding) {
+        return false;
+    }
+    
+    /*  As soon as we really know our config, we record it, so that on
+        subsequent calls to the codec, we are sure we will always get the same
+        result.
+    */
+    if (SkBitmap::kNo_Config != fBitmap.config()) {
+        fConfig = fBitmap.config();
+    }
+    
+    if (NULL != fBitmap.getPixels() ||
+            (SkBitmap::kNo_Config != fBitmap.config() &&
+             SkImageDecoder::kDecodeBounds_Mode == mode)) {
+        return true;
+    }
+
+    SkASSERT(fBitmap.getPixels() == NULL);
+
+    fStream->rewind();
+        
+    SkImageDecoder* codec = SkImageDecoder::Factory(fStream);
+    if (codec) {
+        SkAutoTDelete<SkImageDecoder> ad(codec);
+
+        codec->setSampleSize(fSampleSize);
+        if (this->onDecode(codec, fStream, &fBitmap, fConfig, mode)) {
+            return true;
+        }
+    }
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+    if (NULL == codec) {
+        SkDebugf("--- ImageRef: <%s> failed to find codec\n", this->getURI());
+    } else {
+        SkDebugf("--- ImageRef: <%s> failed in codec for %d mode\n",
+                 this->getURI(), mode);
+    }
+#endif
+    fErrorInDecoding = true;
+    fBitmap.reset();
+    return false;
+}
+
+void* SkImageRef::onLockPixels(SkColorTable** ct) {
+    SkASSERT(&gImageRefMutex == this->mutex());
+
+    if (NULL == fBitmap.getPixels()) {
+        (void)this->prepareBitmap(SkImageDecoder::kDecodePixels_Mode);
+    }
+
+    if (ct) {
+        *ct = fBitmap.getColorTable();
+    }
+    return fBitmap.getPixels();
+}
+
+void SkImageRef::onUnlockPixels() {
+    // we're already have the mutex locked
+    SkASSERT(&gImageRefMutex == this->mutex());
+}
+
+size_t SkImageRef::ramUsed() const {
+    size_t size = 0;
+
+    if (fBitmap.getPixels()) {
+        size = fBitmap.getSize();
+        if (fBitmap.getColorTable()) {
+            size += fBitmap.getColorTable()->count() * sizeof(SkPMColor);
+        }
+    }
+    return size;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageRef::SkImageRef(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer, &gImageRefMutex), fErrorInDecoding(false) {
+    fConfig = (SkBitmap::Config)buffer.readU8();
+    fSampleSize = buffer.readU8();
+    size_t length = buffer.readU32();
+    fStream = SkNEW_ARGS(SkMemoryStream, (length));
+    buffer.read((void*)fStream->getMemoryBase(), length);
+
+    fPrev = fNext = NULL;
+}
+
+void SkImageRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+
+    buffer.write8(fConfig);
+    buffer.write8(fSampleSize);
+    size_t length = fStream->getLength();
+    buffer.write32(length);
+    fStream->rewind();
+    buffer.readFromStream(fStream, length);
+}
+
diff --git a/src/images/SkImageRefPool.cpp b/src/images/SkImageRefPool.cpp
new file mode 100644
index 0000000..e322507
--- /dev/null
+++ b/src/images/SkImageRefPool.cpp
@@ -0,0 +1,186 @@
+#include "SkImageRefPool.h"
+#include "SkImageRef.h"
+#include "SkThread.h"
+
+SkImageRefPool::SkImageRefPool() {
+    fRAMBudget = 0; // means no explicit limit
+    fRAMUsed = 0;
+    fCount = 0;
+    fHead = fTail = NULL;
+}
+
+SkImageRefPool::~SkImageRefPool() {
+    //    SkASSERT(NULL == fHead);
+}
+
+void SkImageRefPool::setRAMBudget(size_t size) {
+    if (fRAMBudget != size) {
+        fRAMBudget = size;
+        this->purgeIfNeeded();
+    }
+}
+
+void SkImageRefPool::justAddedPixels(SkImageRef* ref) {
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+    SkDebugf("=== ImagePool: add pixels %s [%d %d %d] bytes=%d heap=%d\n",
+             ref->getURI(),
+             ref->fBitmap.width(), ref->fBitmap.height(),
+             ref->fBitmap.bytesPerPixel(),
+             ref->fBitmap.getSize(), (int)fRAMUsed);
+#endif
+    fRAMUsed += ref->ramUsed();
+    this->purgeIfNeeded();
+}
+
+void SkImageRefPool::canLosePixels(SkImageRef* ref) {
+    // the refs near fHead have recently been released (used)
+    // if we purge, we purge from the tail
+    this->detach(ref);
+    this->addToHead(ref);
+    this->purgeIfNeeded();
+}
+
+void SkImageRefPool::purgeIfNeeded() {
+    // do nothing if we have a zero-budget (i.e. unlimited)
+    if (fRAMBudget != 0) {
+        this->setRAMUsed(fRAMBudget);
+    }
+}
+
+void SkImageRefPool::setRAMUsed(size_t limit) {
+    SkImageRef* ref = fTail;
+    
+    while (NULL != ref && fRAMUsed > limit) {
+        // only purge it if its pixels are unlocked
+        if (0 == ref->getLockCount() && ref->fBitmap.getPixels()) {
+            size_t size = ref->ramUsed();
+            SkASSERT(size <= fRAMUsed);
+            fRAMUsed -= size;
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+            SkDebugf("=== ImagePool: purge %s [%d %d %d] bytes=%d heap=%d\n",
+                     ref->getURI(),
+                     ref->fBitmap.width(), ref->fBitmap.height(),
+                     ref->fBitmap.bytesPerPixel(),
+                     (int)size, (int)fRAMUsed);
+#endif
+            
+            // remember the bitmap config (don't call reset),
+            // just clear the pixel memory
+            ref->fBitmap.setPixels(NULL);
+            SkASSERT(NULL == ref->fBitmap.getPixels());
+        }
+        ref = ref->fPrev;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkImageRefPool::addToHead(SkImageRef* ref) {
+    ref->fNext = fHead;
+    ref->fPrev = NULL;
+    
+    if (fHead) {
+        SkASSERT(NULL == fHead->fPrev);
+        fHead->fPrev = ref;
+    }
+    fHead = ref;
+    
+    if (NULL == fTail) {
+        fTail = ref;
+    }
+    fCount += 1;
+    SkASSERT(computeCount() == fCount);
+    
+    fRAMUsed += ref->ramUsed();
+}
+
+void SkImageRefPool::addToTail(SkImageRef* ref) {
+    ref->fNext = NULL;
+    ref->fPrev = fTail;
+    
+    if (fTail) {
+        SkASSERT(NULL == fTail->fNext);
+        fTail->fNext = ref;
+    }
+    fTail = ref;
+    
+    if (NULL == fHead) {
+        fHead = ref;
+    }
+    fCount += 1;
+    SkASSERT(computeCount() == fCount);
+    
+    fRAMUsed += ref->ramUsed();
+}
+
+void SkImageRefPool::detach(SkImageRef* ref) {
+    SkASSERT(fCount > 0);
+    
+    if (fHead == ref) {
+        fHead = ref->fNext;
+    }
+    if (fTail == ref) {
+        fTail = ref->fPrev;
+    }
+    if (ref->fPrev) {
+        ref->fPrev->fNext = ref->fNext;
+    }
+    if (ref->fNext) {
+        ref->fNext->fPrev = ref->fPrev;
+    }
+    
+    ref->fNext = ref->fPrev = NULL;
+    
+    fCount -= 1;
+    SkASSERT(computeCount() == fCount);
+    
+    SkASSERT(fRAMUsed >= ref->ramUsed());
+    fRAMUsed -= ref->ramUsed();
+}
+
+int SkImageRefPool::computeCount() const {
+    SkImageRef* ref = fHead;
+    int count = 0;
+    
+    while (ref != NULL) {
+        count += 1;
+        ref = ref->fNext;
+    }
+    
+#ifdef SK_DEBUG
+    ref = fTail;
+    int count2 = 0;
+    
+    while (ref != NULL) {
+        count2 += 1;
+        ref = ref->fPrev;
+    }
+    SkASSERT(count2 == count);
+#endif
+    
+    return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+void SkImageRefPool::dump() const {
+#if defined(SK_DEBUG) || defined(DUMP_IMAGEREF_LIFECYCLE)
+    SkDebugf("ImagePool dump: bugdet: %d used: %d count: %d\n",
+             (int)fRAMBudget, (int)fRAMUsed, fCount);
+    
+    SkImageRef* ref = fHead;
+    
+    while (ref != NULL) {
+        SkDebugf("  [%3d %3d %d] ram=%d data=%d locks=%d %s\n", ref->fBitmap.width(),
+                 ref->fBitmap.height(), ref->fBitmap.config(),
+                 ref->ramUsed(), (int)ref->fStream->getLength(),
+                 ref->getLockCount(), ref->getURI());
+        
+        ref = ref->fNext;
+    }
+#endif
+}
+
diff --git a/src/images/SkImageRefPool.h b/src/images/SkImageRefPool.h
new file mode 100644
index 0000000..b2eb7b3
--- /dev/null
+++ b/src/images/SkImageRefPool.h
@@ -0,0 +1,43 @@
+#ifndef SkImageRefPool_DEFINED
+#define SkImageRefPool_DEFINED
+
+#include "SkTypes.h"
+
+class SkImageRef;
+class SkImageRef_GlobalPool;
+
+class SkImageRefPool {
+public:
+    SkImageRefPool();
+    ~SkImageRefPool();
+    
+    size_t  getRAMBudget() const { return fRAMBudget; }
+    void    setRAMBudget(size_t);
+    
+    size_t  getRAMUsed() const { return fRAMUsed; }
+    void    setRAMUsed(size_t limit);
+    
+    void addToHead(SkImageRef*);
+    void addToTail(SkImageRef*);
+    void detach(SkImageRef*);
+
+    void dump() const;
+    
+private:
+    size_t fRAMBudget;
+    size_t fRAMUsed;
+    
+    int         fCount;
+    SkImageRef* fHead, *fTail;
+    
+    int computeCount() const;
+    
+    friend class SkImageRef_GlobalPool;
+    
+    void justAddedPixels(SkImageRef*);
+    void canLosePixels(SkImageRef*);
+    void purgeIfNeeded();
+};
+
+#endif
+
diff --git a/src/images/SkImageRef_GlobalPool.cpp b/src/images/SkImageRef_GlobalPool.cpp
new file mode 100644
index 0000000..1f0bc43
--- /dev/null
+++ b/src/images/SkImageRef_GlobalPool.cpp
@@ -0,0 +1,83 @@
+#include "SkImageRef_GlobalPool.h"
+#include "SkImageRefPool.h"
+#include "SkThread.h"
+
+extern SkMutex gImageRefMutex;
+
+static SkImageRefPool gGlobalImageRefPool;
+
+SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkStream* stream,
+                                             SkBitmap::Config config,
+                                             int sampleSize)
+        : SkImageRef(stream, config, sampleSize) {
+    this->mutex()->acquire();
+    gGlobalImageRefPool.addToHead(this);
+    this->mutex()->release();
+}
+
+SkImageRef_GlobalPool::~SkImageRef_GlobalPool() {
+    this->mutex()->acquire();
+    gGlobalImageRefPool.detach(this);
+    this->mutex()->release();
+}
+    
+bool SkImageRef_GlobalPool::onDecode(SkImageDecoder* codec, SkStream* stream,
+                                     SkBitmap* bitmap, SkBitmap::Config config,
+                                     SkImageDecoder::Mode mode) {
+    if (!this->INHERITED::onDecode(codec, stream, bitmap, config, mode)) {
+        return false;
+    }
+    if (mode == SkImageDecoder::kDecodePixels_Mode) {
+        gGlobalImageRefPool.justAddedPixels(this);
+    }
+    return true;
+}
+    
+void SkImageRef_GlobalPool::onUnlockPixels() {
+    this->INHERITED::onUnlockPixels();
+    
+    gGlobalImageRefPool.canLosePixels(this);
+}
+
+SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {
+    this->mutex()->acquire();
+    gGlobalImageRefPool.addToHead(this);
+    this->mutex()->release();
+}
+
+SkPixelRef* SkImageRef_GlobalPool::Create(SkFlattenableReadBuffer& buffer) {
+    return SkNEW_ARGS(SkImageRef_GlobalPool, (buffer));
+}
+
+static SkPixelRef::Registrar::Registrar reg("SkImageRef_GlobalPool",
+                                            SkImageRef_GlobalPool::Create);
+
+///////////////////////////////////////////////////////////////////////////////
+// global imagerefpool wrappers
+
+size_t SkImageRef_GlobalPool::GetRAMBudget() {
+    SkAutoMutexAcquire ac(gImageRefMutex);
+    return gGlobalImageRefPool.getRAMBudget();
+}
+
+void SkImageRef_GlobalPool::SetRAMBudget(size_t size) {
+    SkAutoMutexAcquire ac(gImageRefMutex);
+    gGlobalImageRefPool.setRAMBudget(size);
+}
+
+size_t SkImageRef_GlobalPool::GetRAMUsed() {
+    SkAutoMutexAcquire ac(gImageRefMutex);    
+    return gGlobalImageRefPool.getRAMUsed();
+}
+
+void SkImageRef_GlobalPool::SetRAMUsed(size_t usage) {
+    SkAutoMutexAcquire ac(gImageRefMutex);
+    gGlobalImageRefPool.setRAMUsed(usage);
+}
+
+void SkImageRef_GlobalPool::DumpPool() {
+    SkAutoMutexAcquire ac(gImageRefMutex);
+    gGlobalImageRefPool.dump();
+}
+
diff --git a/src/images/SkMMapStream.cpp b/src/images/SkMMapStream.cpp
new file mode 100644
index 0000000..2aee945
--- /dev/null
+++ b/src/images/SkMMapStream.cpp
@@ -0,0 +1,63 @@
+#include "SkMMapStream.h"
+
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+SkMMAPStream::SkMMAPStream(const char filename[])
+{
+    fFildes = -1;   // initialize to failure case
+
+    int fildes = open(filename, O_RDONLY);
+    if (fildes < 0)
+    {
+        SkDEBUGF(("---- failed to open(%s) for mmap stream error=%d\n", filename, errno));
+        return;
+    }
+
+    off_t size = lseek(fildes, 0, SEEK_END);    // find the file size
+    if (size == -1)
+    {
+        SkDEBUGF(("---- failed to lseek(%s) for mmap stream error=%d\n", filename, errno));
+        close(fildes);
+        return;
+    }
+    (void)lseek(fildes, 0, SEEK_SET);   // restore file offset to beginning
+
+    void* addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fildes, 0);
+    if (MAP_FAILED == addr)
+    {
+        SkDEBUGF(("---- failed to mmap(%s) for mmap stream error=%d\n", filename, errno));
+        close(fildes);
+        return;
+    }
+
+    this->INHERITED::setMemory(addr, size);
+
+    fFildes = fildes;
+    fAddr = addr;
+    fSize = size;
+}
+
+SkMMAPStream::~SkMMAPStream()
+{
+    this->closeMMap();
+}
+
+void SkMMAPStream::setMemory(const void* data, size_t length)
+{
+    this->closeMMap();
+    this->INHERITED::setMemory(data, length);
+}
+
+void SkMMAPStream::closeMMap()
+{
+    if (fFildes >= 0)
+    {
+        munmap(fAddr, fSize);
+        close(fFildes);
+        fFildes = -1;
+    }
+}
+
diff --git a/src/images/SkMovie.cpp b/src/images/SkMovie.cpp
new file mode 100644
index 0000000..7186ed5
--- /dev/null
+++ b/src/images/SkMovie.cpp
@@ -0,0 +1,101 @@
+#include "SkMovie.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+// We should never see this in normal operation since our time values are
+// 0-based. So we use it as a sentinal.
+#define UNINITIALIZED_MSEC ((SkMSec)-1)
+
+SkMovie::SkMovie()
+{
+    fInfo.fDuration = UNINITIALIZED_MSEC;  // uninitialized
+    fCurrTime = UNINITIALIZED_MSEC; // uninitialized
+    fNeedBitmap = true;
+}
+
+void SkMovie::ensureInfo()
+{
+    if (fInfo.fDuration == UNINITIALIZED_MSEC && !this->onGetInfo(&fInfo))
+        memset(&fInfo, 0, sizeof(fInfo));   // failure
+}
+
+SkMSec SkMovie::duration()
+{
+    this->ensureInfo();
+    return fInfo.fDuration;
+}
+
+int SkMovie::width()
+{
+    this->ensureInfo();
+    return fInfo.fWidth;
+}
+
+int SkMovie::height()
+{
+    this->ensureInfo();
+    return fInfo.fHeight;
+}
+
+int SkMovie::isOpaque()
+{
+    this->ensureInfo();
+    return fInfo.fIsOpaque;
+}
+
+bool SkMovie::setTime(SkMSec time)
+{
+    SkMSec dur = this->duration();
+    if (time > dur)
+        time = dur;
+        
+    bool changed = false;
+    if (time != fCurrTime)
+    {
+        fCurrTime = time;
+        changed = this->onSetTime(time);
+        fNeedBitmap |= changed;
+    }
+    return changed;
+}
+
+const SkBitmap& SkMovie::bitmap()
+{
+    if (fCurrTime == UNINITIALIZED_MSEC)    // uninitialized
+        this->setTime(0);
+
+    if (fNeedBitmap)
+    {
+        if (!this->onGetBitmap(&fBitmap))   // failure
+            fBitmap.reset();
+        fNeedBitmap = false;
+    }
+    return fBitmap;
+}
+
+////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+SkMovie* SkMovie::DecodeMemory(const void* data, size_t length) {
+    SkMemoryStream stream(data, length, false);
+    return SkMovie::DecodeStream(&stream);
+}
+
+SkMovie* SkMovie::DecodeFile(const char path[])
+{
+    SkMovie* movie = NULL;
+
+    SkFILEStream stream(path);
+    if (stream.isValid()) {
+        movie = SkMovie::DecodeStream(&stream);
+    }
+#ifdef SK_DEBUG
+    else {
+        SkDebugf("Movie file not found <%s>\n", path);
+    }
+#endif
+
+    return movie;
+}
+
diff --git a/src/images/SkMovie_gif.cpp b/src/images/SkMovie_gif.cpp
new file mode 100644
index 0000000..ca9c812
--- /dev/null
+++ b/src/images/SkMovie_gif.cpp
@@ -0,0 +1,224 @@
+/* libs/graphics/images/SkImageDecoder_libgif.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 "SkMovie.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+#include "gif_lib.h"
+
+class SkGIFMovie : public SkMovie {
+public:
+    SkGIFMovie(SkStream* stream);
+    virtual ~SkGIFMovie();
+
+protected:
+    virtual bool onGetInfo(Info*);
+    virtual bool onSetTime(SkMSec);
+    virtual bool onGetBitmap(SkBitmap*);
+    
+private:
+    GifFileType* fGIF;
+    SavedImage* fCurrSavedImage;
+};
+
+SkMovie* SkMovie_GIF_Factory(SkStream* stream) {
+    char buf[GIF_STAMP_LEN];
+    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
+        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
+            stream->rewind();
+            return SkNEW_ARGS(SkGIFMovie, (stream));
+        }
+    }
+    return NULL;
+}
+
+static int Decode(GifFileType* fileType, GifByteType* out, int size) {
+    SkStream* stream = (SkStream*) fileType->UserData;
+    return (int) stream->read(out, size);
+}
+
+SkGIFMovie::SkGIFMovie(SkStream* stream)
+{
+    fGIF = DGifOpen( stream, Decode );
+    if (NULL == fGIF)
+        return;
+
+    if (DGifSlurp(fGIF) != GIF_OK)
+    {
+        DGifCloseFile(fGIF);
+        fGIF = NULL;
+    }
+    fCurrSavedImage = NULL;
+}
+
+SkGIFMovie::~SkGIFMovie()
+{
+    if (fGIF)
+        DGifCloseFile(fGIF);
+}
+
+static SkMSec savedimage_duration(const SavedImage* image)
+{
+    for (int j = 0; j < image->ExtensionBlockCount; j++)
+    {
+        if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
+        {
+            int size = image->ExtensionBlocks[j].ByteCount;
+            SkASSERT(size >= 4);
+            const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
+            return ((b[2] << 8) | b[1]) * 10;
+        }
+    }
+    return 0;
+}
+
+bool SkGIFMovie::onGetInfo(Info* info)
+{
+    if (NULL == fGIF)
+        return false;
+
+    SkMSec dur = 0;
+    for (int i = 0; i < fGIF->ImageCount; i++)
+        dur += savedimage_duration(&fGIF->SavedImages[i]);
+
+    info->fDuration = dur;
+    info->fWidth = fGIF->SWidth;
+    info->fHeight = fGIF->SHeight;
+    info->fIsOpaque = false;    // how to compute?
+    return true;
+}
+
+bool SkGIFMovie::onSetTime(SkMSec time)
+{
+    if (NULL == fGIF)
+        return false;
+
+    SkMSec dur = 0;
+    for (int i = 0; i < fGIF->ImageCount; i++)
+    {
+        dur += savedimage_duration(&fGIF->SavedImages[i]);
+        if (dur >= time)
+        {
+            SavedImage* prev = fCurrSavedImage;
+            fCurrSavedImage = &fGIF->SavedImages[i];
+            return prev != fCurrSavedImage;
+        }
+    }
+    fCurrSavedImage = &fGIF->SavedImages[fGIF->ImageCount - 1];
+    return true;
+}
+
+bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
+{
+    GifFileType* gif = fGIF;
+    if (NULL == gif)
+        return false;
+
+    // should we check for the Image cmap or the global (SColorMap) first? 
+    ColorMapObject* cmap = gif->SColorMap;
+    if (cmap == NULL)
+        cmap = gif->Image.ColorMap;
+
+    if (cmap == NULL || gif->ImageCount < 1 || cmap->ColorCount != (1 << cmap->BitsPerPixel))
+    {
+        SkASSERT(!"bad colortable setup");
+        return false;
+    }
+
+    const int width = gif->SWidth;
+    const int height = gif->SHeight;
+    if (width <= 0 || height <= 0) {
+        return false;
+    }
+
+    SavedImage*      gif_image = fCurrSavedImage;
+    SkBitmap::Config config = SkBitmap::kIndex8_Config;
+
+    SkColorTable* colorTable = SkNEW_ARGS(SkColorTable, (cmap->ColorCount));
+    SkAutoUnref aur(colorTable);
+
+    bm->setConfig(config, width, height, 0);
+    if (!bm->allocPixels(colorTable)) {
+        return false;
+    }
+
+    int transparent = -1;
+    for (int i = 0; i < gif_image->ExtensionBlockCount; ++i) {
+      ExtensionBlock* eb = gif_image->ExtensionBlocks + i;
+      if (eb->Function == 0xF9 && 
+          eb->ByteCount == 4) {
+        bool has_transparency = ((eb->Bytes[0] & 1) == 1);
+        if (has_transparency) {
+          transparent = (unsigned char)eb->Bytes[3];
+        }
+      }
+    }
+
+    SkPMColor* colorPtr = colorTable->lockColors();
+
+    if (transparent >= 0)
+        memset(colorPtr, 0, cmap->ColorCount * 4);
+    else
+        colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+
+    for (int index = 0; index < cmap->ColorCount; index++)
+    {
+        if (transparent != index)
+            colorPtr[index] = SkPackARGB32(0xFF, cmap->Colors[index].Red, 
+                cmap->Colors[index].Green, cmap->Colors[index].Blue);
+    }
+    colorTable->unlockColors(true);
+
+    unsigned char* in = (unsigned char*)gif_image->RasterBits;
+    unsigned char* out = bm->getAddr8(0, 0);
+    if (gif->Image.Interlace) {
+
+      // deinterlace
+        int row;
+      // group 1 - every 8th row, starting with row 0
+      for (row = 0; row < height; row += 8) {
+        memcpy(out + width * row, in, width);
+        in += width;
+      }
+
+      // group 2 - every 8th row, starting with row 4
+      for (row = 4; row < height; row += 8) {
+        memcpy(out + width * row, in, width);
+        in += width;
+      }
+
+      // group 3 - every 4th row, starting with row 2
+      for (row = 2; row < height; row += 4) {
+        memcpy(out + width * row, in, width);
+        in += width;
+      }
+
+      for (row = 1; row < height; row += 2) {
+        memcpy(out + width * row, in, width);
+        in += width;
+      }
+
+    } else {
+      memcpy(out, in, width * height);
+    }
+    return true;
+}
diff --git a/src/images/SkPageFlipper.cpp b/src/images/SkPageFlipper.cpp
new file mode 100644
index 0000000..526ba09
--- /dev/null
+++ b/src/images/SkPageFlipper.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPageFlipper.h"
+
+SkPageFlipper::SkPageFlipper() {
+    fWidth = 0;
+    fHeight = 0;
+    fDirty0 = &fDirty0Storage;
+    fDirty1 = &fDirty1Storage;
+
+    fDirty0->setEmpty();
+    fDirty1->setEmpty();
+}
+
+SkPageFlipper::SkPageFlipper(int width, int height) {
+    fWidth = width;
+    fHeight = height;
+    fDirty0 = &fDirty0Storage;
+    fDirty1 = &fDirty1Storage;
+
+    fDirty0->setRect(0, 0, width, height);
+    fDirty1->setEmpty();
+}
+
+void SkPageFlipper::resize(int width, int height) {
+    fWidth = width;
+    fHeight = height;
+    
+    // this is the opposite of the constructors
+    fDirty1->setRect(0, 0, width, height);
+    fDirty0->setEmpty();
+}
+
+void SkPageFlipper::inval() {
+    fDirty1->setRect(0, 0, fWidth, fHeight);
+}
+
+void SkPageFlipper::inval(const SkIRect& rect) {
+    SkIRect r;
+    r.set(0, 0, fWidth, fHeight);
+    if (r.intersect(rect)) {
+        fDirty1->op(r, SkRegion::kUnion_Op);
+    }
+}
+
+void SkPageFlipper::inval(const SkRegion& rgn) {
+    SkRegion r;
+    r.setRect(0, 0, fWidth, fHeight);
+    if (r.op(rgn, SkRegion::kIntersect_Op)) {
+        fDirty1->op(r, SkRegion::kUnion_Op);
+    }
+}
+
+void SkPageFlipper::inval(const SkRect& rect, bool antialias) {
+    SkIRect r;
+    rect.round(&r);
+    if (antialias) {
+        r.inset(-1, -1);
+    }
+    this->inval(r);
+}
+
+const SkRegion& SkPageFlipper::update(SkRegion* copyBits) {
+    // Copy over anything new from page0 that isn't dirty in page1
+    copyBits->op(*fDirty0, *fDirty1, SkRegion::kDifference_Op);
+    SkTSwap<SkRegion*>(fDirty0, fDirty1);
+    fDirty1->setEmpty();
+    return *fDirty0;
+}
+
+
diff --git a/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp
new file mode 100644
index 0000000..15f4432
--- /dev/null
+++ b/src/images/SkScaledBitmapSampler.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2007, 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 "SkScaledBitmapSampler.h"
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+
+// 8888
+
+static bool Sample_Gray_D8888(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int) {
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPackARGB32(0xFF, src[0], src[0], src[0]);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBx_D8888(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int) {
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBA_D8888(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int) {
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    unsigned alphaMask = 0xFF;
+    for (int x = 0; x < width; x++) {
+        unsigned alpha = src[3];
+        dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
+        src += deltaSrc;
+        alphaMask &= alpha;
+    }
+    return alphaMask != 0xFF;
+}
+
+// 565
+
+static bool Sample_Gray_D565(void* SK_RESTRICT dstRow,
+                             const uint8_t* SK_RESTRICT src,
+                             int width, int deltaSrc, int) {
+    uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPack888ToRGB16(src[0], src[0], src[0]);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_Gray_D565_D(void* SK_RESTRICT dstRow,
+                               const uint8_t* SK_RESTRICT src,
+                               int width, int deltaSrc, int y) {
+    uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+    DITHER_565_SCAN(y);
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkDitherRGBTo565(src[0], src[0], src[0], DITHER_VALUE(x));
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow,
+                             const uint8_t* SK_RESTRICT src,
+                             int width, int deltaSrc, int) {
+    uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPack888ToRGB16(src[0], src[1], src[2]);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBx_D565_D(void* SK_RESTRICT dstRow,
+                               const uint8_t* SK_RESTRICT src,
+                               int width, int deltaSrc, int y) {
+    uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+    DITHER_565_SCAN(y);
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkDitherRGBTo565(src[0], src[1], src[2], DITHER_VALUE(x));
+        src += deltaSrc;
+    }
+    return false;
+}
+
+// 4444
+
+static bool Sample_Gray_D4444(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int) {
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    for (int x = 0; x < width; x++) {
+        unsigned gray = src[0] >> 4;
+        dst[x] = SkPackARGB4444(0xF, gray, gray, gray);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_Gray_D4444_D(void* SK_RESTRICT dstRow,
+                                const uint8_t* SK_RESTRICT src,
+                                int width, int deltaSrc, int y) {
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    DITHER_4444_SCAN(y);
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[0], src[0],
+                                      DITHER_VALUE(x));
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBx_D4444(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int) {
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPackARGB4444(0xF, src[0] >> 4, src[1] >> 4, src[2] >> 4);
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBx_D4444_D(void* SK_RESTRICT dstRow,
+                                const uint8_t* SK_RESTRICT src,
+                                int width, int deltaSrc, int y) {
+    SkPMColor16* dst = (SkPMColor16*)dstRow;
+    DITHER_4444_SCAN(y);
+
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[1], src[2],
+                                      DITHER_VALUE(x));
+        src += deltaSrc;
+    }
+    return false;
+}
+
+static bool Sample_RGBA_D4444(void* SK_RESTRICT dstRow,
+                              const uint8_t* SK_RESTRICT src,
+                              int width, int deltaSrc, int) {
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    unsigned alphaMask = 0xFF;
+
+    for (int x = 0; x < width; x++) {
+        unsigned alpha = src[3];
+        SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
+        dst[x] = SkPixel32ToPixel4444(c);
+        src += deltaSrc;
+        alphaMask &= alpha;
+    }
+    return alphaMask != 0xFF;
+}
+
+static bool Sample_RGBA_D4444_D(void* SK_RESTRICT dstRow,
+                                const uint8_t* SK_RESTRICT src,
+                                int width, int deltaSrc, int y) {
+    SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+    unsigned alphaMask = 0xFF;
+    DITHER_4444_SCAN(y);
+
+    for (int x = 0; x < width; x++) {
+        unsigned alpha = src[3];
+        SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
+        dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
+        src += deltaSrc;
+        alphaMask &= alpha;
+    }
+    return alphaMask != 0xFF;
+}
+
+// Index
+
+static bool Sample_Index_DI(void* SK_RESTRICT dstRow,
+                            const uint8_t* SK_RESTRICT src,
+                            int width, int deltaSrc, int) {
+    if (1 == deltaSrc) {
+        memcpy(dstRow, src, width);
+    } else {
+        uint8_t* SK_RESTRICT dst = (uint8_t*)dstRow;
+        for (int x = 0; x < width; x++) {
+            dst[x] = src[0];
+            src += deltaSrc;
+        }
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkScaledBitmapSampler.h"
+
+SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height,
+                                             int sampleSize) {
+    if (width <= 0 || height <= 0) {
+        sk_throw();
+    }
+    
+    if (sampleSize <= 1) {
+        fScaledWidth = width;
+        fScaledHeight = height;
+        fX0 = fY0 = 0;
+        fDX = fDY = 1;
+        return;
+    }
+    
+    int dx = SkMin32(sampleSize, width);
+    int dy = SkMin32(sampleSize, height);
+    
+    fScaledWidth = width / dx;
+    fScaledHeight = height / dy;
+    
+    SkASSERT(fScaledWidth > 0);
+    SkASSERT(fScaledHeight > 0);
+    
+    fX0 = dx >> 1;
+    fY0 = dy >> 1;
+    
+    SkASSERT(fX0 >= 0 && fX0 < width);
+    SkASSERT(fY0 >= 0 && fY0 < height);
+    
+    fDX = dx;
+    fDY = dy;
+    
+    SkASSERT(fDX > 0 && (fX0 + fDX * (fScaledWidth - 1)) < width);
+    SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height);
+    
+    fRowProc = NULL;
+}
+
+bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither) {
+    static const RowProc gProcs[] = {
+        // 8888 (no dither distinction)
+        Sample_Gray_D8888,  Sample_Gray_D8888,
+        Sample_RGBx_D8888,  Sample_RGBx_D8888,
+        Sample_RGBA_D8888,  Sample_RGBA_D8888,
+        NULL,               NULL,
+        // 565 (no alpha distinction)
+        Sample_Gray_D565,   Sample_Gray_D565_D,
+        Sample_RGBx_D565,   Sample_RGBx_D565_D,
+        Sample_RGBx_D565,   Sample_RGBx_D565_D,
+        NULL,               NULL,
+        // 4444
+        Sample_Gray_D4444,  Sample_Gray_D4444_D,
+        Sample_RGBx_D4444,  Sample_RGBx_D4444_D,
+        Sample_RGBA_D4444,  Sample_RGBA_D4444_D,
+        NULL,               NULL,
+        // Index8
+        NULL,               NULL,
+        NULL,               NULL,
+        NULL,               NULL,
+        Sample_Index_DI,    Sample_Index_DI,
+    };
+
+    
+    int index = 0;
+    if (dither) {
+        index += 1;
+    }
+    switch (sc) {
+        case SkScaledBitmapSampler::kGray:
+            fSrcPixelSize = 1;
+            index += 0;
+            break;
+        case SkScaledBitmapSampler::kRGB:
+            fSrcPixelSize = 3;
+            index += 2;
+            break;
+        case SkScaledBitmapSampler::kRGBX:
+            fSrcPixelSize = 4;
+            index += 2;
+            break;
+        case SkScaledBitmapSampler::kRGBA:
+            fSrcPixelSize = 4;
+            index += 4;
+            break;
+        case SkScaledBitmapSampler::kIndex:
+            fSrcPixelSize = 1;
+            index += 6;
+            break;
+        default:
+            return false;
+    }
+
+    switch (dst->config()) {
+        case SkBitmap::kARGB_8888_Config:
+            index += 0;
+            break;
+        case SkBitmap::kRGB_565_Config:
+            index += 8;
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            index += 16;
+            break;
+        case SkBitmap::kIndex8_Config:
+            index += 24;
+            break;
+        default:
+            return false;
+    }
+    
+    fRowProc = gProcs[index];
+    fDstRow = (char*)dst->getPixels();
+    fDstRowBytes = dst->rowBytes();
+    fCurrY = 0;
+    return fRowProc != NULL;
+}
+
+bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) {
+    SkASSERT((unsigned)fCurrY < (unsigned)fScaledHeight);
+
+    bool hadAlpha = fRowProc(fDstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
+                             fDX * fSrcPixelSize, fCurrY);
+    fDstRow += fDstRowBytes;
+    fCurrY += 1;
+    return hadAlpha;
+}
diff --git a/src/images/SkScaledBitmapSampler.h b/src/images/SkScaledBitmapSampler.h
new file mode 100644
index 0000000..0bb9924
--- /dev/null
+++ b/src/images/SkScaledBitmapSampler.h
@@ -0,0 +1,55 @@
+#ifndef SkScaledBitmapSampler_DEFINED
+#define SkScaledBitmapSampler_DEFINED
+
+#include "SkTypes.h"
+
+class SkBitmap;
+
+class SkScaledBitmapSampler {
+public:
+    SkScaledBitmapSampler(int origWidth, int origHeight, int cellSize);
+    
+    int scaledWidth() const { return fScaledWidth; }
+    int scaledHeight() const { return fScaledHeight; }
+    
+    int srcY0() const { return fY0; }
+    int srcDY() const { return fDY; }
+
+    enum SrcConfig {
+        kGray,  // 1 byte per pixel
+        kIndex, // 1 byte per pixel
+        kRGB,   // 3 bytes per pixel
+        kRGBX,  // 4 byes per pixel (ignore 4th)
+        kRGBA   // 4 bytes per pixel
+    };
+
+    // Given a dst bitmap (with pixels already allocated) and a src-config,
+    // prepares iterator to process the src colors and write them into dst.
+    // Returns false if the request cannot be fulfulled.
+    bool begin(SkBitmap* dst, SrcConfig sc, bool doDither);
+    // call with row of src pixels, for y = 0...scaledHeight-1.
+    // returns true if the row had non-opaque alpha in it
+    bool next(const uint8_t* SK_RESTRICT src);
+
+private:
+    int fScaledWidth;
+    int fScaledHeight;
+
+    int fX0;    // first X coord to sample
+    int fY0;    // first Y coord (scanline) to sample
+    int fDX;    // step between X samples
+    int fDY;    // step between Y samples
+    
+    typedef bool (*RowProc)(void* SK_RESTRICT dstRow,
+                            const uint8_t* SK_RESTRICT src,
+                            int width, int deltaSrc, int y);
+    
+    // setup state
+    char*   fDstRow; // points into bitmap's pixels
+    int     fDstRowBytes;
+    int     fCurrY; // used for dithering
+    int     fSrcPixelSize;  // 1, 3, 4    
+    RowProc fRowProc;
+};
+
+#endif
diff --git a/src/images/SkStream.cpp b/src/images/SkStream.cpp
new file mode 100644
index 0000000..b199a1b
--- /dev/null
+++ b/src/images/SkStream.cpp
@@ -0,0 +1,856 @@
+/* libs/graphics/images/SkStream.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 "SkStream.h"
+#include "SkFixed.h"
+#include "SkString.h"
+#include "SkOSFile.h"
+
+SkStream::~SkStream() {}
+
+const char* SkStream::getFileName()
+{
+    // override in subclass if you represent a file
+    return NULL;
+}
+
+const void* SkStream::getMemoryBase()
+{
+    // override in subclass if you represent a memory block
+    return NULL;
+}
+
+size_t SkStream::skip(size_t size)
+{
+    /*  Check for size == 0, and just return 0. If we passed that
+        to read(), it would interpret it as a request for the entire
+        size of the stream.
+    */
+    return size ? this->read(NULL, size) : 0;
+}
+
+int8_t SkStream::readS8() {
+    int8_t value;
+    size_t len = this->read(&value, 1);
+    SkASSERT(1 == len);
+    return value;
+}
+
+int16_t SkStream::readS16() {
+    int16_t value;
+    size_t len = this->read(&value, 2);
+    SkASSERT(2 == len);
+    return value;
+}
+
+int32_t SkStream::readS32() {
+    int32_t value;
+    size_t len = this->read(&value, 4);
+    SkASSERT(4 == len);
+    return value;
+}
+
+SkScalar SkStream::readScalar() {
+    SkScalar value;
+    size_t len = this->read(&value, sizeof(SkScalar));
+    SkASSERT(sizeof(SkScalar) == len);
+    return value;
+}
+
+size_t SkStream::readPackedUInt() {
+    uint8_t byte;    
+    if (!this->read(&byte, 1)) {
+        return 0;
+    }
+    if (byte != 0xFF) {
+        return byte;
+    }
+    
+    uint16_t word;
+    if (!this->read(&word, 2)) {
+        return 0;
+    }
+    if (word != 0xFFFF) {
+        return word;
+    }
+    
+    uint32_t quad;
+    if (!this->read(&quad, 4)) {
+        return 0;
+    }
+    return quad;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+SkWStream::~SkWStream()
+{
+}
+
+void SkWStream::newline()
+{
+    this->write("\n", 1);
+}
+
+void SkWStream::flush()
+{
+}
+
+bool SkWStream::writeText(const char text[])
+{
+    SkASSERT(text);
+    return this->write(text, strlen(text));
+}
+
+bool SkWStream::writeDecAsText(int32_t dec)
+{
+    SkString    tmp;
+    tmp.appendS32(dec);
+    return this->write(tmp.c_str(), tmp.size());
+}
+
+bool SkWStream::writeHexAsText(uint32_t hex, int digits)
+{
+    SkString    tmp;
+    tmp.appendHex(hex, digits);
+    return this->write(tmp.c_str(), tmp.size());
+}
+
+bool SkWStream::writeScalarAsText(SkScalar value)
+{
+    SkString    tmp;
+    tmp.appendScalar(value);
+    return this->write(tmp.c_str(), tmp.size());
+}
+
+bool SkWStream::write8(U8CPU value) {
+    uint8_t v = SkToU8(value);
+    return this->write(&v, 1);
+}
+
+bool SkWStream::write16(U16CPU value) {
+    uint16_t v = SkToU16(value);
+    return this->write(&v, 2);
+}
+
+bool SkWStream::write32(uint32_t value) {
+    return this->write(&value, 4);
+}
+
+bool SkWStream::writeScalar(SkScalar value) {
+    return this->write(&value, sizeof(value));
+}
+
+bool SkWStream::writePackedUInt(size_t value) {
+    if (value < 0xFF) {
+        return this->write8(value);
+    } else if (value < 0xFFFF) {
+        return this->write8(0xFF) && this->write16(value);
+    } else {
+        return this->write16(0xFFFF) && this->write32(value);
+    }
+}
+
+bool SkWStream::writeStream(SkStream* stream, size_t length) {
+    char scratch[1024];
+    const size_t MAX = sizeof(scratch);
+    
+    while (length != 0) {
+        size_t n = length;
+        if (n > MAX) {
+            n = MAX;
+        }
+        stream->read(scratch, n);
+        if (!this->write(scratch, n)) {
+            return false;
+        }
+        length -= n;
+    }
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkFILEStream::SkFILEStream(const char file[]) : fName(file)
+{
+#ifdef SK_BUILD_FOR_BREW
+    if (SkStrEndsWith(fName.c_str(), ".xml"))
+        fName.writable_str()[fName.size()-3] = 'b';
+#endif
+
+    fFILE = file ? sk_fopen(fName.c_str(), kRead_SkFILE_Flag) : NULL;
+}
+
+SkFILEStream::~SkFILEStream()
+{
+    if (fFILE)
+        sk_fclose(fFILE);
+}
+
+void SkFILEStream::setPath(const char path[])
+{
+    fName.set(path);
+#ifdef SK_BUILD_FOR_BREW
+    if (SkStrEndsWith(fName.c_str(), ".xml"))
+        fName.writable_str()[fName.size()-3] = 'b';
+#endif
+
+    if (fFILE)
+    {
+        sk_fclose(fFILE);
+        fFILE = NULL;
+    }
+    if (path)
+        fFILE = sk_fopen(fName.c_str(), kRead_SkFILE_Flag);
+}
+
+const char* SkFILEStream::getFileName()
+{
+    return fName.c_str();
+}
+
+bool SkFILEStream::rewind()
+{
+    if (fFILE)
+    {
+        if (sk_frewind(fFILE))
+            return true;
+        // we hit an error
+        sk_fclose(fFILE);
+        fFILE = NULL;
+    }
+    return false;
+}
+
+size_t SkFILEStream::read(void* buffer, size_t size)
+{
+    if (fFILE)
+    {
+        if (buffer == NULL && size == 0)    // special signature, they want the total size
+            return sk_fgetsize(fFILE);
+        else
+            return sk_fread(buffer, size, fFILE);
+    }
+    return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkMemoryStream::SkMemoryStream()
+{
+    fWeOwnTheData = false;
+    this->setMemory(NULL, 0);
+}
+
+SkMemoryStream::SkMemoryStream(size_t size) {
+    fWeOwnTheData = true;
+    fOffset = 0;
+    fSize = size;
+    fSrc = sk_malloc_throw(size);
+}
+
+SkMemoryStream::SkMemoryStream(const void* src, size_t size, bool copyData)
+{
+    fWeOwnTheData = false;
+    this->setMemory(src, size, copyData);
+}
+
+SkMemoryStream::~SkMemoryStream()
+{
+    if (fWeOwnTheData)
+        sk_free((void*)fSrc);
+}
+
+void SkMemoryStream::setMemory(const void* src, size_t size, bool copyData)
+{
+    if (fWeOwnTheData)
+        sk_free((void*)fSrc);
+
+    fSize = size;
+    fOffset = 0;
+    fWeOwnTheData = copyData;
+
+    if (copyData)
+    {
+        void* copy = sk_malloc_throw(size);
+        memcpy(copy, src, size);
+        src = copy;
+    }
+    fSrc = src;
+}
+
+void SkMemoryStream::skipToAlign4()
+{
+    // cast to remove unary-minus warning
+    fOffset += -(int)fOffset & 0x03;
+}
+
+bool SkMemoryStream::rewind()
+{
+    fOffset = 0;
+    return true;
+}
+
+size_t SkMemoryStream::read(void* buffer, size_t size)
+{
+    if (buffer == NULL && size == 0)    // special signature, they want the total size
+        return fSize;
+
+    // if buffer is NULL, seek ahead by size
+
+    if (size == 0)
+        return 0;
+    if (size > fSize - fOffset)
+        size = fSize - fOffset;
+    if (buffer) {
+        memcpy(buffer, (const char*)fSrc + fOffset, size);
+    }
+    fOffset += size;
+    return size;
+}
+
+const void* SkMemoryStream::getMemoryBase()
+{
+    return fSrc;
+}
+
+const void* SkMemoryStream::getAtPos()
+{
+    return (const char*)fSrc + fOffset;
+}
+
+size_t SkMemoryStream::seek(size_t offset)
+{
+    if (offset > fSize)
+        offset = fSize;
+    fOffset = offset;
+    return offset;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkBufferStream::SkBufferStream(SkStream* proxy, size_t bufferSize)
+    : fProxy(proxy)
+{
+    SkASSERT(proxy != NULL);
+    proxy->ref();
+    this->init(NULL, bufferSize);
+}
+
+SkBufferStream::SkBufferStream(SkStream* proxy, void* buffer, size_t bufferSize)
+    : fProxy(proxy)
+{
+    SkASSERT(proxy != NULL);
+    SkASSERT(buffer == NULL || bufferSize != 0);    // init(addr, 0) makes no sense, we must know how big their buffer is
+    proxy->ref();
+    this->init(buffer, bufferSize);
+}
+
+void SkBufferStream::init(void* buffer, size_t bufferSize)
+{
+    if (bufferSize == 0)
+        bufferSize = kDefaultBufferSize;
+
+    fOrigBufferSize = bufferSize;
+    fBufferSize = bufferSize;
+    fBufferOffset = bufferSize; // to trigger a reload on the first read()
+
+    if (buffer == NULL)
+    {
+        fBuffer = (char*)sk_malloc_throw(fBufferSize);
+        fWeOwnTheBuffer = true;
+    }
+    else
+    {
+        fBuffer = (char*)buffer;
+        fWeOwnTheBuffer = false;
+    }
+}
+
+SkBufferStream::~SkBufferStream()
+{
+    fProxy->unref();
+    if (fWeOwnTheBuffer)
+        sk_free(fBuffer);
+}
+
+bool SkBufferStream::rewind()
+{
+    fBufferOffset = fBufferSize = fOrigBufferSize;
+    return fProxy->rewind();
+}
+
+const char* SkBufferStream::getFileName()
+{
+    return fProxy->getFileName();
+}
+
+#ifdef SK_DEBUG
+//  #define SK_TRACE_BUFFERSTREAM
+#endif
+
+size_t SkBufferStream::read(void* buffer, size_t size) {
+#ifdef SK_TRACE_BUFFERSTREAM
+    SkDebugf("Request %d", size);
+#endif
+
+    if (buffer == NULL && size == 0) {
+        return fProxy->read(buffer, size);    // requesting total size
+    }
+
+    if (0 == size) {
+        return 0;
+    }
+
+    // skip size bytes
+    if (NULL == buffer) {
+        size_t remaining = fBufferSize - fBufferOffset;
+        if (remaining >= size) {
+            fBufferOffset += size;
+            return size;
+        }
+        // if we get here, we are being asked to skip beyond our current buffer
+        // so reset our offset to force a read next time, and skip the diff
+        // in our proxy
+        fBufferOffset = fOrigBufferSize;
+        return remaining + fProxy->read(NULL, size - remaining);
+    }
+
+    size_t s = size;
+    size_t actuallyRead = 0;
+
+    // flush what we can from our fBuffer
+    if (fBufferOffset < fBufferSize)
+    {
+        if (s > fBufferSize - fBufferOffset)
+            s = fBufferSize - fBufferOffset;
+        memcpy(buffer, fBuffer + fBufferOffset, s);
+#ifdef SK_TRACE_BUFFERSTREAM
+        SkDebugf(" flush %d", s);
+#endif
+        size -= s;
+        fBufferOffset += s;
+        buffer = (char*)buffer + s;
+        actuallyRead = s;
+    }
+
+    // check if there is more to read
+    if (size)
+    {
+        SkASSERT(fBufferOffset >= fBufferSize); // need to refill our fBuffer
+
+        if (size < fBufferSize) // lets try to read more than the request
+        {
+            s = fProxy->read(fBuffer, fBufferSize);
+#ifdef SK_TRACE_BUFFERSTREAM
+            SkDebugf(" read %d into fBuffer", s);
+#endif
+            if (size > s)   // they asked for too much
+                size = s;
+            if (size)
+            {
+                memcpy(buffer, fBuffer, size);
+                actuallyRead += size;
+#ifdef SK_TRACE_BUFFERSTREAM
+                SkDebugf(" memcpy %d into dst", size);
+#endif
+            }
+
+            fBufferOffset = size;
+            fBufferSize = s;        // record the (possibly smaller) size for the buffer
+        }
+        else    // just do a direct read
+        {
+            actuallyRead += fProxy->read(buffer, size);
+#ifdef SK_TRACE_BUFFERSTREAM
+            SkDebugf(" direct read %d", size);
+#endif
+        }
+    }
+#ifdef SK_TRACE_BUFFERSTREAM
+    SkDebugf("\n");
+#endif
+    return actuallyRead;
+}
+
+const void* SkBufferStream::getMemoryBase()
+{
+    return fProxy->getMemoryBase();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkFILEWStream::SkFILEWStream(const char path[])
+{
+    fFILE = sk_fopen(path, kWrite_SkFILE_Flag);
+}
+
+SkFILEWStream::~SkFILEWStream()
+{
+    if (fFILE)
+        sk_fclose(fFILE);
+}
+
+bool SkFILEWStream::write(const void* buffer, size_t size)
+{
+    if (fFILE == NULL)
+        return false;
+
+    if (sk_fwrite(buffer, size, fFILE) != size)
+    {
+        SkDEBUGCODE(SkDebugf("SkFILEWStream failed writing %d bytes\n", size);)
+        sk_fclose(fFILE);
+        fFILE = NULL;
+        return false;
+    }
+    return true;
+}
+
+void SkFILEWStream::flush()
+{
+    if (fFILE)
+        sk_fflush(fFILE);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+SkMemoryWStream::SkMemoryWStream(void* buffer, size_t size)
+    : fBuffer((char*)buffer), fMaxLength(size), fBytesWritten(0)
+{
+}
+
+bool SkMemoryWStream::write(const void* buffer, size_t size)
+{
+    size = SkMin32(size, fMaxLength - fBytesWritten);
+    if (size > 0)
+    {
+        memcpy(fBuffer + fBytesWritten, buffer, size);
+        fBytesWritten += size;
+        return true;
+    }
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+#define SkDynamicMemoryWStream_MinBlockSize   256
+
+struct SkDynamicMemoryWStream::Block {
+    Block*  fNext;
+    char*   fCurr;
+    char*   fStop;
+
+    const char* start() const { return (const char*)(this + 1); }
+    char*   start() { return (char*)(this + 1); }
+    size_t  avail() const { return fStop - fCurr; }
+    size_t  written() const { return fCurr - this->start(); }
+    
+    void init(size_t size)
+    {
+        fNext = NULL;
+        fCurr = this->start();
+        fStop = this->start() + size;
+    }
+    
+    const void* append(const void* data, size_t size)
+    {
+        SkASSERT((size_t)(fStop - fCurr) >= size);
+        memcpy(fCurr, data, size);
+        fCurr += size;
+        return (const void*)((const char*)data + size);
+    }
+};
+
+SkDynamicMemoryWStream::SkDynamicMemoryWStream() : fHead(NULL), fTail(NULL), fBytesWritten(0), fCopyToCache(NULL)
+{
+}
+
+SkDynamicMemoryWStream::~SkDynamicMemoryWStream()
+{
+    reset();
+}
+
+const char* SkDynamicMemoryWStream::detach()
+{
+    const char* result = getStream();
+    fCopyToCache = NULL;
+    return result;
+}
+
+void SkDynamicMemoryWStream::reset()
+{
+    sk_free(fCopyToCache);
+    Block*  block = fHead;
+    
+    while (block != NULL) {
+        Block*  next = block->fNext;
+        sk_free(block);
+        block = next;
+    }
+    fHead = fTail = NULL;
+    fBytesWritten = 0;
+    fCopyToCache = NULL;
+}
+
+bool SkDynamicMemoryWStream::write(const void* buffer, size_t count)
+{
+    if (count > 0) {
+
+        if (fCopyToCache) {
+            sk_free(fCopyToCache);
+            fCopyToCache = NULL;
+        }
+        fBytesWritten += count;
+        
+        size_t  size;
+        
+        if (fTail != NULL && fTail->avail() > 0) {
+            size = SkMin32(fTail->avail(), count);
+            buffer = fTail->append(buffer, size);
+            SkASSERT(count >= size);
+            count -= size;        
+            if (count == 0)
+                return true;
+        }
+            
+        size = SkMax32(count, SkDynamicMemoryWStream_MinBlockSize);
+        Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
+        block->init(size);
+        block->append(buffer, count);
+        
+        if (fTail != NULL)
+            fTail->fNext = block;
+        else
+            fHead = fTail = block;
+        fTail = block;
+    }
+    return true;
+}
+
+bool SkDynamicMemoryWStream::write(const void* buffer, size_t offset, size_t count)
+{
+    if (offset + count > fBytesWritten)
+        return false; // test does not partially modify
+    Block* block = fHead;
+    while (block != NULL) {
+        size_t size = block->written();
+        if (offset < size) {
+            size_t part = offset + count > size ? size - offset : count;
+            memcpy(block->start() + offset, buffer, part);
+            if (count <= part)
+                return true;
+            count -= part;
+            buffer = (const void*) ((char* ) buffer + part);
+        }
+        offset = offset > size ? offset - size : 0;
+        block = block->fNext;
+    }
+    return false;
+}
+
+bool SkDynamicMemoryWStream::read(void* buffer, size_t offset, size_t count)
+{
+    if (offset + count > fBytesWritten)
+        return false; // test does not partially modify
+    Block* block = fHead;
+    while (block != NULL) {
+        size_t size = block->written();
+        if (offset < size) {
+            size_t part = offset + count > size ? size - offset : count;
+            memcpy(buffer, block->start() + offset, part);
+            if (count <= part)
+                return true;
+            count -= part;
+            buffer = (void*) ((char* ) buffer + part);
+        }
+        offset = offset > size ? offset - size : 0;
+        block = block->fNext;
+    }
+    return false;
+}
+
+void SkDynamicMemoryWStream::copyTo(void* dst) const
+{
+    Block* block = fHead;
+    
+    while (block != NULL) {
+        size_t size = block->written();
+        memcpy(dst, block->start(), size);
+        dst = (void*)((char*)dst + size);
+        block = block->fNext;
+    }
+}
+
+const char* SkDynamicMemoryWStream::getStream() const
+{
+    if (fCopyToCache == NULL) {
+        fCopyToCache = (char*)sk_malloc_throw(fBytesWritten);
+        this->copyTo(fCopyToCache);
+    }
+    return fCopyToCache;
+}
+
+void SkDynamicMemoryWStream::padToAlign4()
+{
+    // cast to remove unary-minus warning
+    int padBytes = -(int)fBytesWritten & 0x03;
+    if (padBytes == 0)
+        return;
+    int zero = 0;
+    write(&zero, padBytes);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkDebugWStream::newline()
+{
+#ifdef SK_DEBUG
+    SkDebugf("\n");
+#endif
+}
+
+bool SkDebugWStream::write(const void* buffer, size_t size)
+{
+#ifdef SK_DEBUG
+    char* s = new char[size+1];
+    memcpy(s, buffer, size);
+    s[size] = 0;
+    SkDebugf("%s", s);
+    delete[] s;
+#endif
+    return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#include "SkRandom.h"
+
+#ifdef SK_SUPPORT_UNITTEST
+#define MAX_SIZE    (256 * 1024)
+
+static void random_fill(SkRandom& rand, void* buffer, size_t size) {
+    char* p = (char*)buffer;
+    char* stop = p + size;
+    while (p < stop) {
+        *p++ = (char)(rand.nextU() >> 8);
+    }
+}
+
+static void test_buffer() {
+    SkRandom rand;
+    SkAutoMalloc am(MAX_SIZE * 2);
+    char* storage = (char*)am.get();
+    char* storage2 = storage + MAX_SIZE;
+
+    random_fill(rand, storage, MAX_SIZE);
+
+    for (int sizeTimes = 0; sizeTimes < 100; sizeTimes++) {
+        int size = rand.nextU() % MAX_SIZE;
+        if (size == 0) {
+            size = MAX_SIZE;
+        }
+        for (int times = 0; times < 100; times++) {
+            int bufferSize = 1 + (rand.nextU() & 0xFFFF);
+            SkMemoryStream mstream(storage, size);
+            SkBufferStream bstream(&mstream, bufferSize);
+            
+            int bytesRead = 0;
+            while (bytesRead < size) {
+                int s = 17 + (rand.nextU() & 0xFFFF);
+                int ss = bstream.read(storage2, s);
+                SkASSERT(ss > 0 && ss <= s);
+                SkASSERT(bytesRead + ss <= size);
+                SkASSERT(memcmp(storage + bytesRead, storage2, ss) == 0);
+                bytesRead += ss;
+            }
+            SkASSERT(bytesRead == size);
+        }
+    }
+}
+#endif
+
+void SkStream::UnitTest() {
+#ifdef SK_SUPPORT_UNITTEST
+    {
+        static const char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+        char            copy[sizeof(s)];
+        SkRandom        rand;
+        
+        for (int i = 0; i < 65; i++)
+        {
+            char*           copyPtr = copy;
+            SkMemoryStream  mem(s, sizeof(s));
+            SkBufferStream  buff(&mem, i);
+            
+            do {
+                copyPtr += buff.read(copyPtr, rand.nextU() & 15);
+            } while (copyPtr < copy + sizeof(s));
+            SkASSERT(copyPtr == copy + sizeof(s));
+            SkASSERT(memcmp(s, copy, sizeof(s)) == 0);
+        }
+    }
+    test_buffer();
+#endif
+}
+
+void SkWStream::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    {
+        SkDebugWStream  s;
+
+        s.writeText("testing wstream helpers\n");
+        s.writeText("compare: 0 ");         s.writeDecAsText(0);    s.newline();
+        s.writeText("compare: 591 ");       s.writeDecAsText(591);  s.newline();
+        s.writeText("compare: -9125 ");     s.writeDecAsText(-9125);    s.newline();
+        s.writeText("compare: 0 ");         s.writeHexAsText(0, 0); s.newline();
+        s.writeText("compare: 03FA ");      s.writeHexAsText(0x3FA, 4); s.newline();
+        s.writeText("compare: DEADBEEF ");  s.writeHexAsText(0xDEADBEEF, 4);    s.newline();
+        s.writeText("compare: 0 ");         s.writeScalarAsText(SkIntToScalar(0));  s.newline();
+        s.writeText("compare: 27 ");        s.writeScalarAsText(SkIntToScalar(27)); s.newline();
+        s.writeText("compare: -119 ");      s.writeScalarAsText(SkIntToScalar(-119));   s.newline();
+        s.writeText("compare: 851.3333 ");  s.writeScalarAsText(SkIntToScalar(851) + SK_Scalar1/3); s.newline();
+        s.writeText("compare: -0.08 ");     s.writeScalarAsText(-SK_Scalar1*8/100); s.newline();
+    }
+
+    {
+        SkDynamicMemoryWStream  ds;
+        const char s[] = "abcdefghijklmnopqrstuvwxyz";
+        int i;
+        for (i = 0; i < 100; i++) {
+            bool result = ds.write(s, 26);
+            SkASSERT(result);
+        }
+        SkASSERT(ds.getOffset() == 100 * 26);
+        char* dst = new char[100 * 26 + 1];
+        dst[100*26] = '*';
+        ds.copyTo(dst);
+        SkASSERT(dst[100*26] == '*');
+   //     char* p = dst;
+        for (i = 0; i < 100; i++)
+            SkASSERT(memcmp(&dst[i * 26], s, 26) == 0);
+        SkASSERT(memcmp(dst, ds.getStream(), 100*26) == 0);
+        delete[] dst;
+    }
+#endif
+}
+
+#endif
diff --git a/src/images/bmpdecoderhelper.cpp b/src/images/bmpdecoderhelper.cpp
new file mode 100644
index 0000000..acabf44
--- /dev/null
+++ b/src/images/bmpdecoderhelper.cpp
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2007, 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.
+ */
+// Author: cevans@google.com (Chris Evans)
+
+#include "bmpdecoderhelper.h"
+
+namespace image_codec {
+
+static const int kBmpHeaderSize = 14;
+static const int kBmpInfoSize = 40;
+static const int kBmpOS2InfoSize = 12;
+static const int kMaxDim = SHRT_MAX / 2;
+
+bool BmpDecoderHelper::DecodeImage(const char* p,
+                                   int len,
+                                   int max_pixels,
+                                   BmpDecoderCallback* callback) {
+  data_ = reinterpret_cast<const uint8*>(p);
+  pos_ = 0;
+  len_ = len;
+  inverted_ = true;
+  // Parse the header structure.
+  if (len < kBmpHeaderSize + 4) {
+    return false;
+  }
+  GetShort();  // Signature.
+  GetInt();  // Size.
+  GetInt();  // Reserved.
+  int offset = GetInt();
+  // Parse the info structure.
+  int infoSize = GetInt();
+  if (infoSize != kBmpOS2InfoSize && infoSize < kBmpInfoSize) {
+    return false;
+  }
+  int cols = 0;
+  int comp = 0;
+  int colLen = 4;
+  if (infoSize >= kBmpInfoSize) {
+    if (len < kBmpHeaderSize + kBmpInfoSize) {
+      return false;
+    }
+    width_ = GetInt();
+    height_ = GetInt();
+    GetShort();  // Planes.
+    bpp_ = GetShort();
+    comp = GetInt();
+    GetInt();  // Size.
+    GetInt();  // XPPM.
+    GetInt();  // YPPM.
+    cols = GetInt();
+    GetInt();  // Important colours.
+  } else {
+    if (len < kBmpHeaderSize + kBmpOS2InfoSize) {
+      return false;
+    }
+    colLen = 3;
+    width_ = GetShort();
+    height_ = GetShort();
+    GetShort();  // Planes.
+    bpp_ = GetShort();
+  }
+  if (height_ < 0) {
+    height_ = -height_;
+    inverted_ = false;
+  }
+  if (width_ <= 0 || width_ > kMaxDim || height_ <= 0 || height_ > kMaxDim) {
+    return false;
+  }
+  if (width_ * height_ > max_pixels) {
+    return false;
+  }
+  if (cols < 0 || cols > 256) {
+    return false;
+  }
+  // Allocate then read in the colour map.
+  if (cols == 0 && bpp_ <= 8) {
+    cols = 1 << bpp_;
+  }
+  if (bpp_ <= 8 || cols > 0) {
+    uint8* colBuf = new uint8[256 * 3];
+    memset(colBuf, '\0', 256 * 3);
+    colTab_.reset(colBuf);
+  }
+  if (cols > 0) {
+    if (pos_ + (cols * colLen) > len_) {
+      return false;
+    }
+    for (int i = 0; i < cols; ++i) {
+      int base = i * 3;
+      colTab_[base + 2] = GetByte();
+      colTab_[base + 1] = GetByte();
+      colTab_[base] = GetByte();
+      if (colLen == 4) {
+        GetByte();
+      }
+    }
+  }
+  // Read in the compression data if necessary.
+  redBits_ = 0x7c00;
+  greenBits_ = 0x03e0;
+  blueBits_ = 0x001f;
+  bool rle = false;
+  if (comp == 1 || comp == 2) {
+    rle = true;
+  } else if (comp == 3) {
+    if (pos_ + 12 > len_) {
+      return false;
+    }
+    redBits_ = GetInt() & 0xffff;
+    greenBits_ = GetInt() & 0xffff;
+    blueBits_ = GetInt() & 0xffff;
+  }
+  redShiftRight_ = CalcShiftRight(redBits_);
+  greenShiftRight_ = CalcShiftRight(greenBits_);
+  blueShiftRight_ = CalcShiftRight(blueBits_);
+  redShiftLeft_ = CalcShiftLeft(redBits_);
+  greenShiftLeft_ = CalcShiftLeft(greenBits_);
+  blueShiftLeft_ = CalcShiftLeft(blueBits_);
+  rowPad_ = 0;
+  pixelPad_ = 0;
+  int rowLen;
+  if (bpp_ == 32) {
+    rowLen = width_ * 4;
+    pixelPad_ = 1;
+  } else if (bpp_ == 24) {
+    rowLen = width_ * 3;
+  } else if (bpp_ == 16) {
+    rowLen = width_ * 2;
+  } else if (bpp_ == 8) {
+    rowLen = width_;
+  } else if (bpp_ == 4) {
+    rowLen = width_ / 2;
+    if (width_ & 1) {
+      rowLen++;
+    }
+  } else if (bpp_ == 1) {
+    rowLen = width_ / 8;
+    if (width_ & 7) {
+      rowLen++;
+    }
+  } else {
+    return false;
+  }
+  // Round the rowLen up to a multiple of 4.
+  if (rowLen % 4 != 0) {
+    rowPad_ = 4 - (rowLen % 4);
+    rowLen += rowPad_;
+  }
+
+  if (offset > 0 && offset > pos_ && offset < len_) {
+    pos_ = offset;
+  }
+  // Deliberately off-by-one; a load of BMPs seem to have their last byte
+  // missing.
+  if (!rle && (pos_ + (rowLen * height_) > len_ + 1)) {
+    return false;
+  }
+
+  output_ = callback->SetSize(width_, height_);
+  if (NULL == output_) {
+    return true;  // meaning we succeeded, but they want us to stop now
+  }
+
+  if (rle && (bpp_ == 4 || bpp_ == 8)) {
+    DoRLEDecode();
+  } else {
+    DoStandardDecode();
+  }
+  return true;
+}
+
+void BmpDecoderHelper::DoRLEDecode() {
+  static const uint8 RLE_ESCAPE = 0;
+  static const uint8 RLE_EOL = 0;
+  static const uint8 RLE_EOF = 1;
+  static const uint8 RLE_DELTA = 2;
+  int x = 0;
+  int y = height_ - 1;
+  while (pos_ < len_ - 1) {
+    uint8 cmd = GetByte();
+    if (cmd != RLE_ESCAPE) {
+      uint8 pixels = GetByte();
+      int num = 0;
+      uint8 col = pixels;
+      while (cmd-- && x < width_) {
+        if (bpp_ == 4) {
+          if (num & 1) {
+            col = pixels & 0xf;
+          } else {
+            col = pixels >> 4;
+          }
+        }
+        PutPixel(x++, y, col);
+        num++;
+      }
+    } else {
+      cmd = GetByte();
+      if (cmd == RLE_EOF) {
+        return;
+      } else if (cmd == RLE_EOL) {
+        x = 0;
+        y--;
+        if (y < 0) {
+          return;
+        }
+      } else if (cmd == RLE_DELTA) {
+        if (pos_ < len_ - 1) {
+          uint8 dx = GetByte();
+          uint8 dy = GetByte();
+          x += dx;
+          if (x > width_) {
+            x = width_;
+          }
+          y -= dy;
+          if (y < 0) {
+            return;
+          }
+        }
+      } else {
+        int num = 0;
+        int bytesRead = 0;
+        uint8 val = 0;
+        while (cmd-- && pos_ < len_) {
+          if (bpp_ == 8 || !(num & 1)) {
+            val = GetByte();
+            bytesRead++;
+          }
+          uint8 col = val;
+          if (bpp_ == 4) {
+            if (num & 1) {
+              col = col & 0xf;
+            } else {
+              col >>= 4;
+            }
+          }
+          if (x < width_) {
+            PutPixel(x++, y, col);
+          }
+          num++;
+        }
+        // All pixel runs must be an even number of bytes - skip a byte if we
+        // read an odd number.
+        if ((bytesRead & 1) && pos_ < len_) {
+          GetByte();
+        }
+      }
+    }
+  }
+}
+
+void BmpDecoderHelper::PutPixel(int x, int y, uint8 col) {
+  CHECK(x >= 0 && x < width_);
+  CHECK(y >= 0 && y < height_);
+  if (!inverted_) {
+    y = height_ - (y + 1);
+  }
+
+  int base = ((y * width_) + x) * 3;
+  int colBase = col * 3;
+  output_[base] = colTab_[colBase];
+  output_[base + 1] = colTab_[colBase + 1];
+  output_[base + 2] = colTab_[colBase + 2];
+}
+
+void BmpDecoderHelper::DoStandardDecode() {
+  int row = 0;
+  uint8 currVal = 0;
+  for (int h = height_ - 1; h >= 0; h--, row++) {
+    int realH = h;
+    if (!inverted_) {
+      realH = height_ - (h + 1);
+    }
+    uint8* line = output_ + (3 * width_ * realH);
+    for (int w = 0; w < width_; w++) {
+      if (bpp_ >= 24) {
+        line[2] = GetByte();
+        line[1] = GetByte();
+        line[0] = GetByte();
+      } else if (bpp_ == 16) {
+        uint32 val = GetShort();
+        line[0] = ((val & redBits_) >> redShiftRight_) << redShiftLeft_;
+        line[1] = ((val & greenBits_) >> greenShiftRight_) << greenShiftLeft_;
+        line[2] = ((val & blueBits_) >> blueShiftRight_) << blueShiftLeft_;
+      } else if (bpp_ <= 8) {
+        uint8 col;
+        if (bpp_ == 8) {
+          col = GetByte();
+        } else if (bpp_ == 4) {
+          if ((w % 2) == 0) {
+            currVal = GetByte();
+            col = currVal >> 4;
+          } else {
+            col = currVal & 0xf;
+          }
+        } else {
+          if ((w % 8) == 0) {
+            currVal = GetByte();
+          }
+          int bit = w & 7;
+          col = ((currVal >> (7 - bit)) & 1);
+        }
+        int base = col * 3;
+        line[0] = colTab_[base];
+        line[1] = colTab_[base + 1];
+        line[2] = colTab_[base + 2];
+      }
+      line += 3;
+      for (int i = 0; i < pixelPad_; ++i) {
+        GetByte();
+      }
+    }
+    for (int i = 0; i < rowPad_; ++i) {
+      GetByte();
+    }
+  }
+}
+
+int BmpDecoderHelper::GetInt() {
+  uint8 b1 = GetByte();
+  uint8 b2 = GetByte();
+  uint8 b3 = GetByte();
+  uint8 b4 = GetByte();
+  return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
+}
+
+int BmpDecoderHelper::GetShort() {
+  uint8 b1 = GetByte();
+  uint8 b2 = GetByte();
+  return b1 | (b2 << 8);
+}
+
+uint8 BmpDecoderHelper::GetByte() {
+  CHECK(pos_ >= 0 && pos_ <= len_);
+  // We deliberately allow this off-by-one access to cater for BMPs with their
+  // last byte missing.
+  if (pos_ == len_) {
+    return 0;
+  }
+  return data_[pos_++];
+}
+
+int BmpDecoderHelper::CalcShiftRight(uint32 mask) {
+  int ret = 0;
+  while (mask != 0 && !(mask & 1)) {
+    mask >>= 1;
+    ret++;
+  }
+  return ret;
+}
+
+int BmpDecoderHelper::CalcShiftLeft(uint32 mask) {
+  int ret = 0;
+  while (mask != 0 && !(mask & 1)) {
+    mask >>= 1;
+  }
+  while (mask != 0 && !(mask & 0x80)) {
+    mask <<= 1;
+    ret++;
+  }
+  return ret;
+}
+
+}  // namespace image_codec
diff --git a/src/images/bmpdecoderhelper.h b/src/images/bmpdecoderhelper.h
new file mode 100644
index 0000000..07f0ae5
--- /dev/null
+++ b/src/images/bmpdecoderhelper.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2007, 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 IMAGE_CODEC_BMPDECODERHELPER_H__
+#define IMAGE_CODEC_BMPDECODERHELPER_H__
+
+///////////////////////////////////////////////////////////////////////////////
+// this section is my current "glue" between google3 code and android.
+// will be fixed soon
+
+#include "SkTypes.h"
+#include <limits.h>
+#define DISALLOW_EVIL_CONSTRUCTORS(name)
+#define CHECK(predicate)  SkASSERT(predicate)
+typedef uint8_t uint8;
+typedef uint32_t uint32;
+
+template <typename T> class scoped_array {
+private:
+  T* ptr_;
+  scoped_array(scoped_array const&);
+  scoped_array& operator=(const scoped_array&);
+
+public:
+  explicit scoped_array(T* p = 0) : ptr_(p) {}
+  ~scoped_array() {
+    delete[] ptr_;
+  }
+  
+  void reset(T* p = 0) {
+    if (p != ptr_) {
+      delete[] ptr_;
+      ptr_ = p;
+    }
+  }
+  
+  T& operator[](int i) const {
+    return ptr_[i];
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace image_codec {
+
+class BmpDecoderCallback {
+ public:
+  BmpDecoderCallback() { }
+  virtual ~BmpDecoderCallback() {}
+  
+  /**
+   * This is called once for an image. It is passed the width and height and
+   * should return the address of a buffer that is large enough to store
+   * all of the resulting pixels (widht * height * 3 bytes). If it returns NULL,
+   * then the decoder will abort, but return true, as the caller has received
+   * valid dimensions.
+   */
+  virtual uint8* SetSize(int width, int height) = 0;
+   
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(BmpDecoderCallback);
+};
+
+class BmpDecoderHelper {
+ public:
+  BmpDecoderHelper() { }
+  ~BmpDecoderHelper() { }
+  bool DecodeImage(const char* data,
+                   int len,
+                   int max_pixels,
+                   BmpDecoderCallback* callback);
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(BmpDecoderHelper);
+
+  void DoRLEDecode();
+  void DoStandardDecode();
+  void PutPixel(int x, int y, uint8 col);
+
+  int GetInt();
+  int GetShort();
+  uint8 GetByte();
+  int CalcShiftRight(uint32 mask);
+  int CalcShiftLeft(uint32 mask);
+
+  const uint8* data_;
+  int pos_;
+  int len_;
+  int width_;
+  int height_;
+  int bpp_;
+  int pixelPad_;
+  int rowPad_;
+  scoped_array<uint8> colTab_;
+  uint32 redBits_;
+  uint32 greenBits_;
+  uint32 blueBits_;
+  int redShiftRight_;
+  int greenShiftRight_;
+  int blueShiftRight_;
+  int redShiftLeft_;
+  int greenShiftLeft_;
+  int blueShiftLeft_;
+  uint8* output_;
+  bool inverted_;
+};
+  
+} // namespace
+
+#endif
diff --git a/src/images/fpdfemb_ext.h b/src/images/fpdfemb_ext.h
new file mode 100644
index 0000000..d82c4df
--- /dev/null
+++ b/src/images/fpdfemb_ext.h
@@ -0,0 +1,81 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Extended interfaces for JPEG, JPEG2000 and JBIG2 decoders **/
+typedef struct
+{
+	/** Initialize the decoding context, with memory allocator provided by FPDFEMB.
+	   Implementation should return a pointer to the decoding context.
+	*/
+	void*	(*Init)(void* (*alloc_func)(unsigned int), void (*free_func)(void*));
+
+	/** Finish with the decoding. */
+	void	(*Finish)(void* pContext);
+
+	/** Input JPEG encoded data from the source.
+		This function may be called multiple times during decoding progress.
+	*/
+	void	(*Input)(void* pContext, const unsigned char* src_buf, unsigned long src_size);
+
+	/** Read the header information. Return non-zero for success, 0 for failure */
+	int		(*ReadHeader)(void* pContext);
+
+	/** Get info from the decoder, including image width, height and number of components */
+	void	(*GetInfo)(void* pContext, int* width, int* height, int* nComps);
+
+	/** Read one scanline from decoded image */
+	int		(*ReadScanline)(void* pContext, unsigned char* dest_buf);
+
+	/** Get number of available source bytes left in the input stream */
+	unsigned long	(*GetAvailInput)(void* pContext);
+} FPDFEMB_JPEG_DECODER;
+
+void FPDFEMB_SetJpegDecoder(FPDFEMB_JPEG_DECODER* pDecoder);
+
+typedef struct 
+{
+	/** Initialize the decoder with the full source data.
+		Implementation should return a pointer to the context.
+	*/
+	void*	(*Init)(const unsigned char* src_buf, unsigned long src_size);
+
+	/** Destroy the context */
+	void	(*Finish)(void* context);
+
+	/** Get image info from the context, including width, height, number of components
+		in original codestream, and number of components in output image. For some
+		particular type of encoded image, like paletted image, these two numbers of 
+		components may vary.
+	*/
+	void	(*GetInfo)(void* context, unsigned long* width, unsigned long* height, 
+				unsigned long* codestream_nComps, unsigned long* output_nComps);
+
+	/** Do the real data decoding, output to a pre-allocated buffer.
+		bTranslateColor indicates whether the decoder should use JPEG2000 embedded
+		color space info to translate image into sRGB color space.
+		"offsets" array describes the byte order of all components. For example,
+		{2,1,0} means the first components is output to last byte.
+	*/
+	void	(*Decode)(void* context, unsigned char* dest_buf, int pitch, 
+				int bTranslateColor, unsigned char* offsets);
+} FPDFEMB_JPEG2000_DECODER;
+
+void FPDFEMB_SetJpeg2000Decoder(FPDFEMB_JPEG2000_DECODER* pDecoder);
+
+typedef struct
+{
+	/** Do the whole decoding process. Supplied parameters include width, height, source image
+		data and size, global data and size (can be shared among different images), destination
+		buffer and scanline pitch in dest buffer.
+	*/
+	void	(*Decode)(unsigned long width, unsigned long height, const unsigned char* src_buf,
+				unsigned long src_size, const unsigned char* global_buf, unsigned long global_size, 
+				unsigned char* dest_buf, int dest_pitch);
+} FPDFEMB_JBIG2_DECODER;
+
+void FPDFEMB_SetJbig2Decoder(FPDFEMB_JBIG2_DECODER* pDecoder);
+
+#ifdef __cplusplus
+};
+#endif
diff --git a/src/ports/SkFontHost_FONTPATH.cpp b/src/ports/SkFontHost_FONTPATH.cpp
new file mode 100644
index 0000000..3cbccaf
--- /dev/null
+++ b/src/ports/SkFontHost_FONTPATH.cpp
@@ -0,0 +1,415 @@
+/* libs/graphics/ports/SkFontHost_android.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkString.h"
+#include "SkStream.h"
+#include <stdio.h>
+
+/* define this if we can use mmap() to access fonts from the filesystem */
+#define SK_CAN_USE_MMAP 
+
+#ifndef SK_FONTPATH
+    #define SK_FONTPATH "the complete path for a font file"
+#endif
+
+struct FontFaceRec {
+    const char* fFileName;    
+    uint8_t     fFamilyIndex;
+    SkBool8     fBold;
+    SkBool8     fItalic;
+
+    static const FontFaceRec& FindFace(const FontFaceRec rec[], int count, int isBold, int isItalic);
+};
+
+struct FontFamilyRec {
+    const FontFaceRec*  fFaces;
+    int                 fFaceCount;
+};
+
+const FontFaceRec& FontFaceRec::FindFace(const FontFaceRec rec[], int count, int isBold, int isItalic)
+{
+    SkASSERT(count > 0);
+    
+    int i;
+
+    // look for an exact match
+    for (i = 0; i < count; i++) {
+        if (rec[i].fBold == isBold && rec[i].fItalic == isItalic)
+            return rec[i];
+    }
+    // look for a match in the bold field
+    for (i = 0; i < count; i++) {
+        if (rec[i].fBold == isBold)
+            return rec[i];
+    }
+    // look for a normal/regular face
+    for (i = 0; i < count; i++) {
+        if (!rec[i].fBold && !rec[i].fItalic)
+            return rec[i];
+    }
+    // give up
+    return rec[0];
+}
+
+enum {
+    DEFAULT_FAMILY_INDEX,
+    
+    FAMILY_INDEX_COUNT
+};
+
+static const FontFaceRec gDefaultFaces[] = {
+    { SK_FONTPATH, DEFAULT_FAMILY_INDEX, 0,  0 }
+};
+
+// This table must be in the same order as the ..._FAMILY_INDEX enum specifies
+static const FontFamilyRec gFamilies[] = {
+    { gDefaultFaces,   SK_ARRAY_COUNT(gDefaultFaces)  }
+};
+
+#define DEFAULT_FAMILY_INDEX            DEFAULT_FAMILY_INDEX
+#define DEFAULT_FAMILY_FACE_INDEX       0
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+/* map common "web" font names to our font list */
+
+struct FontFamilyMatchRec {
+    const char* fLCName;
+    int         fFamilyIndex;
+};
+
+/*  This is a table of synonyms for collapsing font names
+    down to their pseudo-equivalents (i.e. in terms of fonts
+    we actually have.)
+    Keep this sorted by the first field so we can do a binary search.
+    If this gets big, we could switch to a hash...
+*/
+static const FontFamilyMatchRec gMatches[] = {
+#if 0
+    { "Ahem",               Ahem_FAMILY_INDEX },
+    { "arial",              SANS_FAMILY_INDEX },
+    { "courier",            MONO_FAMILY_INDEX },
+    { "courier new",        MONO_FAMILY_INDEX },
+    { "cursive",            SERIF_FAMILY_INDEX },
+    { "fantasy",            SERIF_FAMILY_INDEX },
+    { "georgia",            SERIF_FAMILY_INDEX },
+    { "goudy",              SERIF_FAMILY_INDEX },
+    { "helvetica",          SANS_FAMILY_INDEX },
+    { "palatino",           SERIF_FAMILY_INDEX },
+    { "tahoma",             SANS_FAMILY_INDEX },
+    { "sans-serif",         SANS_FAMILY_INDEX },
+    { "serif",              SERIF_FAMILY_INDEX },
+    { "times",              SERIF_FAMILY_INDEX },
+    { "times new roman",    SERIF_FAMILY_INDEX },
+    { "verdana",            SANS_FAMILY_INDEX }
+#endif
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTSearch.h"
+
+static bool contains_only_ascii(const char s[])
+{
+    for (;;)
+    {
+        int c = *s++;
+        if (c == 0)
+            break;
+        if ((c >> 7) != 0)
+            return false;
+    }
+    return true;
+}
+
+#define TRACE_FONT_NAME(code)
+//#define TRACE_FONT_NAME(code)   code
+
+const FontFamilyRec* find_family_rec(const char target[])
+{
+    int     index;
+
+    //  If we're asked for a font name that contains non-ascii,
+    //  1) SkStrLCSearch can't handle it
+    //  2) All of our fonts are have ascii names, so...
+
+TRACE_FONT_NAME(printf("----------------- font request <%s>", target);)
+
+    if (contains_only_ascii(target))
+    {
+        // Search for the font by matching the entire name
+        index = SkStrLCSearch(&gMatches[0].fLCName, SK_ARRAY_COUNT(gMatches), target, sizeof(gMatches[0]));
+        if (index >= 0)
+        {
+            TRACE_FONT_NAME(printf(" found %d\n", index);)
+            return &gFamilies[gMatches[index].fFamilyIndex];
+        }
+    }
+
+    // Sniff for key words...
+
+#if 0
+    if (strstr(target, "sans") || strstr(target, "Sans"))
+    {
+        TRACE_FONT_NAME(printf(" found sans\n");)
+        return &gFamilies[SANS_FAMILY_INDEX];
+    }
+    if (strstr(target, "serif") || strstr(target, "Serif"))
+    {
+        TRACE_FONT_NAME(printf(" found serif\n");)
+        return &gFamilies[SERIF_FAMILY_INDEX];
+    }
+    if (strstr(target, "mono") || strstr(target, "Mono"))
+    {
+        TRACE_FONT_NAME(printf(" found mono\n");)
+        return &gFamilies[MONO_FAMILY_INDEX];
+    }
+#endif
+
+    TRACE_FONT_NAME(printf(" use default\n");)
+    // we give up, just give them the default font
+    return &gFamilies[DEFAULT_FAMILY_INDEX];
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static const FontFaceRec* get_default_face()
+{
+    return &gFamilies[DEFAULT_FAMILY_INDEX].fFaces[DEFAULT_FAMILY_FACE_INDEX];
+}
+
+class FontFaceRec_Typeface : public SkTypeface {
+public:
+    FontFaceRec_Typeface(const FontFaceRec& face) : fFace(face)
+    {
+        int style = 0;
+        if (face.fBold)
+            style |= SkTypeface::kBold;
+        if (face.fItalic)
+            style |= SkTypeface::kItalic;
+        this->setStyle((SkTypeface::Style)style);
+    }
+
+    // This global const reference completely identifies the face
+    const FontFaceRec& fFace;
+};
+
+static const FontFaceRec* get_typeface_rec(const SkTypeface* face)
+{
+    const FontFaceRec_Typeface* f = (FontFaceRec_Typeface*)face;
+    return f ? &f->fFace : get_default_face();
+}
+
+static uint32_t ptr2uint32(const void* p)
+{
+    // cast so we avoid warnings on 64bit machines that a ptr difference
+    // which might be 64bits is being trucated from 64 to 32
+    return (uint32_t)((char*)p - (char*)0);
+}
+
+uint32_t SkFontHost::TypefaceHash(const SkTypeface* face)
+{
+    // just use our address as the hash value
+    return ptr2uint32(get_typeface_rec(face));
+}
+
+bool SkFontHost::TypefaceEqual(const SkTypeface* facea, const SkTypeface* faceb)
+{
+    return get_typeface_rec(facea) == get_typeface_rec(faceb);
+}
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style)
+{
+    const FontFamilyRec* family;
+    
+    if (familyFace)
+        family = &gFamilies[((FontFaceRec_Typeface*)familyFace)->fFace.fFamilyIndex];
+    else if (familyName)
+        family = find_family_rec(familyName);
+    else
+        family = &gFamilies[DEFAULT_FAMILY_INDEX];
+
+    const FontFaceRec& face = FontFaceRec::FindFace(family->fFaces, family->fFaceCount,
+                                                    (style & SkTypeface::kBold) != 0,
+                                                    (style & SkTypeface::kItalic) != 0);
+
+    // if we're returning our input parameter, no need to create a new instance
+    if (familyFace != NULL && &((FontFaceRec_Typeface*)familyFace)->fFace == &face)
+    {
+        familyFace->ref();
+        return (SkTypeface*)familyFace;
+    }
+    return SkNEW_ARGS(FontFaceRec_Typeface, (face));
+}
+
+uint32_t SkFontHost::FlattenTypeface(const SkTypeface* tface, void* buffer)
+{
+    const FontFaceRec* face;
+    
+    if (tface)
+        face = &((const FontFaceRec_Typeface*)tface)->fFace;
+    else
+       face = get_default_face();
+
+    size_t  size = sizeof(face);
+    if (buffer)
+        memcpy(buffer, &face, size);
+    return size;
+}
+
+void SkFontHost::GetDescriptorKeyString(const SkDescriptor* desc, SkString* key)
+{
+    key->set(SK_FONTPATH);
+}
+
+#ifdef SK_CAN_USE_MMAP
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+class SkMMAPStream : public SkMemoryStream {
+public:
+    SkMMAPStream(const char filename[]);
+    virtual ~SkMMAPStream();
+
+    virtual void setMemory(const void* data, size_t length);
+private:
+    int     fFildes;
+    void*   fAddr;
+    size_t  fSize;
+    
+    void closeMMap();
+    
+    typedef SkMemoryStream INHERITED;
+};
+
+SkMMAPStream::SkMMAPStream(const char filename[])
+{
+    fFildes = -1;   // initialize to failure case
+
+    int fildes = open(filename, O_RDONLY);
+    if (fildes < 0)
+    {
+        SkDEBUGF(("---- failed to open(%s) for mmap stream error=%d\n", filename, errno));
+        return;
+    }
+
+    off_t size = lseek(fildes, 0, SEEK_END);    // find the file size
+    if (size == -1)
+    {
+        SkDEBUGF(("---- failed to lseek(%s) for mmap stream error=%d\n", filename, errno));
+        close(fildes);
+        return;
+    }
+    (void)lseek(fildes, 0, SEEK_SET);   // restore file offset to beginning
+
+    void* addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fildes, 0);
+    if (MAP_FAILED == addr)
+    {
+        SkDEBUGF(("---- failed to mmap(%s) for mmap stream error=%d\n", filename, errno));
+        close(fildes);
+        return;
+    }
+
+    this->INHERITED::setMemory(addr, size);
+
+    fFildes = fildes;
+    fAddr = addr;
+    fSize = size;
+}
+
+SkMMAPStream::~SkMMAPStream()
+{
+    this->closeMMap();
+}
+
+void SkMMAPStream::setMemory(const void* data, size_t length)
+{
+    this->closeMMap();
+    this->INHERITED::setMemory(data, length);
+}
+
+void SkMMAPStream::closeMMap()
+{
+    if (fFildes >= 0)
+    {
+        munmap(fAddr, fSize);
+        close(fFildes);
+        fFildes = -1;
+    }
+}
+
+#endif
+
+SkStream* SkFontHost::OpenDescriptorStream(const SkDescriptor* desc, const char keyString[])
+{
+    // our key string IS our filename, so we can ignore desc
+    SkStream* strm;
+
+#ifdef SK_CAN_USE_MMAP
+    strm = new SkMMAPStream(keyString);
+    if (strm->getLength() > 0)
+        return strm;
+
+    // strm not valid
+    delete strm;
+    // fall through to FILEStream attempt
+#endif
+
+    strm = new SkFILEStream(keyString);
+    if (strm->getLength() > 0)
+        return strm;
+
+    // strm not valid
+    delete strm;
+    return NULL;
+}
+
+SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec)
+{
+    const FontFaceRec* face = get_default_face();
+
+    SkAutoDescriptor    ad(sizeof(rec) + sizeof(face) + SkDescriptor::ComputeOverhead(2));
+    SkDescriptor*       desc = ad.getDesc();
+
+    desc->init();
+    desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+    desc->addEntry(kTypeface_SkDescriptorTag, sizeof(face), &face);
+    desc->computeChecksum();
+
+    return SkFontHost::CreateScalerContext(desc);
+}
+
+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar)
+{
+    return 0;   // nothing to do (change me if you want to limit the font cache)
+}
+
+int SkFontHost::ComputeGammaFlag(const SkPaint& paint)
+{
+    return 0;
+}
+
+void SkFontHost::GetGammaTables(const uint8_t* tables[2])
+{
+    tables[0] = NULL;   // black gamma (e.g. exp=1.4)
+    tables[1] = NULL;   // white gamma (e.g. exp= 1/1.4)
+}
+
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
new file mode 100644
index 0000000..5855eec
--- /dev/null
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -0,0 +1,829 @@
+/* libs/graphics/ports/SkFontHost_FreeType.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 "SkScalerContext.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkDescriptor.h"
+#include "SkFDot6.h"
+#include "SkFontHost.h"
+#include "SkMask.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkThread.h"
+#include "SkTemplates.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_SIZES_H
+#ifdef   FT_ADVANCES_H
+#include FT_ADVANCES_H
+#endif
+
+//#define ENABLE_GLYPH_SPEW     // for tracing calls
+//#define DUMP_STRIKE_CREATION
+
+#ifdef SK_DEBUG
+    #define SkASSERT_CONTINUE(pred)                                                         \
+        do {                                                                                \
+            if (!(pred))                                                                    \
+                SkDebugf("file %s:%d: assert failed '" #pred "'\n", __FILE__, __LINE__);    \
+        } while (false)
+#else
+    #define SkASSERT_CONTINUE(pred)
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+
+struct SkFaceRec;
+
+static SkMutex      gFTMutex;
+static int          gFTCount;
+static FT_Library   gFTLibrary;
+static SkFaceRec*   gFaceRecHead;
+
+/////////////////////////////////////////////////////////////////////////
+
+class SkScalerContext_FreeType : public SkScalerContext {
+public:
+    SkScalerContext_FreeType(const SkDescriptor* desc);
+    virtual ~SkScalerContext_FreeType();
+
+protected:
+    virtual unsigned generateGlyphCount() const;
+    virtual uint16_t generateCharToGlyph(SkUnichar uni);
+    virtual void generateAdvance(SkGlyph* glyph);
+    virtual void generateMetrics(SkGlyph* glyph);
+    virtual void generateImage(const SkGlyph& glyph);
+    virtual void generatePath(const SkGlyph& glyph, SkPath* path);
+    virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
+                                     SkPaint::FontMetrics* my);
+
+private:
+    SkFaceRec*  fFaceRec;
+    FT_Face     fFace;              // reference to shared face in gFaceRecHead
+    FT_Size     fFTSize;            // our own copy
+    SkFixed     fScaleX, fScaleY;
+    FT_Matrix   fMatrix22;
+    uint32_t    fLoadGlyphFlags;
+
+    FT_Error setupSize();
+};
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+struct SkFaceRec {
+    SkFaceRec*      fNext;
+    FT_Face         fFace;
+    FT_StreamRec    fFTStream;
+    SkStream*       fSkStream;
+    uint32_t        fRefCnt;
+    uint32_t        fFontID;
+
+    SkFaceRec(SkStream* strm, uint32_t fontID);
+    ~SkFaceRec() {
+        SkFontHost::CloseStream(fFontID, fSkStream);
+    }
+};
+
+extern "C" {
+    static unsigned long sk_stream_read(FT_Stream       stream,
+                                        unsigned long   offset,
+                                        unsigned char*  buffer,
+                                        unsigned long   count ) {
+        SkStream* str = (SkStream*)stream->descriptor.pointer;
+
+        if (count) {
+            if (!str->rewind()) {
+                return 0;
+            } else {
+                unsigned long ret;
+                if (offset) {
+                    ret = str->read(NULL, offset);
+                    if (ret != offset) {
+                        return 0;
+                    }
+                }
+                ret = str->read(buffer, count);
+                if (ret != count) {
+                    return 0;
+                }
+                count = ret;
+            }
+        }
+        return count;
+    }
+
+    static void sk_stream_close( FT_Stream stream) {}
+}
+
+SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID)
+        : fSkStream(strm), fFontID(fontID) {
+//    SkDEBUGF(("SkFaceRec: opening %s (%p)\n", key.c_str(), strm));
+
+    bzero(&fFTStream, sizeof(fFTStream));
+    fFTStream.size = fSkStream->getLength();
+    fFTStream.descriptor.pointer = fSkStream;
+    fFTStream.read  = sk_stream_read;
+    fFTStream.close = sk_stream_close;
+}
+
+static SkFaceRec* ref_ft_face(uint32_t fontID) {
+    SkFaceRec* rec = gFaceRecHead;
+    while (rec) {
+        if (rec->fFontID == fontID) {
+            SkASSERT(rec->fFace);
+            rec->fRefCnt += 1;
+            return rec;
+        }
+        rec = rec->fNext;
+    }
+
+    SkStream* strm = SkFontHost::OpenStream(fontID);
+    if (NULL == strm) {
+        SkDEBUGF(("SkFontHost::OpenStream failed opening %x\n", fontID));
+        sk_throw();
+        return 0;
+    }
+
+    // this passes ownership of strm to the rec
+    rec = SkNEW_ARGS(SkFaceRec, (strm, fontID));
+
+    FT_Open_Args    args;
+    memset(&args, 0, sizeof(args));
+    const void* memoryBase = strm->getMemoryBase();
+
+    if (NULL != memoryBase) {
+//printf("mmap(%s)\n", keyString.c_str());
+        args.flags = FT_OPEN_MEMORY;
+        args.memory_base = (const FT_Byte*)memoryBase;
+        args.memory_size = strm->getLength();
+    } else {
+//printf("fopen(%s)\n", keyString.c_str());
+        args.flags = FT_OPEN_STREAM;
+        args.stream = &rec->fFTStream;
+    }
+
+    FT_Error err = FT_Open_Face(gFTLibrary, &args, 0, &rec->fFace);
+
+    if (err) {    // bad filename, try the default font
+        fprintf(stderr, "ERROR: unable to open font '%x'\n", fontID);
+        SkDELETE(rec);
+        sk_throw();
+        return 0;
+    } else {
+        SkASSERT(rec->fFace);
+        //fprintf(stderr, "Opened font '%s'\n", filename.c_str());
+        rec->fNext = gFaceRecHead;
+        gFaceRecHead = rec;
+        rec->fRefCnt = 1;
+        return rec;
+    }
+}
+
+static void unref_ft_face(FT_Face face) {
+    SkFaceRec*  rec = gFaceRecHead;
+    SkFaceRec*  prev = NULL;
+    while (rec) {
+        SkFaceRec* next = rec->fNext;
+        if (rec->fFace == face) {
+            if (--rec->fRefCnt == 0) {
+                if (prev) {
+                    prev->fNext = next;
+                } else {
+                    gFaceRecHead = next;
+                }
+                FT_Done_Face(face);
+                SkDELETE(rec);
+            }
+            return;
+        }
+        prev = rec;
+        rec = next;
+    }
+    SkASSERT("shouldn't get here, face not in list");
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
+        : SkScalerContext(desc), fFTSize(NULL) {
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    FT_Error    err;
+
+    if (gFTCount == 0) {
+        err = FT_Init_FreeType(&gFTLibrary);
+//        SkDEBUGF(("FT_Init_FreeType returned %d\n", err));
+        SkASSERT(err == 0);
+    }
+    ++gFTCount;
+
+    // load the font file
+    fFaceRec = ref_ft_face(fRec.fFontID);
+    fFace = fFaceRec ? fFaceRec->fFace : NULL;
+
+    // compute our factors from the record
+
+    SkMatrix    m;
+
+    fRec.getSingleMatrix(&m);
+
+#ifdef DUMP_STRIKE_CREATION
+    SkString     keyString;
+    SkFontHost::GetDescriptorKeyString(desc, &keyString);
+    printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize),
+           SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX),
+           SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]),
+           SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]),
+           fRec.fHints, fRec.fMaskFormat, keyString.c_str());
+#endif
+
+    //  now compute our scale factors
+    SkScalar    sx = m.getScaleX();
+    SkScalar    sy = m.getScaleY();
+
+    if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) {
+        // sort of give up on hinting
+        sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX()));
+        sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy));
+        sx = sy = SkScalarAve(sx, sy);
+
+        SkScalar inv = SkScalarInvert(sx);
+
+        // flip the skew elements to go from our Y-down system to FreeType's
+        fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv));
+        fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv));
+        fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv));
+        fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv));
+    } else {
+        fMatrix22.xx = fMatrix22.yy = SK_Fixed1;
+        fMatrix22.xy = fMatrix22.yx = 0;
+    }
+
+    fScaleX = SkScalarToFixed(sx);
+    fScaleY = SkScalarToFixed(sy);
+
+    // compute the flags we send to Load_Glyph
+    {
+        uint32_t flags = FT_LOAD_DEFAULT;
+        uint32_t render_flags = FT_LOAD_TARGET_NORMAL;
+
+        // we force autohinting at the moment
+
+        switch (fRec.fHints) {
+        case kNo_Hints:
+            flags |= FT_LOAD_NO_HINTING;
+            break;
+        case kSubpixel_Hints:
+            flags |= FT_LOAD_FORCE_AUTOHINT;
+            render_flags = FT_LOAD_TARGET_LIGHT;
+            break;
+        case kNormal_Hints:
+            flags |= FT_LOAD_FORCE_AUTOHINT;
+#ifdef ANDROID
+            /*  Switch to light hinting (vertical only) to address some chars
+                that behaved poorly with NORMAL. In the future we could consider
+                making this choice exposed at runtime to the caller.
+            */
+            render_flags = FT_LOAD_TARGET_LIGHT;
+#endif
+            break;
+        }
+
+        if (SkMask::kBW_Format == fRec.fMaskFormat)
+            render_flags = FT_LOAD_TARGET_MONO;
+        else if (SkMask::kLCD_Format == fRec.fMaskFormat)
+            render_flags = FT_LOAD_TARGET_LCD;
+
+        fLoadGlyphFlags = flags | render_flags;
+    }
+
+    // now create the FT_Size
+
+    {
+        FT_Error    err;
+
+        err = FT_New_Size(fFace, &fFTSize);
+        if (err != 0) {
+            SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n",
+                        fFaceRec->fFontID, fScaleX, fScaleY, err));
+            fFace = NULL;
+            return;
+        }
+
+        err = FT_Activate_Size(fFTSize);
+        if (err != 0) {
+            SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
+                        fFaceRec->fFontID, fScaleX, fScaleY, err));
+            fFTSize = NULL;
+        }
+
+        err = FT_Set_Char_Size( fFace,
+                                SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY),
+                                72, 72);
+        if (err != 0) {
+            SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
+                        fFaceRec->fFontID, fScaleX, fScaleY, err));
+            fFace = NULL;
+            return;
+        }
+
+        FT_Set_Transform( fFace, &fMatrix22, NULL);
+    }
+}
+
+SkScalerContext_FreeType::~SkScalerContext_FreeType() {
+    if (fFTSize != NULL) {
+        FT_Done_Size(fFTSize);
+    }
+
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    if (fFace != NULL) {
+        unref_ft_face(fFace);
+    }
+    if (--gFTCount == 0) {
+//        SkDEBUGF(("FT_Done_FreeType\n"));
+        FT_Done_FreeType(gFTLibrary);
+        SkDEBUGCODE(gFTLibrary = NULL;)
+    }
+}
+
+/*  We call this before each use of the fFace, since we may be sharing
+    this face with other context (at different sizes).
+*/
+FT_Error SkScalerContext_FreeType::setupSize() {
+    /*  In the off-chance that a font has been removed, we want to error out
+        right away, so call resolve just to be sure.
+
+        TODO: perhaps we can skip this, by walking the global font cache and
+        killing all of the contexts when we know that a given fontID is going
+        away...
+     */
+    if (SkFontHost::ResolveTypeface(fRec.fFontID) == NULL) {
+        return (FT_Error)-1;
+    }
+
+    FT_Error    err = FT_Activate_Size(fFTSize);
+
+    if (err != 0) {
+        SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
+                    fFaceRec->fFontID, fScaleX, fScaleY, err));
+        fFTSize = NULL;
+    } else {
+        // seems we need to reset this every time (not sure why, but without it
+        // I get random italics from some other fFTSize)
+        FT_Set_Transform( fFace, &fMatrix22, NULL);
+    }
+    return err;
+}
+
+unsigned SkScalerContext_FreeType::generateGlyphCount() const {
+    return fFace->num_glyphs;
+}
+
+uint16_t SkScalerContext_FreeType::generateCharToGlyph(SkUnichar uni) {
+    return SkToU16(FT_Get_Char_Index( fFace, uni ));
+}
+
+static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
+    switch (format) {
+        case SkMask::kBW_Format:
+            return FT_PIXEL_MODE_MONO;
+        case SkMask::kLCD_Format:
+            return FT_PIXEL_MODE_LCD;
+        case SkMask::kA8_Format:
+        default:
+            return FT_PIXEL_MODE_GRAY;
+    }
+}
+
+static void set_glyph_metrics_on_error(SkGlyph* glyph) {
+    glyph->fRsbDelta = 0;
+    glyph->fLsbDelta = 0;
+    glyph->fWidth    = 0;
+    glyph->fHeight   = 0;
+    glyph->fTop      = 0;
+    glyph->fLeft     = 0;
+    glyph->fAdvanceX = 0;
+    glyph->fAdvanceY = 0;
+}
+
+void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) {
+#ifdef FT_ADVANCES_H
+   /* unhinted and light hinted text have linearly scaled advances
+    * which are very cheap to compute with some font formats...
+    */
+    {
+        SkAutoMutexAcquire  ac(gFTMutex);
+
+        if (this->setupSize()) {
+            set_glyph_metrics_on_error(glyph);
+            return;
+        }
+
+        FT_Error    error;
+        FT_Fixed    advance;
+
+        error = FT_Get_Advance( fFace, glyph->getGlyphID(fBaseGlyphCount),
+                                fLoadGlyphFlags | FT_ADVANCE_FLAG_FAST_ONLY,
+                                &advance );
+        if (0 == error) {
+            glyph->fRsbDelta = 0;
+            glyph->fLsbDelta = 0;
+            glyph->fAdvanceX = advance;  // advance *2/3; //DEBUG
+            glyph->fAdvanceY = 0;
+            return;
+        }
+    }
+#endif /* FT_ADVANCES_H */
+    /* otherwise, we need to load/hint the glyph, which is slower */
+    this->generateMetrics(glyph);
+    return;
+}
+
+void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    glyph->fRsbDelta = 0;
+    glyph->fLsbDelta = 0;
+
+    FT_Error    err;
+
+    if (this->setupSize()) {
+        goto ERROR;
+    }
+
+    err = FT_Load_Glyph( fFace, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags );
+    if (err != 0) {
+        SkDEBUGF(("SkScalerContext_FreeType::generateMetrics(%x): FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
+                    fFaceRec->fFontID, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, err));
+    ERROR:
+        set_glyph_metrics_on_error(glyph);
+        return;
+    }
+
+    switch ( fFace->glyph->format ) {
+      case FT_GLYPH_FORMAT_OUTLINE:
+        FT_BBox bbox;
+
+        FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
+
+        if (kSubpixel_Hints == fRec.fHints) {
+            int dx = glyph->getSubXFixed() >> 10;
+            int dy = glyph->getSubYFixed() >> 10;
+            // negate dy since freetype-y-goes-up and skia-y-goes-down
+            bbox.xMin += dx;
+            bbox.yMin -= dy;
+            bbox.xMax += dx;
+            bbox.yMax -= dy;
+        }
+
+        bbox.xMin &= ~63;
+        bbox.yMin &= ~63;
+        bbox.xMax  = (bbox.xMax + 63) & ~63;
+        bbox.yMax  = (bbox.yMax + 63) & ~63;
+
+        glyph->fWidth   = SkToU16((bbox.xMax - bbox.xMin) >> 6);
+        glyph->fHeight  = SkToU16((bbox.yMax - bbox.yMin) >> 6);
+        glyph->fTop     = -SkToS16(bbox.yMax >> 6);
+        glyph->fLeft    = SkToS16(bbox.xMin >> 6);
+        break;
+
+      case FT_GLYPH_FORMAT_BITMAP:
+        glyph->fWidth   = SkToU16(fFace->glyph->bitmap.width);
+        glyph->fHeight  = SkToU16(fFace->glyph->bitmap.rows);
+        glyph->fTop     = -SkToS16(fFace->glyph->bitmap_top);
+        glyph->fLeft    = SkToS16(fFace->glyph->bitmap_left);
+        break;
+
+      default:
+        SkASSERT(!"unknown glyph format");
+        goto ERROR;
+    }
+
+    if (kNormal_Hints == fRec.fHints) {
+        glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x);
+        glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y);
+        if (fRec.fFlags & kDevKernText_Flag) {
+            glyph->fRsbDelta = SkToS8(fFace->glyph->rsb_delta);
+            glyph->fLsbDelta = SkToS8(fFace->glyph->lsb_delta);
+        }
+    } else {
+        glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, fFace->glyph->linearHoriAdvance);
+        glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, fFace->glyph->linearHoriAdvance);
+    }
+
+#ifdef ENABLE_GLYPH_SPEW
+    SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY));
+    SkDEBUGF(("Metrics(glyph:%d flags:0x%x) w:%d\n", glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, glyph->fWidth));
+#endif
+}
+
+void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    FT_Error    err;
+
+    if (this->setupSize()) {
+        goto ERROR;
+    }
+
+    err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), fLoadGlyphFlags);
+    if (err != 0) {
+        SkDEBUGF(("SkScalerContext_FreeType::generateImage: FT_Load_Glyph(glyph:%d width:%d height:%d rb:%d flags:%d) returned 0x%x\n",
+                    glyph.getGlyphID(fBaseGlyphCount), glyph.fWidth, glyph.fHeight, glyph.rowBytes(), fLoadGlyphFlags, err));
+    ERROR:
+        memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
+        return;
+    }
+
+    switch ( fFace->glyph->format ) {
+        case FT_GLYPH_FORMAT_OUTLINE: {
+            FT_Outline* outline = &fFace->glyph->outline;
+            FT_BBox     bbox;
+            FT_Bitmap   target;
+
+            int dx = 0, dy = 0;
+            if (kSubpixel_Hints == fRec.fHints) {
+                dx = glyph.getSubXFixed() >> 10;
+                dy = glyph.getSubYFixed() >> 10;
+                // negate dy since freetype-y-goes-up and skia-y-goes-down
+                dy = -dy;
+            }
+            FT_Outline_Get_CBox(outline, &bbox);
+            /*
+                what we really want to do for subpixel is
+                    offset(dx, dy)
+                    compute_bounds
+                    offset(bbox & !63)
+                but that is two calls to offset, so we do the following, which
+                achieves the same thing with only one offset call.
+            */
+            FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
+                                          dy - ((bbox.yMin + dy) & ~63));
+
+            target.width = glyph.fWidth;
+            target.rows = glyph.fHeight;
+            target.pitch = glyph.rowBytes();
+            target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);
+            target.pixel_mode = compute_pixel_mode(
+                                            (SkMask::Format)fRec.fMaskFormat);
+            target.num_grays = 256;
+
+            memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
+            FT_Outline_Get_Bitmap(gFTLibrary, outline, &target);
+        } break;
+
+        case FT_GLYPH_FORMAT_BITMAP: {
+            SkASSERT_CONTINUE(glyph.fWidth == fFace->glyph->bitmap.width);
+            SkASSERT_CONTINUE(glyph.fHeight == fFace->glyph->bitmap.rows);
+            SkASSERT_CONTINUE(glyph.fTop == -fFace->glyph->bitmap_top);
+            SkASSERT_CONTINUE(glyph.fLeft == fFace->glyph->bitmap_left);
+
+            const uint8_t*  src = (const uint8_t*)fFace->glyph->bitmap.buffer;
+            uint8_t*        dst = (uint8_t*)glyph.fImage;
+            unsigned    srcRowBytes = fFace->glyph->bitmap.pitch;
+            unsigned    dstRowBytes = glyph.rowBytes();
+            unsigned    minRowBytes = SkMin32(srcRowBytes, dstRowBytes);
+            unsigned    extraRowBytes = dstRowBytes - minRowBytes;
+
+            for (int y = fFace->glyph->bitmap.rows - 1; y >= 0; --y) {
+                memcpy(dst, src, minRowBytes);
+                memset(dst + minRowBytes, 0, extraRowBytes);
+                src += srcRowBytes;
+                dst += dstRowBytes;
+            }
+        } break;
+
+    default:
+        SkASSERT(!"unknown glyph format");
+        goto ERROR;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ft2sk(x)    SkFixedToScalar((x) << 10)
+
+#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 3
+    #define CONST_PARAM const
+#else   // older freetype doesn't use const here
+    #define CONST_PARAM
+#endif
+
+static int move_proc(CONST_PARAM FT_Vector* pt, void* ctx) {
+    SkPath* path = (SkPath*)ctx;
+    path->close();  // to close the previous contour (if any)
+    path->moveTo(ft2sk(pt->x), -ft2sk(pt->y));
+    return 0;
+}
+
+static int line_proc(CONST_PARAM FT_Vector* pt, void* ctx) {
+    SkPath* path = (SkPath*)ctx;
+    path->lineTo(ft2sk(pt->x), -ft2sk(pt->y));
+    return 0;
+}
+
+static int quad_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1,
+                     void* ctx) {
+    SkPath* path = (SkPath*)ctx;
+    path->quadTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), -ft2sk(pt1->y));
+    return 0;
+}
+
+static int cubic_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1,
+                      CONST_PARAM FT_Vector* pt2, void* ctx) {
+    SkPath* path = (SkPath*)ctx;
+    path->cubicTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x),
+                  -ft2sk(pt1->y), ft2sk(pt2->x), -ft2sk(pt2->y));
+    return 0;
+}
+
+void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph,
+                                            SkPath* path) {
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    SkASSERT(&glyph && path);
+
+    if (this->setupSize()) {
+        path->reset();
+        return;
+    }
+
+    uint32_t flags = fLoadGlyphFlags;
+    flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
+    flags &= ~FT_LOAD_RENDER;   // don't scan convert (we just want the outline)
+
+    FT_Error err = FT_Load_Glyph( fFace, glyph.getGlyphID(fBaseGlyphCount), flags);
+
+    if (err != 0) {
+        SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
+                    glyph.getGlyphID(fBaseGlyphCount), flags, err));
+        path->reset();
+        return;
+    }
+
+    FT_Outline_Funcs    funcs;
+
+    funcs.move_to   = move_proc;
+    funcs.line_to   = line_proc;
+    funcs.conic_to  = quad_proc;
+    funcs.cubic_to  = cubic_proc;
+    funcs.shift     = 0;
+    funcs.delta     = 0;
+
+    err = FT_Outline_Decompose(&fFace->glyph->outline, &funcs, path);
+
+    if (err != 0) {
+        SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
+                    glyph.getGlyphID(fBaseGlyphCount), flags, err));
+        path->reset();
+        return;
+    }
+
+    path->close();
+}
+
+void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
+                                                   SkPaint::FontMetrics* my) {
+    if (NULL == mx && NULL == my) {
+        return;
+    }
+
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    if (this->setupSize()) {
+        if (mx) {
+            bzero(mx, sizeof(SkPaint::FontMetrics));
+        }
+        if (my) {
+            bzero(my, sizeof(SkPaint::FontMetrics));
+        }
+        return;
+    }
+
+    SkPoint pts[5];
+    SkFixed ys[5];
+    FT_Face face = fFace;
+    int     upem = face->units_per_EM;
+    SkFixed scaleY = fScaleY;
+    SkFixed mxy = fMatrix22.xy;
+    SkFixed myy = fMatrix22.yy;
+
+    int leading = face->height - face->ascender + face->descender;
+    if (leading < 0) {
+        leading = 0;
+    }
+
+    ys[0] = -face->bbox.yMax;
+    ys[1] = -face->ascender;
+    ys[2] = -face->descender;
+    ys[3] = -face->bbox.yMin;
+    ys[4] = leading;
+
+    // convert upem-y values into scalar points
+    for (int i = 0; i < 5; i++) {
+        SkFixed y = SkMulDiv(scaleY, ys[i], upem);
+        SkFixed x = SkFixedMul(mxy, y);
+        y = SkFixedMul(myy, y);
+        pts[i].set(SkFixedToScalar(x), SkFixedToScalar(y));
+    }
+
+    if (mx) {
+        mx->fTop = pts[0].fX;
+        mx->fAscent = pts[1].fX;
+        mx->fDescent = pts[2].fX;
+        mx->fBottom = pts[3].fX;
+        mx->fLeading = pts[4].fX;
+    }
+    if (my) {
+        my->fTop = pts[0].fY;
+        my->fAscent = pts[1].fY;
+        my->fDescent = pts[2].fY;
+        my->fBottom = pts[3].fY;
+        my->fLeading = pts[4].fY;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+    return SkNEW_ARGS(SkScalerContext_FreeType, (desc));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  Export this so that other parts of our FonttHost port can make use of our
+    ability to extract the name+style from a stream, using FreeType's api.
+*/
+SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name) {
+    FT_Library  library;
+    if (FT_Init_FreeType(&library)) {
+        name->set(NULL);
+        return SkTypeface::kNormal;
+    }
+
+    FT_Open_Args    args;
+    memset(&args, 0, sizeof(args));
+
+    const void* memoryBase = stream->getMemoryBase();
+    FT_StreamRec    streamRec;
+
+    if (NULL != memoryBase) {
+        args.flags = FT_OPEN_MEMORY;
+        args.memory_base = (const FT_Byte*)memoryBase;
+        args.memory_size = stream->getLength();
+    } else {
+        memset(&streamRec, 0, sizeof(streamRec));
+        streamRec.size = stream->read(NULL, 0);
+        streamRec.descriptor.pointer = stream;
+        streamRec.read  = sk_stream_read;
+        streamRec.close = sk_stream_close;
+
+        args.flags = FT_OPEN_STREAM;
+        args.stream = &streamRec;
+    }
+
+    FT_Face face;
+    if (FT_Open_Face(library, &args, 0, &face)) {
+        FT_Done_FreeType(library);
+        name->set(NULL);
+        return SkTypeface::kNormal;
+    }
+
+    name->set(face->family_name);
+    int style = SkTypeface::kNormal;
+
+    if (face->style_flags & FT_STYLE_FLAG_BOLD) {
+        style |= SkTypeface::kBold;
+    }
+    if (face->style_flags & FT_STYLE_FLAG_ITALIC) {
+        style |= SkTypeface::kItalic;
+    }
+
+    FT_Done_Face(face);
+    FT_Done_FreeType(library);
+    return (SkTypeface::Style)style;
+}
+
diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp
new file mode 100644
index 0000000..0922e7b
--- /dev/null
+++ b/src/ports/SkFontHost_android.cpp
@@ -0,0 +1,637 @@
+/* libs/graphics/ports/SkFontHost_android.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkMMapStream.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkStream.h"
+#include "SkThread.h"
+#include "SkTSearch.h"
+#include <stdio.h>
+
+#define FONT_CACHE_MEMORY_BUDGET    (768 * 1024)
+
+#ifndef SK_FONT_FILE_PREFIX
+    #define SK_FONT_FILE_PREFIX          "/fonts/"
+#endif
+
+SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name);
+
+static void GetFullPathForSysFonts(SkString* full, const char name[])
+{
+    full->set(getenv("ANDROID_ROOT"));
+    full->append(SK_FONT_FILE_PREFIX);
+    full->append(name);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct FamilyRec;
+
+/*  This guy holds a mapping of a name -> family, used for looking up fonts.
+    Since it is stored in a stretchy array that doesn't preserve object
+    semantics, we don't use constructor/destructors, but just have explicit
+    helpers to manage our internal bookkeeping.
+*/
+struct NameFamilyPair {
+    const char* fName;      // we own this
+    FamilyRec*  fFamily;    // we don't own this, we just reference it
+    
+    void construct(const char name[], FamilyRec* family)
+    {
+        fName = strdup(name);
+        fFamily = family;   // we don't own this, so just record the referene
+    }
+    void destruct()
+    {
+        free((char*)fName);
+        // we don't own family, so just ignore our reference
+    }
+};
+
+// we use atomic_inc to grow this for each typeface we create
+static int32_t gUniqueFontID;
+
+// this is the mutex that protects these globals
+static SkMutex gFamilyMutex;
+static FamilyRec* gFamilyHead;
+static SkTDArray<NameFamilyPair> gNameList;
+
+struct FamilyRec {
+    FamilyRec*  fNext;
+    SkTypeface* fFaces[4];
+    
+    FamilyRec()
+    {
+        fNext = gFamilyHead;
+        memset(fFaces, 0, sizeof(fFaces));
+        gFamilyHead = this;
+    }
+};
+
+static SkTypeface* find_best_face(const FamilyRec* family,
+                                  SkTypeface::Style style)
+{
+    SkTypeface* const* faces = family->fFaces;
+
+    if (faces[style] != NULL) { // exact match
+        return faces[style];
+    }
+    // look for a matching bold
+    style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
+    if (faces[style] != NULL) {
+        return faces[style];
+    }
+    // look for the plain
+    if (faces[SkTypeface::kNormal] != NULL) {
+        return faces[SkTypeface::kNormal];
+    }
+    // look for anything
+    for (int i = 0; i < 4; i++) {
+        if (faces[i] != NULL) {
+            return faces[i];
+        }
+    }
+    // should never get here, since the faces list should not be empty
+    SkASSERT(!"faces list is empty");
+    return NULL;
+}
+
+static FamilyRec* find_family(const SkTypeface* member)
+{
+    FamilyRec* curr = gFamilyHead;
+    while (curr != NULL) {
+        for (int i = 0; i < 4; i++) {
+            if (curr->fFaces[i] == member) {
+                return curr;
+            }
+        }
+        curr = curr->fNext;
+    }
+    return NULL;
+}
+
+static SkTypeface* resolve_uniqueID(uint32_t uniqueID)
+{
+    FamilyRec* curr = gFamilyHead;
+    while (curr != NULL) {
+        for (int i = 0; i < 4; i++) {
+            SkTypeface* face = curr->fFaces[i];
+            if (face != NULL && face->uniqueID() == uniqueID) {
+                return face;
+            }
+        }
+        curr = curr->fNext;
+    }
+    return NULL;
+}
+
+/*  Remove reference to this face from its family. If the resulting family
+    is empty (has no faces), return that family, otherwise return NULL
+*/
+static FamilyRec* remove_from_family(const SkTypeface* face)
+{
+    FamilyRec* family = find_family(face);
+    SkASSERT(family->fFaces[face->style()] == face);
+    family->fFaces[face->style()] = NULL;
+    
+    for (int i = 0; i < 4; i++) {
+        if (family->fFaces[i] != NULL) {    // family is non-empty
+            return NULL;
+        }
+    }
+    return family;  // return the empty family
+}
+
+// maybe we should make FamilyRec be doubly-linked
+static void detach_and_delete_family(FamilyRec* family)
+{
+    FamilyRec* curr = gFamilyHead;
+    FamilyRec* prev = NULL;
+
+    while (curr != NULL) {
+        FamilyRec* next = curr->fNext;
+        if (curr == family) {
+            if (prev == NULL) {
+                gFamilyHead = next;
+            } else {
+                prev->fNext = next;
+            }
+            SkDELETE(family);
+            return;
+        }
+        prev = curr;
+        curr = next;
+    }
+    SkASSERT(!"Yikes, couldn't find family in our list to remove/delete");
+}
+
+static SkTypeface* find_typeface(const char name[], SkTypeface::Style style)
+{
+    NameFamilyPair* list = gNameList.begin();
+    int             count = gNameList.count();
+    
+    int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
+
+    if (index >= 0) {
+        return find_best_face(list[index].fFamily, style);
+    }
+    return NULL;
+}
+
+static SkTypeface* find_typeface(const SkTypeface* familyMember,
+                                 SkTypeface::Style style)
+{
+    const FamilyRec* family = find_family(familyMember);
+    return family ? find_best_face(family, style) : NULL;
+}
+
+static void add_name(const char name[], FamilyRec* family)
+{
+    SkAutoAsciiToLC tolc(name);
+    name = tolc.lc();
+
+    NameFamilyPair* list = gNameList.begin();
+    int             count = gNameList.count();
+    
+    int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
+
+    if (index < 0) {
+        list = gNameList.insert(~index);
+        list->construct(name, family);
+    }
+}
+
+static void remove_from_names(FamilyRec* emptyFamily)
+{
+#ifdef SK_DEBUG
+    for (int i = 0; i < 4; i++) {
+        SkASSERT(emptyFamily->fFaces[i] == NULL);
+    }
+#endif
+
+    SkTDArray<NameFamilyPair>& list = gNameList;
+    
+    // must go backwards when removing
+    for (int i = list.count() - 1; i >= 0; --i) {
+        NameFamilyPair* pair = &list[i];
+        if (pair->fFamily == emptyFamily) {
+            pair->destruct();
+            list.remove(i);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class FamilyTypeface : public SkTypeface {
+public:
+    FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember)
+        : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1)
+    {
+        fIsSysFont = sysFont;
+        
+        SkAutoMutexAcquire  ac(gFamilyMutex);
+        
+        FamilyRec* rec = NULL;
+        if (familyMember) {
+            rec = find_family(familyMember);
+            SkASSERT(rec);
+        } else {
+            rec = SkNEW(FamilyRec);
+        }
+        rec->fFaces[style] = this;
+    }
+    
+    virtual ~FamilyTypeface()
+    {
+        SkAutoMutexAcquire  ac(gFamilyMutex);
+        
+        // remove us from our family. If the family is now empty, we return
+        // that and then remove that family from the name list
+        FamilyRec* family = remove_from_family(this);
+        if (NULL != family) {
+            remove_from_names(family);
+            detach_and_delete_family(family);
+        }
+    }
+    
+    bool isSysFont() const { return fIsSysFont; }
+    
+    virtual SkStream* openStream() = 0;
+    virtual void closeStream(SkStream*) = 0;
+    virtual const char* getUniqueString() const = 0;
+    
+private:
+    bool    fIsSysFont;
+    
+    typedef SkTypeface INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamTypeface : public FamilyTypeface {
+public:
+    StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember,
+                   SkStream* stream)
+        : INHERITED(style, sysFont, familyMember)
+    {
+        fStream = stream;
+    }
+    virtual ~StreamTypeface()
+    {
+        SkDELETE(fStream);
+    }
+    
+    // overrides
+    virtual SkStream* openStream() { return fStream; }
+    virtual void closeStream(SkStream*) {}
+    virtual const char* getUniqueString() const { return NULL; }
+
+private:
+    SkStream* fStream;
+    
+    typedef FamilyTypeface INHERITED;
+};
+
+class FileTypeface : public FamilyTypeface {
+public:
+    FileTypeface(Style style, bool sysFont, SkTypeface* familyMember,
+                 const char path[])
+    : INHERITED(style, sysFont, familyMember)
+    {
+        SkString fullpath;
+        
+        if (sysFont) {
+            GetFullPathForSysFonts(&fullpath, path);
+            path = fullpath.c_str();
+        }
+        fPath.set(path);
+    }
+    
+    // overrides
+    virtual SkStream* openStream()
+    {
+        SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str()));
+
+        // check for failure
+        if (stream->getLength() <= 0) {
+            SkDELETE(stream);
+            // maybe MMAP isn't supported. try FILE
+            stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str()));
+            if (stream->getLength() <= 0) {
+                SkDELETE(stream);
+                stream = NULL;
+            }
+        }
+        return stream;
+    }
+    virtual void closeStream(SkStream* stream)
+    {
+        SkDELETE(stream);
+    }
+    virtual const char* getUniqueString() const {
+        const char* str = strrchr(fPath.c_str(), '/');
+        if (str) {
+            str += 1;   // skip the '/'
+        }
+        return str;
+    }
+
+private:
+    SkString fPath;
+    
+    typedef FamilyTypeface INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool get_name_and_style(const char path[], SkString* name,
+                               SkTypeface::Style* style)
+{
+    SkString        fullpath;
+    GetFullPathForSysFonts(&fullpath, path);
+
+    SkMMAPStream stream(fullpath.c_str());
+    if (stream.getLength() > 0) {
+        *style = find_name_and_style(&stream, name);
+        return true;
+    }
+    else {
+        SkFILEStream stream(fullpath.c_str());
+        if (stream.getLength() > 0) {
+            *style = find_name_and_style(&stream, name);
+            return true;
+        }
+    }
+
+    SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str());
+    return false;
+}
+
+struct FontInitRec {
+    const char*         fFileName;
+    const char* const*  fNames;     // null-terminated list
+};
+
+static const char* gSansNames[] = {
+    "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL
+};
+
+static const char* gSerifNames[] = {
+    "serif", "times", "times new roman", "palatino", "georgia", "baskerville",
+    "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL
+};
+
+static const char* gMonoNames[] = {
+    "monospace", "courier", "courier new", "monaco", NULL
+};
+
+static const char* gFBNames[] = { NULL };
+
+/*  Fonts must be grouped by family, with the first font in a family having the
+    list of names (even if that list is empty), and the following members having
+    null for the list. The names list must be NULL-terminated
+*/
+static const FontInitRec gSystemFonts[] = {
+    { "DroidSans.ttf",              gSansNames  },
+    { "DroidSans-Bold.ttf",         NULL        },
+    { "DroidSerif-Regular.ttf",     gSerifNames },
+    { "DroidSerif-Bold.ttf",        NULL        },
+    { "DroidSerif-Italic.ttf",      NULL        },
+    { "DroidSerif-BoldItalic.ttf",  NULL        },
+    { "DroidSansMono.ttf",          gMonoNames  },
+#ifdef NO_FALLBACK_FONT
+    { "DroidSans.ttf",              gFBNames    }
+#else
+    { "DroidSansFallback.ttf",      gFBNames    }
+#endif
+};
+
+#define DEFAULT_NAMES   gSansNames
+
+// these globals are assigned (once) by load_system_fonts()
+static SkTypeface* gFallBackTypeface;
+static FamilyRec* gDefaultFamily;
+static SkTypeface* gDefaultNormal;
+
+static void load_system_fonts()
+{
+    // check if we've already be called
+    if (NULL != gDefaultNormal) {
+        return;
+    }
+
+    const FontInitRec* rec = gSystemFonts;
+    SkTypeface* firstInFamily = NULL;
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
+        // if we're the first in a new family, clear firstInFamily
+        if (rec[i].fNames != NULL) {
+            firstInFamily = NULL;
+        }
+        
+        SkString name;
+        SkTypeface::Style style;
+        
+        if (!get_name_and_style(rec[i].fFileName, &name, &style)) {
+            SkDebugf("------ can't load <%s> as a font\n", rec[i].fFileName);
+            continue;
+        }
+
+        SkTypeface* tf = SkNEW_ARGS(FileTypeface,
+                                    (style,
+                                     true,  // system-font (cannot delete)
+                                     firstInFamily, // what family to join
+                                     rec[i].fFileName) // filename
+                                    );
+
+        if (rec[i].fNames != NULL) {
+            firstInFamily = tf;
+            const char* const* names = rec[i].fNames;
+
+            // record the fallback if this is it
+            if (names == gFBNames) {
+                gFallBackTypeface = tf;
+            }
+            // record the default family if this is it
+            if (names == DEFAULT_NAMES) {
+                gDefaultFamily = find_family(tf);
+            }
+            // add the names to map to this family
+            FamilyRec* family = find_family(tf);
+            while (*names) {
+                add_name(*names, family);
+                names += 1;
+            }
+        }
+        
+    }
+
+    // do this after all fonts are loaded. This is our default font, and it
+    // acts as a sentinel so we only execute load_system_fonts() once
+    gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+    const char* name = ((FamilyTypeface*)face)->getUniqueString();
+
+    stream->write8((uint8_t)face->getStyle());
+
+    if (NULL == name || 0 == *name) {
+        stream->writePackedUInt(0);
+//        SkDebugf("--- fonthost serialize null\n");
+    } else {
+        uint32_t len = strlen(name);
+        stream->writePackedUInt(len);
+        stream->write(name, len);
+//      SkDebugf("--- fonthost serialize <%s> %d\n", name, face->getStyle());
+    }
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    load_system_fonts();
+
+    int style = stream->readU8();
+
+    int len = stream->readPackedUInt();
+    if (len > 0) {
+        SkString str;
+        str.resize(len);
+        stream->read(str.writable_str(), len);
+        
+        const FontInitRec* rec = gSystemFonts;
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
+            if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
+                // backup until we hit the fNames
+                for (int j = i; j >= 0; --j) {
+                    if (rec[j].fNames != NULL) {
+                        return SkFontHost::FindTypeface(NULL, rec[j].fNames[0],
+                                                    (SkTypeface::Style)style);
+                    }
+                }
+            }
+        }
+    }
+    return SkFontHost::FindTypeface(NULL, NULL, (SkTypeface::Style)style);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace,
+                                     const char familyName[],
+                                     SkTypeface::Style style)
+{
+    load_system_fonts();
+
+    SkAutoMutexAcquire  ac(gFamilyMutex);
+    
+    // clip to legal style bits
+    style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
+    
+    SkTypeface* tf = NULL;
+
+    if (NULL != familyFace) {
+        tf = find_typeface(familyFace, style);
+    } else if (NULL != familyName) {
+//        SkDebugf("======= familyName <%s>\n", familyName);
+        tf = find_typeface(familyName, style);
+    }
+
+    if (NULL == tf) {
+        tf = find_best_face(gDefaultFamily, style);
+    }
+
+    return tf;
+}
+
+SkTypeface* SkFontHost::ResolveTypeface(uint32_t fontID)
+{
+    SkAutoMutexAcquire  ac(gFamilyMutex);
+    
+    return resolve_uniqueID(fontID);
+}
+
+SkStream* SkFontHost::OpenStream(uint32_t fontID)
+{
+    
+    FamilyTypeface* tf = (FamilyTypeface*)SkFontHost::ResolveTypeface(fontID);
+    SkStream* stream = tf ? tf->openStream() : NULL;
+
+    if (NULL == stream || stream->getLength() == 0) {
+        delete stream;
+        stream = NULL;
+    }
+    return stream;
+}
+
+void SkFontHost::CloseStream(uint32_t fontID, SkStream* stream)
+{
+    FamilyTypeface* tf = (FamilyTypeface*)SkFontHost::ResolveTypeface(fontID);
+    if (NULL != tf) {
+        tf->closeStream(stream);
+    }
+}
+
+SkScalerContext* SkFontHost::CreateFallbackScalerContext(
+                                                const SkScalerContext::Rec& rec)
+{
+    load_system_fonts();
+
+    SkAutoDescriptor    ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1));
+    SkDescriptor*       desc = ad.getDesc();
+    
+    desc->init();
+    SkScalerContext::Rec* newRec =
+        (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
+                                              sizeof(rec), &rec);
+    newRec->fFontID = gFallBackTypeface->uniqueID();
+    desc->computeChecksum();
+    
+    return SkFontHost::CreateScalerContext(desc);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::CreateTypeface(SkStream* stream)
+{
+    if (NULL == stream || stream->getLength() <= 0) {
+        SkDELETE(stream);
+        return NULL;
+    }
+    
+    SkString name;
+    SkTypeface::Style style = find_name_and_style(stream, &name);
+
+    return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar)
+{
+    if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
+        return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
+    else
+        return 0;   // nothing to do
+}
+
diff --git a/src/ports/SkFontHost_ascender.cpp b/src/ports/SkFontHost_ascender.cpp
new file mode 100644
index 0000000..2148850
--- /dev/null
+++ b/src/ports/SkFontHost_ascender.cpp
@@ -0,0 +1,211 @@
+#include "SkScalerContext.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkDescriptor.h"
+#include "SkFDot6.h"
+#include "SkFontHost.h"
+#include "SkMask.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkThread.h"
+#include "SkTemplates.h"
+
+#include <acaapi.h>
+
+//////////////////////////////////////////////////////////////////////////
+
+#include "SkMMapStream.h"
+
+class SkScalerContext_Ascender : public SkScalerContext {
+public:
+    SkScalerContext_Ascender(const SkDescriptor* desc);
+    virtual ~SkScalerContext_Ascender();
+
+protected:
+    virtual unsigned generateGlyphCount() const;
+    virtual uint16_t generateCharToGlyph(SkUnichar uni);
+    virtual void generateMetrics(SkGlyph* glyph);
+    virtual void generateImage(const SkGlyph& glyph);
+    virtual void generatePath(const SkGlyph& glyph, SkPath* path);
+    virtual void generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my);
+
+private:
+    aca_FontHandle  fHandle;
+    void*   fWorkspace;
+    void*   fGlyphWorkspace;
+    SkStream*   fFontStream;
+    SkStream*   fHintStream;
+};
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+SkScalerContext_Ascender::SkScalerContext_Ascender(const SkDescriptor* desc)
+    : SkScalerContext(desc)
+{
+    int size = aca_Get_FontHandleRec_Size();
+    fHandle = (aca_FontHandle)sk_malloc_throw(size);
+    
+    // get the pointer to the font
+    
+    fFontStream = new SkMMAPStream("/UcsGB2312-Hei-H.FDL");
+    fHintStream = new SkMMAPStream("/genv6-23.bin");
+    
+    void* hints = sk_malloc_throw(fHintStream->getLength());
+    memcpy(hints, fHintStream->getMemoryBase(), fHintStream->getLength());
+    
+    aca_Create_Font_Handle(fHandle,
+                           (void*)fFontStream->getMemoryBase(), fFontStream->getLength(),
+                           "fred",
+                           hints, fHintStream->getLength());
+    
+    // compute our factors from the record
+
+    SkMatrix    m;
+
+    fRec.getSingleMatrix(&m);
+
+    //  now compute our scale factors
+    SkScalar    sx = m.getScaleX();
+    SkScalar    sy = m.getScaleY();
+    
+    int ppemX = SkScalarRound(sx);
+    int ppemY = SkScalarRound(sy);
+    
+    size = aca_Find_Font_Memory_Required(fHandle, ppemX, ppemY);
+    size *= 8;  // Jeff suggests this :)
+    fWorkspace = sk_malloc_throw(size);
+    aca_Set_Font_Memory(fHandle, (uint8_t*)fWorkspace, size);
+
+    aca_GlyphAttribsRec rec;
+    
+    memset(&rec, 0, sizeof(rec));
+    rec.xSize = ppemX;
+    rec.ySize = ppemY;
+    rec.doAdjust = true;
+    rec.doExceptions = true;
+    rec.doGlyphHints = true;
+    rec.doInterpolate = true;
+    rec.grayMode = 2;
+    aca_Set_Font_Attributes(fHandle, &rec, &size);
+    
+    fGlyphWorkspace = sk_malloc_throw(size);
+    aca_Set_Glyph_Memory(fHandle, fGlyphWorkspace);
+}
+
+SkScalerContext_Ascender::~SkScalerContext_Ascender()
+{
+    delete fHintStream;
+    delete fFontStream;
+    sk_free(fGlyphWorkspace);
+    sk_free(fWorkspace);
+    sk_free(fHandle);
+}
+
+unsigned SkScalerContext_Ascender::generateGlyphCount() const
+{
+    return 1000;
+}
+
+uint16_t SkScalerContext_Ascender::generateCharToGlyph(SkUnichar uni)
+{
+    return (uint16_t)(uni & 0xFFFF);
+}
+
+void SkScalerContext_Ascender::generateMetrics(SkGlyph* glyph)
+{
+    glyph->fRsbDelta = 0;
+    glyph->fLsbDelta = 0;
+    
+    aca_GlyphImageRec   rec;
+    aca_Vector          topLeft;
+    
+    int adv = aca_Get_Adv_Width(fHandle, glyph->getGlyphID());
+    if (aca_GLYPH_NOT_PRESENT == adv)
+        goto ERROR;
+
+    aca_Rasterize(glyph->getGlyphID(), fHandle, &rec, &topLeft);
+
+    if (false)  // error
+    {
+ERROR:
+        glyph->fWidth   = 0;
+        glyph->fHeight  = 0;
+        glyph->fTop     = 0;
+        glyph->fLeft    = 0;
+        glyph->fAdvanceX = 0;
+        glyph->fAdvanceY = 0;
+        return;
+    }
+    
+    glyph->fWidth = rec.width;
+    glyph->fHeight = rec.rows;
+    glyph->fRowBytes = rec.width;
+    glyph->fTop = -topLeft.y;
+    glyph->fLeft = topLeft.x;
+    glyph->fAdvanceX = SkIntToFixed(adv);
+    glyph->fAdvanceY = SkIntToFixed(0);
+}
+
+void SkScalerContext_Ascender::generateImage(const SkGlyph& glyph)
+{
+    aca_GlyphImageRec   rec;
+    aca_Vector          topLeft;
+    
+    aca_Rasterize(glyph.getGlyphID(), fHandle, &rec, &topLeft);
+    
+    const uint8_t* src = (const uint8_t*)rec.buffer;
+    uint8_t* dst = (uint8_t*)glyph.fImage;
+    int height = glyph.fHeight;
+    
+    src += rec.y0 * rec.pitch + rec.x0;
+    while (--height >= 0)
+    {
+        memcpy(dst, src, glyph.fWidth);
+        src += rec.pitch;
+        dst += glyph.fRowBytes;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+void SkScalerContext_Ascender::generatePath(const SkGlyph& glyph, SkPath* path)
+{
+    SkRect r;
+    
+    r.set(0, 0, SkIntToScalar(4), SkIntToScalar(4));
+    path->reset();
+    path->addRect(r);
+}
+
+void SkScalerContext_Ascender::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my)
+{
+    if (NULL == mx && NULL == my)
+        return;
+
+    if (mx)
+    {
+        mx->fTop = SkIntToScalar(-16);
+        mx->fAscent = SkIntToScalar(-16);
+        mx->fDescent = SkIntToScalar(4);
+        mx->fBottom = SkIntToScalar(4);
+        mx->fLeading = 0;
+    }
+    if (my)
+    {
+        my->fTop = SkIntToScalar(-16);
+        my->fAscent = SkIntToScalar(-16);
+        my->fDescent = SkIntToScalar(4);
+        my->fBottom = SkIntToScalar(4);
+        my->fLeading = 0;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc)
+{
+    return SkNEW_ARGS(SkScalerContext_Ascender, (desc));
+}
+
diff --git a/src/ports/SkFontHost_gamma.cpp b/src/ports/SkFontHost_gamma.cpp
new file mode 100644
index 0000000..0b95bce
--- /dev/null
+++ b/src/ports/SkFontHost_gamma.cpp
@@ -0,0 +1,118 @@
+#include "SkFontHost.h"
+#include <math.h>
+
+// define this to use pre-compiled tables for gamma. This is slightly faster,
+// and doesn't create any RW global memory, but means we cannot change the
+// gamma at runtime.
+#define USE_PREDEFINED_GAMMA_TABLES
+
+#ifndef USE_PREDEFINED_GAMMA_TABLES
+    // define this if you want to spew out the "C" code for the tables, given
+    // the current values for SK_BLACK_GAMMA and SK_WHITE_GAMMA.
+    #define DUMP_GAMMA_TABLESx
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef USE_PREDEFINED_GAMMA_TABLES
+
+#include "sk_predefined_gamma.h"
+
+#else   // use writable globals for gamma tables
+
+static bool gGammaIsBuilt;
+static uint8_t gBlackGamma[256], gWhiteGamma[256];
+
+#define SK_BLACK_GAMMA     (1.4f)
+#define SK_WHITE_GAMMA     (1/1.4f)
+
+static void build_power_table(uint8_t table[], float ee)
+{
+    //    printf("------ build_power_table %g\n", ee);
+    for (int i = 0; i < 256; i++)
+    {
+        float x = i / 255.f;
+        //   printf(" %d %g", i, x);
+        x = powf(x, ee);
+        //   printf(" %g", x);
+        int xx = SkScalarRound(SkFloatToScalar(x * 255));
+        //   printf(" %d\n", xx);
+        table[i] = SkToU8(xx);
+    }
+}
+
+#ifdef DUMP_GAMMA_TABLES
+
+#include "SkString.h"
+
+static void dump_a_table(const char name[], const uint8_t table[],
+                         float gamma) {
+    SkDebugf("\n");
+    SkDebugf("\/\/ Gamma table for %g\n", gamma);
+    SkDebugf("static const uint8_t %s[] = {\n", name);
+    for (int y = 0; y < 16; y++) {
+        SkString line, tmp;
+        for (int x = 0; x < 16; x++) {
+            tmp.printf("0x%02X, ", *table++);
+            line.append(tmp);
+        }
+        SkDebugf("    %s\n", line.c_str());
+    }
+    SkDebugf("};\n");
+}
+
+#endif
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::GetGammaTables(const uint8_t* tables[2])
+{
+#ifndef USE_PREDEFINED_GAMMA_TABLES
+    if (!gGammaIsBuilt)
+    {
+        build_power_table(gBlackGamma, SK_BLACK_GAMMA);
+        build_power_table(gWhiteGamma, SK_WHITE_GAMMA);
+        gGammaIsBuilt = true;
+
+#ifdef DUMP_GAMMA_TABLES
+        dump_a_table("gBlackGamma", gBlackGamma, SK_BLACK_GAMMA);
+        dump_a_table("gWhiteGamma", gWhiteGamma, SK_WHITE_GAMMA);
+#endif
+    }
+#endif
+    tables[0] = gBlackGamma;
+    tables[1] = gWhiteGamma;
+}
+
+// If the luminance is <= this value, then apply the black gamma table
+#define BLACK_GAMMA_THRESHOLD   0x40
+
+// If the luminance is >= this value, then apply the white gamma table
+#define WHITE_GAMMA_THRESHOLD   0xC0
+
+int SkFontHost::ComputeGammaFlag(const SkPaint& paint)
+{
+    if (paint.getShader() == NULL)
+    {
+        SkColor c = paint.getColor();
+        int r = SkColorGetR(c);
+        int g = SkColorGetG(c);
+        int b = SkColorGetB(c);
+        int luminance = (r * 2 + g * 5 + b) >> 3;
+        
+        if (luminance <= BLACK_GAMMA_THRESHOLD)
+        {
+        //    printf("------ black gamma for [%d %d %d]\n", r, g, b);
+            return SkScalerContext::kGammaForBlack_Flag;
+        }
+        if (luminance >= WHITE_GAMMA_THRESHOLD)
+        {
+        //    printf("------ white gamma for [%d %d %d]\n", r, g, b);
+            return SkScalerContext::kGammaForWhite_Flag;
+        }
+    }
+    return 0;
+}
+
diff --git a/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp
new file mode 100644
index 0000000..f75718d
--- /dev/null
+++ b/src/ports/SkFontHost_linux.cpp
@@ -0,0 +1,604 @@
+/* libs/graphics/ports/SkFontHost_android.cpp
+ **
+ ** Copyright 2006, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License"); 
+ ** you may not use this file except in compliance with the License. 
+ ** You may obtain a copy of the License at 
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0 
+ **
+ ** Unless required by applicable law or agreed to in writing, software 
+ ** distributed under the License is distributed on an "AS IS" BASIS, 
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ ** See the License for the specific language governing permissions and 
+ ** limitations under the License.
+ */
+
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkMMapStream.h"
+#include "SkOSFile.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkStream.h"
+#include "SkThread.h"
+#include "SkTSearch.h"
+#include <stdio.h>
+
+#define FONT_CACHE_MEMORY_BUDGET    (1 * 1024 * 1024)
+
+#ifndef SK_FONT_FILE_PREFIX
+    #define SK_FONT_FILE_PREFIX      "/usr/share/fonts/truetype/msttcorefonts/"
+#endif
+
+SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name);
+
+static void GetFullPathForSysFonts(SkString* full, const char name[])
+{
+    full->append(SK_FONT_FILE_PREFIX);
+    full->append(name);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct FamilyRec;
+
+/*  This guy holds a mapping of a name -> family, used for looking up fonts.
+ Since it is stored in a stretchy array that doesn't preserve object
+ semantics, we don't use constructor/destructors, but just have explicit
+ helpers to manage our internal bookkeeping.
+ */
+struct NameFamilyPair {
+    const char* fName;      // we own this
+    FamilyRec*  fFamily;    // we don't own this, we just reference it
+    
+    void construct(const char name[], FamilyRec* family)
+    {
+        fName = strdup(name);
+        fFamily = family;   // we don't own this, so just record the referene
+    }
+    void destruct()
+    {
+        free((char*)fName);
+        // we don't own family, so just ignore our reference
+    }
+};
+
+// we use atomic_inc to grow this for each typeface we create
+static int32_t gUniqueFontID;
+
+// this is the mutex that protects these globals
+static SkMutex gFamilyMutex;
+static FamilyRec* gFamilyHead;
+static SkTDArray<NameFamilyPair> gNameList;
+
+struct FamilyRec {
+    FamilyRec*  fNext;
+    SkTypeface* fFaces[4];
+    
+    FamilyRec()
+    {
+        fNext = gFamilyHead;
+        memset(fFaces, 0, sizeof(fFaces));
+        gFamilyHead = this;
+    }
+};
+
+static SkTypeface* find_best_face(const FamilyRec* family,
+                                  SkTypeface::Style style)
+{
+    SkTypeface* const* faces = family->fFaces;
+    
+    if (faces[style] != NULL) { // exact match
+        return faces[style];
+    }
+    // look for a matching bold
+    style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
+    if (faces[style] != NULL) {
+        return faces[style];
+    }
+    // look for the plain
+    if (faces[SkTypeface::kNormal] != NULL) {
+        return faces[SkTypeface::kNormal];
+    }
+    // look for anything
+    for (int i = 0; i < 4; i++) {
+        if (faces[i] != NULL) {
+            return faces[i];
+        }
+    }
+    // should never get here, since the faces list should not be empty
+    SkASSERT(!"faces list is empty");
+    return NULL;
+}
+
+static FamilyRec* find_family(const SkTypeface* member)
+{
+    FamilyRec* curr = gFamilyHead;
+    while (curr != NULL) {
+        for (int i = 0; i < 4; i++) {
+            if (curr->fFaces[i] == member) {
+                return curr;
+            }
+        }
+        curr = curr->fNext;
+    }
+    return NULL;
+}
+
+static SkTypeface* resolve_uniqueID(uint32_t uniqueID)
+{
+    FamilyRec* curr = gFamilyHead;
+    while (curr != NULL) {
+        for (int i = 0; i < 4; i++) {
+            SkTypeface* face = curr->fFaces[i];
+            if (face != NULL && face->uniqueID() == uniqueID) {
+                return face;
+            }
+        }
+        curr = curr->fNext;
+    }
+    return NULL;
+}
+
+/*  Remove reference to this face from its family. If the resulting family
+ is empty (has no faces), return that family, otherwise return NULL
+ */
+static FamilyRec* remove_from_family(const SkTypeface* face)
+{
+    FamilyRec* family = find_family(face);
+    SkASSERT(family->fFaces[face->style()] == face);
+    family->fFaces[face->style()] = NULL;
+    
+    for (int i = 0; i < 4; i++) {
+        if (family->fFaces[i] != NULL) {    // family is non-empty
+            return NULL;
+        }
+    }
+    return family;  // return the empty family
+}
+
+// maybe we should make FamilyRec be doubly-linked
+static void detach_and_delete_family(FamilyRec* family)
+{
+    FamilyRec* curr = gFamilyHead;
+    FamilyRec* prev = NULL;
+    
+    while (curr != NULL) {
+        FamilyRec* next = curr->fNext;
+        if (curr == family) {
+            if (prev == NULL) {
+                gFamilyHead = next;
+            } else {
+                prev->fNext = next;
+            }
+            SkDELETE(family);
+            return;
+        }
+        prev = curr;
+        curr = next;
+    }
+    SkASSERT(!"Yikes, couldn't find family in our list to remove/delete");
+}
+
+static FamilyRec* find_familyrec(const char name[]) {
+    const NameFamilyPair* list = gNameList.begin();    
+    int index = SkStrLCSearch(&list[0].fName, gNameList.count(), name,
+                              sizeof(list[0]));
+    return index >= 0 ? list[index].fFamily : NULL;
+}
+
+static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
+    FamilyRec* rec = find_familyrec(name);
+    return rec ? find_best_face(rec, style) : NULL;
+}
+
+static SkTypeface* find_typeface(const SkTypeface* familyMember,
+                                 SkTypeface::Style style)
+{
+    const FamilyRec* family = find_family(familyMember);
+    return family ? find_best_face(family, style) : NULL;
+}
+
+static void add_name(const char name[], FamilyRec* family)
+{
+    SkAutoAsciiToLC tolc(name);
+    name = tolc.lc();
+    
+    NameFamilyPair* list = gNameList.begin();
+    int             count = gNameList.count();
+    
+    int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
+    
+    if (index < 0) {
+        list = gNameList.insert(~index);
+        list->construct(name, family);
+    }
+}
+
+static void remove_from_names(FamilyRec* emptyFamily)
+{
+#ifdef SK_DEBUG
+    for (int i = 0; i < 4; i++) {
+        SkASSERT(emptyFamily->fFaces[i] == NULL);
+    }
+#endif
+    
+    SkTDArray<NameFamilyPair>& list = gNameList;
+    
+    // must go backwards when removing
+    for (int i = list.count() - 1; i >= 0; --i) {
+        NameFamilyPair* pair = &list[i];
+        if (pair->fFamily == emptyFamily) {
+            pair->destruct();
+            list.remove(i);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class FamilyTypeface : public SkTypeface {
+public:
+    FamilyTypeface(Style style, bool sysFont, FamilyRec* family)
+    : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1)
+    {
+        fIsSysFont = sysFont;
+        
+        SkAutoMutexAcquire  ac(gFamilyMutex);
+        
+        if (NULL == family) {
+            family = SkNEW(FamilyRec);
+        }
+        family->fFaces[style] = this;
+        fFamilyRec = family;    // just record it so we can return it if asked
+    }
+    
+    virtual ~FamilyTypeface()
+    {
+        SkAutoMutexAcquire  ac(gFamilyMutex);
+        
+        // remove us from our family. If the family is now empty, we return
+        // that and then remove that family from the name list
+        FamilyRec* family = remove_from_family(this);
+        if (NULL != family) {
+            remove_from_names(family);
+            detach_and_delete_family(family);
+        }
+    }
+    
+    bool isSysFont() const { return fIsSysFont; }
+    FamilyRec* getFamily() const { return fFamilyRec; }
+    
+    virtual SkStream* openStream() = 0;
+    virtual void closeStream(SkStream*) = 0;
+    virtual const char* getUniqueString() const = 0;
+    
+private:
+    FamilyRec*  fFamilyRec; // we don't own this, just point to it
+    bool        fIsSysFont;
+    
+    typedef SkTypeface INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamTypeface : public FamilyTypeface {
+public:
+    StreamTypeface(Style style, bool sysFont, FamilyRec* family,
+                   SkStream* stream)
+    : INHERITED(style, sysFont, family)
+    {
+        fStream = stream;
+    }
+    virtual ~StreamTypeface()
+    {
+        SkDELETE(fStream);
+    }
+    
+    // overrides
+    virtual SkStream* openStream() { return fStream; }
+    virtual void closeStream(SkStream*) {}
+    virtual const char* getUniqueString() const { return NULL; }
+    
+private:
+    SkStream* fStream;
+    
+    typedef FamilyTypeface INHERITED;
+};
+
+class FileTypeface : public FamilyTypeface {
+public:
+    FileTypeface(Style style, bool sysFont, FamilyRec* family,
+                 const char path[])
+        : INHERITED(style, sysFont, family) {
+        fPath.set(path);
+    }
+    
+    // overrides
+    virtual SkStream* openStream()
+    {
+        SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str()));
+        
+        // check for failure
+        if (stream->getLength() <= 0) {
+            SkDELETE(stream);
+            // maybe MMAP isn't supported. try FILE
+            stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str()));
+            if (stream->getLength() <= 0) {
+                SkDELETE(stream);
+                stream = NULL;
+            }
+        }
+        return stream;
+    }
+    virtual void closeStream(SkStream* stream)
+    {
+        SkDELETE(stream);
+    }
+    virtual const char* getUniqueString() const {
+        const char* str = strrchr(fPath.c_str(), '/');
+        if (str) {
+            str += 1;   // skip the '/'
+        }
+        return str;
+    }
+    
+private:
+    SkString fPath;
+    
+    typedef FamilyTypeface INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool get_name_and_style(const char path[], SkString* name,
+                               SkTypeface::Style* style)
+{    
+    SkMMAPStream stream(path);
+    if (stream.getLength() > 0) {
+        *style = find_name_and_style(&stream, name);
+        return true;
+    }
+    else {
+        SkFILEStream stream(path);
+        if (stream.getLength() > 0) {
+            *style = find_name_and_style(&stream, name);
+            return true;
+        }
+    }
+    
+    SkDebugf("---- failed to open <%s> as a font\n", path);
+    return false;
+}
+
+// these globals are assigned (once) by load_system_fonts()
+static SkTypeface* gFallBackTypeface;
+static FamilyRec* gDefaultFamily;
+static SkTypeface* gDefaultNormal;
+
+static void load_system_fonts()
+{
+    // check if we've already be called
+    if (NULL != gDefaultNormal) {
+        return;
+    }
+    
+    SkOSFile::Iter    iter(SK_FONT_FILE_PREFIX, ".ttf");
+    SkString          name;
+
+    while (iter.next(&name, false)) {
+        SkString filename;
+        GetFullPathForSysFonts(&filename, name.c_str());
+//    while (filename.size() == 0) { filename.set("/usr/share/fonts/truetype/msttcorefonts/Arial.ttf");
+
+        SkString realname;
+        SkTypeface::Style style;
+        
+        if (!get_name_and_style(filename.c_str(), &realname, &style)) {
+            SkDebugf("------ can't load <%s> as a font\n", filename.c_str());
+            continue;
+        }
+        
+//        SkDebugf("font: <%s> %d <%s>\n", realname.c_str(), style, filename.c_str());
+        
+        FamilyRec* family = find_familyrec(realname.c_str());
+        // this constructor puts us into the global gFamilyHead llist
+        FamilyTypeface* tf = SkNEW_ARGS(FileTypeface,
+                                        (style,
+                                         true,  // system-font (cannot delete)
+                                         family, // what family to join
+                                         filename.c_str()) // filename
+                                        );
+
+        if (NULL == family) {
+            add_name(realname.c_str(), tf->getFamily());
+        }
+    }
+    
+    // do this after all fonts are loaded. This is our default font, and it
+    // acts as a sentinel so we only execute load_system_fonts() once
+    static const char* gDefaultNames[] = {
+        "Arial", "Verdana", "Times New Roman", NULL
+    };
+    const char** names = gDefaultNames;
+    while (*names) {
+        SkTypeface* tf = find_typeface(*names++, SkTypeface::kNormal);
+        if (tf) {
+            gDefaultNormal = tf;
+            break;
+        }
+    }
+    // check if we found *something*
+    if (NULL == gDefaultNormal) {
+        if (NULL == gFamilyHead) {
+            sk_throw();
+        }
+        for (int i = 0; i < 4; i++) {
+            if ((gDefaultNormal = gFamilyHead->fFaces[i]) != NULL) {
+                break;
+            }
+        }
+    }
+    if (NULL == gDefaultNormal) {
+        sk_throw();
+    }
+    gFallBackTypeface = gDefaultNormal;    
+    gDefaultFamily = find_family(gDefaultNormal);
+
+//    SkDebugf("---- default %p head %p family %p\n", gDefaultNormal, gFamilyHead, gDefaultFamily);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+#if 0
+    const char* name = ((FamilyTypeface*)face)->getUniqueString();
+    
+    stream->write8((uint8_t)face->getStyle());
+    
+    if (NULL == name || 0 == *name) {
+        stream->writePackedUInt(0);
+        //        SkDebugf("--- fonthost serialize null\n");
+    } else {
+        uint32_t len = strlen(name);
+        stream->writePackedUInt(len);
+        stream->write(name, len);
+        //      SkDebugf("--- fonthost serialize <%s> %d\n", name, face->getStyle());
+    }
+#endif
+    sk_throw();
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+#if 0
+    load_system_fonts();
+    
+    int style = stream->readU8();
+    
+    int len = stream->readPackedUInt();
+    if (len > 0) {
+        SkString str;
+        str.resize(len);
+        stream->read(str.writable_str(), len);
+        
+        const FontInitRec* rec = gSystemFonts;
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
+            if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
+                // backup until we hit the fNames
+                for (int j = i; j >= 0; --j) {
+                    if (rec[j].fNames != NULL) {
+                        return SkFontHost::FindTypeface(NULL, rec[j].fNames[0],
+                                                        (SkTypeface::Style)style);
+                    }
+                }
+            }
+        }
+    }
+    return SkFontHost::FindTypeface(NULL, NULL, (SkTypeface::Style)style);
+#endif
+    sk_throw();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace,
+                                     const char familyName[],
+                                     SkTypeface::Style style)
+{
+    load_system_fonts();
+    
+    SkAutoMutexAcquire  ac(gFamilyMutex);
+    
+    // clip to legal style bits
+    style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
+    
+    SkTypeface* tf = NULL;
+    
+    if (NULL != familyFace) {
+        tf = find_typeface(familyFace, style);
+    } else if (NULL != familyName) {
+        //        SkDebugf("======= familyName <%s>\n", familyName);
+        tf = find_typeface(familyName, style);
+    }
+    
+    if (NULL == tf) {
+        tf = find_best_face(gDefaultFamily, style);
+    }
+    
+    return tf;
+}
+
+SkTypeface* SkFontHost::ResolveTypeface(uint32_t fontID)
+{
+    SkAutoMutexAcquire  ac(gFamilyMutex);
+    
+    return resolve_uniqueID(fontID);
+}
+
+SkStream* SkFontHost::OpenStream(uint32_t fontID)
+{
+    
+    FamilyTypeface* tf = (FamilyTypeface*)SkFontHost::ResolveTypeface(fontID);
+    SkStream* stream = tf ? tf->openStream() : NULL;
+    
+    if (NULL == stream || stream->getLength() == 0) {
+        delete stream;
+        stream = NULL;
+    }
+    return stream;
+}
+
+void SkFontHost::CloseStream(uint32_t fontID, SkStream* stream)
+{
+    FamilyTypeface* tf = (FamilyTypeface*)SkFontHost::ResolveTypeface(fontID);
+    if (NULL != tf) {
+        tf->closeStream(stream);
+    }
+}
+
+SkScalerContext* SkFontHost::CreateFallbackScalerContext(
+                                                         const SkScalerContext::Rec& rec)
+{
+    load_system_fonts();
+    
+    SkAutoDescriptor    ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1));
+    SkDescriptor*       desc = ad.getDesc();
+    
+    desc->init();
+    SkScalerContext::Rec* newRec =
+    (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
+                                          sizeof(rec), &rec);
+    newRec->fFontID = gFallBackTypeface->uniqueID();
+    desc->computeChecksum();
+    
+    return SkFontHost::CreateScalerContext(desc);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::CreateTypeface(SkStream* stream)
+{
+    if (NULL == stream || stream->getLength() <= 0) {
+        SkDELETE(stream);
+        return NULL;
+    }
+    
+    SkString name;
+    SkTypeface::Style style = find_name_and_style(stream, &name);
+    
+    return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar)
+{
+    if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
+        return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
+    else
+        return 0;   // nothing to do
+}
+
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
new file mode 100755
index 0000000..af44cf5
--- /dev/null
+++ b/src/ports/SkFontHost_mac.cpp
@@ -0,0 +1,563 @@
+/*

+ ** Copyright 2006, The Android Open Source Project
+ **

+ ** Licensed under the Apache License, Version 2.0 (the "License"); 

+ ** you may not use this file except in compliance with the License. 

+ ** You may obtain a copy of the License at 

+ **

+ **     http://www.apache.org/licenses/LICENSE-2.0 

+ **

+ ** Unless required by applicable law or agreed to in writing, software 

+ ** distributed under the License is distributed on an "AS IS" BASIS, 

+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

+ ** See the License for the specific language governing permissions and 

+ ** limitations under the License.

+*/

+

+#include "SkFontHost.h"

+#include "SkDescriptor.h"

+

+// Give 1MB font cache budget

+#define FONT_CACHE_MEMORY_BUDGET    (1024 * 1024)

+

+const char* gDefaultfont = "Arial"; // hard code for now

+static SkMutex      gFTMutex;

+

+inline SkPoint F32PtToSkPoint(const Float32Point p)

+{

+    SkPoint sp = { SkFloatToFixed(p.x),SkFloatToFixed(p.y) };

+    return sp;

+}

+

+static inline uint32_t _rotl(uint32_t v, uint32_t r) 

+{

+    return (v << r | v >> (32 - r));

+}

+

+// This will generate a unique ID based on the fontname + fontstyle

+// and also used by upper layer

+uint32_t FontFaceChecksum(const char *name,SkTypeface::Style style)

+{

+    if (!name) return style;

+    

+    char* q = (char*)name;

+

+    // From "Performance in Practice of String Hashing Functions"

+    // Ramakrishna & Zobel

+    const uint32_t L = 5;

+    const uint32_t R = 2;

+     

+    uint32_t h = 0x12345678;

+    while (*q) {

+        uint32_t ql = tolower(*q);

+        h ^= ((h << L) + (h >> R) + ql);

+        q ++;

+    }

+

+    // add style

+    h = _rotl(h, 3) ^ style;

+

+    return h;

+}

+

+#pragma mark -

+struct SkFaceRec {

+    SkFaceRec*      fNext;

+    uint32_t        fRefCnt;

+    ATSUFontID      fFontID;

+    ATSUStyle       fStyle;

+

+    SkFaceRec() : fFontID(0), fRefCnt(0), fStyle(NULL) {};

+

+    ~SkFaceRec() {

+        if (fStyle) {

+            ::ATSUDisposeStyle(fStyle);

+            fStyle = NULL;

+        }

+    }

+

+    uint32_t ref() {

+        return ++fRefCnt;

+    }

+};

+

+// Font Face list

+static SkFaceRec*   gFaceRecHead = NULL;

+

+static SkFaceRec* find_ft_face(const ATSUFontID fontID) {

+    SkFaceRec* rec = gFaceRecHead;

+    while (rec) {

+        if (rec->fFontID == fontID) {

+            return rec;

+        }

+        rec = rec->fNext;

+    }

+

+    return NULL;

+}

+

+static SkFaceRec* insert_ft_face(const ATSUFontID afontID, const ATSUStyle atsuStyle) {

+    SkFaceRec* rec = find_ft_face(afontID);

+    if (rec) {

+        return rec;  // found?

+    }

+

+    rec = SkNEW(SkFaceRec);

+    rec->fFontID = afontID;

+    rec->fStyle = atsuStyle;

+    rec->fNext = gFaceRecHead;

+    gFaceRecHead = rec;

+

+    return rec;

+}

+

+static void unref_ft_face(const ATSUFontID fontID) {

+

+    SkFaceRec* rec = gFaceRecHead;

+    SkFaceRec* prev = NULL;

+    while (rec) {

+        SkFaceRec* next = rec->fNext;

+        if (rec->fFontID == fontID) {

+            if (--rec->fRefCnt == 0) {

+                if (prev)

+                    prev->fNext = next;

+                else

+                    gFaceRecHead = next;

+

+                SkDELETE(rec);

+            }

+            return;

+        }

+        prev = rec;

+        rec = next;

+    }

+    SkASSERT("shouldn't get here, face not in list");

+}

+

+#pragma mark -

+

+// have to do this because SkTypeface::SkTypeface() is protected

+class SkTypeface_Mac : public SkTypeface {

+public:

+    SkTypeface_Mac(SkTypeface::Style style, uint32_t id) : SkTypeface(style, id) {}

+

+    ~SkTypeface_Mac() {}

+};

+

+#pragma mark -

+

+static SkTypeface* CreateTypeface_(const char *name, const SkTypeface::Style style) {

+

+    OSStatus err;

+    ATSUStyle atsuStyle;

+    ::ATSUCreateStyle(&atsuStyle);

+    if (name != NULL) {

+        static const ATSUAttributeTag fontTag = kATSUFontTag;

+        static const ByteCount fontTagSize =  sizeof(ATSUFontID);

+    

+        ATSUFontID fontID = 0;

+#if 1

+        err = ::ATSUFindFontFromName(

+                name,strlen(name),kFontNoNameCode,   /*  instead of regular, kFontFamilyName returns bold and/or italic sometimes, but why this works?? */

+                kFontMacintoshPlatform,kFontNoScriptCode,kFontNoLanguageCode,&fontID);

+#else    

+        CFStringRef cfontName = CFStringCreateWithCString(NULL, name, kCFStringEncodingASCII); 

+        ATSFontRef fontRef = ::ATSFontFindFromName(cfontName,kATSOptionFlagsDefault);

+        fontID = ::FMGetFontFromATSFontRef(fontRef);

+        CFRelease(cfontName);

+#endif    

+        if (0 != fontID) {

+            const ATSUAttributeValuePtr values[] = { &fontID };

+            err = ::ATSUSetAttributes(atsuStyle,1,&fontTag,&fontTagSize,values);

+        }

+        else {

+        }

+    }

+    if (style != SkTypeface::kNormal) {

+        Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);

+        Boolean fontBold = ((style & SkTypeface::kBold) != 0);

+        const ATSUAttributeTag tags[2] =        { kATSUQDBoldfaceTag, kATSUQDItalicTag };

+        const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };

+        const ByteCount sizes[2] =                { sizeof(Boolean), sizeof(Boolean) };

+        err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);

+    }

+

+    uint32_t cs = FontFaceChecksum(name,style);

+    SkTypeface_Mac* ptypeface = new SkTypeface_Mac(style,cs);

+

+    if (NULL == ptypeface) {

+        SkASSERT(false);

+        return NULL;

+    }

+

+    SkFaceRec* rec = insert_ft_face(cs, atsuStyle);

+    SkASSERT(rec);

+

+    return ptypeface;

+}

+

+static SkTypeface* CreateTypeface_(const SkFaceRec* rec, const SkTypeface::Style style) {

+

+    OSStatus err;

+    ATSUStyle atsuStyle;

+    err = ::ATSUCreateAndCopyStyle(rec->fStyle, &atsuStyle);

+

+    Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);

+    Boolean fontBold = ((style & SkTypeface::kBold) != 0);

+    const ATSUAttributeTag tags[2] =        { kATSUQDBoldfaceTag, kATSUQDItalicTag };

+    const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };

+    const ByteCount sizes[2] =                { sizeof(Boolean), sizeof(Boolean) };

+    err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);

+

+    // get old font id and name

+    ATSUFontID fontID = 0;

+    ByteCount actual = 0;

+    err = ::ATSUGetAttribute(rec->fStyle,kATSUFontTag,sizeof(ATSUFontID),&fontID,&actual);

+

+    ByteCount actualLength = 0;

+    char *fontname = NULL;

+    err = ::ATSUFindFontName(fontID , kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,

+        kFontNoLanguageCode , 0 , NULL , &actualLength , NULL );

+    if ( err == noErr)

+    {

+        actualLength += 1 ;

+        fontname = (char*)malloc( actualLength );

+        err = ::ATSUFindFontName(fontID, kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,

+            kFontNoLanguageCode, actualLength, fontname , NULL, NULL);

+    }

+

+    SkTypeface_Mac* ptypeface = NULL;

+    if (fontname == NULL) {

+        ptypeface = new SkTypeface_Mac(style,rec->fFontID);

+        return ptypeface;

+    }

+    else {

+        uint32_t cs = FontFaceChecksum(fontname,style);

+        ptypeface = new SkTypeface_Mac(style, cs);

+

+        if (NULL == ptypeface) {

+            SkASSERT(false);

+            return NULL;

+        }

+

+        free(fontname);

+

+        insert_ft_face(cs,atsuStyle);

+    }

+    return ptypeface;

+}

+

+#pragma mark -

+

+class SkScalerContext_Mac : public SkScalerContext {

+public:

+    SkScalerContext_Mac(const SkDescriptor* desc);

+    virtual ~SkScalerContext_Mac();

+

+protected:

+    virtual unsigned generateGlyphCount() const;

+    virtual uint16_t generateCharToGlyph(SkUnichar uni);

+    virtual void generateAdvance(SkGlyph* glyph);

+    virtual void generateMetrics(SkGlyph* glyph);

+    virtual void generateImage(const SkGlyph& glyph);

+    virtual void generatePath(const SkGlyph& glyph, SkPath* path);

+    virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent);

+    virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);

+    virtual SkDeviceContext getDC() { return NULL; } // not implemented on Mac

+

+private:

+    ATSUTextLayout  fLayout;

+    ATSUStyle       fStyle;

+    

+    static OSStatus MoveTo(const Float32Point *pt, void *cb);

+    static OSStatus Line(const Float32Point *pt, void *cb);

+    static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);

+    static OSStatus Close(void *cb);

+};

+

+SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)

+    : SkScalerContext(desc), fLayout(0), fStyle(0)

+{

+    SkAutoMutexAcquire  ac(gFTMutex);

+    OSStatus err;

+    

+    SkFaceRec* rec = find_ft_face(fRec.fFontID);

+    if (rec) {

+        rec->ref();

+        err = ::ATSUCreateAndCopyStyle(rec->fStyle, &fStyle);

+    }

+    else {

+        SkASSERT(false);

+        // create a default

+        err = ::ATSUCreateStyle(&fStyle);

+    }

+

+    uint32_t size = SkFixedFloor(fRec.fTextSize);

+    Fixed fixedSize = IntToFixed(size);

+    static const ATSUAttributeTag sizeTag = kATSUSizeTag;

+    static const ByteCount sizeTagSize = sizeof(Fixed);

+    const ATSUAttributeValuePtr values[] = { &fixedSize };

+    err = ::ATSUSetAttributes(fStyle,1,&sizeTag,&sizeTagSize,values);

+

+    err = ::ATSUCreateTextLayout(&fLayout);

+}

+

+SkScalerContext_Mac::~SkScalerContext_Mac()

+{

+    unref_ft_face(fRec.fFontID);

+

+    ::ATSUDisposeTextLayout(fLayout);

+    ::ATSUDisposeStyle(fStyle);

+}

+

+unsigned SkScalerContext_Mac::generateGlyphCount() const

+{

+    return 0xFFFF;

+}

+

+uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)

+{

+    SkAutoMutexAcquire  ac(gFTMutex);

+    

+    OSStatus err;

+    UniChar achar = uni;

+    err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);

+    err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);

+        

+    ATSLayoutRecord *layoutPtr;

+    ItemCount count;

+    ATSGlyphRef glyph;

+    

+    err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);

+    glyph = layoutPtr->glyphID;

+    ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);

+    return glyph;

+}

+

+void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {

+    this->generateMetrics(glyph);

+}

+

+void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph)

+{

+    GlyphID glyphID = glyph->fID;

+    ATSGlyphScreenMetrics metrics= { 0 };

+    

+    glyph->fRsbDelta = 0;

+    glyph->fLsbDelta = 0;

+    

+    OSStatus err = ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);

+    if (err == noErr) {

+        glyph->fAdvanceX = SkFloatToFixed(metrics.deviceAdvance.x);

+        glyph->fAdvanceY = SkFloatToFixed(metrics.deviceAdvance.y);

+        //glyph->fWidth = metrics.width;

+        //glyph->fHeight = metrics.height;

+        glyph->fWidth = metrics.width + ceil(metrics.sideBearing.x - metrics.otherSideBearing.x);

+        glyph->fHeight = metrics.height + ceil(metrics.sideBearing.y - metrics.otherSideBearing.y) + 1;

+

+        glyph->fTop = -metrics.topLeft.y;

+        glyph->fLeft = metrics.topLeft.x;

+    }

+}

+

+void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {

+    //SkASSERT(false);

+    if (mx)

+        memset(mx, 0, sizeof(SkPaint::FontMetrics));

+    if (my)

+        memset(my, 0, sizeof(SkPaint::FontMetrics));

+    return;

+}

+

+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)

+{

+    SkAutoMutexAcquire  ac(gFTMutex);

+    

+    GlyphID glyphID = glyph.fID;

+    ATSGlyphScreenMetrics metrics= { 0 };

+    

+    SkASSERT(fLayout);

+    OSStatus err = ::ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);

+

+//    uint32_t w = metrics.width;

+//    uint32_t h = metrics.height;

+//    uint32_t pitch = (w + 3) & ~0x3;

+//    if (pitch != glyph.rowBytes()) {

+//        SkASSERT(false); // it's different from previously cacluated in generateMetrics(), so the size of glyph.fImage buffer is incorrect!

+//    }

+    

+    CGColorSpaceRef greyColorSpace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);

+    CGContextRef contextRef = ::CGBitmapContextCreate((uint8_t*)glyph.fImage, glyph.fWidth, glyph.fHeight, 8, glyph.rowBytes(), greyColorSpace, kCGImageAlphaNone);

+    if (!contextRef) {

+        SkASSERT(false);

+        return;

+    }

+        

+    ::CGContextSetFillColorSpace(contextRef, greyColorSpace); 

+    ::CGContextSetStrokeColorSpace(contextRef, greyColorSpace); 

+                

+    ::CGContextSetGrayFillColor(contextRef, 0.0, 1.0);

+    ::CGContextFillRect(contextRef, ::CGRectMake(0, 0, glyph.fWidth, glyph.fHeight));

+                

+    ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);

+    ::CGContextSetGrayStrokeColor(contextRef, 1.0, 1.0);

+    ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);

+    

+    ATSUAttributeTag tag = kATSUCGContextTag;

+    ByteCount size = sizeof(CGContextRef);

+    ATSUAttributeValuePtr value = &contextRef;

+    err = ::ATSUSetLayoutControls(fLayout,1,&tag,&size,&value);

+    err = ::ATSUDrawText(fLayout,kATSUFromTextBeginning,kATSUToTextEnd,FloatToFixed(-metrics.topLeft.x),FloatToFixed(glyph.fHeight-metrics.topLeft.y));

+    ::CGContextRelease(contextRef);

+}

+

+void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)

+{

+    SkAutoMutexAcquire  ac(gFTMutex);

+    OSStatus err,result;

+    

+    err = ::ATSUGlyphGetCubicPaths(

+            fStyle,glyph.fID,

+            &SkScalerContext_Mac::MoveTo,

+            &SkScalerContext_Mac::Line,

+            &SkScalerContext_Mac::Curve,

+            &SkScalerContext_Mac::Close,

+            path,&result);

+    SkASSERT(err == noErr);

+}

+

+void SkScalerContext_Mac::generateLineHeight(SkPoint* ascent, SkPoint* descent)

+{

+    ATSUTextMeasurement     textAscent, textDescent;

+    ByteCount actual = 0;

+    OSStatus err = ::ATSUGetAttribute(fStyle,kATSULineAscentTag,sizeof(ATSUTextMeasurement),&textAscent,&actual);

+    ascent->set(0,textAscent);

+    err = ::ATSUGetAttribute(fStyle,kATSULineDescentTag,sizeof(ATSUTextMeasurement),&textDescent,&actual);

+    descent->set(0,textDescent);

+}

+

+OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)

+{

+    reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));

+    return noErr;

+}

+

+OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)

+{

+    reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));

+    return noErr;

+}

+

+OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb)

+{

+    reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),F32PtToSkPoint(*pt2),F32PtToSkPoint(*pt3));

+    return noErr;

+}

+

+OSStatus SkScalerContext_Mac::Close(void *cb)

+{

+    reinterpret_cast<SkPath*>(cb)->close();

+    return noErr;

+}

+

+#pragma mark -

+

+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {

+    SkASSERT(!"SkFontHost::Serialize unimplemented");

+}

+

+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {

+    SkASSERT(!"SkFontHost::Deserialize unimplemented");

+    return NULL;

+}

+

+SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) {

+

+    //Should not be used on Mac, keep linker happy

+    SkASSERT(false);

+    return CreateTypeface_(gDefaultfont,SkTypeface::kNormal);

+}

+

+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc)

+{

+    return new SkScalerContext_Mac(desc);

+}

+

+SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec)

+{

+    SkAutoDescriptor    ad(sizeof(rec) + sizeof(gDefaultfont) + SkDescriptor::ComputeOverhead(2));

+    SkDescriptor*       desc = ad.getDesc();

+

+    desc->init();

+    SkScalerContext::Rec* newRec =

+        (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);

+

+    CreateTypeface_(gDefaultfont,SkTypeface::kNormal);

+    newRec->fFontID = FontFaceChecksum(gDefaultfont,SkTypeface::kNormal);

+    desc->computeChecksum();

+

+    return SkFontHost::CreateScalerContext(desc);

+}

+

+

+    /** Return the closest matching typeface given either an existing family

+        (specified by a typeface in that family) or by a familyName, and a

+        requested style.

+        1) If familyFace is null, use famillyName.

+        2) If famillyName is null, use familyFace.

+        3) If both are null, return the default font that best matches style

+        This MUST not return NULL.

+    */

+

+SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) {

+    

+    SkAutoMutexAcquire  ac(gFTMutex);

+    

+    // clip to legal style bits

+    style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);

+    

+    SkTypeface* tf = NULL;

+    

+    if (NULL == familyFace && NULL == familyName) {

+        tf = CreateTypeface_(gDefaultfont,style);

+    }

+    else {        

+        if (NULL != familyFace) {

+            uint32_t id = familyFace->uniqueID();

+            SkFaceRec* rec = find_ft_face(id);

+            if (!rec) {

+                SkASSERT(false);

+                tf = CreateTypeface_(gDefaultfont,style);

+            }

+            else {

+                tf = CreateTypeface_(rec,style);

+            }

+        }

+        else {

+            tf = CreateTypeface_(familyName,style);

+        }

+    }

+

+    if (NULL == tf) {

+        tf = CreateTypeface_(gDefaultfont,style);

+    }

+    return tf;

+

+}

+

+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {

+    if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)

+        return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;

+    else

+        return 0;   // nothing to do

+}

+

+int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {

+    return 0;

+}

+

+void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {

+    tables[0] = NULL;   // black gamma (e.g. exp=1.4)

+    tables[1] = NULL;   // white gamma (e.g. exp= 1/1.4)

+}

+

diff --git a/src/ports/SkFontHost_none.cpp b/src/ports/SkFontHost_none.cpp
new file mode 100644
index 0000000..b45cf16
--- /dev/null
+++ b/src/ports/SkFontHost_none.cpp
@@ -0,0 +1,82 @@
+/* Copyright 2006-2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include "SkFontHost.h"
+
+SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace,
+                                     const char famillyName[],
+                                     SkTypeface::Style style) {
+    SkASSERT(!"SkFontHost::FindTypeface unimplemented");
+    return NULL;
+}
+
+SkTypeface* SkFontHost::ResolveTypeface(uint32_t uniqueID) {
+    SkASSERT(!"SkFontHost::ResolveTypeface unimplemented");
+    return NULL;
+}
+
+SkStream* SkFontHost::OpenStream(uint32_t uniqueID) {
+    SkASSERT(!"SkFontHost::OpenStream unimplemented");
+    return NULL;
+}
+
+void SkFontHost::CloseStream(uint32_t uniqueID, SkStream*) {
+    SkASSERT(!"SkFontHost::CloseStream unimplemented");
+}
+
+SkTypeface* SkFontHost::CreateTypeface(SkStream*) {
+    SkASSERT(!"SkFontHost::CreateTypeface unimplemented");
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+    SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    SkASSERT(!"SkFontHost::Deserialize unimplemented");
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+    SkASSERT(!"SkFontHost::CreateScalarContext unimplemented");
+    return NULL;
+}
+
+SkScalerContext* SkFontHost::CreateFallbackScalerContext(
+                                                const SkScalerContext::Rec&) {
+    SkASSERT(!"SkFontHost::CreateFallbackScalerContext unimplemented");
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
+    return 0;   // nothing to do (change me if you want to limit the font cache)
+}
+
+int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
+    return 0;
+}
+
+void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
+    tables[0] = NULL;   // black gamma (e.g. exp=1.4)
+    tables[1] = NULL;   // white gamma (e.g. exp= 1/1.4)
+}
+
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
new file mode 100644
index 0000000..c2ee51e
--- /dev/null
+++ b/src/ports/SkFontHost_win.cpp
@@ -0,0 +1,601 @@
+/*

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

+//#include "SkStream.h"

+

+#include "SkFontHost.h"

+#include "SkDescriptor.h"

+#include "SkThread.h"

+

+#ifdef WIN32

+#include "windows.h"

+#include "tchar.h"

+

+// client3d has to undefine this for now

+#define CAN_USE_LOGFONT_NAME

+

+static SkMutex gFTMutex;

+

+// these globals are loaded (once) by get_default_font()

+static LOGFONT gDefaultFont = {0};

+

+static const uint16_t BUFFERSIZE = (16384 - 32);

+static uint8_t glyphbuf[BUFFERSIZE]; 

+

+// Give 1MB font cache budget

+#define FONT_CACHE_MEMORY_BUDGET    (1024 * 1024)

+

+static inline FIXED SkFixedToFIXED(SkFixed x) {

+    return *(FIXED*)(&x);

+}

+

+static inline FIXED SkScalarToFIXED(SkScalar x) {

+    return SkFixedToFIXED(SkScalarToFixed(x));

+}

+

+// This will generate a unique ID based on the fontname + fontstyle

+// and also used by upper layer

+uint32_t FontFaceChecksum(const TCHAR *q, SkTypeface::Style style)

+{

+    if (!q) return style;

+    

+    // From "Performance in Practice of String Hashing Functions"

+    // Ramakrishna & Zobel

+    const uint32_t L = 5;

+    const uint32_t R = 2;

+    

+    uint32_t h = 0x12345678;

+    while (*q) {

+        //uint32_t ql = tolower(*q);

+        h ^= ((h << L) + (h >> R) + *q);

+        q ++;

+    }

+    

+    // add style

+    h = _rotl(h, 3) ^ style;

+    

+    return h;

+}

+

+static SkTypeface::Style GetFontStyle(const LOGFONT& lf) {         

+    int style = SkTypeface::kNormal;

+    if (lf.lfWeight == FW_SEMIBOLD || lf.lfWeight == FW_DEMIBOLD || lf.lfWeight == FW_BOLD)

+        style |= SkTypeface::kBold;

+    if (lf.lfItalic)

+        style |= SkTypeface::kItalic;

+    

+    return (SkTypeface::Style)style;

+}

+

+struct SkFaceRec {

+    SkFaceRec*      fNext;

+    uint32_t        fRefCnt;

+    uint32_t        fFontID;    // checksum of fFace

+    LOGFONT         fFace;

+    

+    SkFaceRec() : fFontID(-1), fRefCnt(0) { 

+        memset(&fFace, 0, sizeof(LOGFONT));

+    }

+    ~SkFaceRec() {}

+    

+    uint32_t ref() {

+        return ++fRefCnt;

+    }

+};

+

+// Font Face list

+static SkFaceRec*   gFaceRecHead = NULL;

+

+static SkFaceRec* find_ft_face(uint32_t fontID) {

+    SkFaceRec* rec = gFaceRecHead;

+    while (rec) {

+        if (rec->fFontID == fontID) {

+            return rec;

+        }

+        rec = rec->fNext;

+    }

+    

+    return NULL;

+}

+

+static SkFaceRec* insert_ft_face(const LOGFONT& lf) {

+    // need a const char*

+    uint32_t id = FontFaceChecksum(&(lf.lfFaceName[0]), GetFontStyle(lf));

+    SkFaceRec* rec = find_ft_face(id);

+    if (rec) {

+        return rec;  // found?

+    }

+    

+    rec = SkNEW(SkFaceRec);

+    rec->fFontID = id;

+    memcpy(&(rec->fFace), &lf, sizeof(LOGFONT));

+    rec->fNext = gFaceRecHead;

+    gFaceRecHead = rec;

+    

+    return rec;

+}

+

+static void unref_ft_face(uint32_t fontID) {

+    

+    SkFaceRec* rec = gFaceRecHead;

+    SkFaceRec* prev = NULL;

+    while (rec) {

+        SkFaceRec* next = rec->fNext;

+        if (rec->fFontID == fontID) {

+            if (--rec->fRefCnt == 0) {

+                if (prev)

+                    prev->fNext = next;

+                else

+                    gFaceRecHead = next;

+                

+                SkDELETE(rec);

+            }

+            return;

+        }

+        prev = rec;

+        rec = next;

+    }

+    SkASSERT("shouldn't get here, face not in list");

+}

+

+// have to do this because SkTypeface::SkTypeface() is protected

+class FontFaceRec_Typeface : public SkTypeface {

+public:

+    

+    FontFaceRec_Typeface(Style style, uint32_t id) : SkTypeface(style, id) {};

+    

+    virtual ~FontFaceRec_Typeface() {};

+};

+

+static const LOGFONT* get_default_font() {

+    // don't hardcode on Windows, Win2000, XP, Vista, and international all have different default

+    // and the user could change too

+    

+    if (gDefaultFont.lfFaceName[0] != 0) {

+        return &gDefaultFont;

+    }

+    

+    NONCLIENTMETRICS ncm;

+    ncm.cbSize = sizeof(NONCLIENTMETRICS);

+    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);

+    

+    memcpy(&gDefaultFont, &(ncm.lfMessageFont), sizeof(LOGFONT));

+    

+    return &gDefaultFont;

+}

+

+static SkTypeface* CreateTypeface_(const LOGFONT& lf) {

+    

+    SkTypeface::Style style = GetFontStyle(lf);

+    FontFaceRec_Typeface* ptypeface = new FontFaceRec_Typeface(style, FontFaceChecksum(lf.lfFaceName, style));

+    

+    if (NULL == ptypeface) {

+        SkASSERT(false);

+        return NULL;

+    }

+    

+    SkFaceRec* rec = insert_ft_face(lf);

+    SkASSERT(rec);

+    

+    return ptypeface;

+}

+

+class SkScalerContext_Windows : public SkScalerContext {

+public:

+    SkScalerContext_Windows(const SkDescriptor* desc);

+    virtual ~SkScalerContext_Windows();

+    

+protected:

+    virtual unsigned generateGlyphCount() const;

+    virtual uint16_t generateCharToGlyph(SkUnichar uni);

+    virtual void generateAdvance(SkGlyph* glyph);

+    virtual void generateMetrics(SkGlyph* glyph);

+    virtual void generateImage(const SkGlyph& glyph);

+    virtual void generatePath(const SkGlyph& glyph, SkPath* path);

+    virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent);

+    virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);

+    //virtual SkDeviceContext getDC() {return ddc;}

+private:

+    uint32_t    fFontID;

+    LOGFONT     lf;              

+    MAT2        mat22;

+    HDC         ddc;

+    HFONT       savefont;

+    HFONT       font;

+};

+

+SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) : SkScalerContext(desc), ddc(0), font(0), savefont(0) {

+    SkAutoMutexAcquire  ac(gFTMutex);

+    

+    fFontID = fRec.fFontID;

+    SkFaceRec* rec = find_ft_face(fRec.fFontID);

+    if (rec) {

+        rec->ref();

+        memcpy(&lf, &(rec->fFace), sizeof(LOGFONT));

+    }

+    else {

+        SkASSERT(false);

+        memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));

+    }

+    

+    mat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);    

+    mat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);

+    mat22.eM21 = SkScalarToFIXED(fRec.fPost2x2[1][0]);

+    mat22.eM22 = SkScalarToFIXED(-fRec.fPost2x2[1][1]);

+    

+    ddc = ::CreateCompatibleDC(NULL);

+    SetBkMode(ddc, TRANSPARENT);

+    

+    lf.lfHeight = SkScalarFloor(fRec.fTextSize);

+    font = CreateFontIndirect(&lf);

+    savefont = (HFONT)SelectObject(ddc, font);

+}

+

+SkScalerContext_Windows::~SkScalerContext_Windows() {

+    unref_ft_face(fFontID);

+    

+    if (ddc) {

+        ::SelectObject(ddc, savefont);

+        ::DeleteDC(ddc);

+        ddc = NULL;

+    }

+    if (font) {

+        ::DeleteObject(font);

+    }

+}

+

+unsigned SkScalerContext_Windows::generateGlyphCount() const {

+    return 0xFFFF;

+    //    return fFace->num_glyphs;

+}

+

+uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {

+    

+    //uint16_t index = 0;

+    //GetGlyphIndicesW(ddc, &(uint16_t&)uni, 1, &index, 0);

+    //return index;

+    

+    // let's just use the uni as index on Windows

+    return SkToU16(uni);

+}

+

+void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {

+    this->generateMetrics(glyph);

+}

+

+void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {

+    

+    SkASSERT(ddc);

+    

+    GLYPHMETRICS gm;

+    memset(&gm, 0, sizeof(gm));

+    

+    glyph->fRsbDelta = 0;

+    glyph->fLsbDelta = 0;

+    

+    UINT glyphIndexFlag = 0; //glyph->fIsCodePoint ? 0 : GGO_GLYPH_INDEX;

+    //    UINT glyphIndexFlag = GGO_GLYPH_INDEX;

+    // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller

+    // BlackBlox; we need the bigger one in case we need the image.  fAdvance is the same.

+    uint32_t ret = GetGlyphOutlineW(ddc, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | glyphIndexFlag, &gm, 0, NULL, &mat22);

+    

+    if (GDI_ERROR != ret) {

+        if (ret == 0) {

+            // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!

+            gm.gmBlackBoxX = gm.gmBlackBoxY = 0;

+        }

+        glyph->fWidth   = gm.gmBlackBoxX;

+        glyph->fHeight  = gm.gmBlackBoxY;

+        glyph->fTop     = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);

+        glyph->fLeft    = SkToS16(gm.gmptGlyphOrigin.x);

+        glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);

+        glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);

+    } else {

+        glyph->fWidth = 0;

+    }

+    

+#if 0

+    char buf[1024];

+    sprintf(buf, "generateMetrics: id:%d, w=%d, h=%d, font:%s, fh:%d\n", glyph->fID, glyph->fWidth, glyph->fHeight, lf.lfFaceName, lf.lfHeight);

+    OutputDebugString(buf);

+#endif

+}

+

+void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {

+    //SkASSERT(false);

+    if (mx)

+        memset(mx, 0, sizeof(SkPaint::FontMetrics));

+    if (my)

+        memset(my, 0, sizeof(SkPaint::FontMetrics));

+    return;

+}

+

+void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {

+    

+    SkAutoMutexAcquire  ac(gFTMutex);

+    

+    SkASSERT(ddc);

+    

+    GLYPHMETRICS gm;

+    memset(&gm, 0, sizeof(gm));

+    

+#if 0

+    char buf[1024];

+    sprintf(buf, "generateImage: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);

+    OutputDebugString(buf);

+#endif

+    

+    uint32_t bytecount = 0;

+    UINT glyphIndexFlag = 0; //glyph.fIsCodePoint ? 0 : GGO_GLYPH_INDEX;

+    //    UINT glyphIndexFlag = GGO_GLYPH_INDEX;

+    uint32_t total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_GRAY8_BITMAP | glyphIndexFlag, &gm, 0, NULL, &mat22);

+    if (GDI_ERROR != total_size && total_size > 0) {

+        uint8_t *pBuff = new uint8_t[total_size];

+        if (NULL != pBuff) {

+            total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_GRAY8_BITMAP | glyphIndexFlag, &gm, total_size, pBuff, &mat22);

+            

+            SkASSERT(total_size != GDI_ERROR);

+            

+            SkASSERT(glyph.fWidth == gm.gmBlackBoxX);

+            SkASSERT(glyph.fHeight == gm.gmBlackBoxY);

+            

+            uint8_t* dst = (uint8_t*)glyph.fImage;

+            uint32_t pitch = (gm.gmBlackBoxX + 3) & ~0x3;

+            if (pitch != glyph.rowBytes()) {

+                SkASSERT(false); // glyph.fImage has different rowsize!?

+            }

+            

+            for (int32_t y = gm.gmBlackBoxY - 1; y >= 0; y--) {

+                uint8_t* src = pBuff + pitch * y;

+                

+                for (uint32_t x = 0; x < gm.gmBlackBoxX; x++) {

+                    if (*src > 63) {

+                        *dst = 0xFF;

+                    }

+                    else {

+                        *dst = *src << 2; // scale to 0-255

+                    }

+                    dst++;

+                    src++;

+                    bytecount++;

+                }

+                memset(dst, 0, glyph.rowBytes() - glyph.fWidth);

+                dst += glyph.rowBytes() - glyph.fWidth;

+            }

+            

+            delete[] pBuff;

+        }

+    }

+    

+    SkASSERT(GDI_ERROR != total_size && total_size >= 0);

+    

+}

+

+void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {

+    

+    SkAutoMutexAcquire  ac(gFTMutex);

+    

+    SkASSERT(&glyph && path);

+    SkASSERT(ddc);

+    

+    path->reset();

+    

+#if 0

+    char buf[1024];

+    sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);

+    OutputDebugString(buf);

+#endif

+    

+    GLYPHMETRICS gm;

+    UINT glyphIndexFlag = 0; //glyph.fIsCodePoint ? 0 : GGO_GLYPH_INDEX;

+    uint32_t total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_NATIVE | glyphIndexFlag, &gm, BUFFERSIZE, glyphbuf, &mat22);

+    

+    if (GDI_ERROR != total_size) {

+        

+        const uint8_t* cur_glyph = glyphbuf;

+        const uint8_t* end_glyph = glyphbuf + total_size;

+        

+        while(cur_glyph < end_glyph) {

+            const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;

+            

+            const uint8_t* end_poly = cur_glyph + th->cb;

+            const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);

+            

+            path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));

+            

+            while(cur_poly < end_poly) {

+                const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;

+                

+                if (pc->wType == TT_PRIM_LINE) {

+                    for (uint16_t i = 0; i < pc->cpfx; i++) {

+                        path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));

+                    }

+                }

+                

+                if (pc->wType == TT_PRIM_QSPLINE) {

+                    for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline

+                        POINTFX pnt_b = pc->apfx[u];    // B is always the current point

+                        POINTFX pnt_c = pc->apfx[u+1];

+                        

+                        if (u < pc->cpfx - 2) {          // If not on last spline, compute C                            

+                            pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));

+                            pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));

+                        }

+                        

+                        path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));

+                    }

+                }

+                cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;

+            }

+            cur_glyph += th->cb;

+            path->close();

+        }

+    }

+    else {

+        SkASSERT(false);

+    }

+    //char buf[1024];

+    //sprintf(buf, "generatePath: count:%d\n", count);

+    //OutputDebugString(buf);

+}

+

+

+// Note:  not sure this is the correct implementation

+void SkScalerContext_Windows::generateLineHeight(SkPoint* ascent, SkPoint* descent) {

+    

+    SkASSERT(ddc);

+    

+    OUTLINETEXTMETRIC otm;

+    

+    uint32_t ret = GetOutlineTextMetrics(ddc, sizeof(otm), &otm);

+    

+    if (sizeof(otm) == ret) {

+        if (ascent)

+            ascent->iset(0, otm.otmAscent);

+        if (descent)

+            descent->iset(0, otm.otmDescent);

+    }

+    

+    return;

+}

+

+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {

+    SkASSERT(!"SkFontHost::Serialize unimplemented");

+}

+

+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {

+    SkASSERT(!"SkFontHost::Deserialize unimplemented");

+    return NULL;

+}

+

+SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) {

+    

+    //Should not be used on Windows, keep linker happy

+    SkASSERT(false);

+    get_default_font();

+    return CreateTypeface_(gDefaultFont);

+}

+

+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {

+    return SkNEW_ARGS(SkScalerContext_Windows, (desc));

+}

+

+SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec) {

+    get_default_font();

+    

+    SkAutoDescriptor    ad(sizeof(rec) + sizeof(gDefaultFont) + SkDescriptor::ComputeOverhead(2));

+    SkDescriptor*       desc = ad.getDesc();

+    

+    desc->init();

+    SkScalerContext::Rec* newRec =

+    (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);

+    

+    get_default_font();

+    CreateTypeface_(gDefaultFont);

+    newRec->fFontID = FontFaceChecksum(gDefaultFont.lfFaceName, GetFontStyle(gDefaultFont));

+    desc->computeChecksum();

+    

+    return SkFontHost::CreateScalerContext(desc);

+}

+

+/** Return the closest matching typeface given either an existing family

+ (specified by a typeface in that family) or by a familyName, and a

+ requested style.

+ 1) If familyFace is null, use famillyName.

+ 2) If famillyName is null, use familyFace.

+ 3) If both are null, return the default font that best matches style

+ This MUST not return NULL.

+ */

+

+SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) {

+    

+    SkAutoMutexAcquire  ac(gFTMutex);

+

+#ifndef CAN_USE_LOGFONT_NAME

+    familyName = NULL;

+    familyFace = NULL;

+#endif

+

+    // clip to legal style bits

+    style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);

+    

+    SkTypeface* tf = NULL;

+    if (NULL == familyFace && NULL == familyName) {

+        LOGFONT lf;

+        get_default_font();

+        memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));

+        lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ; 

+        lf.lfItalic = ((style & SkTypeface::kItalic) != 0);

+        tf = CreateTypeface_(lf);

+    } else {

+#ifdef CAN_USE_LOGFONT_NAME

+        LOGFONT lf;

+        if (NULL != familyFace) {

+            uint32_t id = familyFace->uniqueID();

+            SkFaceRec* rec = find_ft_face(id);

+            if (!rec) {

+                SkASSERT(false);

+                get_default_font();

+                memcpy(&lf, &gDefaultFont, sizeof(LOGFONT));

+            }

+            else {

+                memcpy(&lf, &(rec->fFace), sizeof(LOGFONT));

+            }

+        }

+        else {

+            memset(&lf, 0, sizeof(LOGFONT));

+            

+            lf.lfHeight = -11; // default

+            lf.lfQuality = PROOF_QUALITY;

+            lf.lfCharSet = DEFAULT_CHARSET;

+            

+            _tcsncpy(lf.lfFaceName, familyName, LF_FACESIZE);

+            lf.lfFaceName[LF_FACESIZE-1] = '\0';

+        }

+        

+        // use the style desired

+        lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ; 

+        lf.lfItalic = ((style & SkTypeface::kItalic) != 0);

+        tf = CreateTypeface_(lf);

+#endif

+    }

+

+    if (NULL == tf) {

+        get_default_font();

+        tf = CreateTypeface_(gDefaultFont);

+    }

+    return tf;

+}

+

+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {

+    if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)

+        return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;

+    else

+        return 0;   // nothing to do

+}

+

+int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {

+    return 0;

+}

+

+void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {

+    tables[0] = NULL;   // black gamma (e.g. exp=1.4)

+    tables[1] = NULL;   // white gamma (e.g. exp= 1/1.4)

+}

+

+#endif // WIN32

+

diff --git a/src/ports/SkGlobals_global.cpp b/src/ports/SkGlobals_global.cpp
new file mode 100644
index 0000000..d87568b
--- /dev/null
+++ b/src/ports/SkGlobals_global.cpp
@@ -0,0 +1,28 @@
+/* libs/graphics/ports/SkGlobals_global.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 "SkGlobals.h"
+#include "SkThread.h"
+
+static SkGlobals::BootStrap gBootStrap;
+
+SkGlobals::BootStrap& SkGlobals::GetBootStrap()
+{
+    return gBootStrap;
+}
+
+
diff --git a/src/ports/SkImageDecoder_Factory.cpp b/src/ports/SkImageDecoder_Factory.cpp
new file mode 100644
index 0000000..d0053cf
--- /dev/null
+++ b/src/ports/SkImageDecoder_Factory.cpp
@@ -0,0 +1,104 @@
+/* libs/graphics/ports/SkImageDecoder_Factory.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 "SkImageDecoder.h"
+#include "SkMovie.h"
+#include "SkStream.h"
+
+extern SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream*);
+extern SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream*);
+extern SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream*);
+extern SkImageDecoder* SkImageDecoder_PNG_Factory(SkStream*);
+extern SkImageDecoder* SkImageDecoder_WBMP_Factory(SkStream*);
+extern SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream*);
+
+typedef SkImageDecoder* (*SkImageDecoderFactoryProc)(SkStream*);
+
+struct CodecFormat {
+    SkImageDecoderFactoryProc   fProc;
+    SkImageDecoder::Format      fFormat;
+};
+
+static const CodecFormat gPairs[] = {
+    { SkImageDecoder_GIF_Factory,   SkImageDecoder::kGIF_Format },
+    { SkImageDecoder_PNG_Factory,   SkImageDecoder::kPNG_Format },
+    { SkImageDecoder_ICO_Factory,   SkImageDecoder::kICO_Format },
+    { SkImageDecoder_WBMP_Factory,  SkImageDecoder::kWBMP_Format },
+    { SkImageDecoder_BMP_Factory,   SkImageDecoder::kBMP_Format },
+    { SkImageDecoder_JPEG_Factory,  SkImageDecoder::kJPEG_Format }
+};
+
+SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
+        SkImageDecoder* codec = gPairs[i].fProc(stream);
+        stream->rewind();
+        if (NULL != codec) {
+            return codec;
+        }
+    }
+    return NULL;
+}
+
+bool SkImageDecoder::SupportsFormat(Format format) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
+        if (gPairs[i].fFormat == format) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+typedef SkMovie* (*SkMovieFactoryProc)(SkStream*);
+
+extern SkMovie* SkMovie_GIF_Factory(SkStream*);
+
+static const SkMovieFactoryProc gMovieProcs[] = {
+    SkMovie_GIF_Factory
+};
+
+SkMovie* SkMovie::DecodeStream(SkStream* stream) {
+    for (unsigned i = 0; i < SK_ARRAY_COUNT(gMovieProcs); i++) {
+        SkMovie* movie = gMovieProcs[i](stream);
+        if (NULL != movie) {
+            return movie;
+        }
+        stream->rewind();
+    }
+    return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+
+extern SkImageEncoder* SkImageEncoder_JPEG_Factory();
+extern SkImageEncoder* SkImageEncoder_PNG_Factory();
+
+SkImageEncoder* SkImageEncoder::Create(Type t) {
+    switch (t) {
+        case kJPEG_Type:
+            return SkImageEncoder_JPEG_Factory();
+        case kPNG_Type:
+            return SkImageEncoder_PNG_Factory();
+        default:
+            return NULL;
+    }
+}
+
+#endif
diff --git a/src/ports/SkImageRef_ashmem.cpp b/src/ports/SkImageRef_ashmem.cpp
new file mode 100644
index 0000000..a904bae
--- /dev/null
+++ b/src/ports/SkImageRef_ashmem.cpp
@@ -0,0 +1,203 @@
+#include "SkImageRef_ashmem.h"
+#include "SkImageDecoder.h"
+#include "SkThread.h"
+
+#include <sys/mman.h>
+#include <unistd.h>
+#include <cutils/ashmem.h>
+
+//#define TRACE_ASH_PURGE     // just trace purges
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+    #define DUMP_ASHMEM_LIFECYCLE
+#else
+//    #define DUMP_ASHMEM_LIFECYCLE
+#endif
+
+// ashmem likes lengths on page boundaries
+static size_t roundToPageSize(size_t size) {
+    const size_t mask = getpagesize() - 1;
+    size_t newsize = (size + mask) & ~mask;
+//    SkDebugf("---- oldsize %d newsize %d\n", size, newsize);
+    return newsize;
+}
+
+SkImageRef_ashmem::SkImageRef_ashmem(SkStream* stream,
+                                             SkBitmap::Config config,
+                                             int sampleSize)
+        : SkImageRef(stream, config, sampleSize) {
+            
+    fRec.fFD = -1;
+    fRec.fAddr = NULL;
+    fRec.fSize = 0;
+    fRec.fPinned = false;
+            
+    fCT = NULL;
+}
+
+SkImageRef_ashmem::~SkImageRef_ashmem() {
+    fCT->safeUnref();
+    this->closeFD();
+}
+
+void SkImageRef_ashmem::closeFD() {
+    if (-1 != fRec.fFD) {
+#ifdef DUMP_ASHMEM_LIFECYCLE
+        SkDebugf("=== ashmem close %d\n", fRec.fFD);
+#endif
+        SkASSERT(fRec.fAddr);
+        SkASSERT(fRec.fSize);
+        munmap(fRec.fAddr, fRec.fSize);
+        close(fRec.fFD);
+        fRec.fFD = -1;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class AshmemAllocator : public SkBitmap::Allocator {
+public:
+    AshmemAllocator(SkAshmemRec* rec, const char name[])
+        : fRec(rec), fName(name) {}
+
+    virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
+        const size_t size = roundToPageSize(bm->getSize());
+        int fd = fRec->fFD;
+        void* addr = fRec->fAddr;
+
+        SkASSERT(!fRec->fPinned);
+
+        if (-1 == fd) {
+            SkASSERT(NULL == addr);
+            SkASSERT(0 == fRec->fSize);
+            
+            fd = ashmem_create_region(fName, size);
+#ifdef DUMP_ASHMEM_LIFECYCLE
+            SkDebugf("=== ashmem_create_region %s size=%d fd=%d\n", fName, size, fd);
+#endif
+            if (-1 == fd) {
+                SkDebugf("------- imageref_ashmem create failed <%s> %d\n",
+                         fName, size);
+                return false;
+            }
+            
+            int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
+            if (err) {
+                SkDebugf("------ ashmem_set_prot_region(%d) failed %d %d\n",
+                         fd, err, errno);
+                return false;
+            }
+            
+            addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+            if (-1 == (long)addr) {
+                SkDebugf("---------- mmap failed for imageref_ashmem size=%d err=%d\n",
+                         size, errno);
+                return false;
+            }
+            
+            fRec->fFD = fd;
+            fRec->fAddr = addr;
+            fRec->fSize = size;
+        } else {
+            SkASSERT(addr);
+            SkASSERT(size == fRec->fSize);
+            (void)ashmem_pin_region(fd, 0, 0);
+        }
+
+        bm->setPixels(addr, ct);
+        fRec->fPinned = true;
+        return true;
+    }
+    
+private:
+    // we just point to our caller's memory, these are not copies
+    SkAshmemRec* fRec;
+    const char*  fName;
+};
+
+bool SkImageRef_ashmem::onDecode(SkImageDecoder* codec, SkStream* stream,
+                                 SkBitmap* bitmap, SkBitmap::Config config,
+                                 SkImageDecoder::Mode mode) {
+    
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        return this->INHERITED::onDecode(codec, stream, bitmap, config, mode);
+    }
+
+    AshmemAllocator alloc(&fRec, this->getURI());
+    
+    codec->setAllocator(&alloc);
+    bool success = this->INHERITED::onDecode(codec, stream, bitmap, config,
+                                             mode);
+    // remove the allocator, since its on the stack
+    codec->setAllocator(NULL);
+
+    if (success) {
+        // remember the colortable (if any)
+        SkRefCnt_SafeAssign(fCT, bitmap->getColorTable());
+        return true;
+    } else {
+        if (fRec.fPinned) {
+            ashmem_unpin_region(fRec.fFD, 0, 0);
+            fRec.fPinned = false;
+        }
+        this->closeFD();
+        return false;
+    }
+}
+
+void* SkImageRef_ashmem::onLockPixels(SkColorTable** ct) {
+    SkASSERT(fBitmap.getPixels() == NULL);
+    SkASSERT(fBitmap.getColorTable() == NULL);
+
+    // fast case: check if we can just pin and get the cached data
+    if (-1 != fRec.fFD) {
+        SkASSERT(fRec.fAddr);
+        SkASSERT(!fRec.fPinned);
+        int pin = ashmem_pin_region(fRec.fFD, 0, 0);
+
+        if (ASHMEM_NOT_PURGED == pin) { // yea, fast case!
+            fBitmap.setPixels(fRec.fAddr, fCT);
+            fRec.fPinned = true;
+        } else if (ASHMEM_WAS_PURGED == pin) {
+            ashmem_unpin_region(fRec.fFD, 0, 0);
+            // let go of our colortable if we lost the pixels. Well get it back
+            // again when we re-decode
+            if (fCT) {
+                fCT->unref();
+                fCT = NULL;
+            }
+#if defined(DUMP_ASHMEM_LIFECYCLE) || defined(TRACE_ASH_PURGE)
+            SkDebugf("===== ashmem purged %d\n", fBitmap.getSize());
+#endif
+        } else {
+            SkDebugf("===== ashmem pin_region(%d) returned %d, treating as error %d\n",
+                     fRec.fFD, pin, errno);
+            // return null result for failure
+            if (ct) {
+                *ct = NULL;
+            }
+            return NULL;
+        }
+    } else {
+        // no FD, will create an ashmem region in allocator
+    }
+    
+    return this->INHERITED::onLockPixels(ct);
+}
+
+void SkImageRef_ashmem::onUnlockPixels() {
+    this->INHERITED::onUnlockPixels();
+    
+    if (-1 != fRec.fFD) {
+        SkASSERT(fRec.fAddr);
+        SkASSERT(fRec.fPinned);
+        
+        ashmem_unpin_region(fRec.fFD, 0, 0);
+        fRec.fPinned = false;
+    }
+    
+    // we clear this with or without an error, since we've either closed or
+    // unpinned the region
+    fBitmap.setPixels(NULL, NULL);
+}
+
diff --git a/src/ports/SkImageRef_ashmem.h b/src/ports/SkImageRef_ashmem.h
new file mode 100644
index 0000000..193a01d
--- /dev/null
+++ b/src/ports/SkImageRef_ashmem.h
@@ -0,0 +1,35 @@
+#ifndef SkImageRef_ashmem_DEFINED
+#define SkImageRef_ashmem_DEFINED
+
+#include "SkImageRef.h"
+
+struct SkAshmemRec {
+    int     fFD;
+    void*   fAddr;
+    size_t  fSize;
+    bool    fPinned;
+};
+
+class SkImageRef_ashmem : public SkImageRef {
+public:
+    SkImageRef_ashmem(SkStream*, SkBitmap::Config, int sampleSize = 1);
+    virtual ~SkImageRef_ashmem();
+    
+protected:
+    virtual bool onDecode(SkImageDecoder* codec, SkStream* stream,
+                          SkBitmap* bitmap, SkBitmap::Config config,
+                          SkImageDecoder::Mode mode);
+    
+    virtual void* onLockPixels(SkColorTable**);
+    virtual void onUnlockPixels();
+    
+private:
+    void closeFD();
+
+    SkColorTable* fCT;
+    SkAshmemRec fRec;
+
+    typedef SkImageRef INHERITED;
+};
+
+#endif
diff --git a/src/ports/SkOSEvent_android.cpp b/src/ports/SkOSEvent_android.cpp
new file mode 100644
index 0000000..59d6191
--- /dev/null
+++ b/src/ports/SkOSEvent_android.cpp
@@ -0,0 +1,155 @@
+/* libs/graphics/ports/SkOSEvent_android.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include "SkEvent.h"
+#include "utils/threads.h"
+#include <stdio.h>
+
+using namespace android;
+
+Mutex gEventQMutex;
+Condition gEventQCondition;
+
+void SkEvent::SignalNonEmptyQueue()
+{
+    gEventQCondition.broadcast();
+}
+
+///////////////////////////////////////////////////////////////////
+
+#ifdef FMS_ARCH_ANDROID_ARM
+
+// don't have pthreads.h, and therefore no timedwait(), so we punt for the demo
+
+void SkEvent::SignalQueueTimer(SkMSec delay)
+{
+}
+
+void SkEvent_start_timer_thread()
+{
+}
+
+void SkEvent_stop_timer_thread()
+{
+}
+
+#else
+
+#include <pthread.h>
+#include <errno.h>
+
+static pthread_t        gTimerThread;
+static pthread_mutex_t  gTimerMutex;
+static pthread_cond_t   gTimerCond;
+static timespec         gTimeSpec;
+
+static void* timer_event_thread_proc(void*)
+{
+    for (;;)
+    {
+        int status;
+        
+        pthread_mutex_lock(&gTimerMutex);
+
+        timespec spec = gTimeSpec;
+        // mark our global to be zero
+        // so we don't call timedwait again on a stale value
+        gTimeSpec.tv_sec = 0;
+        gTimeSpec.tv_nsec = 0;
+
+        if (spec.tv_sec == 0 && spec.tv_nsec == 0)
+            status = pthread_cond_wait(&gTimerCond, &gTimerMutex);
+        else
+            status = pthread_cond_timedwait(&gTimerCond, &gTimerMutex, &spec);
+        
+        if (status == 0)    // someone signaled us with a new time
+        {
+            pthread_mutex_unlock(&gTimerMutex);
+        }
+        else
+        {
+            SkASSERT(status == ETIMEDOUT);  // no need to unlock the mutex (its unlocked)
+            // this is the payoff. Signal the event queue to wake up
+            // and also check the delay-queue
+            gEventQCondition.broadcast();
+        }
+    }
+    return 0;
+}
+
+#define kThousand   (1000)
+#define kMillion    (kThousand * kThousand)
+#define kBillion    (kThousand * kThousand * kThousand)
+
+void SkEvent::SignalQueueTimer(SkMSec delay)
+{
+    pthread_mutex_lock(&gTimerMutex);
+
+    if (delay)
+    {
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+
+        // normalize tv
+        if (tv.tv_usec >= kMillion)
+        {
+            tv.tv_sec += tv.tv_usec / kMillion;
+            tv.tv_usec %= kMillion;
+        }
+
+        // add tv + delay, scale each up to land on nanoseconds
+        gTimeSpec.tv_nsec   = (tv.tv_usec + (delay % kThousand) * kThousand) * kThousand;
+        gTimeSpec.tv_sec    = (tv.tv_sec + (delay / kThousand) * kThousand) * kThousand;
+        
+        // check for overflow in nsec
+        if ((unsigned long)gTimeSpec.tv_nsec >= kBillion)
+        {
+            gTimeSpec.tv_nsec -= kBillion;
+            gTimeSpec.tv_sec += 1;
+            SkASSERT((unsigned long)gTimeSpec.tv_nsec < kBillion);
+        }
+
+    //  printf("SignalQueueTimer(%d) timespec(%d %d)\n", delay, gTimeSpec.tv_sec, gTimeSpec.tv_nsec);
+    }
+    else    // cancel the timer
+    {
+        gTimeSpec.tv_nsec = 0;
+        gTimeSpec.tv_sec = 0;
+    }
+
+    pthread_mutex_unlock(&gTimerMutex);
+    pthread_cond_signal(&gTimerCond);
+}
+
+void SkEvent_start_timer_thread()
+{
+    int             status;
+    pthread_attr_t  attr;
+    
+    status = pthread_attr_init(&attr);
+    SkASSERT(status == 0);
+    status = pthread_create(&gTimerThread, &attr, timer_event_thread_proc, 0);
+    SkASSERT(status == 0);
+}
+
+void SkEvent_stop_timer_thread()
+{
+    int status = pthread_cancel(gTimerThread);
+    SkASSERT(status == 0);
+}
+
+#endif
diff --git a/src/ports/SkOSEvent_dummy.cpp b/src/ports/SkOSEvent_dummy.cpp
new file mode 100644
index 0000000..f061b6e
--- /dev/null
+++ b/src/ports/SkOSEvent_dummy.cpp
@@ -0,0 +1,28 @@
+/* libs/graphics/ports/SkOSEvent_dummy.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::SignalNonEmptyQueue()
+{
+
+}
+
+void SkEvent::SignalQueueTimer(SkMSec delay)
+{
+
+}
diff --git a/src/ports/SkOSFile_stdio.cpp b/src/ports/SkOSFile_stdio.cpp
new file mode 100644
index 0000000..7438f7b
--- /dev/null
+++ b/src/ports/SkOSFile_stdio.cpp
@@ -0,0 +1,106 @@
+/* libs/graphics/ports/SkOSFile_stdio.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 "SkOSFile.h"
+
+#ifndef SK_BUILD_FOR_BREW
+
+#include <stdio.h>
+#include <errno.h>
+
+SkFILE* sk_fopen(const char path[], SkFILE_Flags flags)
+{
+    char    perm[4];
+    char*   p = perm;
+
+    if (flags & kRead_SkFILE_Flag)
+        *p++ = 'r';
+    if (flags & kWrite_SkFILE_Flag)
+        *p++ = 'w';
+    *p++ = 'b';
+    *p = 0;
+
+    SkFILE* f = (SkFILE*)::fopen(path, perm);
+#if 0
+    if (NULL == f)
+        SkDebugf("sk_fopen failed for %s (%s), errno=%s\n", path, perm, strerror(errno));
+#endif
+    return f;
+}
+
+size_t sk_fgetsize(SkFILE* f)
+{
+    SkASSERT(f);
+
+    size_t  curr = ::ftell((FILE*)f);       // remember where we are
+    ::fseek((FILE*)f, 0, SEEK_END);         // go to the end
+    size_t size = ::ftell((FILE*)f);        // record the size
+    ::fseek((FILE*)f, (long)curr, SEEK_SET);        // go back to our prev loc
+    return size;
+}
+
+bool sk_frewind(SkFILE* f)
+{
+    SkASSERT(f);
+    ::rewind((FILE*)f);
+//  ::fseek((FILE*)f, 0, SEEK_SET);
+    return true;
+}
+
+size_t sk_fread(void* buffer, size_t byteCount, SkFILE* f)
+{
+    SkASSERT(f);
+    if (buffer == NULL)
+    {
+        size_t curr = ::ftell((FILE*)f);
+        if ((long)curr == -1) {
+            SkDEBUGF(("sk_fread: ftell(%p) returned -1 feof:%d ferror:%d\n", f, feof((FILE*)f), ferror((FILE*)f)));
+            return 0;
+        }
+    //  ::fseek((FILE*)f, (long)(curr + byteCount), SEEK_SET);
+        int err = ::fseek((FILE*)f, (long)byteCount, SEEK_CUR);
+        if (err != 0) {
+            SkDEBUGF(("sk_fread: fseek(%d) tell:%d failed with feof:%d ferror:%d returned:%d\n",
+                        byteCount, curr, feof((FILE*)f), ferror((FILE*)f), err));
+            return 0;
+        }
+        return byteCount;
+    }
+    else
+        return ::fread(buffer, 1, byteCount, (FILE*)f);
+}
+
+size_t sk_fwrite(const void* buffer, size_t byteCount, SkFILE* f)
+{
+    SkASSERT(f);
+    return ::fwrite(buffer, 1, byteCount, (FILE*)f);
+}
+
+void sk_fflush(SkFILE* f)
+{
+    SkASSERT(f);
+    ::fflush((FILE*)f);
+}
+
+void sk_fclose(SkFILE* f)
+{
+    SkASSERT(f);
+    ::fclose((FILE*)f);
+}
+
+#endif
+
diff --git a/src/ports/SkThread_none.cpp b/src/ports/SkThread_none.cpp
new file mode 100644
index 0000000..37a3834
--- /dev/null
+++ b/src/ports/SkThread_none.cpp
@@ -0,0 +1,49 @@
+/* libs/graphics/ports/SkThread_none.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 "SkThread.h"
+
+int32_t sk_atomic_inc(int32_t* addr)
+{
+    int32_t value = *addr;
+    *addr = value + 1;
+    return value;
+}
+
+int32_t sk_atomic_dec(int32_t* addr)
+{
+    int32_t value = *addr;
+    *addr = value - 1;
+    return value;
+}
+
+SkMutex::SkMutex(bool /* isGlobal */)
+{
+}
+
+SkMutex::~SkMutex()
+{
+}
+
+void SkMutex::acquire()
+{
+}
+
+void SkMutex::release()
+{
+}
+
diff --git a/src/ports/SkThread_pthread.cpp b/src/ports/SkThread_pthread.cpp
new file mode 100644
index 0000000..4ee857d
--- /dev/null
+++ b/src/ports/SkThread_pthread.cpp
@@ -0,0 +1,90 @@
+#include "SkThread.h"
+
+#include <pthread.h>
+#include <errno.h>
+
+SkMutex gAtomicMutex;
+
+int32_t sk_atomic_inc(int32_t* addr)
+{
+    SkAutoMutexAcquire ac(gAtomicMutex);
+
+    int32_t value = *addr;
+    *addr = value + 1;
+    return value;
+}
+
+int32_t sk_atomic_dec(int32_t* addr)
+{
+    SkAutoMutexAcquire ac(gAtomicMutex);
+    
+    int32_t value = *addr;
+    *addr = value - 1;
+    return value;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void print_pthread_error(int status)
+{
+    switch (status) {
+    case 0: // success
+        break;
+    case EINVAL:
+        printf("pthread error [%d] EINVAL\n", status);
+        break;
+    case EBUSY:
+        printf("pthread error [%d] EBUSY\n", status);
+        break;
+    default:
+        printf("pthread error [%d] unknown\n", status);
+        break;
+    }
+}
+
+SkMutex::SkMutex(bool isGlobal) : fIsGlobal(isGlobal)
+{
+    if (sizeof(pthread_mutex_t) > sizeof(fStorage))
+    {
+        SkDEBUGF(("pthread mutex size = %d\n", sizeof(pthread_mutex_t)));
+        SkASSERT(!"mutex storage is too small");
+    }
+
+    int status;
+    pthread_mutexattr_t attr;
+
+    status = pthread_mutexattr_init(&attr);
+    print_pthread_error(status);
+    SkASSERT(0 == status);
+    
+    status = pthread_mutex_init((pthread_mutex_t*)fStorage, &attr);
+    print_pthread_error(status);
+    SkASSERT(0 == status);
+}
+
+SkMutex::~SkMutex()
+{
+    int status = pthread_mutex_destroy((pthread_mutex_t*)fStorage);
+    
+    // only report errors on non-global mutexes
+    if (!fIsGlobal)
+    {
+        print_pthread_error(status);
+        SkASSERT(0 == status);
+    }
+}
+
+void SkMutex::acquire()
+{
+    int status = pthread_mutex_lock((pthread_mutex_t*)fStorage);
+    print_pthread_error(status);
+    SkASSERT(0 == status);
+}
+
+void SkMutex::release()
+{
+    int status = pthread_mutex_unlock((pthread_mutex_t*)fStorage);
+    print_pthread_error(status);
+    SkASSERT(0 == status);
+}
+
diff --git a/src/ports/SkThread_win.cpp b/src/ports/SkThread_win.cpp
new file mode 100644
index 0000000..d3f3e21
--- /dev/null
+++ b/src/ports/SkThread_win.cpp
@@ -0,0 +1,64 @@
+/* libs/graphics/ports/SkThread_none.cpp
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <windows.h>
+#include "SkThread.h"
+
+namespace {
+
+template <bool>
+struct CompileAssert {
+};
+
+}  // namespace
+
+#define COMPILE_ASSERT(expr, msg) \
+  typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+
+int32_t sk_atomic_inc(int32_t* addr)
+{
+    // InterlockedIncrement returns the new value, we want to return the old.
+    return InterlockedIncrement(reinterpret_cast<LONG*>(addr)) - 1;
+}
+
+int32_t sk_atomic_dec(int32_t* addr)
+{
+    return InterlockedDecrement(reinterpret_cast<LONG*>(addr)) + 1;
+}
+
+SkMutex::SkMutex(bool /* isGlobal */)
+{
+    COMPILE_ASSERT(sizeof(fStorage) > sizeof(CRITICAL_SECTION),
+                   NotEnoughSizeForCriticalSection);
+    InitializeCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage));
+}
+
+SkMutex::~SkMutex()
+{
+    DeleteCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage));
+}
+
+void SkMutex::acquire()
+{
+    EnterCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage));
+}
+
+void SkMutex::release()
+{
+    LeaveCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage));
+}
+
diff --git a/src/ports/SkTime_Unix.cpp b/src/ports/SkTime_Unix.cpp
new file mode 100644
index 0000000..1bf3a76
--- /dev/null
+++ b/src/ports/SkTime_Unix.cpp
@@ -0,0 +1,50 @@
+/* libs/graphics/ports/SkTime_Unix.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 "SkTime.h"
+
+#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC)
+#include <sys/time.h>
+#include <time.h>
+
+void SkTime::GetDateTime(DateTime* dt)
+{
+    if (dt)
+    {
+        time_t m_time;
+        time(&m_time);
+        struct tm* tstruct;
+        tstruct = localtime(&m_time);
+
+        dt->fYear       = tstruct->tm_year;
+        dt->fMonth      = SkToU8(tstruct->tm_mon + 1);
+        dt->fDayOfWeek  = SkToU8(tstruct->tm_wday);
+        dt->fDay        = SkToU8(tstruct->tm_mday);
+        dt->fHour       = SkToU8(tstruct->tm_hour);
+        dt->fMinute     = SkToU8(tstruct->tm_min);
+        dt->fSecond     = SkToU8(tstruct->tm_sec);
+    }
+}
+
+SkMSec SkTime::GetMSecs()
+{
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return (SkMSec) (tv.tv_sec * 1000 + tv.tv_usec / 1000 ); // microseconds to milliseconds
+}
+
+#endif
diff --git a/src/ports/SkXMLParser_empty.cpp b/src/ports/SkXMLParser_empty.cpp
new file mode 100644
index 0000000..9a27306
--- /dev/null
+++ b/src/ports/SkXMLParser_empty.cpp
@@ -0,0 +1,36 @@
+/* libs/graphics/ports/SkXMLParser_empty.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+// Copyright Skia Inc. 2004 - 2005
+// 
+#include "SkXMLParser.h"
+
+bool SkXMLParser::parse(SkStream& docStream)
+{
+    return false;
+}
+
+bool SkXMLParser::parse(const char doc[], size_t len)
+{
+    return false;
+}
+
+void SkXMLParser::GetNativeErrorString(int error, SkString* str)
+{
+
+}
+
diff --git a/src/ports/SkXMLParser_expat.cpp b/src/ports/SkXMLParser_expat.cpp
new file mode 100644
index 0000000..7694d50
--- /dev/null
+++ b/src/ports/SkXMLParser_expat.cpp
@@ -0,0 +1,149 @@
+/* libs/graphics/ports/SkXMLParser_expat.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 "SkXMLParser.h"
+#include "SkString.h"
+#include "SkStream.h"
+
+#include "expat.h"
+
+#ifdef SK_BUILD_FOR_PPI
+#define CHAR_16_TO_9
+#endif
+
+#if defined CHAR_16_TO_9
+inline size_t sk_wcslen(const short* char16) {
+    const short* start = char16;
+    while (*char16)
+        char16++;
+    return char16 - start;
+}
+
+inline const char* ConvertUnicodeToChar(const short* ch16, size_t len, SkAutoMalloc& ch8Malloc) {
+    char* ch8 = (char*) ch8Malloc.get();
+    int index;
+    for (index = 0; index < len; index++) 
+        ch8[index] = (char) ch16[index];
+    ch8[index] = '\0';
+    return ch8;
+}
+#endif
+
+static void XMLCALL start_proc(void *data, const char *el, const char **attr)
+{
+#if defined CHAR_16_TO_9
+    size_t len = sk_wcslen((const short*) el);
+    SkAutoMalloc    el8(len + 1);
+    el = ConvertUnicodeToChar((const short*) el, len, el8);
+#endif
+    if (((SkXMLParser*)data)->startElement(el)) {
+        XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false);
+        return;
+    }
+    while (*attr)
+    {
+        const char* attr0 = attr[0];
+        const char* attr1 = attr[1];
+#if defined CHAR_16_TO_9
+        size_t len0 = sk_wcslen((const short*) attr0);
+        SkAutoMalloc    attr0_8(len0 + 1);
+        attr0 = ConvertUnicodeToChar((const short*) attr0, len0, attr0_8);
+        size_t len1 = sk_wcslen((const short*) attr1);
+        SkAutoMalloc    attr1_8(len1 + 1);
+        attr1 = ConvertUnicodeToChar((const short*) attr1, len1, attr1_8);
+#endif
+        if (((SkXMLParser*)data)->addAttribute(attr0, attr1)) {
+            XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false);
+            return;
+        }
+        attr += 2;
+    }
+}
+
+static void XMLCALL end_proc(void *data, const char *el)
+{
+#if defined CHAR_16_TO_9
+    size_t len = sk_wcslen((const short*) el);
+    SkAutoMalloc    el8(len + 1);
+    el = ConvertUnicodeToChar((const short*) el, len, el8);
+#endif
+    if (((SkXMLParser*)data)->endElement(el))
+        XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false);
+}
+
+static void XMLCALL text_proc(void* data, const char* text, int len)
+{
+#if defined CHAR_16_TO_9
+    SkAutoMalloc    text8(len + 1);
+    text = ConvertUnicodeToChar((const short*) text, len, text8);
+#endif
+    if (((SkXMLParser*)data)->text(text, len))
+        XML_StopParser((XML_Parser) ((SkXMLParser*)data)->fParser, false);
+}
+
+bool SkXMLParser::parse(const char doc[], size_t len)
+{
+    if (len == 0) {
+        fError->fCode = SkXMLParserError::kEmptyFile;
+        reportError(NULL);
+        return false;
+    }
+    XML_Parser p = XML_ParserCreate(NULL);
+    SkASSERT(p);
+    fParser = p;
+    XML_SetElementHandler(p, start_proc, end_proc);
+    XML_SetCharacterDataHandler(p, text_proc);
+    XML_SetUserData(p, this);
+
+    bool success = true;
+    int error = XML_Parse(p, doc, len, true);
+    if (error == XML_STATUS_ERROR) {
+        reportError(p);
+        success = false;
+    }
+    XML_ParserFree(p);
+    return success;
+}
+
+bool SkXMLParser::parse(SkStream& input)
+{
+    size_t          len = input.read(NULL, 0);
+    SkAutoMalloc    am(len);
+    char*           doc = (char*)am.get();
+
+    input.rewind();
+    size_t  len2 = input.read(doc, len);
+    SkASSERT(len2 == len);
+
+    return this->parse(doc, len2);
+}
+
+void SkXMLParser::reportError(void* p)
+{
+    XML_Parser parser = (XML_Parser) p;
+    if (fError && parser) {
+        fError->fNativeCode = XML_GetErrorCode(parser);
+        fError->fLineNumber = XML_GetCurrentLineNumber(parser);
+    }
+}
+
+void SkXMLParser::GetNativeErrorString(int error, SkString* str)
+{
+    if (str)
+        str->set(XML_ErrorString((XML_Error) error));
+}
+
diff --git a/src/ports/SkXMLParser_tinyxml.cpp b/src/ports/SkXMLParser_tinyxml.cpp
new file mode 100644
index 0000000..7f57b80
--- /dev/null
+++ b/src/ports/SkXMLParser_tinyxml.cpp
@@ -0,0 +1,96 @@
+/* libs/graphics/ports/SkXMLParser_tinyxml.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 "SkXMLParser.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "tinyxml.h"
+
+static void walk_elem(SkXMLParser* parser, const TiXmlElement* elem)
+{
+    //printf("walk_elem(%s) ", elem->Value());
+
+    parser->startElement(elem->Value());
+
+    const TiXmlAttribute* attr = elem->FirstAttribute();
+    while (attr)
+    {
+        //printf("walk_elem_attr(%s=\"%s\") ", attr->Name(), attr->Value());
+    
+        parser->addAttribute(attr->Name(), attr->Value());
+        attr = attr->Next();
+    }
+    //printf("\n");
+    
+    const TiXmlNode* node = elem->FirstChild();
+    while (node)
+    {
+        if (node->ToElement())
+            walk_elem(parser, node->ToElement());
+        else if (node->ToText())
+            parser->text(node->Value(), strlen(node->Value()));
+        node = node->NextSibling();
+    }
+    
+    parser->endElement(elem->Value());
+}
+
+static bool load_buf(SkXMLParser* parser, const char buf[])
+{
+    TiXmlDocument                   doc;
+
+    (void)doc.Parse(buf);
+    if (doc.Error())
+    {
+        printf("tinyxml error: <%s> row[%d] col[%d]\n", doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol());
+        return false;
+    }
+    
+    walk_elem(parser, doc.RootElement());
+    return true;
+}
+
+bool SkXMLParser::parse(SkStream& stream)
+{
+    size_t size = stream.read(NULL, 0);
+    
+    SkAutoMalloc    buffer(size + 1);
+    char*           buf = (char*)buffer.get();
+    
+    stream.read(buf, size);
+    buf[size] = 0;
+    
+    return load_buf(this, buf);
+}
+
+bool SkXMLParser::parse(const char doc[], size_t len)
+{
+    SkAutoMalloc    buffer(len + 1);
+    char*           buf = (char*)buffer.get();
+    
+    memcpy(buf, doc, len);
+    buf[len] = 0;
+    
+    return load_buf(this, buf);
+}
+
+void SkXMLParser::GetNativeErrorString(int error, SkString* str)
+{
+    if (str)
+        str->set("GetNativeErrorString not implemented for TinyXml");
+}
+
diff --git a/src/ports/SkXMLPullParser_expat.cpp b/src/ports/SkXMLPullParser_expat.cpp
new file mode 100644
index 0000000..949c7a9
--- /dev/null
+++ b/src/ports/SkXMLPullParser_expat.cpp
@@ -0,0 +1,222 @@
+/* libs/graphics/ports/SkXMLParser_expat.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 "SkXMLParser.h"
+#include "SkChunkAlloc.h"
+#include "SkString.h"
+#include "SkStream.h"
+
+#include "expat.h"
+
+static inline char* dupstr(SkChunkAlloc& chunk, const char src[], size_t len)
+{
+    SkASSERT(src);
+    char*   dst = (char*)chunk.alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType);
+    
+    memcpy(dst, src, len);    
+    dst[len] = 0;
+    return dst;
+}
+
+static inline int count_pairs(const char** p)
+{
+    const char** start = p;
+    while (*p)
+    {
+        SkASSERT(p[1] != NULL);
+        p += 2;
+    }
+    return (p - start) >> 1;
+}
+
+struct Data {
+    Data() : fAlloc(2048), fState(NORMAL) {}
+    
+    XML_Parser              fParser;
+    SkXMLPullParser::Curr*  fCurr;
+    SkChunkAlloc            fAlloc;
+    
+    enum State {
+        NORMAL,
+        MISSED_START_TAG,
+        RETURN_END_TAG
+    };
+    State fState;
+    const char* fEndTag;    // if state is RETURN_END_TAG
+};
+
+static void XMLCALL start_proc(void *data, const char *el, const char **attr)
+{
+    Data*                   p = (Data*)data;
+    SkXMLPullParser::Curr*  c = p->fCurr;
+    SkChunkAlloc&           alloc = p->fAlloc;
+
+    c->fName = dupstr(alloc, el, strlen(el));
+    
+    int n = count_pairs(attr);
+    SkXMLPullParser::AttrInfo* info = (SkXMLPullParser::AttrInfo*)alloc.alloc(n * sizeof(SkXMLPullParser::AttrInfo),
+                                                                              SkChunkAlloc::kThrow_AllocFailType);
+    c->fAttrInfoCount = n;
+    c->fAttrInfos = info;
+
+    for (int i = 0; i < n; i++)
+    {
+        info[i].fName = dupstr(alloc, attr[0], strlen(attr[0]));
+        info[i].fValue = dupstr(alloc, attr[1], strlen(attr[1]));
+        attr += 2;
+    }
+
+    c->fEventType = SkXMLPullParser::START_TAG;
+    XML_StopParser(p->fParser, true);
+}
+
+static void XMLCALL end_proc(void *data, const char *el)
+{
+    Data*                   p = (Data*)data;
+    SkXMLPullParser::Curr*  c = p->fCurr;
+
+    if (c->fEventType == SkXMLPullParser::START_TAG)
+    {
+        /*  if we get here, we were called with a start_tag immediately
+            followed by this end_tag. The caller will only see the end_tag,
+            so we set a flag to notify them of the missed start_tag
+        */
+        p->fState = Data::MISSED_START_TAG;
+        
+        SkASSERT(c->fName != NULL);
+        SkASSERT(strcmp(c->fName, el) == 0);
+    }
+    else
+        c->fName = dupstr(p->fAlloc, el, strlen(el));
+
+    c->fEventType = SkXMLPullParser::END_TAG;
+    XML_StopParser(p->fParser, true);
+}
+
+#include <ctype.h>
+
+static bool isws(const char s[])
+{
+    for (; *s; s++)
+        if (!isspace(*s))
+            return false;
+    return true;
+}
+
+static void XMLCALL text_proc(void* data, const char* text, int len)
+{
+    Data*                   p = (Data*)data;
+    SkXMLPullParser::Curr*  c = p->fCurr;
+
+    c->fName = dupstr(p->fAlloc, text, len);
+    c->fIsWhitespace = isws(c->fName);
+
+    c->fEventType = SkXMLPullParser::TEXT;
+    XML_StopParser(p->fParser, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+struct SkXMLPullParser::Impl {
+    Data    fData;
+    void*   fBuffer;
+    size_t  fBufferLen;
+};
+
+static void reportError(XML_Parser parser)
+{
+    XML_Error code = XML_GetErrorCode(parser);
+    int lineNumber = XML_GetCurrentLineNumber(parser);
+    const char* msg = XML_ErrorString(code);
+    
+    printf("-------- XML error [%d] on line %d, %s\n", code, lineNumber, msg);
+}
+
+bool SkXMLPullParser::onInit()
+{
+    fImpl = new Impl;
+
+    XML_Parser p = XML_ParserCreate(NULL);
+    SkASSERT(p);
+
+    fImpl->fData.fParser = p;
+    fImpl->fData.fCurr = &fCurr;
+
+    XML_SetElementHandler(p, start_proc, end_proc);
+    XML_SetCharacterDataHandler(p, text_proc);
+    XML_SetUserData(p, &fImpl->fData);
+
+    size_t len = fStream->read(NULL, 0);
+    fImpl->fBufferLen = len;
+    fImpl->fBuffer = sk_malloc_throw(len);
+    fStream->rewind();
+    size_t  len2 = fStream->read(fImpl->fBuffer, len);
+    return len2 == len;
+}
+
+void SkXMLPullParser::onExit()
+{
+    sk_free(fImpl->fBuffer);
+    XML_ParserFree(fImpl->fData.fParser);
+    delete fImpl;
+    fImpl = NULL;
+}
+
+SkXMLPullParser::EventType SkXMLPullParser::onNextToken()
+{
+    if (Data::RETURN_END_TAG == fImpl->fData.fState)
+    {
+        fImpl->fData.fState = Data::NORMAL;
+        fCurr.fName = fImpl->fData.fEndTag; // restore name from (below) save
+        return SkXMLPullParser::END_TAG;
+    }
+
+    fImpl->fData.fAlloc.reuse();
+
+    XML_Parser p = fImpl->fData.fParser;
+    XML_Status status;
+
+    status = XML_ResumeParser(p);
+    
+CHECK_STATUS:
+    switch (status) {
+    case XML_STATUS_OK:
+        return SkXMLPullParser::END_DOCUMENT;
+
+    case XML_STATUS_ERROR:
+        if (XML_GetErrorCode(p) != XML_ERROR_NOT_SUSPENDED)
+        {
+            reportError(p);
+            return SkXMLPullParser::ERROR;
+        }
+        status = XML_Parse(p, (const char*)fImpl->fBuffer, fImpl->fBufferLen, true);
+        goto CHECK_STATUS;
+
+    case XML_STATUS_SUSPENDED:
+        if (Data::MISSED_START_TAG == fImpl->fData.fState)
+        {
+            // return a start_tag, and clear the flag so we return end_tag next
+            SkASSERT(SkXMLPullParser::END_TAG == fCurr.fEventType);
+            fImpl->fData.fState = Data::RETURN_END_TAG;
+            fImpl->fData.fEndTag = fCurr.fName;  // save this pointer            
+            return SkXMLPullParser::START_TAG;
+        }
+        break;
+    }
+    return fCurr.fEventType;
+}
+
diff --git a/src/ports/sk_predefined_gamma.h b/src/ports/sk_predefined_gamma.h
new file mode 100644
index 0000000..0818b30
--- /dev/null
+++ b/src/ports/sk_predefined_gamma.h
@@ -0,0 +1,44 @@
+#ifndef SK_PREDEFINED_GAMMA_H
+#define SK_PREDEFINED_GAMMA_H
+
+// Gamma table for 1.4
+static const uint8_t gBlackGamma[] = {
+    0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 
+    0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D, 
+    0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 
+    0x19, 0x19, 0x1A, 0x1B, 0x1C, 0x1C, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21, 0x22, 0x22, 0x23, 0x24, 
+    0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x31, 
+    0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 
+    0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 
+    0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 
+    0x61, 0x62, 0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 
+    0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x84, 
+    0x85, 0x86, 0x87, 0x88, 0x89, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x91, 0x92, 0x93, 0x94, 0x95, 0x97, 
+    0x98, 0x99, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA3, 0xA4, 0xA5, 0xA6, 0xA8, 0xA9, 0xAA, 
+    0xAB, 0xAD, 0xAE, 0xAF, 0xB0, 0xB2, 0xB3, 0xB4, 0xB5, 0xB7, 0xB8, 0xB9, 0xBB, 0xBC, 0xBD, 0xBE, 
+    0xC0, 0xC1, 0xC2, 0xC4, 0xC5, 0xC6, 0xC8, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF, 0xD1, 0xD2, 0xD3, 
+    0xD5, 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3, 0xE5, 0xE6, 0xE8, 0xE9, 
+    0xEA, 0xEC, 0xED, 0xEE, 0xF0, 0xF1, 0xF2, 0xF4, 0xF5, 0xF7, 0xF8, 0xF9, 0xFB, 0xFC, 0xFE, 0xFF, 
+};
+
+// Gamma table for 0.714286
+static const uint8_t gWhiteGamma[] = {
+    0x00, 0x05, 0x08, 0x0B, 0x0D, 0x0F, 0x12, 0x14, 0x16, 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20, 0x22, 
+    0x23, 0x25, 0x26, 0x28, 0x29, 0x2B, 0x2C, 0x2E, 0x2F, 0x31, 0x32, 0x33, 0x35, 0x36, 0x37, 0x39, 
+    0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x40, 0x41, 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 
+    0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 
+    0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 
+    0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 
+    0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 
+    0x8E, 0x8F, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x97, 0x98, 0x99, 0x9A, 0x9B, 
+    0x9C, 0x9D, 0x9E, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 
+    0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB4, 0xB5, 0xB6, 
+    0xB7, 0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC0, 0xC1, 0xC2, 0xC3, 
+    0xC4, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 
+    0xD0, 0xD1, 0xD2, 0xD3, 0xD3, 0xD4, 0xD5, 0xD6, 0xD6, 0xD7, 0xD8, 0xD9, 0xD9, 0xDA, 0xDB, 0xDC, 
+    0xDC, 0xDD, 0xDE, 0xDF, 0xDF, 0xE0, 0xE1, 0xE2, 0xE2, 0xE3, 0xE4, 0xE5, 0xE5, 0xE6, 0xE7, 0xE8, 
+    0xE8, 0xE9, 0xEA, 0xEB, 0xEB, 0xEC, 0xED, 0xEE, 0xEE, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, 0xF3, 0xF3, 
+    0xF4, 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF9, 0xF9, 0xFA, 0xFB, 0xFB, 0xFC, 0xFD, 0xFE, 0xFE, 0xFF, 
+};
+
+#endif
diff --git a/src/svg/SkSVG.cpp b/src/svg/SkSVG.cpp
new file mode 100644
index 0000000..bcd62bf
--- /dev/null
+++ b/src/svg/SkSVG.cpp
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVG.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 "SkSVG.h"
+#include 'SkSVGParser.h"
+
+SkSVG::SkSVG() {
+}
+
+SkSVG::~SkSVG() {
+}
+
+bool SkSVG::decodeStream(SkStream* stream);
+{
+    size_t size = stream->read(nil, 0);
+    SkAutoMalloc    storage(size);
+    char* data = (char*)storage.get();
+    size_t actual = stream->read(data, size);
+    SkASSERT(size == actual);
+    SkSVGParser parser(*fMaker);
+    return parser.parse(data, actual, &fErrorCode, &fErrorLineNumber);
+}
diff --git a/src/svg/SkSVGCircle.cpp b/src/svg/SkSVGCircle.cpp
new file mode 100644
index 0000000..d27521d
--- /dev/null
+++ b/src/svg/SkSVGCircle.cpp
@@ -0,0 +1,53 @@
+/* libs/graphics/svg/SkSVGCircle.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 "SkSVGCircle.h"
+#include "SkSVGParser.h"
+#include "SkParse.h"
+#include <stdio.h>
+
+const SkSVGAttribute SkSVGCircle::gAttributes[] = {
+    SVG_ATTRIBUTE(cx),
+    SVG_ATTRIBUTE(cy),
+    SVG_ATTRIBUTE(r)
+};
+
+DEFINE_SVG_INFO(Circle)
+
+void SkSVGCircle::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("oval");
+    INHERITED::translate(parser, defState);
+    SkScalar cx, cy, r;
+    SkParse::FindScalar(f_cx.c_str(), &cx);
+    SkParse::FindScalar(f_cy.c_str(), &cy);
+    SkParse::FindScalar(f_r.c_str(), &r);
+    SkScalar left, top, right, bottom;
+    left = cx - r;
+    top = cy - r;
+    right = cx + r;
+    bottom = cy + r;
+    char scratch[16];
+    sprintf(scratch, "%g", left);
+    parser._addAttribute("left", scratch);
+    sprintf(scratch, "%g", top);
+    parser._addAttribute("top", scratch);
+    sprintf(scratch, "%g", right);
+    parser._addAttribute("right", scratch);
+    sprintf(scratch, "%g", bottom);
+    parser._addAttribute("bottom", scratch);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGCircle.h b/src/svg/SkSVGCircle.h
new file mode 100644
index 0000000..343a367
--- /dev/null
+++ b/src/svg/SkSVGCircle.h
@@ -0,0 +1,32 @@
+/* libs/graphics/svg/SkSVGCircle.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 SkSVGCircle_DEFINED
+#define SkSVGCircle_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGCircle : public SkSVGElement {
+    DECLARE_SVG_INFO(Circle);
+private:
+    SkString f_cx;
+    SkString f_cy;
+    SkString f_r;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGCircle_DEFINED
diff --git a/src/svg/SkSVGClipPath.cpp b/src/svg/SkSVGClipPath.cpp
new file mode 100644
index 0000000..bd71429
--- /dev/null
+++ b/src/svg/SkSVGClipPath.cpp
@@ -0,0 +1,48 @@
+/* libs/graphics/svg/SkSVGClipPath.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 "SkSVGClipPath.h"
+#include "SkSVGParser.h"
+#include "SkSVGUse.h"
+
+DEFINE_SVG_NO_INFO(ClipPath)
+
+bool SkSVGClipPath::isDef() {
+    return true;
+}
+
+bool SkSVGClipPath::isNotDef() {
+    return false;
+}
+
+void SkSVGClipPath::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("clip");
+    INHERITED::translate(parser, defState);
+    SkASSERT(fChildren.count() == 1);
+    SkSVGElement* child = *fChildren.begin();
+    SkASSERT(child->getType() == SkSVGType_Use);
+    SkSVGUse* use = (SkSVGUse*) child;
+    SkSVGElement* ref;
+    const char* refStr = &use->f_xlink_href.c_str()[1];
+    SkASSERT(parser.getIDs().find(refStr, &ref));
+    SkASSERT(ref);
+    if (ref->getType() == SkSVGType_Rect) 
+        parser._addAttribute("rectangle", refStr);
+    else
+        parser._addAttribute("path", refStr);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGClipPath.h b/src/svg/SkSVGClipPath.h
new file mode 100644
index 0000000..6fa17b8
--- /dev/null
+++ b/src/svg/SkSVGClipPath.h
@@ -0,0 +1,31 @@
+/* libs/graphics/svg/SkSVGClipPath.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 SkSVGClipPath_DEFINED
+#define SkSVGClipPath_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGClipPath : public SkSVGElement {
+    DECLARE_SVG_INFO(ClipPath);
+    virtual bool isDef();
+    virtual bool isNotDef();
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGClipPath_DEFINED
diff --git a/src/svg/SkSVGDefs.cpp b/src/svg/SkSVGDefs.cpp
new file mode 100644
index 0000000..0499075
--- /dev/null
+++ b/src/svg/SkSVGDefs.cpp
@@ -0,0 +1,32 @@
+/* libs/graphics/svg/SkSVGDefs.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 "SkSVGDefs.h"
+
+DEFINE_SVG_NO_INFO(Defs)
+
+bool SkSVGDefs::isDef() {
+    return true;
+}
+
+bool SkSVGDefs::isNotDef() {
+    return false;
+}
+
+void SkSVGDefs::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGDefs.h b/src/svg/SkSVGDefs.h
new file mode 100644
index 0000000..45f0106
--- /dev/null
+++ b/src/svg/SkSVGDefs.h
@@ -0,0 +1,31 @@
+/* libs/graphics/svg/SkSVGDefs.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 SkSVGDefs_DEFINED
+#define SkSVGDefs_DEFINED
+
+#include "SkSVGGroup.h"
+
+class SkSVGDefs : public SkSVGGroup {
+    DECLARE_SVG_INFO(Defs);
+    virtual bool isDef();
+    virtual bool isNotDef();
+private:
+    typedef SkSVGGroup INHERITED;
+};
+
+#endif // SkSVGDefs_DEFINED
diff --git a/src/svg/SkSVGElements.cpp b/src/svg/SkSVGElements.cpp
new file mode 100644
index 0000000..8e195c7
--- /dev/null
+++ b/src/svg/SkSVGElements.cpp
@@ -0,0 +1,96 @@
+/* libs/graphics/svg/SkSVGElements.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 "SkSVGElements.h"
+#include "SkSVGParser.h"
+
+SkSVGBase::~SkSVGBase() {
+}
+
+void SkSVGBase::addAttribute(SkSVGParser& parser, int attrIndex, 
+        const char* attrValue, size_t attrLength) {
+    SkString* first = (SkString*) ((char*) this + sizeof(SkSVGElement));
+    first += attrIndex;
+    first->set(attrValue, attrLength);
+}
+
+
+SkSVGElement::SkSVGElement() : fParent(NULL), fIsDef(false), fIsNotDef(true) {
+}
+
+SkSVGElement::~SkSVGElement() {
+}
+
+SkSVGElement* SkSVGElement::getGradient() {
+    return NULL;
+}
+
+bool SkSVGElement::isGroupParent() {
+    SkSVGElement* parent = fParent;
+    while (parent) {
+        if (parent->getType() != SkSVGType_G)
+            return false;
+        parent = parent->fParent;
+    }
+    return true;
+}
+
+bool SkSVGElement::isDef() {
+    return isGroupParent() == false ? fParent->isDef() : fIsDef;
+}
+
+bool SkSVGElement::isFlushable() {
+    return true;
+}
+
+bool SkSVGElement::isGroup() {
+    return false;
+}
+
+bool SkSVGElement::isNotDef() {
+    return isGroupParent() == false ? fParent->isNotDef() : fIsNotDef;
+}
+
+bool SkSVGElement::onEndElement(SkSVGParser& parser) {
+    if (f_id.size() > 0)
+        parser.getIDs().set(f_id.c_str(), f_id.size(), this);
+    return false;
+}
+
+bool SkSVGElement::onStartElement(SkSVGElement* child) {
+    *fChildren.append() = child;
+    return false;
+}
+
+void SkSVGElement::translate(SkSVGParser& parser, bool) {
+    if (f_id.size() > 0)
+        SVG_ADD_ATTRIBUTE(id);
+}
+
+void SkSVGElement::setIsDef() {
+    fIsDef = isDef();
+}
+
+//void SkSVGElement::setIsNotDef() {
+//  fIsNotDef = isNotDef();
+//}
+
+void SkSVGElement::write(SkSVGParser& , SkString& ) {
+    SkASSERT(0); 
+}
+
+
diff --git a/src/svg/SkSVGElements.h b/src/svg/SkSVGElements.h
new file mode 100644
index 0000000..ed3f921
--- /dev/null
+++ b/src/svg/SkSVGElements.h
@@ -0,0 +1,81 @@
+/* libs/graphics/svg/SkSVGElements.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 SkSVGElements_DEFINED
+#define SkSVGElements_DEFINED
+
+#include "SkSVGPaintState.h"
+#include "SkSVGTypes.h"
+#include "SkTDArray.h"
+
+class SkSVGParser;
+
+#define DECLARE_SVG_INFO(_type) \
+public: \
+    virtual ~SkSVG##_type(); \
+    static const SkSVGAttribute gAttributes[]; \
+    virtual int getAttributes(const SkSVGAttribute** attrPtr); \
+    virtual SkSVGTypes getType() const; \
+    virtual void translate(SkSVGParser& parser, bool defState); \
+    typedef SkSVG##_type BASE_CLASS
+
+#define DEFINE_SVG_INFO(_type) \
+    SkSVG##_type::~SkSVG##_type() {} \
+    int SkSVG##_type::getAttributes(const SkSVGAttribute** attrPtr) { \
+        *attrPtr = gAttributes; \
+        return SK_ARRAY_COUNT(gAttributes); \
+    } \
+    SkSVGTypes SkSVG##_type::getType() const { return SkSVGType_##_type; }
+
+#define DEFINE_SVG_NO_INFO(_type) \
+    SkSVG##_type::~SkSVG##_type() {} \
+    int SkSVG##_type::getAttributes(const SkSVGAttribute** ) { return 0; } \
+    SkSVGTypes SkSVG##_type::getType() const { return SkSVGType_##_type; }
+
+
+struct SkSVGTypeName {
+    const char* fName;
+    SkSVGTypes fType;
+};
+
+class SkSVGElement : public SkSVGBase {
+public:
+    SkSVGElement();
+    virtual ~SkSVGElement();
+    virtual SkSVGElement* getGradient();
+    virtual SkSVGTypes getType() const  = 0;
+    virtual bool isDef();
+    virtual bool isFlushable();
+    virtual bool isGroup();
+    virtual bool isNotDef();
+    virtual bool onEndElement(SkSVGParser& parser);
+    virtual bool onStartElement(SkSVGElement* child);
+    void setIsDef();
+//  void setIsNotDef();
+    virtual void translate(SkSVGParser& parser, bool defState);
+    virtual void write(SkSVGParser& , SkString& color);
+    SkString f_id;
+    SkSVGPaint fPaintState;
+    SkTDArray<SkSVGElement*> fChildren;
+    SkSVGElement* fParent;
+    bool fIsDef;
+    bool fIsNotDef;
+private:
+    bool isGroupParent();
+};
+
+#endif // SkSVGElements_DEFINED
diff --git a/src/svg/SkSVGEllipse.cpp b/src/svg/SkSVGEllipse.cpp
new file mode 100644
index 0000000..3752b83
--- /dev/null
+++ b/src/svg/SkSVGEllipse.cpp
@@ -0,0 +1,55 @@
+/* libs/graphics/svg/SkSVGEllipse.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 "SkSVGEllipse.h"
+#include "SkSVGParser.h"
+#include "SkParse.h"
+#include <stdio.h>
+
+const SkSVGAttribute SkSVGEllipse::gAttributes[] = {
+    SVG_ATTRIBUTE(cx),
+    SVG_ATTRIBUTE(cy),
+    SVG_ATTRIBUTE(rx),
+    SVG_ATTRIBUTE(ry)
+};
+
+DEFINE_SVG_INFO(Ellipse)
+
+void SkSVGEllipse::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("oval");
+    INHERITED::translate(parser, defState);
+    SkScalar cx, cy, rx, ry;
+    SkParse::FindScalar(f_cx.c_str(), &cx);
+    SkParse::FindScalar(f_cy.c_str(), &cy);
+    SkParse::FindScalar(f_rx.c_str(), &rx);
+    SkParse::FindScalar(f_ry.c_str(), &ry);
+    SkScalar left, top, right, bottom;
+    left = cx - rx;
+    top = cy - ry;
+    right = cx + rx;
+    bottom = cy + ry;
+    char scratch[16];
+    sprintf(scratch, "%g", left);
+    parser._addAttribute("left", scratch);
+    sprintf(scratch, "%g", top);
+    parser._addAttribute("top", scratch);
+    sprintf(scratch, "%g", right);
+    parser._addAttribute("right", scratch);
+    sprintf(scratch, "%g", bottom);
+    parser._addAttribute("bottom", scratch);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGEllipse.h b/src/svg/SkSVGEllipse.h
new file mode 100644
index 0000000..566f16c
--- /dev/null
+++ b/src/svg/SkSVGEllipse.h
@@ -0,0 +1,33 @@
+/* libs/graphics/svg/SkSVGEllipse.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 SkSVGEllipse_DEFINED
+#define SkSVGEllipse_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGEllipse : public SkSVGElement {
+    DECLARE_SVG_INFO(Ellipse);
+private:
+    SkString f_cx;
+    SkString f_cy;
+    SkString f_rx;
+    SkString f_ry;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGEllipse_DEFINED
diff --git a/src/svg/SkSVGFeColorMatrix.cpp b/src/svg/SkSVGFeColorMatrix.cpp
new file mode 100644
index 0000000..a758704
--- /dev/null
+++ b/src/svg/SkSVGFeColorMatrix.cpp
@@ -0,0 +1,32 @@
+/* libs/graphics/svg/SkSVGFeColorMatrix.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 "SkSVGFeColorMatrix.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGFeColorMatrix::gAttributes[] = {
+    SVG_LITERAL_ATTRIBUTE(color-interpolation-filters, f_color_interpolation_filters),
+    SVG_ATTRIBUTE(result),
+    SVG_ATTRIBUTE(type),
+    SVG_ATTRIBUTE(values)
+};
+
+DEFINE_SVG_INFO(FeColorMatrix)
+
+void SkSVGFeColorMatrix::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGFeColorMatrix.h b/src/svg/SkSVGFeColorMatrix.h
new file mode 100644
index 0000000..431d532
--- /dev/null
+++ b/src/svg/SkSVGFeColorMatrix.h
@@ -0,0 +1,34 @@
+/* libs/graphics/svg/SkSVGFeColorMatrix.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 SkSVGFeColorMatrix_DEFINED
+#define SkSVGFeColorMatrix_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGFeColorMatrix : public SkSVGElement {
+    DECLARE_SVG_INFO(FeColorMatrix);
+protected:
+    SkString f_color_interpolation_filters;
+    SkString f_result;
+    SkString f_type;
+    SkString f_values;
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGFeColorMatrix_DEFINED
diff --git a/src/svg/SkSVGFilter.cpp b/src/svg/SkSVGFilter.cpp
new file mode 100644
index 0000000..1d32e56
--- /dev/null
+++ b/src/svg/SkSVGFilter.cpp
@@ -0,0 +1,33 @@
+/* libs/graphics/svg/SkSVGFilter.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 "SkSVGFilter.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGFilter::gAttributes[] = {
+    SVG_ATTRIBUTE(filterUnits),
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(x),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Filter)
+
+void SkSVGFilter::translate(SkSVGParser& parser, bool defState) {
+//  INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGFilter.h b/src/svg/SkSVGFilter.h
new file mode 100644
index 0000000..6ec9bd5
--- /dev/null
+++ b/src/svg/SkSVGFilter.h
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVGFilter.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 SkSVGFilter_DEFINED
+#define SkSVGFilter_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGFilter : public SkSVGElement {
+    DECLARE_SVG_INFO(Filter);
+protected:
+    SkString f_filterUnits;
+    SkString f_height;
+    SkString f_width;
+    SkString f_x;
+    SkString f_y;
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGFilter_DEFINEDRITED;
+
diff --git a/src/svg/SkSVGG.cpp b/src/svg/SkSVGG.cpp
new file mode 100644
index 0000000..f73b8b8
--- /dev/null
+++ b/src/svg/SkSVGG.cpp
@@ -0,0 +1,24 @@
+/* libs/graphics/svg/SkSVGG.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 "SkSVGG.h"
+
+DEFINE_SVG_NO_INFO(G)
+
+void SkSVGG::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGG.h b/src/svg/SkSVGG.h
new file mode 100644
index 0000000..4c5c68a
--- /dev/null
+++ b/src/svg/SkSVGG.h
@@ -0,0 +1,29 @@
+/* libs/graphics/svg/SkSVGG.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 SkSVGG_DEFINED
+#define SkSVGG_DEFINED
+
+#include "SkSVGGroup.h"
+
+class SkSVGG : public SkSVGGroup {
+    DECLARE_SVG_INFO(G);
+private:
+    typedef SkSVGGroup INHERITED;
+};
+
+#endif // SkSVGG_DEFINED
diff --git a/src/svg/SkSVGGradient.cpp b/src/svg/SkSVGGradient.cpp
new file mode 100644
index 0000000..c89bdc9
--- /dev/null
+++ b/src/svg/SkSVGGradient.cpp
@@ -0,0 +1,123 @@
+/* libs/graphics/svg/SkSVGGradient.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 "SkSVGGradient.h"
+#include "SkSVGParser.h"
+#include "SkSVGStop.h"
+
+SkSVGGradient::SkSVGGradient() {
+}
+
+SkSVGElement* SkSVGGradient::getGradient() {
+    return this;
+}
+
+bool SkSVGGradient::isDef() {
+    return true;
+}
+
+bool SkSVGGradient::isNotDef() {
+    return false;
+}
+
+void SkSVGGradient::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+    // !!! no support for 'objectBoundingBox' yet
+    bool first = true;
+    bool addedFirst = false;
+    bool addedLast = false;
+    SkString offsets("[");
+    SkString* lastOffset = NULL;
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkASSERT((*ptr)->getType() == SkSVGType_Stop);
+        SkSVGStop* stop = (SkSVGStop*) *ptr;
+        if (first && stop->f_offset.equals("0") == false) {
+            addedFirst = true;
+            offsets.append("0,");
+        }
+        SkString* thisOffset = &stop->f_offset;
+        if (lastOffset && thisOffset->equals(*lastOffset)) {
+            if (thisOffset->equals("1")) {
+                offsets.remove(offsets.size() - 2, 2);
+                offsets.append(".999,");
+            } else {
+                SkASSERT(0); // !!! need to write this case
+            }
+        }
+        offsets.append(*thisOffset);
+        if (ptr == fChildren.end() - 1) { // last
+            if (stop->f_offset.equals("1") == false) {
+                offsets.append(",1");
+                addedLast = true;
+            }
+        } else
+            offsets.appendUnichar(',');
+        first = false;
+        lastOffset = thisOffset;
+    }
+    offsets.appendUnichar(']');
+    parser._addAttribute("offsets", offsets);
+    if (addedFirst)
+        parser.translate(*fChildren.begin(), defState);
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++)
+        parser.translate(*ptr, defState);
+    if (addedLast)
+        parser.translate(*(fChildren.end() - 1), defState);
+}
+
+void SkSVGGradient::translateGradientUnits(SkString& units) {
+    // !!! no support for 'objectBoundingBox' yet
+    SkASSERT(strcmp(units.c_str(), "userSpaceOnUse") == 0);
+}
+
+void SkSVGGradient::write(SkSVGParser& parser, SkString& baseColor) {
+    if (baseColor.c_str()[0] != '#')
+        return;
+    SkSVGPaint* saveHead = parser.fHead;
+    parser.fHead = &fPaintState;
+    parser.fSuppressPaint = true;
+    SkString originalID(f_id);
+    f_id.set("mask"); // write out gradient named given name + color (less initial #)
+    f_id.append(baseColor.c_str() + 1); 
+    SkString originalColors;
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkSVGStop* colorElement = (SkSVGStop*) *ptr;
+        SkString& color = colorElement->fPaintState.f_stopColor;
+        originalColors.append(color);
+        originalColors.appendUnichar(',');
+        SkASSERT(color.c_str()[0] == '#');
+        SkString replacement;
+        replacement.set("0x");
+        replacement.append(color.c_str() + 1, 2); // add stop colors using given color, turning existing stop color into alpha
+        SkASSERT(baseColor.c_str()[0] == '#');
+        SkASSERT(baseColor.size() == 7);
+        replacement.append(baseColor.c_str() + 1);
+        color.set(replacement);
+    }
+    translate(parser, true);
+    const char* originalPtr = originalColors.c_str(); // restore original gradient values
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkSVGStop* color = (SkSVGStop*) *ptr;
+        const char* originalEnd = strchr(originalPtr, ',');
+        color->fPaintState.f_stopColor.set(originalPtr, originalEnd - originalPtr);
+        originalPtr = originalEnd + 1;
+    }
+    f_id.set(originalID);
+    parser.fSuppressPaint = false;
+    parser.fHead = saveHead;
+}
+
diff --git a/src/svg/SkSVGGradient.h b/src/svg/SkSVGGradient.h
new file mode 100644
index 0000000..873b258
--- /dev/null
+++ b/src/svg/SkSVGGradient.h
@@ -0,0 +1,37 @@
+/* libs/graphics/svg/SkSVGGradient.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 SkSVGGradient_DEFINED
+#define SkSVGGradient_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGGradient : public SkSVGElement {
+public:
+    SkSVGGradient();
+    virtual SkSVGElement* getGradient();
+    virtual bool isDef();
+    virtual bool isNotDef();
+    virtual void write(SkSVGParser& , SkString& color);
+protected:
+    void translate(SkSVGParser& , bool defState);
+    void translateGradientUnits(SkString& units);
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGGradient_DEFINED
diff --git a/src/svg/SkSVGGroup.cpp b/src/svg/SkSVGGroup.cpp
new file mode 100644
index 0000000..069aa56
--- /dev/null
+++ b/src/svg/SkSVGGroup.cpp
@@ -0,0 +1,53 @@
+/* libs/graphics/svg/SkSVGGroup.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 "SkSVGGroup.h"
+#include "SkSVGParser.h"
+
+SkSVGGroup::SkSVGGroup() {
+    fIsNotDef = false;
+}
+
+SkSVGElement* SkSVGGroup::getGradient() {
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkSVGElement* result = (*ptr)->getGradient();
+        if (result != NULL)
+            return result;
+    }
+    return NULL;
+}
+
+bool SkSVGGroup::isDef() {
+    return fParent ? fParent->isDef() : false;
+}
+
+bool SkSVGGroup::isFlushable() {
+    return false;
+}
+
+bool SkSVGGroup::isGroup() {
+    return true;
+}
+
+bool SkSVGGroup::isNotDef() {
+    return fParent ? fParent->isNotDef() : false;
+}
+
+void SkSVGGroup::translate(SkSVGParser& parser, bool defState) {
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++)
+        parser.translate(*ptr, defState);
+}
diff --git a/src/svg/SkSVGGroup.h b/src/svg/SkSVGGroup.h
new file mode 100644
index 0000000..2e34cbb
--- /dev/null
+++ b/src/svg/SkSVGGroup.h
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVGGroup.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 SkSVGGroup_DEFINED
+#define SkSVGGroup_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGGroup : public SkSVGElement {
+public:
+    SkSVGGroup();
+    virtual SkSVGElement* getGradient();
+    virtual bool isDef();
+    virtual bool isFlushable();
+    virtual bool isGroup();
+    virtual bool isNotDef();
+    void translate(SkSVGParser& , bool defState);
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGGroup_DEFINED
diff --git a/src/svg/SkSVGImage.cpp b/src/svg/SkSVGImage.cpp
new file mode 100644
index 0000000..b641c9f7
--- /dev/null
+++ b/src/svg/SkSVGImage.cpp
@@ -0,0 +1,52 @@
+/* libs/graphics/svg/SkSVGImage.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 "SkSVGImage.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGImage::gAttributes[] = {
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(x),
+    SVG_LITERAL_ATTRIBUTE(xlink:href, f_xlink_href),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Image)
+
+void SkSVGImage::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("image");
+    INHERITED::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE(x);
+    SVG_ADD_ATTRIBUTE(y);
+//  SVG_ADD_ATTRIBUTE(width);
+//  SVG_ADD_ATTRIBUTE(height);
+    translateImage(parser);
+    parser._endElement();
+}
+
+void SkSVGImage::translateImage(SkSVGParser& parser) {
+    SkASSERT(f_xlink_href.size() > 0);
+    const char* data = f_xlink_href.c_str();
+    SkASSERT(strncmp(data, "data:image/", 11) == 0);
+    data += 11;
+    SkASSERT(strncmp(data, "png;", 4) == 0 || strncmp(data, "jpeg;", 5) == 0);
+    data = strchr(data, ';');
+    SkASSERT(strncmp(data, ";base64,", 8) == 0);
+    data += 8;
+    parser._addAttribute("base64", data);
+}
diff --git a/src/svg/SkSVGImage.h b/src/svg/SkSVGImage.h
new file mode 100644
index 0000000..2f73b0d
--- /dev/null
+++ b/src/svg/SkSVGImage.h
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVGImage.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 SkSVGImage_DEFINED
+#define SkSVGImage_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGImage : public SkSVGElement {
+public:
+    DECLARE_SVG_INFO(Image);
+private:
+    void translateImage(SkSVGParser& parser);
+    SkString f_height;
+    SkString f_width;
+    SkString f_x;
+    SkString f_xlink_href;
+    SkString f_y;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGImage_DEFINED
diff --git a/src/svg/SkSVGLine.cpp b/src/svg/SkSVGLine.cpp
new file mode 100644
index 0000000..105822a
--- /dev/null
+++ b/src/svg/SkSVGLine.cpp
@@ -0,0 +1,38 @@
+/* libs/graphics/svg/SkSVGLine.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 "SkSVGLine.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGLine::gAttributes[] = {
+    SVG_ATTRIBUTE(x1),
+    SVG_ATTRIBUTE(x2),
+    SVG_ATTRIBUTE(y1),
+    SVG_ATTRIBUTE(y2)
+};
+
+DEFINE_SVG_INFO(Line)
+
+void SkSVGLine::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("line");
+    INHERITED::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE(x1);
+    SVG_ADD_ATTRIBUTE(y1);
+    SVG_ADD_ATTRIBUTE(x2);
+    SVG_ADD_ATTRIBUTE(y2);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGLine.h b/src/svg/SkSVGLine.h
new file mode 100644
index 0000000..f2cfce9
--- /dev/null
+++ b/src/svg/SkSVGLine.h
@@ -0,0 +1,33 @@
+/* libs/graphics/svg/SkSVGLine.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 SkSVGLine_DEFINED
+#define SkSVGLine_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGLine : public SkSVGElement {
+    DECLARE_SVG_INFO(Line);
+private:
+    SkString f_x1;
+    SkString f_x2;
+    SkString f_y1;
+    SkString f_y2;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGLine_DEFINED
diff --git a/src/svg/SkSVGLinearGradient.cpp b/src/svg/SkSVGLinearGradient.cpp
new file mode 100644
index 0000000..a474d15
--- /dev/null
+++ b/src/svg/SkSVGLinearGradient.cpp
@@ -0,0 +1,52 @@
+/* libs/graphics/svg/SkSVGLinearGradient.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 "SkSVGLinearGradient.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGLinearGradient::gAttributes[] = {
+    SVG_ATTRIBUTE(gradientTransform),
+    SVG_ATTRIBUTE(gradientUnits),
+    SVG_ATTRIBUTE(x1),
+    SVG_ATTRIBUTE(x2),
+    SVG_ATTRIBUTE(y1),
+    SVG_ATTRIBUTE(y2)
+};
+
+DEFINE_SVG_INFO(LinearGradient)
+
+void SkSVGLinearGradient::translate(SkSVGParser& parser, bool defState) {
+    if (fMatrixID.size() == 0)
+        parser.translateMatrix(f_gradientTransform, &fMatrixID);
+    parser._startElement("linearGradient");
+    if (fMatrixID.size() > 0)
+        parser._addAttribute("matrix", fMatrixID);
+    INHERITED::translateGradientUnits(f_gradientUnits);
+    SkString points;
+    points.appendUnichar('[');
+    points.append(f_x1);
+    points.appendUnichar(',');
+    points.append(f_y1);
+    points.appendUnichar(',');
+    points.append(f_x2);
+    points.appendUnichar(',');
+    points.append(f_y2);
+    points.appendUnichar(']');
+    parser._addAttribute("points", points.c_str());
+    INHERITED::translate(parser, defState);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGLinearGradient.h b/src/svg/SkSVGLinearGradient.h
new file mode 100644
index 0000000..85e28e7
--- /dev/null
+++ b/src/svg/SkSVGLinearGradient.h
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVGLinearGradient.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 SkSVGLinearGradient_DEFINED
+#define SkSVGLinearGradient_DEFINED
+
+#include "SkSVGGradient.h"
+
+class SkSVGLinearGradient : public SkSVGGradient {
+    DECLARE_SVG_INFO(LinearGradient);
+private:
+    SkString f_gradientTransform;
+    SkString f_gradientUnits;
+    SkString f_x1;
+    SkString f_x2;
+    SkString f_y1;
+    SkString f_y2;
+    SkString fMatrixID;
+    typedef SkSVGGradient INHERITED;
+};
+
+#endif // SkSVGLinearGradient_DEFINED
diff --git a/src/svg/SkSVGMask.cpp b/src/svg/SkSVGMask.cpp
new file mode 100644
index 0000000..eb93118
--- /dev/null
+++ b/src/svg/SkSVGMask.cpp
@@ -0,0 +1,41 @@
+/* libs/graphics/svg/SkSVGMask.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 "SkSVGMask.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGMask::gAttributes[] = {
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(maskUnits),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(x),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Mask)
+
+bool SkSVGMask::isDef() {
+    return false;
+}
+
+bool SkSVGMask::isNotDef() {
+    return false;
+}
+
+void SkSVGMask::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGMask.h b/src/svg/SkSVGMask.h
new file mode 100644
index 0000000..6e349b4
--- /dev/null
+++ b/src/svg/SkSVGMask.h
@@ -0,0 +1,37 @@
+/* libs/graphics/svg/SkSVGMask.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 SkSVGMask_DEFINED
+#define SkSVGMask_DEFINED
+
+#include "SkSVGGroup.h"
+
+class SkSVGMask : public SkSVGGroup {
+    DECLARE_SVG_INFO(Mask);
+    virtual bool isDef();
+    virtual bool isNotDef();
+protected:
+    SkString f_height;
+    SkString f_maskUnits;
+    SkString f_width;
+    SkString f_x;
+    SkString f_y;
+private:
+    typedef SkSVGGroup INHERITED;
+};
+
+#endif // SkSVGMask_DEFINED
diff --git a/src/svg/SkSVGMetadata.cpp b/src/svg/SkSVGMetadata.cpp
new file mode 100644
index 0000000..7c9e6ba
--- /dev/null
+++ b/src/svg/SkSVGMetadata.cpp
@@ -0,0 +1,32 @@
+/* libs/graphics/svg/SkSVGMetadata.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 "SkSVGMetadata.h"
+#include "SkSVGParser.h"
+
+DEFINE_SVG_NO_INFO(Metadata)
+
+bool SkSVGMetadata::isDef() {
+    return false;
+}
+
+bool SkSVGMetadata::isNotDef() {
+    return false;
+}
+
+void SkSVGMetadata::translate(SkSVGParser& parser, bool defState) {
+}
diff --git a/src/svg/SkSVGMetadata.h b/src/svg/SkSVGMetadata.h
new file mode 100644
index 0000000..f3b5b6c
--- /dev/null
+++ b/src/svg/SkSVGMetadata.h
@@ -0,0 +1,31 @@
+/* libs/graphics/svg/SkSVGMetadata.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 SkSVGMetadata_DEFINED
+#define SkSVGMetadata_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGMetadata : public SkSVGElement {
+    DECLARE_SVG_INFO(Metadata);
+    virtual bool isDef();
+    virtual bool isNotDef();
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGMetadata_DEFINED
diff --git a/src/svg/SkSVGPaintState.cpp b/src/svg/SkSVGPaintState.cpp
new file mode 100644
index 0000000..7fc90c7
--- /dev/null
+++ b/src/svg/SkSVGPaintState.cpp
@@ -0,0 +1,463 @@
+/* libs/graphics/svg/SkSVGPaintState.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 "SkSVGPaintState.h"
+#include "SkSVGElements.h"
+#include "SkSVGParser.h"
+#include "SkParse.h"
+
+SkSVGAttribute SkSVGPaint::gAttributes[] = {
+    SVG_LITERAL_ATTRIBUTE(clip-path, f_clipPath),
+    SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule),
+    SVG_LITERAL_ATTRIBUTE(enable-background, f_enableBackground),
+    SVG_ATTRIBUTE(fill),
+    SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule),
+    SVG_ATTRIBUTE(filter),
+    SVG_LITERAL_ATTRIBUTE(font-family, f_fontFamily),
+    SVG_LITERAL_ATTRIBUTE(font-size, f_fontSize),
+    SVG_LITERAL_ATTRIBUTE(letter-spacing, f_letterSpacing),
+    SVG_ATTRIBUTE(mask),
+    SVG_ATTRIBUTE(opacity),
+    SVG_LITERAL_ATTRIBUTE(stop-color, f_stopColor),
+    SVG_LITERAL_ATTRIBUTE(stop-opacity, f_stopOpacity),
+    SVG_ATTRIBUTE(stroke),
+    SVG_LITERAL_ATTRIBUTE(stroke-dasharray, f_strokeDasharray),
+    SVG_LITERAL_ATTRIBUTE(stroke-linecap, f_strokeLinecap),
+    SVG_LITERAL_ATTRIBUTE(stroke-linejoin, f_strokeLinejoin),
+    SVG_LITERAL_ATTRIBUTE(stroke-miterlimit, f_strokeMiterlimit),
+    SVG_LITERAL_ATTRIBUTE(stroke-width, f_strokeWidth),
+    SVG_ATTRIBUTE(style),
+    SVG_ATTRIBUTE(transform)
+};
+
+const int SkSVGPaint::kAttributesSize = SK_ARRAY_COUNT(SkSVGPaint::gAttributes);
+
+SkSVGPaint::SkSVGPaint() : fNext(NULL) {
+}
+
+SkString* SkSVGPaint::operator[](int index) {
+    SkASSERT(index >= 0);
+    SkASSERT(index < &fTerminal - &fInitial);
+    SkASSERT(&fTerminal - &fInitial == kTerminal - kInitial);
+    SkString* result = &fInitial + index + 1;
+    return result;
+}
+
+void SkSVGPaint::addAttribute(SkSVGParser& parser, int attrIndex, 
+        const char* attrValue, size_t attrLength) {
+    SkString* attr = (*this)[attrIndex];
+    switch(attrIndex) {
+        case kClipPath:
+        case kClipRule:
+        case kEnableBackground:
+        case kFill:
+        case kFillRule:
+        case kFilter:
+        case kFontFamily:
+        case kFontSize:
+        case kLetterSpacing:
+        case kMask:
+        case kOpacity:
+        case kStopColor:
+        case kStopOpacity:
+        case kStroke:
+        case kStroke_Dasharray:
+        case kStroke_Linecap:
+        case kStroke_Linejoin:
+        case kStroke_Miterlimit:
+        case kStroke_Width:
+        case kTransform:
+            attr->set(attrValue, attrLength);
+            return;
+        case kStyle: {
+            // iterate through colon / semi-colon delimited pairs
+            int pairs = SkParse::Count(attrValue, ';');
+            const char* attrEnd = attrValue + attrLength;
+            do {
+                const char* end = strchr(attrValue, ';');
+                if (end == NULL)
+                    end = attrEnd;
+                const char* delimiter = strchr(attrValue, ':');
+                SkASSERT(delimiter != 0 && delimiter < end);
+                int index = parser.findAttribute(this, attrValue, (int) (delimiter - attrValue), true);
+                SkASSERT(index >= 0);
+                delimiter++;
+                addAttribute(parser, index, delimiter, (int) (end - delimiter));
+                attrValue = end + 1;
+            } while (--pairs);
+            return;
+            }
+        default:
+            SkASSERT(0);
+    }
+}
+
+bool SkSVGPaint::flush(SkSVGParser& parser, bool isFlushable, bool isDef) {
+    SkSVGPaint current;
+    SkSVGPaint* walking = parser.fHead;
+    int index;
+    while (walking != NULL) {
+        for (index = kInitial + 1; index < kTerminal; index++) {
+            SkString* lastAttr = (*walking)[index];
+            if (lastAttr->size() == 0)
+                continue;
+            if (current[index]->size() > 0)
+                continue;
+            current[index]->set(*lastAttr);
+        }
+        walking = walking->fNext;
+    }
+    bool paintChanged = false;
+    SkSVGPaint& lastState = parser.fLastFlush;
+    if (isFlushable == false) {
+        if (isDef == true) {
+            if (current.f_mask.size() > 0 && current.f_mask.equals(lastState.f_mask) == false) {
+                SkSVGElement* found;
+                const char* idStart = strchr(current.f_mask.c_str(), '#');
+                SkASSERT(idStart);
+                SkString id(idStart + 1, strlen(idStart) - 2);
+                bool itsFound = parser.fIDs.find(id.c_str(), &found);
+                SkASSERT(itsFound);
+                SkSVGElement* gradient = found->getGradient();
+                if (gradient) {
+                    gradient->write(parser, current.f_fill);
+                    gradient->write(parser, current.f_stroke);
+                }
+            }
+        }
+        goto setLast;
+    }
+    {
+        bool changed[kTerminal];
+        memset(changed, 0, sizeof(changed));
+        for (index = kInitial + 1; index < kTerminal; index++) {
+            if (index == kTransform || index == kClipPath || index == kStopColor || index == kStopOpacity ||
+                    index == kClipRule || index == kFillRule)
+                continue;
+            SkString* lastAttr = lastState[index];
+            SkString* currentAttr = current[index];
+            paintChanged |= changed[index] = lastAttr->equals(*currentAttr) == false;
+        }
+        if (paintChanged) {
+            if (current.f_mask.size() > 0) {
+                if (current.f_fill.equals("none") == false && strncmp(current.f_fill.c_str(), "url(#", 5) != 0) {
+                    SkASSERT(current.f_fill.c_str()[0] == '#');
+                    SkString replacement("url(#mask");
+                    replacement.append(current.f_fill.c_str() + 1);
+                    replacement.appendUnichar(')');
+                    current.f_fill.set(replacement);
+                }
+                if (current.f_stroke.equals("none") == false && strncmp(current.f_stroke.c_str(), "url(#", 5) != 0) {
+                    SkASSERT(current.f_stroke.c_str()[0] == '#');
+                    SkString replacement("url(#mask");
+                    replacement.append(current.f_stroke.c_str() + 1);
+                    replacement.appendUnichar(')');
+                    current.f_stroke.set(replacement);
+                }
+            }
+            if (current.f_fill.equals("none") && current.f_stroke.equals("none"))
+                current.f_opacity.set("0");
+            if (parser.fSuppressPaint == false) {
+                parser._startElement("paint");
+                bool success = writeChangedAttributes(parser, current, changed);
+                if (success == false)
+                    return paintChanged;
+                success = writeChangedElements(parser, current, changed);
+                if (success == false)
+                    return paintChanged;
+                parser._endElement(); // paint
+            }
+        }
+    }
+setLast:
+    for (index = kInitial + 1; index < kTerminal; index++) {
+        SkString* lastAttr = lastState[index];
+        SkString* currentAttr = current[index];
+        lastAttr->set(*currentAttr);
+    }
+    return paintChanged;
+}
+
+int SkSVGPaint::getAttributes(const SkSVGAttribute** attrPtr) {
+    *attrPtr = gAttributes;
+    return kAttributesSize;
+}
+
+void SkSVGPaint::setSave(SkSVGParser& parser) {
+    SkTDArray<SkString*> clips;
+    SkSVGPaint* walking = parser.fHead;
+    int index;
+    SkMatrix sum;
+    sum.reset();
+    while (walking != NULL) {
+        for (index = kInitial + 1; index < kTerminal; index++) {
+            SkString* lastAttr = (*walking)[index];
+            if (lastAttr->size() == 0)
+                continue;
+            if (index == kTransform) {
+                const char* str = lastAttr->c_str();
+                SkASSERT(strncmp(str, "matrix(", 7) == 0);
+                str += 6;
+                const char* strEnd = strrchr(str, ')');
+                SkASSERT(strEnd != NULL);
+                SkString mat(str, strEnd - str);
+                SkSVGParser::ConvertToArray(mat);
+                SkScalar values[6];
+                SkParse::FindScalars(mat.c_str() + 1, values, 6);
+                SkMatrix matrix;
+                matrix.reset();
+                matrix.setScaleX(values[0]);
+                matrix.setSkewY(values[1]);
+                matrix.setSkewX(values[2]);
+                matrix.setScaleY(values[3]);
+                matrix.setTranslateX(values[4]);
+                matrix.setTranslateY(values[5]);
+                sum.setConcat(matrix, sum);
+                continue;
+            }
+            if ( index == kClipPath) 
+                *clips.insert(0) = lastAttr;
+        }
+        walking = walking->fNext;
+    }
+    if ((sum == parser.fLastTransform) == false) {
+        SkMatrix inverse;
+        bool success = parser.fLastTransform.invert(&inverse);
+        SkASSERT(success == true);
+        SkMatrix output;
+        output.setConcat(inverse, sum);
+        parser.fLastTransform = sum;
+        SkString outputStr;
+        outputStr.appendUnichar('[');
+        outputStr.appendScalar(output.getScaleX());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getSkewX());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getTranslateX());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getSkewY());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getScaleY());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getTranslateY());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getPerspX());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getPerspY());
+        outputStr.append(",1]");
+        parser._startElement("matrix");
+        parser._addAttributeLen("matrix", outputStr.c_str(), outputStr.size());
+        parser._endElement();
+    }
+#if 0   // incomplete
+    if (parser.fTransformClips.size() > 0) {
+        // need to reset the clip when the 'g' scope is ended
+        parser._startElement("add");
+        const char* start = strchr(current->f_clipPath.c_str(), '#') + 1;
+        SkASSERT(start);
+        parser._addAttributeLen("use", start, strlen(start) - 1);
+        parser._endElement();   // clip
+    }
+#endif
+}
+
+bool SkSVGPaint::writeChangedAttributes(SkSVGParser& parser, 
+        SkSVGPaint& current, bool* changed) {
+    SkSVGPaint& lastState = parser.fLastFlush;
+    for (int index = kInitial + 1; index < kTerminal; index++) {
+        if (changed[index] == false)
+                continue;
+        SkString* topAttr = current[index];
+        size_t attrLength = topAttr->size();
+        if (attrLength == 0)
+            continue;
+        const char* attrValue = topAttr->c_str();
+        SkString* lastAttr = lastState[index];
+        switch(index) {
+            case kClipPath:
+            case kClipRule:
+            case kEnableBackground:
+                break;
+            case kFill:
+                if (topAttr->equals("none") == false && lastAttr->equals("none") == true) 
+                    parser._addAttribute("stroke", "false");
+                goto fillStrokeAttrCommon;
+            case kFillRule:
+            case kFilter:
+            case kFontFamily:
+                break;
+            case kFontSize:
+                parser._addAttributeLen("textSize", attrValue, attrLength);
+                break;
+            case kLetterSpacing:
+                parser._addAttributeLen("textTracking", attrValue, attrLength);
+                break;
+            case kMask:
+                break;
+            case kOpacity:
+                break;
+            case kStopColor:
+                break;
+            case kStopOpacity:
+                break;
+            case kStroke:
+                if (topAttr->equals("none") == false && lastAttr->equals("none") == true) 
+                    parser._addAttribute("stroke", "true");
+fillStrokeAttrCommon:
+                if (strncmp(attrValue, "url(", 4) == 0) {
+                    SkASSERT(attrValue[4] == '#');
+                    const char* idStart = attrValue + 5;
+                    char* idEnd = strrchr(attrValue, ')');
+                    SkASSERT(idStart < idEnd);
+                    SkString id(idStart, idEnd - idStart);
+                    SkSVGElement* found;
+                    if (strncmp(id.c_str(), "mask", 4) != 0) {
+                        bool itsFound = parser.fIDs.find(id.c_str(), &found);
+                        SkASSERT(itsFound);
+                        SkASSERT(found->getType() == SkSVGType_LinearGradient ||
+                            found->getType() == SkSVGType_RadialGradient);
+                    }
+                    parser._addAttribute("shader", id.c_str());
+                }
+                break;
+            case kStroke_Dasharray:
+                break;
+            case kStroke_Linecap:
+                parser._addAttributeLen("strokeCap", attrValue, attrLength);
+                break;
+            case kStroke_Linejoin:
+                parser._addAttributeLen("strokeJoin", attrValue, attrLength);
+                break;
+            case kStroke_Miterlimit:
+                parser._addAttributeLen("strokeMiter", attrValue, attrLength);
+                break;
+            case kStroke_Width:
+                parser._addAttributeLen("strokeWidth", attrValue, attrLength);
+            case kStyle:
+            case kTransform:
+                break;
+        default:
+            SkASSERT(0);
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SkSVGPaint::writeChangedElements(SkSVGParser& parser,
+        SkSVGPaint& current, bool* changed) {
+    SkSVGPaint& lastState = parser.fLastFlush;
+    for (int index = kInitial + 1; index < kTerminal; index++) {
+        SkString* topAttr = current[index];
+        size_t attrLength = topAttr->size();
+        if (attrLength == 0)
+            continue;
+        const char* attrValue = topAttr->c_str();
+        SkString* lastAttr = lastState[index];
+        switch(index) {
+            case kClipPath:
+            case kClipRule:
+                // !!! need to add this outside of paint
+                break;
+            case kEnableBackground:
+                // !!! don't know what to do with this
+                break;
+            case kFill:
+                goto addColor;
+            case kFillRule:
+            case kFilter:
+                break;
+            case kFontFamily:
+                parser._startElement("typeface");
+                parser._addAttributeLen("fontName", attrValue, attrLength);
+                parser._endElement();   // typeface
+                break;
+            case kFontSize:
+            case kLetterSpacing:
+                break;
+            case kMask:
+            case kOpacity:
+                if (changed[kStroke] == false && changed[kFill] == false) {
+                    parser._startElement("color");
+                    SkString& opacity = current.f_opacity;
+                    parser._addAttributeLen("color", parser.fLastColor.c_str(), parser.fLastColor.size());
+                    parser._addAttributeLen("alpha", opacity.c_str(), opacity.size());
+                    parser._endElement();   // color
+                }
+                break;
+            case kStopColor:
+                break;
+            case kStopOpacity:
+                break;
+            case kStroke:
+addColor:
+                if (strncmp(lastAttr->c_str(), "url(", 4) == 0 && strncmp(attrValue, "url(", 4) != 0) {
+                    parser._startElement("shader");
+                    parser._endElement();
+                }
+                if (topAttr->equals(*lastAttr))
+                    continue;
+                {
+                    bool urlRef = strncmp(attrValue, "url(", 4) == 0;
+                    bool colorNone = strcmp(attrValue, "none") == 0;
+                    bool lastEqual = parser.fLastColor.equals(attrValue, attrLength);
+                    bool newColor = urlRef == false && colorNone == false && lastEqual == false;
+                    if (newColor || changed[kOpacity]) {
+                        parser._startElement("color");
+                        if (newColor || changed[kOpacity]) {
+                            parser._addAttributeLen("color", attrValue, attrLength);
+                            parser.fLastColor.set(attrValue, attrLength);
+                        }
+                        if (changed[kOpacity]) {
+                            SkString& opacity = current.f_opacity;
+                            parser._addAttributeLen("alpha", opacity.c_str(), opacity.size());
+                        }
+                        parser._endElement();   // color
+                    }
+                }
+                break;
+            case kStroke_Dasharray:
+                parser._startElement("dash");
+                SkSVGParser::ConvertToArray(*topAttr);
+                parser._addAttribute("intervals", topAttr->c_str());
+                parser._endElement();   // dash
+            break;
+            case kStroke_Linecap:
+            case kStroke_Linejoin:
+            case kStroke_Miterlimit:
+            case kStroke_Width:
+            case kStyle:
+            case kTransform:
+                break;
+        default:
+            SkASSERT(0);
+            return false;
+        }
+    }
+    return true;
+}
+                
+void SkSVGPaint::Push(SkSVGPaint** head, SkSVGPaint* newRecord) {
+    newRecord->fNext = *head;
+    *head = newRecord;
+}
+
+void SkSVGPaint::Pop(SkSVGPaint** head) {
+    SkSVGPaint* next = (*head)->fNext;
+    *head = next;
+}
+
diff --git a/src/svg/SkSVGParser.cpp b/src/svg/SkSVGParser.cpp
new file mode 100644
index 0000000..b55c5ed
--- /dev/null
+++ b/src/svg/SkSVGParser.cpp
@@ -0,0 +1,443 @@
+/* libs/graphics/svg/SkSVGParser.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 "SkSVGParser.h"
+#include "SkSVGCircle.h"
+#include "SkSVGClipPath.h"
+#include "SkSVGDefs.h"
+#include "SkSVGEllipse.h"
+#include "SkSVGFeColorMatrix.h"
+#include "SkSVGFilter.h"
+#include "SkSVGG.h"
+#include "SkSVGImage.h"
+#include "SkSVGLine.h"
+#include "SkSVGLinearGradient.h"
+#include "SkSVGMask.h"
+#include "SkSVGMetadata.h"
+#include "SkSVGPath.h"
+#include "SkSVGPolygon.h"
+#include "SkSVGPolyline.h"
+#include "SkSVGRadialGradient.h"
+#include "SkSVGRect.h"
+#include "SkSVGSVG.h"
+#include "SkSVGStop.h"
+#include "SkSVGSymbol.h"
+#include "SkSVGText.h"
+#include "SkSVGUse.h"
+#include "SkTSearch.h"
+#include <stdio.h>
+
+static int gGeneratedMatrixID = 0;
+
+SkSVGParser::SkSVGParser() : fHead(&fEmptyPaint), fIDs(256),
+        fXMLWriter(&fStream), fCurrElement(NULL), fInSVG(false), fSuppressPaint(false) {
+    fLastTransform.reset();
+    fEmptyPaint.f_fill.set("black");
+    fEmptyPaint.f_stroke.set("none");
+    fEmptyPaint.f_strokeMiterlimit.set("4");
+    fEmptyPaint.f_fillRule.set("winding");
+    fEmptyPaint.f_opacity.set("1");
+    fEmptyPaint.fNext = NULL;
+    for (int index = SkSVGPaint::kInitial + 1; index < SkSVGPaint::kTerminal; index++) {
+        SkString* initial = fEmptyPaint[index];
+        if (initial->size() == 0)
+            continue;
+        fLastFlush[index]->set(*initial);
+    }
+}
+
+SkSVGParser::~SkSVGParser() {
+}
+
+void SkSVGParser::Delete(SkTDArray<SkSVGElement*>& fChildren) {
+    SkSVGElement** ptr;
+    for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        Delete((*ptr)->fChildren);
+        delete *ptr;
+    }
+}
+
+int SkSVGParser::findAttribute(SkSVGBase* element, const char* attrValue,
+        size_t len, bool isPaint) {
+    const SkSVGAttribute* attributes;
+    int count = element->getAttributes(&attributes);
+    int result = 0;
+    while (result < count) {
+        if (strncmp(attributes->fName, attrValue, len) == 0 && strlen(attributes->fName) == len) {
+            SkASSERT(result == (attributes->fOffset - 
+                (isPaint ? sizeof(SkString) : sizeof(SkSVGElement))) / sizeof(SkString));
+            return result;
+        }
+        attributes++;
+        result++;
+    }
+    return -1;
+}
+
+const char* SkSVGParser::getFinal() {
+    _startElement("screenplay");
+    // generate defs
+    SkSVGElement** ptr;
+    for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkSVGElement* element = *ptr;
+        translate(element, true);
+    }
+    // generate onLoad
+    _startElement("event");
+    _addAttribute("kind", "onLoad");
+    _startElement("paint");
+    _addAttribute("antiAlias", "true");
+    _endElement();
+    for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkSVGElement* element = *ptr;
+        translate(element, false);
+    }
+    _endElement(); // event
+    _endElement(); // screenplay
+    Delete(fChildren);
+    fStream.write("", 1);
+    return fStream.getStream();
+}
+
+SkString& SkSVGParser::getPaintLast(SkSVGPaint::Field field) {
+    SkSVGPaint* state = fHead;
+    do {
+        SkString* attr = (*state)[field];
+        SkASSERT(attr);
+        if (attr->size() > 0)
+            return *attr;
+        state = state->fNext;
+    } while (state);
+    SkASSERT(0);
+    SkASSERT(fEmptyPaint[field]);
+    return *fEmptyPaint[field];
+}
+
+bool SkSVGParser::isStrokeAndFill(  SkSVGPaint** strokeState, SkSVGPaint** fillState) {
+    SkSVGPaint* walking = fHead;
+    bool stroke = false;
+    bool fill = false;
+    bool strokeSet = false;
+    bool fillSet = false;
+    while (walking != NULL) {
+        if (strokeSet == false && walking->f_stroke.size() > 0) {
+            stroke = walking->f_stroke.equals("none") == false;
+            *strokeState = walking;
+            strokeSet = true;
+        }
+        if (fillSet == false && walking->f_fill.size() > 0) {
+            fill = walking->f_fill.equals("none") == false;
+            *fillState = walking;
+            fillSet = true;
+        }
+        walking = walking->fNext;
+    }
+    return stroke && fill;
+}
+
+bool SkSVGParser::onAddAttribute(const char name[], const char value[]) {
+    return onAddAttributeLen(name, value, strlen(value));
+}
+
+bool SkSVGParser::onAddAttributeLen(const char name[], const char value[], size_t len) {
+    if (fCurrElement == NULL)    // this signals we should ignore attributes for this element
+        return true;
+    if (fCurrElement->fIsDef == false && fCurrElement->fIsNotDef == false)
+        return true; // also an ignored element
+    size_t nameLen = strlen(name);
+    int attrIndex = findAttribute(fCurrElement, name, nameLen, false);
+    if (attrIndex == -1) {
+        attrIndex = findAttribute(&fCurrElement->fPaintState, name, nameLen, true);
+        if (attrIndex >= 0) {
+            fCurrElement->fPaintState.addAttribute(*this, attrIndex, value, len);
+            return false;
+        }
+        if (nameLen == 2 && strncmp("id", name, nameLen) == 0) {
+            fCurrElement->f_id.set(value, len);
+            return false;
+        }
+        if (strchr(name, ':') != 0) // part of a different namespace
+            return false;
+    }
+    SkASSERT(attrIndex >= 0);
+    fCurrElement->addAttribute(*this, attrIndex, value, len);
+    return false;
+}
+
+bool SkSVGParser::onEndElement(const char elem[]) {
+    int parentIndex = fParents.count() - 1;
+    if (parentIndex >= 0) {
+        SkSVGElement* element = fParents[parentIndex];
+        element->onEndElement(*this);
+        fParents.remove(parentIndex);
+    }
+    return false;
+}
+
+bool SkSVGParser::onStartElement(const char name[]) {
+    return onStartElementLen(name, strlen(name));
+}
+
+bool SkSVGParser::onStartElementLen(const char name[], size_t len) {
+    if (strncmp(name, "svg", len) == 0) {
+        fInSVG = true;
+    } else if (fInSVG == false)
+        return false;
+    const char* nextColon = strchr(name, ':');
+    if (nextColon && nextColon - name < len)
+        return false;
+    SkSVGTypes type = GetType(name, len);
+    SkASSERT(type >= 0);
+    if (type < 0)
+        return true;
+    SkSVGElement* parent = fParents.count() > 0 ? fParents.top() : NULL;
+    SkSVGElement* element = CreateElement(type, parent);
+    bool result = false;
+    if (parent) {
+        element->fParent = parent;
+        result = fParents.top()->onStartElement(element);
+    } else
+        *fChildren.append() = element;
+    if (strncmp(name, "svg", len) != 0)
+        *fParents.append() = element;
+    fCurrElement = element;
+    return result;
+}
+
+bool SkSVGParser::onText(const char text[], int len) {
+    if (fInSVG == false)
+        return false;
+    SkSVGTypes type = fCurrElement->getType(); 
+    if (type != SkSVGType_Text && type != SkSVGType_Tspan)
+        return false;
+    SkSVGText* textElement = (SkSVGText*) fCurrElement;
+    textElement->f_text.set(text, len);
+    return false;
+}
+
+static int32_t strokeFillID = 0;
+
+void SkSVGParser::translate(SkSVGElement* element, bool isDef) {
+    SkSVGPaint::Push(&fHead, &element->fPaintState);
+    bool isFlushable = element->isFlushable();
+    if ((element->fIsDef == false && element->fIsNotDef == false) ||
+        (element->fIsDef && isDef == false && element->fIsNotDef == false) ||
+        (element->fIsDef == false && isDef && element->fIsNotDef)) {
+        isFlushable = false;
+    }
+    SkSVGPaint* strokeState = NULL, * fillState = NULL;
+    if (isFlushable)
+        element->fPaintState.setSave(*this);
+    if (isFlushable && isStrokeAndFill(&strokeState, &fillState)) {
+        SkString& elementID = element->f_id;
+        if (elementID.size() == 0) {
+            elementID.set("sf");
+            elementID.appendS32(++strokeFillID);
+        }
+        SkString saveStroke(strokeState->f_stroke);
+        SkString saveFill(fillState->f_fill);
+        strokeState->f_stroke.set("none");
+        element->fPaintState.flush(*this, isFlushable, isDef);
+        element->translate(*this, isDef);
+        strokeState->f_stroke.set(saveStroke);
+        fillState->f_fill.set("none");
+        if (element->fPaintState.flush(*this, isFlushable, isDef)) {
+            _startElement("add");
+            _addAttributeLen("use", elementID.c_str(), elementID.size());
+            _endElement();  // add
+        }
+        fillState->f_fill.set(saveFill);
+    } else {
+        element->fPaintState.flush(*this, isFlushable, isDef);
+        if (isFlushable || element->isGroup())
+            element->translate(*this, isDef);
+    }
+    SkSVGPaint::Pop(&fHead);
+}
+
+void SkSVGParser::translateMatrix(SkString& string, SkString* stringID) {
+    if (string.size() == 0)
+        return;
+    if (stringID->size() > 0) {
+        _startElement("add");
+        _addAttribute("use", stringID->c_str());
+        _endElement(); // add
+        return;
+    }
+    SkASSERT(strncmp(string.c_str(), "matrix", 6) == 0);
+    ++gGeneratedMatrixID;
+    _startElement("matrix");
+    char idStr[24];
+    strcpy(idStr, "sk_matrix");
+    sprintf(idStr + strlen(idStr), "%d", gGeneratedMatrixID);
+    _addAttribute("id", idStr);
+    stringID->set(idStr);
+    const char* str = string.c_str();
+    SkASSERT(strncmp(str, "matrix(", 7) == 0);
+    str += 6;
+    const char* strEnd = strrchr(str, ')');
+    SkASSERT(strEnd != NULL);
+    SkString mat(str, strEnd - str);
+    ConvertToArray(mat);
+    const char* elems[6];
+    static const int order[] = {0, 3, 1, 4, 2, 5};
+    const int* orderPtr = order;
+    str = mat.c_str();
+    strEnd = str + mat.size();
+    while (str < strEnd) {
+        elems[*orderPtr++] = str;
+        while (str < strEnd && *str != ',' )
+            str++;
+        str++;
+    }
+    string.reset();
+    for (int index = 0; index < 6; index++) {
+        const char* end = strchr(elems[index], ',');
+        if (end == NULL)
+            end= strchr(elems[index], ']');
+        string.append(elems[index], end - elems[index] + 1);
+    }
+    string.remove(string.size() - 1, 1);
+    string.append(",0,0,1]");    
+    _addAttribute("matrix", string);
+    _endElement();  // matrix
+}
+
+static bool is_whitespace(char ch) {
+    return ch > 0 && ch <= ' ';
+}
+
+void SkSVGParser::ConvertToArray(SkString& vals) {
+    vals.appendUnichar(']');
+    char* valCh = (char*) vals.c_str();
+    valCh[0] = '[';
+    int index = 1;
+    while (valCh[index] != ']') {
+        while (is_whitespace(valCh[index]))
+            index++;
+        bool foundComma = false;
+        char next;
+        do {
+            next = valCh[index++];
+            if (next == ',') {
+                foundComma = true;
+                continue;
+            }
+            if (next == ']') {
+                index--;
+                goto undoLastComma;
+            }
+            if (next == ' ')
+                break;
+            foundComma = false;
+        } while (is_whitespace(next) == false);
+        if (foundComma == false)
+            valCh[index - 1] = ',';
+    }
+undoLastComma:
+    while (is_whitespace(valCh[--index]))
+        ;
+    if (valCh[index] == ',')
+        valCh[index] = ' ';
+}
+
+#define CASE_NEW(type) case SkSVGType_##type : created = new SkSVG##type(); break
+
+SkSVGElement* SkSVGParser::CreateElement(SkSVGTypes type, SkSVGElement* parent) {
+    SkSVGElement* created = NULL;
+    switch (type) {
+        CASE_NEW(Circle);
+        CASE_NEW(ClipPath);
+        CASE_NEW(Defs);
+        CASE_NEW(Ellipse);
+        CASE_NEW(FeColorMatrix);
+        CASE_NEW(Filter);
+        CASE_NEW(G);
+        CASE_NEW(Image);
+        CASE_NEW(Line);
+        CASE_NEW(LinearGradient);
+        CASE_NEW(Mask);
+        CASE_NEW(Metadata);
+        CASE_NEW(Path);
+        CASE_NEW(Polygon);
+        CASE_NEW(Polyline);
+        CASE_NEW(RadialGradient);
+        CASE_NEW(Rect);
+        CASE_NEW(Stop);
+        CASE_NEW(SVG);
+        CASE_NEW(Symbol);
+        CASE_NEW(Text);
+        CASE_NEW(Tspan);
+        CASE_NEW(Use);
+        default:
+            SkASSERT(0);
+            return NULL;
+    }
+    created->fParent = parent;
+    bool isDef = created->fIsDef = created->isDef();
+    bool isNotDef = created->fIsNotDef = created->isNotDef();
+    if (isDef) {
+        SkSVGElement* up = parent;
+        while (up && up->fIsDef == false) {
+            up->fIsDef = true;
+            up = up->fParent;
+        }
+    }
+    if (isNotDef) {
+        SkSVGElement* up = parent;
+        while (up && up->fIsNotDef == false) {
+            up->fIsNotDef = true;
+            up = up->fParent;
+        }
+    }
+    return created;
+}
+
+const SkSVGTypeName gSVGTypeNames[] = {
+    {"circle", SkSVGType_Circle},
+    {"clipPath", SkSVGType_ClipPath},
+    {"defs", SkSVGType_Defs},
+    {"ellipse", SkSVGType_Ellipse},
+    {"feColorMatrix", SkSVGType_FeColorMatrix},
+    {"filter", SkSVGType_Filter},
+    {"g", SkSVGType_G},
+    {"image", SkSVGType_Image},
+    {"line", SkSVGType_Line},
+    {"linearGradient", SkSVGType_LinearGradient},
+    {"mask", SkSVGType_Mask},
+    {"metadata", SkSVGType_Metadata},
+    {"path", SkSVGType_Path},
+    {"polygon", SkSVGType_Polygon},
+    {"polyline", SkSVGType_Polyline},
+    {"radialGradient", SkSVGType_RadialGradient},
+    {"rect", SkSVGType_Rect},
+    {"stop", SkSVGType_Stop},
+    {"svg", SkSVGType_SVG},
+    {"symbol", SkSVGType_Symbol},
+    {"text", SkSVGType_Text},
+    {"tspan", SkSVGType_Tspan},
+    {"use", SkSVGType_Use}
+};
+
+const int kSVGTypeNamesSize = SK_ARRAY_COUNT(gSVGTypeNames);
+
+SkSVGTypes SkSVGParser::GetType(const char match[], size_t len ) {
+    int index = SkStrSearch(&gSVGTypeNames[0].fName, kSVGTypeNamesSize, match, 
+        len, sizeof(gSVGTypeNames[0]));
+    return index >= 0 && index < kSVGTypeNamesSize ? gSVGTypeNames[index].fType : 
+        (SkSVGTypes) -1;
+}
diff --git a/src/svg/SkSVGPath.cpp b/src/svg/SkSVGPath.cpp
new file mode 100644
index 0000000..a916c30
--- /dev/null
+++ b/src/svg/SkSVGPath.cpp
@@ -0,0 +1,45 @@
+/* libs/graphics/svg/SkSVGPath.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 "SkSVGPath.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGPath::gAttributes[] = {
+    SVG_ATTRIBUTE(d)
+};
+
+DEFINE_SVG_INFO(Path)
+
+void SkSVGPath::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("path");
+    INHERITED::translate(parser, defState);
+    bool hasMultiplePaths = false;
+    const char* firstZ = strchr(f_d.c_str(), 'z');
+    if (firstZ != NULL) {
+        firstZ++; // skip over 'z'
+        while (*firstZ == ' ')
+            firstZ++;
+        hasMultiplePaths = *firstZ != '\0';
+    }
+    if (hasMultiplePaths) {
+        SkString& fillRule = parser.getPaintLast(SkSVGPaint::kFillRule);
+        if (fillRule.size() > 0) 
+            parser._addAttribute("fillType", fillRule.equals("evenodd") ? "evenOdd" : "winding");
+    }
+    SVG_ADD_ATTRIBUTE(d);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGPath.h b/src/svg/SkSVGPath.h
new file mode 100644
index 0000000..8a0c210
--- /dev/null
+++ b/src/svg/SkSVGPath.h
@@ -0,0 +1,30 @@
+/* libs/graphics/svg/SkSVGPath.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 SkSVGPath_DEFINED
+#define SkSVGPath_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGPath : public SkSVGElement {
+    DECLARE_SVG_INFO(Path);
+private:
+    SkString f_d;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGPath_DEFINED
diff --git a/src/svg/SkSVGPolygon.cpp b/src/svg/SkSVGPolygon.cpp
new file mode 100644
index 0000000..283422c
--- /dev/null
+++ b/src/svg/SkSVGPolygon.cpp
@@ -0,0 +1,41 @@
+/* libs/graphics/svg/SkSVGPolygon.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 "SkSVGPolygon.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGPolygon::gAttributes[] = {
+    SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule),
+    SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule),
+    SVG_ATTRIBUTE(points)
+};
+
+DEFINE_SVG_INFO(Polygon)
+
+void SkSVGPolygon::addAttribute(SkSVGParser& parser, int attrIndex, 
+        const char* attrValue, size_t attrLength) {
+    INHERITED::addAttribute(parser, attrIndex, attrValue, attrLength);
+}
+
+void SkSVGPolygon::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("polygon");
+    SkSVGElement::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE(points);
+    if (f_fillRule.size() > 0) 
+        parser._addAttribute("fillType", f_fillRule.equals("evenodd") ? "evenOdd" : "winding");
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGPolygon.h b/src/svg/SkSVGPolygon.h
new file mode 100644
index 0000000..1f27c5c
--- /dev/null
+++ b/src/svg/SkSVGPolygon.h
@@ -0,0 +1,31 @@
+/* libs/graphics/svg/SkSVGPolygon.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 SkSVGPolygon_DEFINED
+#define SkSVGPolygon_DEFINED
+
+#include "SkSVGPolyline.h"
+
+class SkSVGPolygon : public SkSVGPolyline {
+    DECLARE_SVG_INFO(Polygon);
+    virtual void addAttribute(SkSVGParser& , int attrIndex, 
+        const char* attrValue, size_t attrLength);
+private:
+    typedef SkSVGPolyline INHERITED;
+};
+
+#endif // SkSVGPolygon_DEFINED
diff --git a/src/svg/SkSVGPolyline.cpp b/src/svg/SkSVGPolyline.cpp
new file mode 100644
index 0000000..8432c95
--- /dev/null
+++ b/src/svg/SkSVGPolyline.cpp
@@ -0,0 +1,51 @@
+/* libs/graphics/svg/SkSVGPolyline.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 "SkSVGPolyline.h"
+#include "SkSVGParser.h"
+
+enum {
+    kCliipRule,
+    kFillRule,
+    kPoints
+};
+
+const SkSVGAttribute SkSVGPolyline::gAttributes[] = {
+    SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule),
+    SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule),
+    SVG_ATTRIBUTE(points)
+};
+
+DEFINE_SVG_INFO(Polyline)
+
+void SkSVGPolyline::addAttribute(SkSVGParser& , int attrIndex, 
+        const char* attrValue, size_t attrLength) {
+    if (attrIndex != kPoints)
+        return;
+    f_points.set("[");
+    f_points.append(attrValue, attrLength);
+    SkSVGParser::ConvertToArray(f_points);
+}
+
+void SkSVGPolyline::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("polyline");
+    INHERITED::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE(points);
+    if (f_fillRule.size() > 0) 
+        parser._addAttribute("fillType", f_fillRule.equals("evenodd") ? "evenOdd" : "winding");
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGPolyline.h b/src/svg/SkSVGPolyline.h
new file mode 100644
index 0000000..7f25129
--- /dev/null
+++ b/src/svg/SkSVGPolyline.h
@@ -0,0 +1,35 @@
+/* libs/graphics/svg/SkSVGPolyline.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 SkSVGPolyline_DEFINED
+#define SkSVGPolyline_DEFINED
+
+#include "SkSVGElements.h"
+#include "SkString.h"
+
+class SkSVGPolyline : public SkSVGElement {
+    DECLARE_SVG_INFO(Polyline);
+    virtual void addAttribute(SkSVGParser& , int attrIndex, 
+        const char* attrValue, size_t attrLength);
+protected:
+    SkString f_clipRule;
+    SkString f_fillRule;
+    SkString f_points;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGPolyline_DEFINED
diff --git a/src/svg/SkSVGRadialGradient.cpp b/src/svg/SkSVGRadialGradient.cpp
new file mode 100644
index 0000000..bba8b94
--- /dev/null
+++ b/src/svg/SkSVGRadialGradient.cpp
@@ -0,0 +1,50 @@
+/* libs/graphics/svg/SkSVGRadialGradient.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 "SkSVGRadialGradient.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGRadialGradient::gAttributes[] = {
+    SVG_ATTRIBUTE(cx),
+    SVG_ATTRIBUTE(cy),
+    SVG_ATTRIBUTE(fx),
+    SVG_ATTRIBUTE(fy),
+    SVG_ATTRIBUTE(gradientTransform),
+    SVG_ATTRIBUTE(gradientUnits),
+    SVG_ATTRIBUTE(r)
+};
+
+DEFINE_SVG_INFO(RadialGradient)
+
+void SkSVGRadialGradient::translate(SkSVGParser& parser, bool defState) {
+    if (fMatrixID.size() == 0)
+        parser.translateMatrix(f_gradientTransform, &fMatrixID);
+    parser._startElement("radialGradient");
+    if (fMatrixID.size() > 0)
+        parser._addAttribute("matrix", fMatrixID);
+    INHERITED::translateGradientUnits(f_gradientUnits);
+    SkString center;
+    center.appendUnichar('[');
+    center.append(f_cx);
+    center.appendUnichar(',');
+    center.append(f_cy);
+    center.appendUnichar(']');
+    parser._addAttribute("center", center);
+    parser._addAttribute("radius", f_r);
+    INHERITED::translate(parser, defState);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGRadialGradient.h b/src/svg/SkSVGRadialGradient.h
new file mode 100644
index 0000000..5b04186
--- /dev/null
+++ b/src/svg/SkSVGRadialGradient.h
@@ -0,0 +1,38 @@
+/* libs/graphics/svg/SkSVGRadialGradient.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 SkSVGRadialGradient_DEFINED
+#define SkSVGRadialGradient_DEFINED
+
+#include "SkSVGGradient.h"
+
+class SkSVGRadialGradient : public SkSVGGradient {
+    DECLARE_SVG_INFO(RadialGradient);
+protected:
+    SkString f_cx;
+    SkString f_cy;
+    SkString f_fx;
+    SkString f_fy;
+    SkString f_gradientTransform;
+    SkString f_gradientUnits;
+    SkString f_r;
+    SkString fMatrixID;
+private:
+    typedef SkSVGGradient INHERITED;
+};
+
+#endif // SkSVGRadialGradient_DEFINED
diff --git a/src/svg/SkSVGRect.cpp b/src/svg/SkSVGRect.cpp
new file mode 100644
index 0000000..03fdfdc
--- /dev/null
+++ b/src/svg/SkSVGRect.cpp
@@ -0,0 +1,43 @@
+/* libs/graphics/svg/SkSVGRect.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 "SkSVGRect.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGRect::gAttributes[] = {
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(x),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Rect)
+
+SkSVGRect::SkSVGRect() {
+    f_x.set("0");
+    f_y.set("0");
+}
+
+void SkSVGRect::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("rectangle");
+    INHERITED::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE_ALIAS(left, x);
+    SVG_ADD_ATTRIBUTE_ALIAS(top, y);
+    SVG_ADD_ATTRIBUTE(width);
+    SVG_ADD_ATTRIBUTE(height);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGRect.h b/src/svg/SkSVGRect.h
new file mode 100644
index 0000000..d06372b
--- /dev/null
+++ b/src/svg/SkSVGRect.h
@@ -0,0 +1,34 @@
+/* libs/graphics/svg/SkSVGRect.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 SkSVGRect_DEFINED
+#define SkSVGRect_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGRect : public SkSVGElement {
+    DECLARE_SVG_INFO(Rect);
+    SkSVGRect();
+private:
+    SkString f_height;
+    SkString f_width;
+    SkString f_x;
+    SkString f_y;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGRect_DEFINED
diff --git a/src/svg/SkSVGSVG.cpp b/src/svg/SkSVGSVG.cpp
new file mode 100644
index 0000000..1678fc1
--- /dev/null
+++ b/src/svg/SkSVGSVG.cpp
@@ -0,0 +1,82 @@
+/* libs/graphics/svg/SkSVGSVG.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 "SkSVGSVG.h"
+#include "SkParse.h"
+#include "SkRect.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGSVG::gAttributes[] = {
+    SVG_LITERAL_ATTRIBUTE(enable-background, f_enable_background),
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(overflow),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(version),
+    SVG_ATTRIBUTE(viewBox),
+    SVG_LITERAL_ATTRIBUTE(xml:space, f_xml_space),
+    SVG_ATTRIBUTE(xmlns),
+    SVG_LITERAL_ATTRIBUTE(xmlns:xlink, f_xml_xlink)
+};
+
+DEFINE_SVG_INFO(SVG)
+
+
+bool SkSVGSVG::isFlushable() {
+    return false;
+}
+
+void SkSVGSVG::translate(SkSVGParser& parser, bool defState) {
+    SkScalar height, width;
+    SkScalar viewBox[4];
+    const char* hSuffix = SkParse::FindScalar(f_height.c_str(), &height);
+    if (strcmp(hSuffix, "pt") == 0)
+        height = SkScalarMulDiv(height, SK_Scalar1 * 72, SK_Scalar1 * 96);
+    const char* wSuffix = SkParse::FindScalar(f_width.c_str(), &width);
+    if (strcmp(wSuffix, "pt") == 0)
+        width = SkScalarMulDiv(width, SK_Scalar1 * 72, SK_Scalar1 * 96);
+    SkParse::FindScalars(f_viewBox.c_str(), viewBox, 4);
+    SkRect box;
+    box.fLeft = SkScalarDiv(viewBox[0], width);
+    box.fTop = SkScalarDiv(viewBox[1], height);
+    box.fRight = SkScalarDiv(viewBox[2], width);
+    box.fBottom = SkScalarDiv(viewBox[3], height);
+    if (box.fLeft == 0 && box.fTop == 0 && 
+        box.fRight == SK_Scalar1 && box.fBottom == SK_Scalar1) 
+            return;
+    parser._startElement("matrix");
+    if (box.fLeft != 0) {
+        SkString x;
+        x.appendScalar(box.fLeft);
+        parser._addAttributeLen("translateX", x.c_str(), x.size());
+    }
+    if (box.fTop != 0) {
+        SkString y;
+        y.appendScalar(box.fTop);
+        parser._addAttributeLen("translateY", y.c_str(), y.size());
+    }
+    if (box.fRight != SK_Scalar1) {
+        SkString x;
+        x.appendScalar(box.fRight);
+        parser._addAttributeLen("scaleX", x.c_str(), x.size());
+    }
+    if (box.fBottom != SK_Scalar1) {
+        SkString y;
+        y.appendScalar(box.fBottom);
+        parser._addAttributeLen("scaleY", y.c_str(), y.size());
+    }
+    parser._endElement();   
+}
diff --git a/src/svg/SkSVGSVG.h b/src/svg/SkSVGSVG.h
new file mode 100644
index 0000000..f331ccd
--- /dev/null
+++ b/src/svg/SkSVGSVG.h
@@ -0,0 +1,39 @@
+/* libs/graphics/svg/SkSVGSVG.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 SkSVGSVG_DEFINED
+#define SkSVGSVG_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGSVG : public SkSVGElement {
+    DECLARE_SVG_INFO(SVG);
+    virtual bool isFlushable();
+private:
+    SkString f_enable_background;
+    SkString f_height;
+    SkString f_overflow;
+    SkString f_width;
+    SkString f_version;
+    SkString f_viewBox;
+    SkString f_xml_space;
+    SkString f_xmlns;
+    SkString f_xml_xlink;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGSVG_DEFINED
diff --git a/src/svg/SkSVGStop.cpp b/src/svg/SkSVGStop.cpp
new file mode 100644
index 0000000..0d1d76c
--- /dev/null
+++ b/src/svg/SkSVGStop.cpp
@@ -0,0 +1,32 @@
+/* libs/graphics/svg/SkSVGStop.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 "SkSVGStop.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGStop::gAttributes[] = {
+    SVG_ATTRIBUTE(offset)
+};
+
+DEFINE_SVG_INFO(Stop)
+
+void SkSVGStop::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("color");
+    INHERITED::translate(parser, defState);
+    parser._addAttribute("color", parser.getPaintLast(SkSVGPaint::kStopColor));
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGStop.h b/src/svg/SkSVGStop.h
new file mode 100644
index 0000000..dd11d18
--- /dev/null
+++ b/src/svg/SkSVGStop.h
@@ -0,0 +1,31 @@
+/* libs/graphics/svg/SkSVGStop.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 SkSVGStop_DEFINED
+#define SkSVGStop_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGStop : public SkSVGElement {
+    DECLARE_SVG_INFO(Stop);
+private:
+    SkString f_offset;
+    friend class SkSVGGradient;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGStop_DEFINED
diff --git a/src/svg/SkSVGSymbol.cpp b/src/svg/SkSVGSymbol.cpp
new file mode 100644
index 0000000..f8729d7
--- /dev/null
+++ b/src/svg/SkSVGSymbol.cpp
@@ -0,0 +1,30 @@
+/* libs/graphics/svg/SkSVGSymbol.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 "SkSVGSymbol.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGSymbol::gAttributes[] = {
+    SVG_ATTRIBUTE(viewBox)
+};
+
+DEFINE_SVG_INFO(Symbol)
+
+void SkSVGSymbol::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+    // !!! children need to be written into document 
+}
diff --git a/src/svg/SkSVGSymbol.h b/src/svg/SkSVGSymbol.h
new file mode 100644
index 0000000..c1439dc
--- /dev/null
+++ b/src/svg/SkSVGSymbol.h
@@ -0,0 +1,30 @@
+/* libs/graphics/svg/SkSVGSymbol.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 SkSVGSymbol_DEFINED
+#define SkSVGSymbol_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGSymbol : public SkSVGElement {
+    DECLARE_SVG_INFO(Symbol);
+private:
+    SkString f_viewBox;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGSymbol_DEFINED
diff --git a/src/svg/SkSVGText.cpp b/src/svg/SkSVGText.cpp
new file mode 100644
index 0000000..1c18a74
--- /dev/null
+++ b/src/svg/SkSVGText.cpp
@@ -0,0 +1,47 @@
+/* libs/graphics/svg/SkSVGText.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 "SkSVGText.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGText::gAttributes[] = {
+    SVG_ATTRIBUTE(x),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Text)
+
+void SkSVGText::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("text");
+    INHERITED::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE(x);
+    SVG_ADD_ATTRIBUTE(y);
+    SVG_ADD_ATTRIBUTE(text);
+    parser._endElement();
+}
+
+
+const SkSVGAttribute SkSVGTspan::gAttributes[] = {
+    SVG_ATTRIBUTE(x),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Tspan)
+
+void SkSVGTspan::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGText.h b/src/svg/SkSVGText.h
new file mode 100644
index 0000000..98b0155
--- /dev/null
+++ b/src/svg/SkSVGText.h
@@ -0,0 +1,40 @@
+/* libs/graphics/svg/SkSVGText.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 SkSVGText_DEFINED
+#define SkSVGText_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGText : public SkSVGElement {
+    DECLARE_SVG_INFO(Text);
+protected:
+    SkString f_x;
+    SkString f_y;
+    SkString f_text;    // not an attribute
+private:
+    typedef SkSVGElement INHERITED;
+    friend class SkSVGParser;
+};
+
+class SkSVGTspan : public SkSVGText {
+    DECLARE_SVG_INFO(Tspan);
+private:
+    typedef SkSVGText INHERITED;
+};
+
+#endif // SkSVGText_DEFINED
diff --git a/src/svg/SkSVGUse.cpp b/src/svg/SkSVGUse.cpp
new file mode 100644
index 0000000..ba7b256
--- /dev/null
+++ b/src/svg/SkSVGUse.cpp
@@ -0,0 +1,38 @@
+/* libs/graphics/svg/SkSVGUse.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 "SkSVGUse.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGUse::gAttributes[] = {
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(x),
+    SVG_LITERAL_ATTRIBUTE(xlink:href, f_xlink_href),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Use)
+
+void SkSVGUse::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+    parser._startElement("add");
+    const char* start = strchr(f_xlink_href.c_str(), '#') + 1;
+    SkASSERT(start);
+    parser._addAttributeLen("use", start, strlen(start) - 1);
+    parser._endElement();   // clip
+}
diff --git a/src/svg/SkSVGUse.h b/src/svg/SkSVGUse.h
new file mode 100644
index 0000000..ff85ce6
--- /dev/null
+++ b/src/svg/SkSVGUse.h
@@ -0,0 +1,36 @@
+/* libs/graphics/svg/SkSVGUse.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 SkSVGUse_DEFINED
+#define SkSVGUse_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGUse : public SkSVGElement {
+    DECLARE_SVG_INFO(Use);
+protected:
+    SkString f_height;
+    SkString f_width;
+    SkString f_x;
+    SkString f_xlink_href;
+    SkString f_y;
+private:
+    typedef SkSVGElement INHERITED;
+    friend class SkSVGClipPath;
+};
+
+#endif // SkSVGUse_DEFINED
diff --git a/src/utils/SkCamera.cpp b/src/utils/SkCamera.cpp
new file mode 100644
index 0000000..b02499f
--- /dev/null
+++ b/src/utils/SkCamera.cpp
@@ -0,0 +1,449 @@
+/* libs/graphics/effects/SkCamera.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 "SkCamera.h"
+
+static SkScalar SkScalarDotDiv(int count, const SkScalar a[], int step_a,
+                               const SkScalar b[], int step_b,
+                               SkScalar denom)
+{
+#ifdef SK_SCALAR_IS_FLOAT
+    float prod = 0;
+    for (int i = 0; i < count; i++)
+    {
+        prod += a[0] * b[0];
+        a += step_a;
+        b += step_b;
+    }
+    return prod / denom;
+#else
+    Sk64    prod, tmp;
+
+    prod.set(0);
+    for (int i = 0; i < count; i++)
+    {
+        tmp.setMul(a[0], b[0]);
+        prod.add(tmp);
+        a += step_a;
+        b += step_b;
+    }
+    prod.div(denom, Sk64::kRound_DivOption);
+    return prod.get32();
+#endif
+}
+
+static SkScalar SkScalarDot(int count, const SkScalar a[], int step_a,
+                                       const SkScalar b[], int step_b)
+{
+#ifdef SK_SCALAR_IS_FLOAT
+    float prod = 0;
+    for (int i = 0; i < count; i++)
+    {
+        prod += a[0] * b[0];
+        a += step_a;
+        b += step_b;
+    }
+    return prod;
+#else
+    Sk64    prod, tmp;
+
+    prod.set(0);
+    for (int i = 0; i < count; i++)
+    {
+        tmp.setMul(a[0], b[0]);
+        prod.add(tmp);
+        a += step_a;
+        b += step_b;
+    }
+    return prod.getFixed();
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+SkUnitScalar SkPoint3D::normalize(SkUnit3D* unit) const
+{
+#ifdef SK_SCALAR_IS_FLOAT
+    float mag = sk_float_sqrt(fX*fX + fY*fY + fZ*fZ);
+    if (mag)
+    {
+        float scale = 1.0f / mag;
+        unit->fX = fX * scale;
+        unit->fY = fY * scale;
+        unit->fZ = fZ * scale;
+    }
+#else
+    Sk64    tmp1, tmp2;
+
+    tmp1.setMul(fX, fX);
+    tmp2.setMul(fY, fY);
+    tmp1.add(tmp2);
+    tmp2.setMul(fZ, fZ);
+    tmp1.add(tmp2);
+
+    SkFixed mag = tmp1.getSqrt();
+    if (mag)
+    {
+        // what if mag < SK_Fixed1 ??? we will underflow the fixdiv
+        SkFixed scale = SkFixedDiv(SK_Fract1, mag);
+        unit->fX = SkFixedMul(fX, scale);
+        unit->fY = SkFixedMul(fY, scale);
+        unit->fZ = SkFixedMul(fZ, scale);
+    }
+#endif
+    return mag;
+}
+
+SkUnitScalar SkUnit3D::Dot(const SkUnit3D& a, const SkUnit3D& b)
+{
+    return  SkUnitScalarMul(a.fX, b.fX) +
+            SkUnitScalarMul(a.fY, b.fY) +
+            SkUnitScalarMul(a.fZ, b.fZ);
+}
+
+void SkUnit3D::Cross(const SkUnit3D& a, const SkUnit3D& b, SkUnit3D* cross)
+{
+    SkASSERT(cross);
+
+    // use x,y,z, in case &a == cross or &b == cross
+
+
+    SkScalar x = SkUnitScalarMul(a.fY, b.fZ) - SkUnitScalarMul(a.fZ, b.fY);
+    SkScalar y = SkUnitScalarMul(a.fZ, b.fX) - SkUnitScalarMul(a.fX, b.fY);
+    SkScalar z = SkUnitScalarMul(a.fX, b.fY) - SkUnitScalarMul(a.fY, b.fX);
+
+    cross->set(x, y, z);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+SkPatch3D::SkPatch3D()
+{
+    this->reset();
+}
+
+void SkPatch3D::reset()
+{
+    fOrigin.set(0, 0, 0);
+    fU.set(SK_Scalar1, 0, 0);
+    fV.set(0, -SK_Scalar1, 0);
+}
+
+void SkPatch3D::transform(const SkMatrix3D& m, SkPatch3D* dst) const
+{
+    if (dst == NULL)
+        dst = (SkPatch3D*)this;
+
+    m.mapVector(fU, &dst->fU);
+    m.mapVector(fV, &dst->fV);
+    m.mapPoint(fOrigin, &dst->fOrigin);
+}
+
+SkScalar SkPatch3D::dotWith(SkScalar dx, SkScalar dy, SkScalar dz) const
+{
+    SkScalar cx = SkScalarMul(fU.fY, fV.fZ) - SkScalarMul(fU.fZ, fV.fY);
+    SkScalar cy = SkScalarMul(fU.fZ, fV.fX) - SkScalarMul(fU.fX, fV.fY);
+    SkScalar cz = SkScalarMul(fU.fX, fV.fY) - SkScalarMul(fU.fY, fV.fX);
+
+    return SkScalarMul(cx, dx) + SkScalarMul(cy, dy) + SkScalarMul(cz, dz);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+void SkMatrix3D::reset()
+{
+    memset(fMat, 0, sizeof(fMat));
+    fMat[0][0] = fMat[1][1] = fMat[2][2] = SK_Scalar1;
+}
+
+void SkMatrix3D::setTranslate(SkScalar x, SkScalar y, SkScalar z)
+{
+    memset(fMat, 0, sizeof(fMat));
+    fMat[0][0] = x;
+    fMat[1][1] = y;
+    fMat[2][2] = z;
+}
+
+void SkMatrix3D::setRotateX(SkScalar degX)
+{
+    SkScalar    s, c;
+
+    s = SkScalarSinCos(SkDegreesToRadians(degX), &c);
+    this->setRow(0, SK_Scalar1, 0, 0);
+    this->setRow(1, 0, c, -s);
+    this->setRow(2, 0, s, c);
+}
+
+void SkMatrix3D::setRotateY(SkScalar degY)
+{
+    SkScalar    s, c;
+
+    s = SkScalarSinCos(SkDegreesToRadians(degY), &c);
+    this->setRow(0, c, 0, -s);
+    this->setRow(1, 0, SK_Scalar1, 0);
+    this->setRow(2, s, 0, c);
+}
+
+void SkMatrix3D::setRotateZ(SkScalar degZ)
+{
+    SkScalar    s, c;
+
+    s = SkScalarSinCos(SkDegreesToRadians(degZ), &c);
+    this->setRow(0, c, -s, 0);
+    this->setRow(1, s, c, 0);
+    this->setRow(2, 0, 0, SK_Scalar1);
+}
+
+void SkMatrix3D::preTranslate(SkScalar x, SkScalar y, SkScalar z)
+{
+    SkScalar col[3] = { x, y, z};
+
+    for (int i = 0; i < 3; i++)
+        fMat[i][3] += SkScalarDot(3, &fMat[i][0], 1, col, 1);
+}
+
+void SkMatrix3D::preRotateX(SkScalar degX)
+{
+    SkMatrix3D m;    
+    m.setRotateX(degX);
+    this->setConcat(*this, m);
+}
+
+void SkMatrix3D::preRotateY(SkScalar degY)
+{
+    SkMatrix3D m;    
+    m.setRotateY(degY);
+    this->setConcat(*this, m);
+}
+
+void SkMatrix3D::preRotateZ(SkScalar degZ)
+{
+    SkMatrix3D m;    
+    m.setRotateZ(degZ);
+    this->setConcat(*this, m);
+}
+
+void SkMatrix3D::setConcat(const SkMatrix3D& a, const SkMatrix3D& b)
+{
+    SkMatrix3D  tmp;
+    SkMatrix3D* c = this;
+
+    if (this == &a || this == &b)
+        c = &tmp;
+
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++)
+            c->fMat[i][j] = SkScalarDot(3, &a.fMat[i][0], 1, &b.fMat[0][j], 4);
+        c->fMat[i][3] = SkScalarDot(3, &a.fMat[i][0], 1, &b.fMat[0][3], 4) + a.fMat[i][3];
+    }
+
+    if (c == &tmp)
+        *this = tmp;
+}
+
+void SkMatrix3D::mapPoint(const SkPoint3D& src, SkPoint3D* dst) const
+{
+    SkScalar x = SkScalarDot(3, &fMat[0][0], 1, &src.fX, 1) + fMat[0][3];
+    SkScalar y = SkScalarDot(3, &fMat[1][0], 1, &src.fX, 1) + fMat[1][3];
+    SkScalar z = SkScalarDot(3, &fMat[2][0], 1, &src.fX, 1) + fMat[2][3];
+    dst->set(x, y, z);
+}
+
+void SkMatrix3D::mapVector(const SkVector3D& src, SkVector3D* dst) const
+{
+    SkScalar x = SkScalarDot(3, &fMat[0][0], 1, &src.fX, 1);
+    SkScalar y = SkScalarDot(3, &fMat[1][0], 1, &src.fX, 1);
+    SkScalar z = SkScalarDot(3, &fMat[2][0], 1, &src.fX, 1);
+    dst->set(x, y, z);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+SkCamera3D::SkCamera3D()
+{
+    this->reset();
+}
+
+void SkCamera3D::reset()
+{
+    fLocation.set(0, 0, -SkIntToScalar(576));   // 8 inches backward
+    fAxis.set(0, 0, SK_Scalar1);                // forward
+    fZenith.set(0, -SK_Scalar1, 0);             // up
+
+    fObserver.set(0, 0, fLocation.fZ);
+
+    fNeedToUpdate = true;
+}
+
+void SkCamera3D::update()
+{
+    fNeedToUpdate = true;
+}
+
+void SkCamera3D::doUpdate() const
+{
+    SkUnit3D    axis, zenith, cross;
+
+    fAxis.normalize(&axis);
+
+    {
+        SkScalar dot = SkUnit3D::Dot(*(const SkUnit3D*)(const void*)&fZenith, axis);
+
+        zenith.fX = fZenith.fX - SkUnitScalarMul(dot, axis.fX);
+        zenith.fY = fZenith.fY - SkUnitScalarMul(dot, axis.fY);
+        zenith.fZ = fZenith.fZ - SkUnitScalarMul(dot, axis.fZ);
+
+        (void)((SkPoint3D*)(void*)&zenith)->normalize(&zenith);
+    }
+
+    SkUnit3D::Cross(axis, zenith, &cross);
+
+    {
+        SkMatrix* orien = &fOrientation;
+        SkScalar x = fObserver.fX;
+        SkScalar y = fObserver.fY;
+        SkScalar z = fObserver.fZ;
+
+        orien->set(SkMatrix::kMScaleX, SkUnitScalarMul(x, axis.fX) - SkUnitScalarMul(z, cross.fX));
+        orien->set(SkMatrix::kMSkewX,  SkUnitScalarMul(x, axis.fY) - SkUnitScalarMul(z, cross.fY));
+        orien->set(SkMatrix::kMTransX, SkUnitScalarMul(x, axis.fZ) - SkUnitScalarMul(z, cross.fZ));
+        orien->set(SkMatrix::kMSkewY,  SkUnitScalarMul(y, axis.fX) - SkUnitScalarMul(z, zenith.fX));
+        orien->set(SkMatrix::kMScaleY, SkUnitScalarMul(y, axis.fY) - SkUnitScalarMul(z, zenith.fY));
+        orien->set(SkMatrix::kMTransY, SkUnitScalarMul(y, axis.fZ) - SkUnitScalarMul(z, zenith.fZ));
+        orien->set(SkMatrix::kMPersp0, axis.fX);
+        orien->set(SkMatrix::kMPersp1, axis.fY);
+        orien->set(SkMatrix::kMPersp2, axis.fZ);
+    }
+}
+
+void SkCamera3D::patchToMatrix(const SkPatch3D& quilt, SkMatrix* matrix) const
+{
+    if (fNeedToUpdate)
+    {
+        this->doUpdate();
+        fNeedToUpdate = false;
+    }
+
+    const SkScalar* mapPtr = (const SkScalar*)(const void*)&fOrientation;
+    const SkScalar* patchPtr;
+    SkPoint3D       diff;
+    SkScalar        dot;
+
+    diff.fX = quilt.fOrigin.fX - fLocation.fX;
+    diff.fY = quilt.fOrigin.fY - fLocation.fY;
+    diff.fZ = quilt.fOrigin.fZ - fLocation.fZ;
+
+    dot = SkUnit3D::Dot(*(const SkUnit3D*)(const void*)&diff,
+                        *(const SkUnit3D*)(((const SkScalar*)(const void*)&fOrientation) + 6));
+
+    patchPtr = (const SkScalar*)&quilt;
+    matrix->set(SkMatrix::kMScaleX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
+    matrix->set(SkMatrix::kMSkewY,  SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot));
+    matrix->set(SkMatrix::kMPersp0, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot));
+
+    patchPtr += 3;
+    matrix->set(SkMatrix::kMSkewX,  SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
+    matrix->set(SkMatrix::kMScaleY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot));
+    matrix->set(SkMatrix::kMPersp1, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot));
+
+    patchPtr = (const SkScalar*)(const void*)&diff;
+    matrix->set(SkMatrix::kMTransX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
+    matrix->set(SkMatrix::kMTransY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot));
+    matrix->set(SkMatrix::kMPersp2, SK_UnitScalar1);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+Sk3DView::Sk3DView()
+{
+    fInitialRec.fMatrix.reset();
+    fRec = &fInitialRec;
+}
+
+Sk3DView::~Sk3DView()
+{
+    Rec* rec = fRec;
+    while (rec != &fInitialRec) {
+        Rec* next = rec->fNext;
+        SkDELETE(rec);
+        rec = next;
+    }
+}
+
+void Sk3DView::save()
+{
+    Rec* rec = SkNEW(Rec);
+    rec->fNext = fRec;
+    rec->fMatrix = fRec->fMatrix;
+    fRec = rec;
+}
+
+void Sk3DView::restore()
+{
+    SkASSERT(fRec != &fInitialRec);
+    Rec* next = fRec->fNext;
+    SkDELETE(fRec);
+    fRec = next;
+}
+
+void Sk3DView::translate(SkScalar x, SkScalar y, SkScalar z)
+{
+    fRec->fMatrix.preTranslate(x, y, z);
+}
+
+void Sk3DView::rotateX(SkScalar deg)
+{
+    fRec->fMatrix.preRotateX(deg);
+}
+
+void Sk3DView::rotateY(SkScalar deg)
+{
+    fRec->fMatrix.preRotateY(deg);
+}
+
+void Sk3DView::rotateZ(SkScalar deg)
+{
+    fRec->fMatrix.preRotateZ(deg);
+}
+
+SkScalar Sk3DView::dotWithNormal(SkScalar x, SkScalar y, SkScalar z) const
+{
+    SkPatch3D   patch;
+    patch.transform(fRec->fMatrix);
+    return patch.dotWith(x, y, z);
+}
+
+void Sk3DView::getMatrix(SkMatrix* matrix) const
+{
+    if (matrix != NULL)
+    {
+        SkPatch3D   patch;
+        patch.transform(fRec->fMatrix);
+        fCamera.patchToMatrix(patch, matrix);
+    }
+}
+
+#include "SkCanvas.h"
+
+void Sk3DView::applyToCanvas(SkCanvas* canvas) const
+{
+    SkMatrix    matrix;
+    
+    this->getMatrix(&matrix);
+    canvas->concat(matrix);
+}
+
diff --git a/src/utils/SkColorMatrix.cpp b/src/utils/SkColorMatrix.cpp
new file mode 100644
index 0000000..0a20990
--- /dev/null
+++ b/src/utils/SkColorMatrix.cpp
@@ -0,0 +1,165 @@
+#include "SkColorMatrix.h"
+
+#define kRScale     0
+#define kGScale     6
+#define kBScale     12
+#define kAScale     18
+
+void SkColorMatrix::setIdentity()
+{
+    memset(fMat, 0, sizeof(fMat));
+    fMat[kRScale] = fMat[kGScale] = fMat[kBScale] = fMat[kAScale] = SK_Scalar1;
+}
+
+void SkColorMatrix::setScale(SkScalar rScale, SkScalar gScale, SkScalar bScale,
+                             SkScalar aScale)
+{
+    memset(fMat, 0, sizeof(fMat));
+    fMat[kRScale] = rScale;
+    fMat[kGScale] = gScale;
+    fMat[kBScale] = bScale;
+    fMat[kAScale] = aScale;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkColorMatrix::setRotate(Axis axis, SkScalar degrees)
+{
+    SkScalar S, C;
+
+    S = SkScalarSinCos(SkDegreesToRadians(degrees), &C);
+
+    this->setSinCos(axis, S, C);
+}
+
+void SkColorMatrix::setSinCos(Axis axis, SkScalar sine, SkScalar cosine)
+{
+    SkASSERT((unsigned)axis < 3);
+
+    static const uint8_t gRotateIndex[] = {
+        6, 7, 11, 12,
+        0, 2, 15, 17,
+        0, 1,  5,  6,
+    };
+    const uint8_t* index = gRotateIndex + axis * 4;
+    
+    this->setIdentity();
+    fMat[index[0]] = cosine;
+    fMat[index[1]] = sine;
+    fMat[index[2]] = -sine;
+    fMat[index[3]] = cosine;
+}
+
+void SkColorMatrix::preRotate(Axis axis, SkScalar degrees)
+{
+    SkColorMatrix tmp;
+    tmp.setRotate(axis, degrees);
+    this->preConcat(tmp);
+}
+
+void SkColorMatrix::postRotate(Axis axis, SkScalar degrees)
+{
+    SkColorMatrix tmp;
+    tmp.setRotate(axis, degrees);
+    this->postConcat(tmp);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkColorMatrix::setConcat(const SkColorMatrix& matA,
+                              const SkColorMatrix& matB)
+{
+    SkScalar    tmp[20];
+    SkScalar*   result = fMat;
+
+    if (&matA == this || &matB == this)
+        result = tmp;
+    
+    const SkScalar* a = matA.fMat;
+    const SkScalar* b = matB.fMat;
+
+    int index = 0;
+    for (int j = 0; j < 20; j += 5)
+    {
+        for (int i = 0; i < 4; i++)
+        {
+            result[index++] =   SkScalarMul(a[j + 0], b[i + 0]) + 
+                                SkScalarMul(a[j + 1], b[i + 5]) +
+                                SkScalarMul(a[j + 2], b[i + 10]) +
+                                SkScalarMul(a[j + 3], b[i + 15]);
+        }
+        result[index++] =   SkScalarMul(a[j + 0], b[4]) +
+                            SkScalarMul(a[j + 1], b[9]) +
+                            SkScalarMul(a[j + 2], b[14]) +
+                            SkScalarMul(a[j + 3], b[19]) +
+                            a[j + 4];
+    }
+    
+    if (fMat != result)
+        memcpy(fMat, result, sizeof(fMat));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void setrow(SkScalar row[], SkScalar r, SkScalar g, SkScalar b)
+{
+    row[0] = r;
+    row[1] = g;
+    row[2] = b;
+}
+
+static const SkScalar kHueR = SkFloatToScalar(0.213f);
+static const SkScalar kHueG = SkFloatToScalar(0.715f);
+static const SkScalar kHueB = SkFloatToScalar(0.072f);
+
+void SkColorMatrix::setSaturation(SkScalar sat)
+{
+    memset(fMat, 0, sizeof(fMat));
+
+    const SkScalar R = SkScalarMul(kHueR, SK_Scalar1 - sat);
+    const SkScalar G = SkScalarMul(kHueG, SK_Scalar1 - sat);
+    const SkScalar B = SkScalarMul(kHueB, SK_Scalar1 - sat);
+
+    setrow(fMat +  0, R + sat, G, B);
+    setrow(fMat +  5, R, G + sat, B);
+    setrow(fMat + 10, R, G, B + sat);
+    fMat[18] = SK_Scalar1;
+}
+
+static const SkScalar kR2Y = SkFloatToScalar(0.299f);
+static const SkScalar kG2Y = SkFloatToScalar(0.587f);
+static const SkScalar kB2Y = SkFloatToScalar(0.114f);
+
+static const SkScalar kR2U = SkFloatToScalar(-0.16874f);
+static const SkScalar kG2U = SkFloatToScalar(-0.33126f);
+static const SkScalar kB2U = SkFloatToScalar(0.5f);
+
+static const SkScalar kR2V = SkFloatToScalar(0.5f);
+static const SkScalar kG2V = SkFloatToScalar(-0.41869f);
+static const SkScalar kB2V = SkFloatToScalar(-0.08131f);
+
+void SkColorMatrix::setRGB2YUV()
+{
+    memset(fMat, 0, sizeof(fMat));
+    
+    setrow(fMat +  0, kR2Y, kG2Y, kB2Y);
+    setrow(fMat +  5, kR2U, kG2U, kB2U);
+    setrow(fMat + 10, kR2V, kG2V, kB2V);
+    fMat[18] = SK_Scalar1;
+}
+
+static const SkScalar kV2R = SkFloatToScalar(1.402f);
+static const SkScalar kU2G = SkFloatToScalar(-0.34414f);
+static const SkScalar kV2G = SkFloatToScalar(-0.71414f);
+static const SkScalar kU2B = SkFloatToScalar(1.772f);
+
+void SkColorMatrix::setYUV2RGB()
+{
+    memset(fMat, 0, sizeof(fMat));
+    
+    setrow(fMat +  0, SK_Scalar1, 0, kV2R);
+    setrow(fMat +  5, SK_Scalar1, kU2G, kV2G);
+    setrow(fMat + 10, SK_Scalar1, kU2B, 0);
+    fMat[18] = SK_Scalar1;
+}
+
diff --git a/src/utils/SkCullPoints.cpp b/src/utils/SkCullPoints.cpp
new file mode 100644
index 0000000..23d00b6
--- /dev/null
+++ b/src/utils/SkCullPoints.cpp
@@ -0,0 +1,168 @@
+/* libs/graphics/effects/SkCullPoints.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 "SkCullPoints.h"
+#include "Sk64.h"
+
+static bool cross_product_is_neg(const SkIPoint& v, int dx, int dy)
+{
+#if 0
+    return v.fX * dy - v.fY * dx < 0;
+#else
+    Sk64   tmp0, tmp1;
+    
+    tmp0.setMul(v.fX, dy);
+    tmp1.setMul(dx, v.fY);
+    tmp0.sub(tmp1);
+    return tmp0.isNeg();
+#endif
+}
+
+bool SkCullPoints::sect_test(int x0, int y0, int x1, int y1) const
+{
+    const SkIRect& r = fR;
+
+    if (x0 < r.fLeft    && x1 < r.fLeft ||
+        x0 > r.fRight   && x1 > r.fRight ||
+        y0 < r.fTop     && y1 < r.fTop ||
+        y0 > r.fBottom  && y1 > r.fBottom)
+        return false;
+
+    // since the crossprod test is a little expensive, check for easy-in cases first    
+    if (r.contains(x0, y0) || r.contains(x1, y1))
+        return true;
+
+    // At this point we're not sure, so we do a crossprod test
+    SkIPoint           vec;
+    const SkIPoint*    rAsQuad = fAsQuad;
+    
+    vec.set(x1 - x0, y1 - y0);
+    bool isNeg = cross_product_is_neg(vec, x0 - rAsQuad[0].fX, y0 - rAsQuad[0].fY);
+    for (int i = 1; i < 4; i++) {
+        if (cross_product_is_neg(vec, x0 - rAsQuad[i].fX, y0 - rAsQuad[i].fY) != isNeg)
+        {
+            return true;
+        }
+    }
+    return false;   // we didn't intersect
+}
+
+static void toQuad(const SkIRect& r, SkIPoint quad[4])
+{
+    SkASSERT(quad);
+
+    quad[0].set(r.fLeft, r.fTop);
+    quad[1].set(r.fRight, r.fTop);
+    quad[2].set(r.fRight, r.fBottom);
+    quad[3].set(r.fLeft, r.fBottom);
+}
+
+SkCullPoints::SkCullPoints()
+{
+    SkIRect    r;
+    r.setEmpty();
+    this->reset(r);
+}
+
+SkCullPoints::SkCullPoints(const SkIRect& r)
+{
+    this->reset(r);
+}
+
+void SkCullPoints::reset(const SkIRect& r)
+{
+    fR = r;
+    toQuad(fR, fAsQuad);
+    fPrevPt.set(0, 0);
+    fPrevResult = kNo_Result;
+}
+
+void SkCullPoints::moveTo(int x, int y)
+{
+    fPrevPt.set(x, y);
+    fPrevResult = kNo_Result;   // so we trigger a movetolineto later
+}
+
+SkCullPoints::LineToResult SkCullPoints::lineTo(int x, int y, SkIPoint line[])
+{
+    SkASSERT(line != NULL);
+
+    LineToResult result = kNo_Result;
+    int x0 = fPrevPt.fX;
+    int y0 = fPrevPt.fY;
+    
+    // need to upgrade sect_test to chop the result
+    // and to correctly return kLineTo_Result when the result is connected
+    // to the previous call-out
+    if (this->sect_test(x0, y0, x, y))
+    {
+        line[0].set(x0, y0);
+        line[1].set(x, y);
+        
+        if (fPrevResult != kNo_Result && fPrevPt.equals(x0, y0))
+            result = kLineTo_Result;
+        else
+            result = kMoveToLineTo_Result;
+    }
+
+    fPrevPt.set(x, y);
+    fPrevResult = result;
+
+    return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkPath.h"
+
+SkCullPointsPath::SkCullPointsPath()
+    : fCP(), fPath(NULL)
+{
+}
+
+SkCullPointsPath::SkCullPointsPath(const SkIRect& r, SkPath* dst)
+    : fCP(r), fPath(dst)
+{
+}
+
+void SkCullPointsPath::reset(const SkIRect& r, SkPath* dst)
+{
+    fCP.reset(r);
+    fPath = dst;
+}
+    
+void SkCullPointsPath::moveTo(int x, int y)
+{
+    fCP.moveTo(x, y);
+}
+
+void SkCullPointsPath::lineTo(int x, int y)
+{
+    SkIPoint   pts[2];
+    
+    switch (fCP.lineTo(x, y, pts)) {
+    case SkCullPoints::kMoveToLineTo_Result:
+        fPath->moveTo(SkIntToScalar(pts[0].fX), SkIntToScalar(pts[0].fY));
+        // fall through to the lineto case
+    case SkCullPoints::kLineTo_Result:
+        fPath->lineTo(SkIntToScalar(pts[1].fX), SkIntToScalar(pts[1].fY));
+        break;
+    default:
+        break;
+    }
+}
+
diff --git a/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp
new file mode 100644
index 0000000..fb203ef
--- /dev/null
+++ b/src/utils/SkDumpCanvas.cpp
@@ -0,0 +1,398 @@
+#include "SkDumpCanvas.h"
+#include "SkPixelRef.h"
+#include "SkString.h"
+#include <stdarg.h>
+
+// needed just to know that these are all subclassed from SkFlattenable
+#include "SkShader.h"
+#include "SkPathEffect.h"
+#include "SkXfermode.h"
+#include "SkColorFilter.h"
+#include "SkPathEffect.h"
+#include "SkMaskFilter.h"
+
+static void toString(const SkRect& r, SkString* str) {
+    str->printf("[(%g %g) %g %g]",
+                SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop),
+                SkScalarToFloat(r.width()), SkScalarToFloat(r.height()));
+}
+
+static void toString(const SkIRect& r, SkString* str) {
+    str->printf("[(%d %d) %d %d]", r.fLeft, r.fTop, r.width(), r.height());
+}
+
+static void toString(const SkPath& path, SkString* str) {
+    if (path.isEmpty()) {
+        str->set("path:empty");
+    } else {
+        SkRect bounds;
+        path.computeBounds(&bounds, SkPath::kFast_BoundsType);
+        toString(bounds, str);
+        str->append("]");
+        str->prepend("path:[");
+    }
+}
+
+static const char* toString(SkRegion::Op op) {
+    static const char* gOpNames[] = {
+        "DIFF", "SECT", "UNION", "XOR", "RDIFF", "REPLACE"
+    };
+    return gOpNames[op];
+}
+
+static void toString(const SkRegion& rgn, SkString* str) {
+    toString(rgn.getBounds(), str);
+    str->prepend("Region:[");
+    str->append("]");
+    if (rgn.isComplex()) {
+        str->append(".complex");
+    }
+}
+
+static const char* toString(SkCanvas::VertexMode vm) {
+    static const char* gVMNames[] = {
+        "TRIANGLES", "STRIP", "FAN"
+    };
+    return gVMNames[vm];
+}
+
+static const char* toString(SkCanvas::PointMode pm) {
+    static const char* gPMNames[] = {
+        "POINTS", "LINES", "POLYGON"
+    };
+    return gPMNames[pm];
+}
+
+static const char* toString(SkBitmap::Config config) {
+    static const char* gConfigNames[] = {
+        "NONE", "A1", "A8", "INDEX8", "565", "4444", "8888", "RLE"
+    };
+    return gConfigNames[config];
+}
+
+static void toString(const SkBitmap& bm, SkString* str) {
+    str->printf("bitmap:[%d %d] %s", bm.width(), bm.height(),
+                toString(bm.config()));
+    
+    SkPixelRef* pr = bm.pixelRef();
+    if (NULL == pr) {
+        // show null or the explicit pixel address (rare)
+        str->appendf(" pixels:%p", bm.getPixels());
+    } else {
+        const char* uri = pr->getURI();
+        if (uri) {
+            str->appendf(" uri:\"%s\"", uri);
+        } else {
+            str->appendf(" pixelref:%p", pr);
+        }
+    }
+}
+
+static void toString(const void* text, size_t len, SkPaint::TextEncoding enc,
+                     SkString* str) {
+    switch (enc) {
+        case SkPaint::kUTF8_TextEncoding:
+            str->printf("\"%.*s\"%s", SkMax32(len, 32), text,
+                        len > 32 ? "..." : "");
+            break;
+        case SkPaint::kUTF16_TextEncoding:
+            str->printf("\"%.*S\"%s", SkMax32(len, 32), text,
+                        len > 64 ? "..." : "");
+            break;
+        case SkPaint::kGlyphID_TextEncoding:
+            str->set("<glyphs>");
+            break;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDumpCanvas::SkDumpCanvas(Dumper* dumper) {
+    dumper->safeRef();
+    fDumper = dumper;
+
+    static const int WIDE_OPEN = 16384;
+    SkBitmap emptyBitmap;
+
+    emptyBitmap.setConfig(SkBitmap::kNo_Config, WIDE_OPEN, WIDE_OPEN);
+    this->setBitmapDevice(emptyBitmap);
+}
+
+SkDumpCanvas::~SkDumpCanvas() {
+    fDumper->safeUnref();
+}
+
+void SkDumpCanvas::dump(Verb verb, const SkPaint* paint,
+                        const char format[], ...) {
+    static const size_t BUFFER_SIZE = 1024;
+
+    char    buffer[BUFFER_SIZE];
+    va_list args;
+    va_start(args, format);
+    vsnprintf(buffer, BUFFER_SIZE, format, args);
+    va_end(args);
+    
+    if (fDumper) {
+        fDumper->dump(this, verb, buffer, paint);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkDumpCanvas::save(SaveFlags flags) {
+    this->dump(kSave_Verb, NULL, "save(0x%X)", flags);
+    return this->INHERITED::save(flags);
+}
+
+int SkDumpCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                             SaveFlags flags) {
+    this->dump(kSave_Verb, paint, "saveLayer(0x%X)", flags);
+    return this->INHERITED::saveLayer(bounds, paint, flags);
+}
+
+void SkDumpCanvas::restore() {
+    this->INHERITED::restore();
+    this->dump(kRestore_Verb, NULL, "restore");
+}
+
+bool SkDumpCanvas::translate(SkScalar dx, SkScalar dy) {
+    this->dump(kMatrix_Verb, NULL, "translate(%g %g)",
+               SkScalarToFloat(dx), SkScalarToFloat(dy));
+    return this->INHERITED::translate(dx, dy);
+}
+
+bool SkDumpCanvas::scale(SkScalar sx, SkScalar sy) {
+    this->dump(kMatrix_Verb, NULL, "scale(%g %g)",
+               SkScalarToFloat(sx), SkScalarToFloat(sy));
+    return this->INHERITED::scale(sx, sy);
+}
+
+bool SkDumpCanvas::rotate(SkScalar degrees) {
+    this->dump(kMatrix_Verb, NULL, "rotate(%g)", SkScalarToFloat(degrees));
+    return this->INHERITED::rotate(degrees);
+}
+
+bool SkDumpCanvas::skew(SkScalar sx, SkScalar sy) {
+    this->dump(kMatrix_Verb, NULL, "skew(%g %g)",
+               SkScalarToFloat(sx), SkScalarToFloat(sy));
+    return this->INHERITED::skew(sx, sy);
+}
+
+bool SkDumpCanvas::concat(const SkMatrix& matrix) {
+    SkString str;
+    matrix.toDumpString(&str);
+    this->dump(kMatrix_Verb, NULL, "concat(%s)", str.c_str());
+    return this->INHERITED::concat(matrix);
+}
+
+void SkDumpCanvas::setMatrix(const SkMatrix& matrix) {
+    SkString str;
+    matrix.toDumpString(&str);
+    this->dump(kMatrix_Verb, NULL, "setMatrix(%s)", str.c_str());
+    this->INHERITED::setMatrix(matrix);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkDumpCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
+    SkString str;
+    toString(rect, &str);
+    this->dump(kClip_Verb, NULL, "clipRect(%s %s)", str.c_str(), toString(op));
+    return this->INHERITED::clipRect(rect, op);
+}
+
+bool SkDumpCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
+    SkString str;
+    toString(path, &str);
+    this->dump(kClip_Verb, NULL, "clipPath(%s %s)", str.c_str(), toString(op));
+    return this->INHERITED::clipPath(path, op);
+}
+
+bool SkDumpCanvas::clipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
+    SkString str;
+    toString(deviceRgn, &str);
+    this->dump(kClip_Verb, NULL, "clipRegion(%s %s)", str.c_str(),
+               toString(op));
+    return this->INHERITED::clipRegion(deviceRgn, op);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkDumpCanvas::drawPaint(const SkPaint& paint) {
+    this->dump(kDrawPaint_Verb, &paint, "drawPaint()");
+}
+
+void SkDumpCanvas::drawPoints(PointMode mode, size_t count,
+                               const SkPoint pts[], const SkPaint& paint) {
+    this->dump(kDrawPoints_Verb, &paint, "drawPoints(%s, %d)", toString(mode),
+               count);
+}
+
+void SkDumpCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+    SkString str;
+    toString(rect, &str);
+    this->dump(kDrawRect_Verb, &paint, "drawRect(%s)", str.c_str());
+}
+
+void SkDumpCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    SkString str;
+    toString(path, &str);
+    this->dump(kDrawPath_Verb, &paint, "drawPath(%s)", str.c_str());
+}
+
+void SkDumpCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+                               const SkPaint* paint) {
+    SkString str;
+    toString(bitmap, &str);
+    this->dump(kDrawBitmap_Verb, paint, "drawBitmap(%s (%g %g))", str.c_str(),
+               SkScalarToFloat(x), SkScalarToFloat(y));
+}
+
+void SkDumpCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                                   const SkRect& dst, const SkPaint* paint) {
+    SkString bs, rs;
+    toString(bitmap, &bs);
+    toString(dst, &rs);
+    // show the src-rect only if its not everything
+    if (src && (src->fLeft > 0 || src->fTop > 0 ||
+                src->fRight < bitmap.width() ||
+                src->fBottom < bitmap.height())) {
+        SkString ss;
+        toString(*src, &ss);
+        rs.prependf("%s ", ss.c_str());
+    }
+
+    this->dump(kDrawBitmap_Verb, paint, "drawBitmapRect(%s %s)",
+               bs.c_str(), rs.c_str());
+}
+
+void SkDumpCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                     const SkPaint* paint) {
+    SkString bs, ms;
+    toString(bitmap, &bs);
+    m.toDumpString(&ms);
+    this->dump(kDrawBitmap_Verb, paint, "drawBitmapMatrix(%s %s)",
+               bs.c_str(), ms.c_str());
+}
+
+void SkDumpCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+                               const SkPaint* paint) {
+    SkString str;
+    toString(bitmap, &str);
+    this->dump(kDrawBitmap_Verb, paint, "drawSprite(%s (%d %d))", str.c_str(),
+               x, y);
+}
+
+void SkDumpCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
+                             SkScalar y, const SkPaint& paint) {
+    SkString str;
+    toString(text, byteLength, paint.getTextEncoding(), &str);
+    this->dump(kDrawText_Verb, &paint, "drawText(%s [%d] (%g %g))", str.c_str(),
+               byteLength, SkScalarToFloat(x), SkScalarToFloat(y));
+}
+
+void SkDumpCanvas::drawPosText(const void* text, size_t byteLength,
+                                const SkPoint pos[], const SkPaint& paint) {
+    SkString str;
+    toString(text, byteLength, paint.getTextEncoding(), &str);
+    this->dump(kDrawText_Verb, &paint, "drawPosText(%s [%d] (%g %g ...))",
+               str.c_str(), byteLength, SkScalarToFloat(pos[0].fX),
+               SkScalarToFloat(pos[0].fY));
+}
+
+void SkDumpCanvas::drawPosTextH(const void* text, size_t byteLength,
+                                 const SkScalar xpos[], SkScalar constY,
+                                 const SkPaint& paint) {
+    SkString str;
+    toString(text, byteLength, paint.getTextEncoding(), &str);
+    this->dump(kDrawText_Verb, &paint, "drawPosTextH(%s [%d] (%g %g ...))",
+               str.c_str(), byteLength, SkScalarToFloat(xpos[0]),
+               SkScalarToFloat(constY));
+}
+
+void SkDumpCanvas::drawTextOnPath(const void* text, size_t byteLength,
+                                   const SkPath& path, const SkMatrix* matrix,
+                                   const SkPaint& paint) {
+    SkString str;
+    toString(text, byteLength, paint.getTextEncoding(), &str);
+    this->dump(kDrawText_Verb, &paint, "drawTextOnPath(%s [%d])",
+               str.c_str(), byteLength);
+}
+
+void SkDumpCanvas::drawPicture(SkPicture& picture) {
+    this->dump(kDrawPicture_Verb, NULL, "drawPicture(%p)", &picture);
+}
+
+void SkDumpCanvas::drawVertices(VertexMode vmode, int vertexCount,
+                                 const SkPoint vertices[], const SkPoint texs[],
+                                 const SkColor colors[], SkXfermode* xmode,
+                                 const uint16_t indices[], int indexCount,
+                                 const SkPaint& paint) {
+    this->dump(kDrawVertices_Verb, &paint, "drawVertices(%s [%d] [%g %g ...]",
+               toString(vmode), vertexCount, SkScalarToFloat(vertices[0].fX),
+               SkScalarToFloat(vertices[0].fY));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkFormatDumper::SkFormatDumper(void (*proc)(const char*, void*), void* refcon) {
+    fProc = proc;
+    fRefcon = refcon;
+}
+
+static void appendPtr(SkString* str, const void* ptr, const char name[]) {
+    if (ptr) {
+        str->appendf(" %s:%p", name, ptr);
+    }
+}
+
+static void appendFlattenable(SkString* str, const SkFlattenable* ptr,
+                              const char name[]) {
+    if (ptr) {
+        SkString info;
+        if (ptr->toDumpString(&info)) {
+            str->appendf(" %s", info.c_str());
+        } else {
+            str->appendf(" %s:%p", name, ptr);
+        }
+    }
+}
+
+void SkFormatDumper::dump(SkDumpCanvas* canvas, SkDumpCanvas::Verb verb,
+                          const char str[], const SkPaint* p) {
+    SkString msg, tab;
+    const int level = canvas->getSaveCount() - 1;
+    SkASSERT(level >= 0);
+    for (int i = 0; i < level; i++) {
+        tab.append("\t");
+    }
+    msg.printf("%s%s", tab.c_str(), str);
+    
+    if (p) {
+        msg.appendf(" color:0x%08X flags:%X", p->getColor(), p->getFlags());
+        appendFlattenable(&msg, p->getShader(), "shader");
+        appendFlattenable(&msg, p->getXfermode(), "xfermode");
+        appendFlattenable(&msg, p->getPathEffect(), "pathEffect");
+        appendFlattenable(&msg, p->getMaskFilter(), "maskFilter");
+        appendFlattenable(&msg, p->getPathEffect(), "pathEffect");
+        appendFlattenable(&msg, p->getColorFilter(), "filter");
+        
+        if (SkDumpCanvas::kDrawText_Verb == verb) {
+            msg.appendf(" textSize:%g", SkScalarToFloat(p->getTextSize()));
+            appendPtr(&msg, p->getTypeface(), "typeface");
+        }
+    }
+    
+    fProc(msg.c_str(), fRefcon);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void dumpToDebugf(const char text[], void*) {
+    SkDebugf("%s\n", text);
+}
+
+SkDebugfDumper::SkDebugfDumper() : INHERITED(dumpToDebugf, NULL) {}
+
+
diff --git a/src/utils/SkInterpolator.cpp b/src/utils/SkInterpolator.cpp
new file mode 100644
index 0000000..e4ecd95
--- /dev/null
+++ b/src/utils/SkInterpolator.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkInterpolator.h"
+#include "SkMath.h"
+#include "SkTSearch.h"
+
+SkInterpolatorBase::SkInterpolatorBase() {
+    fStorage    = NULL;
+    fTimes      = NULL;
+    SkDEBUGCODE(fTimesArray = NULL;)
+}
+
+SkInterpolatorBase::~SkInterpolatorBase() {
+    if (fStorage) {
+        sk_free(fStorage);
+    }
+}
+
+void SkInterpolatorBase::reset(int elemCount, int frameCount) {
+    fFlags = 0;
+    fElemCount = SkToU8(elemCount);
+    fFrameCount = SkToS16(frameCount);
+    fRepeat = SK_Scalar1;
+    if (fStorage) {
+        sk_free(fStorage);
+        fStorage = NULL;
+        fTimes = NULL;
+        SkDEBUGCODE(fTimesArray = NULL);
+    }
+}
+
+/*  Each value[] run is formated as:
+        <time (in msec)>
+        <blend>
+        <data[fElemCount]>
+
+    Totaling fElemCount+2 entries per keyframe
+*/
+
+bool SkInterpolatorBase::getDuration(SkMSec* startTime, SkMSec* endTime) const {
+    if (fFrameCount == 0) {
+        return false;
+    }
+
+    if (startTime) {
+        *startTime = fTimes[0].fTime;
+    }
+    if (endTime) {
+        *endTime = fTimes[fFrameCount - 1].fTime;
+    }
+    return true;
+}
+
+SkScalar SkInterpolatorBase::ComputeRelativeT(SkMSec time, SkMSec prevTime,
+                                  SkMSec nextTime, const SkScalar blend[4]) {
+    SkASSERT(time > prevTime && time < nextTime);
+
+    SkScalar t = SkScalarDiv((SkScalar)(time - prevTime),
+                             (SkScalar)(nextTime - prevTime));
+    return blend ?
+            SkUnitCubicInterp(t, blend[0], blend[1], blend[2], blend[3]) : t;
+}
+
+SkInterpolatorBase::Result SkInterpolatorBase::timeToT(SkMSec time, SkScalar* T,
+                                        int* indexPtr, SkBool* exactPtr) const {
+    SkASSERT(fFrameCount > 0);
+    Result  result = kNormal_Result;
+    if (fRepeat != SK_Scalar1) {
+        SkMSec startTime, endTime;
+        this->getDuration(&startTime, &endTime);
+        SkMSec totalTime = endTime - startTime;
+        SkMSec offsetTime = time - startTime;
+        endTime = SkScalarMulFloor(fRepeat, totalTime);
+        if (offsetTime >= endTime) {
+            SkScalar fraction = SkScalarFraction(fRepeat);
+            offsetTime = fraction == 0 && fRepeat > 0 ? totalTime :
+                SkScalarMulFloor(fraction, totalTime);
+            result = kFreezeEnd_Result;
+        } else {
+            int mirror = fFlags & kMirror;
+            offsetTime = offsetTime % (totalTime << mirror);
+            if (offsetTime > totalTime) { // can only be true if fMirror is true
+                offsetTime = (totalTime << 1) - offsetTime;
+            }
+        }
+        time = offsetTime + startTime;
+    }
+
+    int index = SkTSearch<SkMSec>(&fTimes[0].fTime, fFrameCount, time,
+                                  sizeof(SkTimeCode));
+
+    bool    exact = true;
+
+    if (index < 0) {
+        index = ~index;
+        if (index == 0) {
+            result = kFreezeStart_Result;
+        } else if (index == fFrameCount) {
+            if (fFlags & kReset) {
+                index = 0;
+            } else {
+                index -= 1;
+            }
+            result = kFreezeEnd_Result;
+        } else {
+            exact = false;
+        }
+    }
+    SkASSERT(index < fFrameCount);
+    const SkTimeCode* nextTime = &fTimes[index];
+    SkMSec   nextT = nextTime[0].fTime;
+    if (exact) {
+        *T = 0;
+    } else {
+        SkMSec prevT = nextTime[-1].fTime;
+        *T = ComputeRelativeT(time, prevT, nextT, nextTime[-1].fBlend);
+    }
+    *indexPtr = index;
+    *exactPtr = exact;
+    return result;
+}
+
+
+SkInterpolator::SkInterpolator() {
+    INHERITED::reset(0, 0);
+    fValues = NULL;
+    SkDEBUGCODE(fScalarsArray = NULL;)
+}
+
+SkInterpolator::SkInterpolator(int elemCount, int frameCount) {
+    SkASSERT(elemCount > 0);
+    this->reset(elemCount, frameCount);
+}
+
+void SkInterpolator::reset(int elemCount, int frameCount) {
+    INHERITED::reset(elemCount, frameCount);
+    fStorage = sk_malloc_throw((sizeof(SkScalar) * elemCount +
+                                sizeof(SkTimeCode)) * frameCount);
+    fTimes = (SkTimeCode*) fStorage;
+    fValues = (SkScalar*) ((char*) fStorage + sizeof(SkTimeCode) * frameCount);
+#ifdef SK_DEBUG
+    fTimesArray = (SkTimeCode(*)[10]) fTimes;
+    fScalarsArray = (SkScalar(*)[10]) fValues;
+#endif
+}
+
+#define SK_Fixed1Third      (SK_Fixed1/3)
+#define SK_Fixed2Third      (SK_Fixed1*2/3)
+
+static const SkScalar gIdentityBlend[4] = {
+#ifdef SK_SCALAR_IS_FLOAT
+    0.33333333f, 0.33333333f, 0.66666667f, 0.66666667f
+#else
+    SK_Fixed1Third, SK_Fixed1Third, SK_Fixed2Third, SK_Fixed2Third
+#endif
+};
+
+bool SkInterpolator::setKeyFrame(int index, SkMSec time,
+                            const SkScalar values[], const SkScalar blend[4]) {
+    SkASSERT(values != NULL);
+    
+    if (blend == NULL) {
+        blend = gIdentityBlend;
+    }
+
+    bool success = ~index == SkTSearch<SkMSec>(&fTimes->fTime, index, time,
+                                               sizeof(SkTimeCode));
+    SkASSERT(success);
+    if (success) {
+        SkTimeCode* timeCode = &fTimes[index];
+        timeCode->fTime = time;
+        memcpy(timeCode->fBlend, blend, sizeof(timeCode->fBlend));
+        SkScalar* dst = &fValues[fElemCount * index];
+        memcpy(dst, values, fElemCount * sizeof(SkScalar));
+    }
+    return success;
+}
+
+SkInterpolator::Result SkInterpolator::timeToValues(SkMSec time,
+                                                    SkScalar values[]) const {
+    SkScalar T;
+    int index;
+    SkBool exact;
+    Result result = timeToT(time, &T, &index, &exact);
+    if (values) {
+        const SkScalar* nextSrc = &fValues[index * fElemCount];
+
+        if (exact) {
+            memcpy(values, nextSrc, fElemCount * sizeof(SkScalar));
+        } else {
+            SkASSERT(index > 0);
+
+            const SkScalar* prevSrc = nextSrc - fElemCount;
+
+            for (int i = fElemCount - 1; i >= 0; --i) {
+                values[i] = SkScalarInterp(prevSrc[i], nextSrc[i], T);
+            }
+        }
+    }
+    return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef int Dot14;
+#define Dot14_ONE       (1 << 14)
+#define Dot14_HALF      (1 << 13)
+
+#define Dot14ToFloat(x) ((x) / 16384.f)
+
+static inline Dot14 Dot14Mul(Dot14 a, Dot14 b) {
+    return (a * b + Dot14_HALF) >> 14;
+}
+
+static inline Dot14 eval_cubic(Dot14 t, Dot14 A, Dot14 B, Dot14 C) {
+    return Dot14Mul(Dot14Mul(Dot14Mul(C, t) + B, t) + A, t);
+}
+
+static inline Dot14 pin_and_convert(SkScalar x) {
+    if (x <= 0) {
+        return 0;
+    }
+    if (x >= SK_Scalar1) {
+        return Dot14_ONE;
+    }
+    return SkScalarToFixed(x) >> 2;
+}
+
+SkScalar SkUnitCubicInterp(SkScalar value, SkScalar bx, SkScalar by,
+                           SkScalar cx, SkScalar cy) {
+    // pin to the unit-square, and convert to 2.14
+    Dot14 x = pin_and_convert(value);
+    
+    if (x == 0) return 0;
+    if (x == Dot14_ONE) return SK_Scalar1;
+    
+    Dot14 b = pin_and_convert(bx);
+    Dot14 c = pin_and_convert(cx);
+    
+    // Now compute our coefficients from the control points
+    //  t   -> 3b
+    //  t^2 -> 3c - 6b
+    //  t^3 -> 3b - 3c + 1
+    Dot14 A = 3*b;
+    Dot14 B = 3*(c - 2*b);
+    Dot14 C = 3*(b - c) + Dot14_ONE;
+
+    // Now search for a t value given x
+    Dot14   t = Dot14_HALF;
+    Dot14   dt = Dot14_HALF;
+    for (int i = 0; i < 13; i++) {
+        dt >>= 1;
+        Dot14 guess = eval_cubic(t, A, B, C);
+        if (x < guess) {
+            t -= dt;
+        } else {
+            t += dt;
+        }
+    }
+    
+    // Now we have t, so compute the coeff for Y and evaluate
+    b = pin_and_convert(by);
+    c = pin_and_convert(cy);
+    A = 3*b;
+    B = 3*(c - 2*b);
+    C = 3*(b - c) + Dot14_ONE;
+    return SkFixedToScalar(eval_cubic(t, A, B, C) << 2);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#ifdef SK_SUPPORT_UNITTEST
+    static SkScalar* iset(SkScalar array[3], int a, int b, int c) {
+        array[0] = SkIntToScalar(a);
+        array[1] = SkIntToScalar(b);
+        array[2] = SkIntToScalar(c);
+        return array;
+    }
+#endif
+
+void SkInterpolator::UnitTest() {
+#ifdef SK_SUPPORT_UNITTEST
+    SkInterpolator  inter(3, 2);
+    SkScalar        v1[3], v2[3], v[3], vv[3];
+    Result          result;
+
+    inter.setKeyFrame(0, 100, iset(v1, 10, 20, 30), 0);
+    inter.setKeyFrame(1, 200, iset(v2, 110, 220, 330));
+
+    result = inter.timeToValues(0, v);
+    SkASSERT(result == kFreezeStart_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(99, v);
+    SkASSERT(result == kFreezeStart_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(100, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, v1, sizeof(v)) == 0);
+
+    result = inter.timeToValues(200, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, v2, sizeof(v)) == 0);
+
+    result = inter.timeToValues(201, v);
+    SkASSERT(result == kFreezeEnd_Result);
+    SkASSERT(memcmp(v, v2, sizeof(v)) == 0);
+
+    result = inter.timeToValues(150, v);
+    SkASSERT(result == kNormal_Result);
+    SkASSERT(memcmp(v, iset(vv, 60, 120, 180), sizeof(v)) == 0);
+
+    result = inter.timeToValues(125, v);
+    SkASSERT(result == kNormal_Result);
+    result = inter.timeToValues(175, v);
+    SkASSERT(result == kNormal_Result);
+#endif
+}
+
+#endif
+
diff --git a/src/utils/SkNinePatch.cpp b/src/utils/SkNinePatch.cpp
new file mode 100644
index 0000000..b8e11fb
--- /dev/null
+++ b/src/utils/SkNinePatch.cpp
@@ -0,0 +1,287 @@
+/*
+** 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 "SkNinePatch.h"
+#include "SkCanvas.h"
+#include "SkShader.h"
+
+static const uint16_t g3x3Indices[] = {
+    0, 5, 1,    0, 4, 5,
+    1, 6, 2,    1, 5, 6,
+    2, 7, 3,    2, 6, 7,
+    
+    4, 9, 5,    4, 8, 9,
+    5, 10, 6,   5, 9, 10,
+    6, 11, 7,   6, 10, 11,
+    
+    8, 13, 9,   8, 12, 13,
+    9, 14, 10,  9, 13, 14,
+    10, 15, 11, 10, 14, 15
+};
+
+static int fillIndices(uint16_t indices[], int xCount, int yCount) {
+    uint16_t* startIndices = indices;
+    
+    int n = 0;
+    for (int y = 0; y < yCount; y++) {
+        for (int x = 0; x < xCount; x++) {
+            *indices++ = n;
+            *indices++ = n + xCount + 2;
+            *indices++ = n + 1;
+            
+            *indices++ = n;
+            *indices++ = n + xCount + 1;
+            *indices++ = n + xCount + 2;
+            
+            n += 1;
+        }
+        n += 1;
+    }
+    return indices - startIndices;
+}
+
+static void fillRow(SkPoint verts[], SkPoint texs[],
+                    const SkScalar vy, const SkScalar ty,
+                    const SkRect& bounds, const int32_t xDivs[], int numXDivs,
+                    const SkScalar stretchX, int width) {
+    SkScalar vx = bounds.fLeft;
+    verts->set(vx, vy); verts++;
+    texs->set(0, ty); texs++;
+    for (int x = 0; x < numXDivs; x++) {
+        SkScalar tx = SkIntToScalar(xDivs[x]);
+        if (x & 1) {
+            vx += stretchX;
+        } else {
+            vx += tx;
+        }
+        verts->set(vx, vy); verts++;
+        texs->set(tx, ty); texs++;
+    }
+    verts->set(bounds.fRight, vy); verts++;
+    texs->set(SkIntToScalar(width), ty); texs++;
+}
+
+struct Mesh {
+    const SkPoint*  fVerts;
+    const SkPoint*  fTexs;
+    const SkColor*  fColors;
+    const uint16_t* fIndices;
+};
+
+void SkNinePatch::DrawMesh(SkCanvas* canvas, const SkRect& bounds,
+                           const SkBitmap& bitmap,
+                           const int32_t xDivs[], int numXDivs,
+                           const int32_t yDivs[], int numYDivs,
+                           const SkPaint* paint) {
+    if (bounds.isEmpty() || bitmap.width() == 0 || bitmap.height() == 0) {
+        return;
+    }
+    
+    // should try a quick-reject test before calling lockPixels
+    SkAutoLockPixels alp(bitmap);
+    // after the lock, it is valid to check
+    if (!bitmap.readyToDraw()) {
+        return;
+    }
+    
+    // check for degenerate divs (just an optimization, not required)
+    {
+        int i;
+        int zeros = 0;
+        for (i = 0; i < numYDivs && yDivs[i] == 0; i++) {
+            zeros += 1;
+        }
+        numYDivs -= zeros;
+        yDivs += zeros;
+        for (i = numYDivs - 1; i >= 0 && yDivs[i] == bitmap.height(); --i) {
+            numYDivs -= 1;
+        }
+    }
+    
+    Mesh mesh;
+    
+    const int numXStretch = (numXDivs + 1) >> 1;
+    const int numYStretch = (numYDivs + 1) >> 1;
+    
+    if (numXStretch < 1 && numYStretch < 1) {
+    BITMAP_RECT:
+//        SkDebugf("------ drawasamesh revert to bitmaprect\n");
+        canvas->drawBitmapRect(bitmap, NULL, bounds, paint);
+        return;
+    }
+    
+    if (false) {
+        int i;
+        for (i = 0; i < numXDivs; i++) {
+            SkDebugf("--- xdivs[%d] %d\n", i, xDivs[i]);
+        }
+        for (i = 0; i < numYDivs; i++) {
+            SkDebugf("--- ydivs[%d] %d\n", i, yDivs[i]);
+        }
+    }
+    
+    SkScalar stretchX = 0, stretchY = 0;
+    
+    if (numXStretch > 0) {
+        int stretchSize = 0;
+        for (int i = 1; i < numXDivs; i += 2) {
+            stretchSize += xDivs[i] - xDivs[i-1];
+        }
+        int fixed = bitmap.width() - stretchSize;
+        stretchX = (bounds.width() - SkIntToScalar(fixed)) / numXStretch;
+        if (stretchX < 0) {
+            goto BITMAP_RECT;
+        }
+    }
+    
+    if (numYStretch > 0) {
+        int stretchSize = 0;
+        for (int i = 1; i < numYDivs; i += 2) {
+            stretchSize += yDivs[i] - yDivs[i-1];
+        }
+        int fixed = bitmap.height() - stretchSize;
+        stretchY = (bounds.height() - SkIntToScalar(fixed)) / numYStretch;
+        if (stretchY < 0) {
+            goto BITMAP_RECT;
+        }
+    }
+    
+#if 0
+    SkDebugf("---- drawasamesh [%d %d] -> [%g %g] <%d %d> (%g %g)\n",
+             bitmap.width(), bitmap.height(),
+             SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()),
+             numXDivs + 1, numYDivs + 1,
+             SkScalarToFloat(stretchX), SkScalarToFloat(stretchY));
+#endif
+
+    const int vCount = (numXDivs + 2) * (numYDivs + 2);
+    // number of celss * 2 (tris per cell) * 3 (verts per tri)
+    const int indexCount = (numXDivs + 1) * (numYDivs + 1) * 2 * 3;
+    // allocate 2 times, one for verts, one for texs, plus indices
+    SkAutoMalloc storage(vCount * sizeof(SkPoint) * 2 +
+                         indexCount * sizeof(uint16_t));
+    SkPoint* verts = (SkPoint*)storage.get();
+    SkPoint* texs = verts + vCount;
+    uint16_t* indices = (uint16_t*)(texs + vCount);
+    
+    mesh.fVerts = verts;
+    mesh.fTexs = texs;
+    mesh.fColors = NULL;
+    mesh.fIndices = NULL;
+    
+    // we use <= for YDivs, since the prebuild indices work for 3x2 and 3x1 too
+    if (numXDivs == 2 && numYDivs <= 2) {
+        mesh.fIndices = g3x3Indices;
+    } else {
+        int n = fillIndices(indices, numXDivs + 1, numYDivs + 1);
+        SkASSERT(n == indexCount);
+        mesh.fIndices = indices;
+    }
+    
+    SkScalar vy = bounds.fTop;
+    fillRow(verts, texs, vy, 0, bounds, xDivs, numXDivs,
+            stretchX, bitmap.width());
+    verts += numXDivs + 2;
+    texs += numXDivs + 2;
+    for (int y = 0; y < numYDivs; y++) {
+        const SkScalar ty = SkIntToScalar(yDivs[y]);
+        if (y & 1) {
+            vy += stretchY;
+        } else {
+            vy += ty;
+        }
+        fillRow(verts, texs, vy, ty, bounds, xDivs, numXDivs,
+                stretchX, bitmap.width());
+        verts += numXDivs + 2;
+        texs += numXDivs + 2;
+    }
+    fillRow(verts, texs, bounds.fBottom, SkIntToScalar(bitmap.height()),
+            bounds, xDivs, numXDivs, stretchX, bitmap.width());
+    
+    SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+                                                    SkShader::kClamp_TileMode,
+                                                    SkShader::kClamp_TileMode);
+    SkPaint p;
+    if (paint) {
+        p = *paint;
+    }
+    p.setShader(shader)->unref();
+    canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vCount,
+                         mesh.fVerts, mesh.fTexs, mesh.fColors, NULL,
+                         mesh.fIndices, indexCount, p);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawNineViaRects(SkCanvas* canvas, const SkRect& dst,
+                             const SkBitmap& bitmap, const SkIRect& margins,
+                             const SkPaint* paint) {
+    const int32_t srcX[4] = {
+        0, margins.fLeft, bitmap.width() - margins.fRight, bitmap.width()
+    };
+    const int32_t srcY[4] = {
+        0, margins.fTop, bitmap.height() - margins.fBottom, bitmap.height()
+    };
+    const SkScalar dstX[4] = {
+        dst.fLeft, dst.fLeft + SkIntToScalar(margins.fLeft),
+        dst.fRight - SkIntToScalar(margins.fRight), dst.fRight
+    };
+    const SkScalar dstY[4] = {
+        dst.fTop, dst.fTop + SkIntToScalar(margins.fTop),
+        dst.fBottom - SkIntToScalar(margins.fBottom), dst.fBottom
+    };
+    
+    SkIRect s;
+    SkRect  d;
+    for (int y = 0; y < 3; y++) {
+        s.fTop = srcY[y];
+        s.fBottom = srcY[y+1];
+        d.fTop = dstY[y];
+        d.fBottom = dstY[y+1];
+        for (int x = 0; x < 3; x++) {
+            s.fLeft = srcX[x];
+            s.fRight = srcX[x+1];
+            d.fLeft = dstX[x];
+            d.fRight = dstX[x+1];
+            canvas->drawBitmapRect(bitmap, &s, d, paint);
+        }
+    }
+}
+
+void SkNinePatch::DrawNine(SkCanvas* canvas, const SkRect& bounds,
+                           const SkBitmap& bitmap, const SkIRect& margins,
+                           const SkPaint* paint) {
+    /** Our vertices code has numerical precision problems if the transformed
+     coordinates land directly on a 1/2 pixel boundary. To work around that
+     for now, we only take the vertices case if we are in opengl. Also,
+     when not in GL, the vertices impl is slower (more math) than calling
+     the viaRects code.
+     */
+    if (canvas->getViewport(NULL)) {    // returns true for OpenGL
+        int32_t xDivs[2];
+        int32_t yDivs[2];
+        
+        xDivs[0] = margins.fLeft;
+        xDivs[1] = bitmap.width() - margins.fRight;
+        yDivs[0] = margins.fTop;
+        yDivs[1] = bitmap.height() - margins.fBottom;
+        
+        SkNinePatch::DrawMesh(canvas, bounds, bitmap,
+                              xDivs, 2, yDivs, 2, paint);
+    } else {
+        drawNineViaRects(canvas, bounds, bitmap, margins, paint);
+    }
+}
diff --git a/src/utils/SkProxyCanvas.cpp b/src/utils/SkProxyCanvas.cpp
new file mode 100644
index 0000000..2a02b45
--- /dev/null
+++ b/src/utils/SkProxyCanvas.cpp
@@ -0,0 +1,162 @@
+#include "SkProxyCanvas.h"
+
+SkProxyCanvas::SkProxyCanvas(SkCanvas* proxy) : fProxy(proxy) {
+    fProxy->safeRef();
+}
+
+SkProxyCanvas::~SkProxyCanvas() {
+    fProxy->safeUnref();
+}
+    
+void SkProxyCanvas::setProxy(SkCanvas* proxy) {
+    SkRefCnt_SafeAssign(fProxy, proxy);
+}
+
+///////////////////////////////// Overrides ///////////
+
+bool SkProxyCanvas::getViewport(SkIPoint* size) const {
+    return fProxy->getViewport(size);
+}
+
+bool SkProxyCanvas::setViewport(int x, int y) {
+    return fProxy->setViewport(x, y);
+}
+
+SkDevice* SkProxyCanvas::setBitmapDevice(const SkBitmap& bitmap) {
+    return fProxy->setBitmapDevice(bitmap);
+}
+
+int SkProxyCanvas::save(SaveFlags flags) {
+    return fProxy->save(flags);
+}
+
+int SkProxyCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                             SaveFlags flags) {
+    return fProxy->saveLayer(bounds, paint, flags);
+}
+
+void SkProxyCanvas::restore() {
+    fProxy->restore();
+}
+
+bool SkProxyCanvas::translate(SkScalar dx, SkScalar dy) {
+    return fProxy->translate(dx, dy);
+}
+
+bool SkProxyCanvas::scale(SkScalar sx, SkScalar sy) {
+    return fProxy->scale(sx, sy);
+}
+
+bool SkProxyCanvas::rotate(SkScalar degrees) {
+    return fProxy->rotate(degrees);
+}
+
+bool SkProxyCanvas::skew(SkScalar sx, SkScalar sy) {
+    return fProxy->skew(sx, sy);
+}
+
+bool SkProxyCanvas::concat(const SkMatrix& matrix) {
+    return fProxy->concat(matrix);
+}
+
+void SkProxyCanvas::setMatrix(const SkMatrix& matrix) {
+    fProxy->setMatrix(matrix);
+}
+
+bool SkProxyCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
+    return fProxy->clipRect(rect, op);
+}
+
+bool SkProxyCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
+    return fProxy->clipPath(path, op);
+}
+
+bool SkProxyCanvas::clipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
+    return fProxy->clipRegion(deviceRgn, op);
+}
+
+void SkProxyCanvas::drawPaint(const SkPaint& paint) {
+    fProxy->drawPaint(paint);
+}
+
+void SkProxyCanvas::drawPoints(PointMode mode, size_t count,
+                               const SkPoint pts[], const SkPaint& paint) {
+    fProxy->drawPoints(mode, count, pts, paint);
+}
+
+void SkProxyCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+    fProxy->drawRect(rect, paint);
+}
+
+void SkProxyCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    fProxy->drawPath(path, paint);
+}
+
+void SkProxyCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+                               const SkPaint* paint) {
+    fProxy->drawBitmap(bitmap, x, y, paint);
+}
+
+void SkProxyCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                                   const SkRect& dst, const SkPaint* paint) {
+    fProxy->drawBitmapRect(bitmap, src, dst, paint);
+}
+
+void SkProxyCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+                                     const SkPaint* paint) {
+    fProxy->drawBitmapMatrix(bitmap, m, paint);
+}
+
+void SkProxyCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+                               const SkPaint* paint) {
+    fProxy->drawSprite(bitmap, x, y, paint);
+}
+
+void SkProxyCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
+                             SkScalar y, const SkPaint& paint) {
+    fProxy->drawText(text, byteLength, x, y, paint);
+}
+
+void SkProxyCanvas::drawPosText(const void* text, size_t byteLength,
+                                const SkPoint pos[], const SkPaint& paint) {
+    fProxy->drawPosText(text, byteLength, pos, paint);
+}
+
+void SkProxyCanvas::drawPosTextH(const void* text, size_t byteLength,
+                                 const SkScalar xpos[], SkScalar constY,
+                                 const SkPaint& paint) {
+    fProxy->drawPosTextH(text, byteLength, xpos, constY, paint);
+}
+
+void SkProxyCanvas::drawTextOnPath(const void* text, size_t byteLength,
+                                   const SkPath& path, const SkMatrix* matrix,
+                                   const SkPaint& paint) {
+    fProxy->drawTextOnPath(text, byteLength, path, matrix, paint);
+}
+
+void SkProxyCanvas::drawPicture(SkPicture& picture) {
+    fProxy->drawPicture(picture);
+}
+
+void SkProxyCanvas::drawVertices(VertexMode vmode, int vertexCount,
+                                 const SkPoint vertices[], const SkPoint texs[],
+                                 const SkColor colors[], SkXfermode* xmode,
+                                 const uint16_t indices[], int indexCount,
+                                 const SkPaint& paint) {
+    fProxy->drawVertices(vmode, vertexCount, vertices, texs, colors,
+                                     xmode, indices, indexCount, paint);
+}
+
+SkBounder* SkProxyCanvas::setBounder(SkBounder* bounder) {
+    return fProxy->setBounder(bounder);
+}
+
+SkDrawFilter* SkProxyCanvas::setDrawFilter(SkDrawFilter* filter) {
+    return fProxy->setDrawFilter(filter);
+}
+
+SkDevice* SkProxyCanvas::createDevice(SkBitmap::Config config, int width,
+                                int height, bool isOpaque, bool isForLayer) {
+    return fProxy->createDevice(config, width, height, isOpaque, isForLayer);
+}
+
diff --git a/src/utils/SkUnitMappers.cpp b/src/utils/SkUnitMappers.cpp
new file mode 100644
index 0000000..0363a2b
--- /dev/null
+++ b/src/utils/SkUnitMappers.cpp
@@ -0,0 +1,80 @@
+#include "SkUnitMappers.h"
+
+SkDiscreteMapper::SkDiscreteMapper(int segments)
+{
+    if (segments < 2)
+    {
+        fSegments = 0;
+        fScale = 0;
+    }
+    else
+    {
+        if (segments > 0xFFFF)
+            segments = 0xFFFF;
+        fSegments = segments;
+        fScale = SK_Fract1 / (segments - 1);
+    }
+}
+
+uint16_t SkDiscreteMapper::mapUnit16(uint16_t input)
+{
+    SkFixed x = input * fSegments >> 16;
+    x = x * fScale >> 14;
+    x += x << 15 >> 31; // map 0x10000 to 0xFFFF
+    return SkToU16(x);
+}
+
+SkDiscreteMapper::SkDiscreteMapper(SkFlattenableReadBuffer& rb)
+    : SkUnitMapper(rb)
+{
+    fSegments = rb.readU32();
+    fScale = rb.readU32();
+}
+
+SkFlattenable::Factory SkDiscreteMapper::getFactory()
+{
+    return Create;
+}
+
+SkFlattenable* SkDiscreteMapper::Create(SkFlattenableReadBuffer& rb)
+{
+    return SkNEW_ARGS(SkDiscreteMapper, (rb));
+}
+
+void SkDiscreteMapper::flatten(SkFlattenableWriteBuffer& wb)
+{
+    this->INHERITED::flatten(wb);
+
+    wb.write32(fSegments);
+    wb.write32(fScale);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint16_t SkCosineMapper::mapUnit16(uint16_t input)
+{
+    /*  we want to call cosine(input * pi/2) treating input as [0...1)
+        however, the straight multitply would overflow 32bits since input is
+        16bits and pi/2 is 17bits, so we shift down our pi const before we mul
+    */
+    SkFixed rads = (unsigned)(input * (SK_FixedPI >> 2)) >> 15;
+    SkFixed x = SkFixedCos(rads);
+    x += x << 15 >> 31; // map 0x10000 to 0xFFFF
+    return SkToU16(x);
+}
+
+SkCosineMapper::SkCosineMapper(SkFlattenableReadBuffer& rb)
+    : SkUnitMapper(rb)
+{
+}
+
+SkFlattenable::Factory SkCosineMapper::getFactory()
+{
+    return Create;
+}
+
+SkFlattenable* SkCosineMapper::Create(SkFlattenableReadBuffer& rb)
+{
+    return SkNEW_ARGS(SkCosineMapper, (rb));
+}
+
diff --git a/src/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..3356782
--- /dev/null
+++ b/src/views/SkBorderView.cpp
@@ -0,0 +1,87 @@
+#include "SkBorderView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+#include "SkStackViewLayout.h"
+
+SkBorderView::SkBorderView() : fTop(SkIntToScalar(0)), fLeft(SkIntToScalar(0)),
+					fRight(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(nil);
+	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(nil);
+		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..67549b4
--- /dev/null
+++ b/src/views/SkEvent.cpp
@@ -0,0 +1,565 @@
+/* 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);
+}
+
+////////////////////////////////////////////////////////////////
+
+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..f19e913
--- /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		= nil;
+	fScaleType	= kMatrix_ScaleType;
+
+	fData.fAnim	= nil;		// 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(nil);
+	}
+}
+
+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 == nil)
+			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 = nil;
+			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(nil);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImageView::onEvent(const SkEvent& evt)
+{
+	if (evt.isType(SK_EventType_Inval))
+	{
+		if (fUriIsValid)
+			this->inval(nil);
+		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(nil);
+		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(nil);
+	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 = nil;	// 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..27218a9
--- /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 nil;
+}
+
+#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(nil))
+				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(nil);
+			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, nil);
+		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 = nil;
+		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 nil;
+	}
+
+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 = nil;
+	fScrollIndex = 0;
+	fCurrIndex = -1;
+	fRowHeight = SkIntToScalar(16);
+	fVisibleRowCount = 0;
+	fStrCache = nil;
+
+	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(nil);
+		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 == nil)
+		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(nil);
+		}
+	}
+}
+
+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(nil);
+	}
+	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, nil, &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 = nil;
+	}
+}
+
+void SkListView::ensureStrCache(int count)
+{
+	if (fStrCache == nil)
+	{
+		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 != nil;
+				}
+				else	// hack to make toggle work
+				{
+					this->dirtyStrCache();
+					this->inval(nil);
+				}
+			}
+			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")) != nil)
+		SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
+
+	// look for a listsource
+	{
+		SkListSource* src = nil;
+
+		if ((child = dom.getFirstChild(node, "file-listsource")) != nil)
+		{
+			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")) != nil)
+		{
+			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 == nil) 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 = nil;
+	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(nil);
+	}
+}
+
+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 == nil)
+		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(nil);
+		}
+	}
+#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(nil);
+	}
+	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 == nil)
+		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 == nil)
+		{
+			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(nil);	// 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, nil));
+
+					SkView* view = this->sendEventToParents(*evt);
+					delete evt;
+					return view != nil;
+				}
+			}
+			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")) != nil)
+		SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
+
+	// look for a listsource
+	{
+		SkListSource* src = nil;
+
+		if ((child = dom.getFirstChild(node, "file-listsource")) != nil)
+		{
+			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")) != nil)
+		{
+			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..82e5d78
--- /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 = nil;				// our list-source
+	fScrollBar = nil;
+	fAnims = nil;				// array of animators[fVisibleRowCount]
+	fBindings = nil;			// 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()
+{
+	fScrollBar->safeUnref();
+	fSource->safeUnref();
+	delete[] fAnims;
+	delete[] fBindings;
+}
+
+void SkListView::setHasScrollBar(bool hasSB)
+{
+	if (hasSB != this->hasScrollBar())
+	{
+		if (hasSB)
+		{
+			SkASSERT(fScrollBar == nil);
+			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 = nil;
+		}
+		this->dirtyCache(kAnimContent_DirtyFlag);
+	}
+}
+
+void SkListView::setSelection(int index)
+{
+	if (fCurrIndex != index)
+	{
+		fAnimFocusDirty = true;
+		this->inval(nil);
+
+		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(nil);
+			
+			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(nil);
+		
+		if (fScrollBar)
+			fScrollBar->setTotal(fSource->countRecords());
+	}
+	return src;
+}
+
+void SkListView::dirtyCache(unsigned dirtyFlags)
+{
+	if (dirtyFlags & kAnimCount_DirtyFlag)
+	{
+		delete fAnims;
+		fAnims = nil;
+		fAnimContentDirty = true;
+		fAnimFocusDirty = true;
+	}
+	if (dirtyFlags & kAnimContent_DirtyFlag)
+	{
+		if (!fAnimContentDirty)
+		{
+			this->inval(nil);
+			fAnimContentDirty = true;
+		}
+		fAnimFocusDirty = true;
+	}
+}
+
+bool SkListView::ensureCache()
+{
+	if (fSkinName.size() == 0)
+		return false;
+
+	if (fAnims == nil)
+	{
+		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")) != nil)
+	{
+		delete[] fBindings;
+		fBindings = nil;
+		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")) != nil);
+
+			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 = nil;
+
+	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/SkMetaData.cpp b/src/views/SkMetaData.cpp
new file mode 100644
index 0000000..c366bd3
--- /dev/null
+++ b/src/views/SkMetaData.cpp
@@ -0,0 +1,405 @@
+/* libs/graphics/views/SkMetaData.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 "SkMetaData.h"
+
+SkMetaData::SkMetaData() : fRec(NULL)
+{
+}
+
+SkMetaData::SkMetaData(const SkMetaData& src) : fRec(NULL)
+{
+    *this = src;
+}
+
+SkMetaData::~SkMetaData()
+{
+    this->reset();
+}
+
+void SkMetaData::reset()
+{
+    Rec* rec = fRec;
+    while (rec)
+    {
+        Rec* next = rec->fNext;
+        Rec::Free(rec);
+        rec = next;
+    }
+    fRec = NULL;
+}
+
+SkMetaData& SkMetaData::operator=(const SkMetaData& src)
+{
+    this->reset();
+
+    const Rec* rec = src.fRec;
+    while (rec)
+    {
+        this->set(rec->name(), rec->data(), rec->fDataLen, (Type)rec->fType, rec->fDataCount);
+        rec = rec->fNext;
+    }
+    return *this;
+}
+
+void SkMetaData::setS32(const char name[], int32_t value)
+{
+    (void)this->set(name, &value, sizeof(int32_t), kS32_Type, 1);
+}
+
+void SkMetaData::setScalar(const char name[], SkScalar value)
+{
+    (void)this->set(name, &value, sizeof(SkScalar), kScalar_Type, 1);
+}
+
+SkScalar* SkMetaData::setScalars(const char name[], int count, const SkScalar values[])
+{
+    SkASSERT(count > 0);
+    if (count > 0)
+        return (SkScalar*)this->set(name, values, sizeof(SkScalar), kScalar_Type, count);
+    return NULL;
+}
+
+void SkMetaData::setString(const char name[], const char value[])
+{
+    (void)this->set(name, value, sizeof(char), kString_Type, strlen(value) + 1);
+}
+
+void SkMetaData::setPtr(const char name[], void* ptr)
+{
+    (void)this->set(name, &ptr, sizeof(void*), kPtr_Type, 1);
+}
+
+void SkMetaData::setBool(const char name[], bool value)
+{
+    (void)this->set(name, &value, sizeof(bool), kBool_Type, 1);
+}
+
+void* SkMetaData::set(const char name[], const void* data, size_t dataSize, Type type, int count)
+{
+    SkASSERT(name);
+    SkASSERT(dataSize);
+    SkASSERT(count > 0);
+
+    (void)this->remove(name, type);
+
+    size_t  len = strlen(name);
+    Rec*    rec = Rec::Alloc(sizeof(Rec) + dataSize * count + len + 1);
+
+#ifndef SK_DEBUG
+    rec->fType = SkToU8(type);
+#else
+    rec->fType = type;
+#endif
+    rec->fDataLen = SkToU8(dataSize);
+    rec->fDataCount = SkToU16(count);
+    if (data)
+        memcpy(rec->data(), data, dataSize * count);
+    memcpy(rec->name(), name, len + 1);
+
+#ifdef SK_DEBUG
+    rec->fName = rec->name();
+    switch (type) {
+    case kS32_Type:
+        rec->fData.fS32 = *(const int32_t*)rec->data();
+        break;
+    case kScalar_Type:
+        rec->fData.fScalar = *(const SkScalar*)rec->data();
+        break;
+    case kString_Type:
+        rec->fData.fString = (const char*)rec->data();
+        break;
+    case kPtr_Type:
+        rec->fData.fPtr = *(void**)rec->data();
+        break;
+    case kBool_Type:
+        rec->fData.fBool = *(const bool*)rec->data();
+        break;
+    default:
+        SkASSERT(!"bad type");
+        break;
+    }
+#endif
+
+    rec->fNext = fRec;
+    fRec = rec;
+    return rec->data();
+}
+
+bool SkMetaData::findS32(const char name[], int32_t* value) const
+{
+    const Rec* rec = this->find(name, kS32_Type);
+    if (rec)
+    {
+        SkASSERT(rec->fDataCount == 1);
+        if (value)
+            *value = *(const int32_t*)rec->data();
+        return true;
+    }
+    return false;
+}
+
+bool SkMetaData::findScalar(const char name[], SkScalar* value) const
+{
+    const Rec* rec = this->find(name, kScalar_Type);
+    if (rec)
+    {
+        SkASSERT(rec->fDataCount == 1);
+        if (value)
+            *value = *(const SkScalar*)rec->data();
+        return true;
+    }
+    return false;
+}
+
+const SkScalar* SkMetaData::findScalars(const char name[], int* count, SkScalar values[]) const
+{
+    const Rec* rec = this->find(name, kScalar_Type);
+    if (rec)
+    {
+        if (count)
+            *count = rec->fDataCount;
+        if (values)
+            memcpy(values, rec->data(), rec->fDataCount * rec->fDataLen);
+        return (const SkScalar*)rec->data();
+    }
+    return NULL;
+}
+
+bool SkMetaData::findPtr(const char name[], void** value) const
+{
+    const Rec* rec = this->find(name, kPtr_Type);
+    if (rec)
+    {
+        SkASSERT(rec->fDataCount == 1);
+        if (value)
+            *value = *(void**)rec->data();
+        return true;
+    }
+    return false;
+}
+
+const char* SkMetaData::findString(const char name[]) const
+{
+    const Rec* rec = this->find(name, kString_Type);
+    SkASSERT(rec == NULL || rec->fDataLen == sizeof(char));
+    return rec ? (const char*)rec->data() : NULL;
+}
+
+bool SkMetaData::findBool(const char name[], bool* value) const
+{
+    const Rec* rec = this->find(name, kBool_Type);
+    if (rec)
+    {
+        SkASSERT(rec->fDataCount == 1);
+        if (value)
+            *value = *(const bool*)rec->data();
+        return true;
+    }
+    return false;
+}
+
+const SkMetaData::Rec* SkMetaData::find(const char name[], Type type) const
+{
+    const Rec* rec = fRec;
+    while (rec)
+    {
+        if (rec->fType == type && !strcmp(rec->name(), name))
+            return rec;
+        rec = rec->fNext;
+    }
+    return NULL;
+}
+
+bool SkMetaData::remove(const char name[], Type type)
+{
+    Rec* rec = fRec;
+    Rec* prev = NULL;
+    while (rec)
+    {
+        Rec* next = rec->fNext;
+        if (rec->fType == type && !strcmp(rec->name(), name))
+        {
+            if (prev)
+                prev->fNext = next;
+            else
+                fRec = next;
+            Rec::Free(rec);
+            return true;
+        }
+        prev = rec;
+        rec = next;
+    }
+    return false;
+}
+
+bool SkMetaData::removeS32(const char name[])
+{
+    return this->remove(name, kS32_Type);
+}
+
+bool SkMetaData::removeScalar(const char name[])
+{
+    return this->remove(name, kScalar_Type);
+}
+
+bool SkMetaData::removeString(const char name[])
+{
+    return this->remove(name, kString_Type);
+}
+
+bool SkMetaData::removePtr(const char name[])
+{
+    return this->remove(name, kPtr_Type);
+}
+
+bool SkMetaData::removeBool(const char name[])
+{
+    return this->remove(name, kBool_Type);
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+SkMetaData::Iter::Iter(const SkMetaData& metadata)
+{
+    fRec = metadata.fRec;
+}
+
+void SkMetaData::Iter::reset(const SkMetaData& metadata)
+{
+    fRec = metadata.fRec;
+}
+
+const char* SkMetaData::Iter::next(SkMetaData::Type* t, int* count)
+{
+    const char* name = NULL;
+
+    if (fRec)
+    {
+        if (t)
+            *t = (SkMetaData::Type)fRec->fType;
+        if (count)
+            *count = fRec->fDataCount;
+        name = fRec->name();
+
+        fRec = fRec->fNext;
+    }
+    return name;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+SkMetaData::Rec* SkMetaData::Rec::Alloc(size_t size)
+{
+    return (Rec*)sk_malloc_throw(size);
+}
+
+void SkMetaData::Rec::Free(Rec* rec)
+{
+    sk_free(rec);
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkMetaData::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    SkMetaData  m1;
+
+    SkASSERT(!m1.findS32("int"));
+    SkASSERT(!m1.findScalar("scalar"));
+    SkASSERT(!m1.findString("hello"));
+    SkASSERT(!m1.removeS32("int"));
+    SkASSERT(!m1.removeScalar("scalar"));
+    SkASSERT(!m1.removeString("hello"));
+    SkASSERT(!m1.removeString("true"));
+    SkASSERT(!m1.removeString("false"));
+
+    m1.setS32("int", 12345);
+    m1.setScalar("scalar", SK_Scalar1 * 42);
+    m1.setString("hello", "world");
+    m1.setPtr("ptr", &m1);
+    m1.setBool("true", true);
+    m1.setBool("false", false);
+
+    int32_t     n;
+    SkScalar    s;
+
+    m1.setScalar("scalar", SK_Scalar1/2);
+
+    SkASSERT(m1.findS32("int", &n) && n == 12345);
+    SkASSERT(m1.findScalar("scalar", &s) && s == SK_Scalar1/2);
+    SkASSERT(!strcmp(m1.findString("hello"), "world"));
+    SkASSERT(m1.hasBool("true", true));
+    SkASSERT(m1.hasBool("false", false));
+
+    Iter    iter(m1);
+    const char* name;
+
+    static const struct {
+        const char*         fName;
+        SkMetaData::Type    fType;
+        int                 fCount;
+    } gElems[] = {
+        { "int",    SkMetaData::kS32_Type,      1 },
+        { "scalar", SkMetaData::kScalar_Type,   1 },
+        { "ptr",    SkMetaData::kPtr_Type,      1 },
+        { "hello",  SkMetaData::kString_Type,   sizeof("world") },
+        { "true",   SkMetaData::kBool_Type,     1 },
+        { "false",  SkMetaData::kBool_Type,     1 }
+    };
+
+    int                 loop = 0;
+    int count;
+    SkMetaData::Type    t;
+    while ((name = iter.next(&t, &count)) != NULL)
+    {
+        int match = 0;
+        for (unsigned i = 0; i < SK_ARRAY_COUNT(gElems); i++)
+        {
+            if (!strcmp(name, gElems[i].fName))
+            {
+                match += 1;
+                SkASSERT(gElems[i].fType == t);
+                SkASSERT(gElems[i].fCount == count);
+            }
+        }
+        SkASSERT(match == 1);
+        loop += 1;
+    }
+    SkASSERT(loop == SK_ARRAY_COUNT(gElems));
+
+    SkASSERT(m1.removeS32("int"));
+    SkASSERT(m1.removeScalar("scalar"));
+    SkASSERT(m1.removeString("hello"));
+    SkASSERT(m1.removeBool("true"));
+    SkASSERT(m1.removeBool("false"));
+
+    SkASSERT(!m1.findS32("int"));
+    SkASSERT(!m1.findScalar("scalar"));
+    SkASSERT(!m1.findString("hello"));
+    SkASSERT(!m1.findBool("true"));
+    SkASSERT(!m1.findBool("false"));
+#endif
+}
+
+#endif
+
+
diff --git a/src/views/SkOSFile.cpp b/src/views/SkOSFile.cpp
new file mode 100644
index 0000000..c8eeeea
--- /dev/null
+++ b/src/views/SkOSFile.cpp
@@ -0,0 +1,223 @@
+#include "SkOSFile.h"
+
+#ifdef SK_BUILD_FOR_WIN
+
+static U16* concat_to_16(const char src[], const char suffix[])
+{
+	size_t	i, len = strlen(src);
+	size_t  len2 = 3 + (suffix ? strlen(suffix) : 0);
+	U16*	dst = (U16*)sk_malloc_throw((len + len2) * sizeof(U16));
+
+	for (i = 0; i < len; i++)
+		dst[i] = src[i];
+
+	if (i > 0 && dst[i-1] != '/')
+		dst[i++] = '/';
+	dst[i++] = '*';
+
+	if (suffix)
+	{
+		while (*suffix)
+			dst[i++] = *suffix++;
+	}
+	dst[i] = 0;
+	SkASSERT(i + 1 <= len + len2);
+
+	return dst;
+}
+
+SkUTF16_Str::SkUTF16_Str(const char src[])
+{
+	size_t	len = strlen(src);
+
+	fStr = (U16*)sk_malloc_throw((len + 1) * sizeof(U16));
+	for (size_t i = 0; i < len; i++)
+		fStr[i] = src[i];
+	fStr[i] = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkOSFile::Iter::Iter() : fHandle(0), fPath16(nil)
+{
+}
+
+SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fHandle(0), fPath16(nil)
+{
+	this->reset(path, suffix);
+}
+
+SkOSFile::Iter::~Iter()
+{
+	sk_free(fPath16);
+	if (fHandle)
+		::FindClose(fHandle);
+}
+
+void SkOSFile::Iter::reset(const char path[], const char suffix[])
+{
+	if (fHandle)
+	{
+		::FindClose(fHandle);
+		fHandle = 0;
+	}
+	if (path == nil)
+		path = "";
+
+	sk_free(fPath16);
+	fPath16 = concat_to_16(path, suffix);
+}
+
+static bool is_magic_dir(const U16 dir[])
+{
+	// return true for "." and ".."
+	return dir[0] == '.' && (dir[1] == 0 || dir[1] == '.' && dir[2] == 0);
+}
+
+static bool get_the_file(HANDLE handle, SkString* name, WIN32_FIND_DATAW* dataPtr, bool getDir)
+{
+	WIN32_FIND_DATAW	data;
+
+	if (dataPtr == nil)
+	{
+		if (::FindNextFileW(handle, &data))
+			dataPtr = &data;
+		else
+			return false;
+	}
+
+	for (;;)
+	{
+		if (getDir)
+		{
+			if ((dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !is_magic_dir(dataPtr->cFileName))
+				break;
+		}
+		else
+		{
+			if (!(dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+				break;
+		}
+		if (!::FindNextFileW(handle, dataPtr))
+			return false;
+	}
+	// if we get here, we've found a file/dir
+	if (name)
+		name->setUTF16(dataPtr->cFileName);
+	return true;
+}
+
+bool SkOSFile::Iter::next(SkString* name, bool getDir)
+{
+	WIN32_FIND_DATAW	data;
+	WIN32_FIND_DATAW*	dataPtr = nil;
+
+	if (fHandle == 0)	// our first time
+	{
+		if (fPath16 == nil || *fPath16 == 0)	// check for no path
+			return false;
+
+		fHandle = ::FindFirstFileW(fPath16, &data);
+		if (fHandle != 0 && fHandle != (HANDLE)~0)
+			dataPtr = &data;
+	}
+	return fHandle != (HANDLE)~0 && get_the_file(fHandle, name, dataPtr, getDir);
+}
+
+#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)
+
+#if 0
+OSStatus FSPathMakeRef (
+   const UInt8 * path,
+   FSRef * ref,
+   Boolean * isDirectory
+);
+#endif
+
+SkOSFile::Iter::Iter() : fDIR(0)
+{
+}
+
+SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fDIR(0)
+{
+	this->reset(path, suffix);
+}
+
+SkOSFile::Iter::~Iter()
+{
+	if (fDIR)
+		::closedir(fDIR);
+}
+
+void SkOSFile::Iter::reset(const char path[], const char suffix[])
+{
+	if (fDIR)
+	{
+		::closedir(fDIR);
+		fDIR = 0;
+	}
+
+	fPath.set(path);
+	if (path)
+	{
+		fDIR = ::opendir(path);
+		fSuffix.set(suffix);
+	}
+	else
+		fSuffix.reset();
+}
+
+// returns true if suffix is empty, or if str ends with suffix
+static bool issuffixfor(const SkString& suffix, const char str[])
+{
+	size_t  suffixLen = suffix.size();
+	size_t  strLen = strlen(str);
+	
+	return  strLen >= suffixLen &&
+			suffixLen == 0 ||
+			memcmp(suffix.c_str(), str + strLen - suffixLen, suffixLen) == 0;
+}
+
+#include <sys/stat.h>
+
+bool SkOSFile::Iter::next(SkString* name, bool getDir)
+{
+	if (fDIR)
+	{
+		dirent* entry;
+
+		while ((entry = ::readdir(fDIR)) != NULL)
+		{
+			struct stat s;
+			SkString	str(fPath);
+
+			if (!str.endsWith("/") && !str.endsWith("\\"))
+				str.append("/");
+			str.append(entry->d_name);
+
+			if (0 == stat(str.c_str(), &s))
+			{
+				if (getDir)
+				{
+					if (s.st_mode & S_IFDIR)
+						break;
+				}
+				else
+				{
+					if (!(s.st_mode & S_IFDIR) && issuffixfor(fSuffix, entry->d_name))
+						break;
+				}
+			}
+		}
+		if (entry)	// we broke out with a file
+		{
+			if (name)
+				name->set(entry->d_name);
+			return true;
+		}
+	}
+	return false;
+}
+
+#endif
+
diff --git a/src/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/SkOSSound.cpp b/src/views/SkOSSound.cpp
new file mode 100644
index 0000000..13cd037
--- /dev/null
+++ b/src/views/SkOSSound.cpp
@@ -0,0 +1,343 @@
+#include "SkOSSound.h"
+
+#ifdef SK_BUILD_FOR_WIN
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+#include <Mmreg.h>
+#if defined _WIN32 && _MSC_VER >= 1300	// disable nameless struct/union
+#pragma warning ( push )
+#pragma warning ( disable : 4201 )
+#endif
+#include <Mmsystem.h>
+#if defined _WIN32 && _MSC_VER >= 1300	
+#pragma warning ( pop )
+#endif
+#include <stdio.h>
+
+class CWaveFile {
+public:
+	BOOL Open(const char path[]);
+	void Close();
+
+	long Read(char* pData, long nLength);
+
+	long GetLength() const {return m_nLength;}
+	WAVEFORMATEX* GetWaveFormat()	{return (&m_Format);}
+
+protected:
+	FILE* m_pFile;
+	long m_nLength;
+	WAVEFORMATEX m_Format;
+
+private:
+	enum {
+		WF_OFFSET_FORMATTAG	=		20,
+		WF_OFFSET_CHANNELS =			22,
+		WF_OFFSET_SAMPLESPERSEC =		24,
+		WF_OFFSET_AVGBYTESPERSEC =	28,
+		WF_OFFSET_BLOCKALIGN =		32,
+		WF_OFFSET_BITSPERSAMPLE =		34,
+		WF_OFFSET_DATASIZE =			40,
+		WF_OFFSET_DATA =				44,
+		WF_HEADER_SIZE = WF_OFFSET_DATA
+	};
+};
+
+BOOL CWaveFile::Open(const char path[])
+{
+	BYTE aHeader[WF_HEADER_SIZE];
+
+/*	hResInfo = FindResource (hInst, lpName, "WAVE"); 
+
+	if (hResInfo == NULL) 
+	return FALSE; 
+
+	// Load the wave resource. 
+	hRes = LoadResource (hInst, hResInfo); 
+
+	if (hRes == NULL) 
+		return FALSE; 
+
+	// Lock the wave resource and play it. 
+	lpRes = LockResource (0);
+*/
+
+
+	// open file
+//	m_pFile = _tfopen(szFileName, TEXT("rb"));
+	m_pFile = fopen(path, "rb");
+	if (!m_pFile) {
+		return FALSE;
+	}
+
+	// set file length
+	fseek(m_pFile, 0, SEEK_END);
+	m_nLength = ftell(m_pFile) - WF_HEADER_SIZE;
+
+	// set the format attribute members
+	fseek(m_pFile, 0, SEEK_SET);
+	fread(aHeader, 1, WF_HEADER_SIZE, m_pFile);
+	m_Format.wFormatTag = *((WORD*) (aHeader + WF_OFFSET_FORMATTAG));
+	m_Format.nChannels = *((WORD*) (aHeader + WF_OFFSET_CHANNELS));
+	m_Format.nSamplesPerSec = *((DWORD*) (aHeader + WF_OFFSET_SAMPLESPERSEC));
+	m_Format.nAvgBytesPerSec = *((DWORD*) (aHeader + WF_OFFSET_AVGBYTESPERSEC));
+	m_Format.nBlockAlign = *((WORD*) (aHeader + WF_OFFSET_BLOCKALIGN));
+	m_Format.wBitsPerSample = *((WORD*) (aHeader + WF_OFFSET_BITSPERSAMPLE));
+
+	return TRUE;
+}
+
+void CWaveFile::Close()
+{
+	fclose(m_pFile);
+}
+
+long CWaveFile::Read(char* pData, long nLength)
+{
+	return fread(pData, 1, nLength, m_pFile);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+struct SkOSSoundWave {
+	HWAVEOUT hwo;
+	WAVEHDR whdr;
+	DWORD dwOldVolume;
+	CWaveFile waveFile;
+	HANDLE hDoneEvent;
+};
+
+static SkOSSoundWave gWave;
+static bool			 gWavePaused;
+static U8			 gVolume;
+static bool			 gInited = false;
+
+static void init_wave()
+{
+	if (gInited == false)
+	{
+		gWave.hwo = nil;
+		gWavePaused = false;
+		gVolume = 0x80;
+		gInited = true;
+	}
+}
+
+MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol);
+MMRESULT EndWave(SkOSSoundWave* wave);
+
+#define MAX_ERRMSG 256
+
+//#include "SkOSFile.h"	// for utf16
+
+void SkOSSound::Play(const char path[]) 
+{
+	init_wave();
+
+	if (gWave.hwo != nil)
+		SkOSSound::Stop();
+
+	U32 v32 = (gVolume << 8) | gVolume;	// fill it out to 16bits
+	v32 |= v32 << 16;					// set the left and right channels
+
+	StartWave(path, &gWave, v32);
+	gWavePaused = false;
+}
+
+bool SkOSSound::TogglePause() 
+{
+	init_wave();
+
+	if (gWavePaused) 
+		SkOSSound::Resume();
+	else 
+		SkOSSound::Pause(); 
+	return !gWavePaused;
+}
+
+
+void SkOSSound::Pause() 
+{
+	init_wave();
+
+	if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE))
+		return;
+	waveOutPause(gWave.hwo);
+	gWavePaused = true;
+}
+
+void SkOSSound::Resume() 
+{
+	init_wave();
+
+	if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE))
+		return;
+	waveOutRestart(gWave.hwo);
+	gWavePaused = false;
+}
+
+void SkOSSound::Stop() 
+{
+	init_wave();
+
+//	if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE))
+	if (gWave.hwo == nil)
+		return;
+	waveOutReset(gWave.hwo);
+	EndWave(&gWave);
+	gWavePaused = false;
+	gWave.hwo = nil;
+}
+
+U8 SkOSSound::GetVolume()
+{
+	init_wave();
+	return gVolume;
+}
+
+void SkOSSound::SetVolume(U8CPU vol)
+{
+	if ((int)vol < 0)
+		vol = 0;
+	else if (vol > 255)
+		vol = 255;
+
+	init_wave();
+	gVolume = SkToU8(vol);
+
+	if (gWave.hwo)
+	{
+		unsigned long v32 = (vol << 8) | vol;	// fill it out to 16bits
+		v32 |= v32 << 16;						// set the left and right channels
+		waveOutSetVolume(gWave.hwo, v32);
+	}
+}
+
+#if 0
+unsigned long SoundManager::GetPosition()
+{
+	if (fWave.hwo == nil)
+		return 0;
+	MMTIME time;
+	time.wType = TIME_MS;
+	if (waveOutGetPosition(fWave.hwo, &time, sizeof(time)) == MMSYSERR_NOERROR &&
+		time.wType == TIME_MS)
+	{
+		return time.u.ms;
+	}
+	return 0;
+}
+#endif
+
+MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol)
+{
+	HWAVEOUT hwo = nil;
+//	WAVEHDR whdr;
+	MMRESULT mmres = 0;
+//	CWaveFile waveFile;
+//	HANDLE hDoneEvent = wave.hDoneEvent = 
+//		CreateEvent(NULL, FALSE, FALSE, TEXT("DONE_EVENT"));
+	UINT devId;
+//	DWORD dwOldVolume;
+
+	// Open wave file
+	if (!wave->waveFile.Open(path)) {
+//		TCHAR szErrMsg[MAX_ERRMSG];
+//		_stprintf(szErrMsg,	TEXT("Unable to open file: %s\n"), szWavFile);
+//		MessageBox(NULL, szErrMsg, TEXT("File I/O Error"), MB_OK);
+		return MMSYSERR_NOERROR;
+	}
+
+	// Open audio device
+	for (devId = 0; devId < waveOutGetNumDevs(); devId++)
+	{
+		mmres = waveOutOpen(&hwo, devId, wave->waveFile.GetWaveFormat(), 0, 0, CALLBACK_NULL);
+		if (mmres == MMSYSERR_NOERROR)
+		{
+			wave->hwo = hwo;
+			break;
+		}
+	}
+	if (mmres != MMSYSERR_NOERROR)
+	{
+		SkDEBUGCODE(SkDebugf("waveOutOpen(%s) -> %d\n", path, mmres);) 
+		return mmres;
+	}
+
+	// Set volume
+	mmres = waveOutGetVolume(hwo, &wave->dwOldVolume);
+	if (mmres != MMSYSERR_NOERROR) {
+		return mmres;
+	}
+
+	waveOutSetVolume(hwo, vol);
+	if (mmres != MMSYSERR_NOERROR) {
+		return mmres;
+	}
+
+	// Initialize wave header
+	ZeroMemory(&wave->whdr, sizeof(WAVEHDR));
+	wave->whdr.lpData = new char[wave->waveFile.GetLength()];
+	wave->whdr.dwBufferLength = wave->waveFile.GetLength();
+	wave->whdr.dwUser = 0;
+	wave->whdr.dwFlags = 0;
+	wave->whdr.dwLoops = 0;
+	wave->whdr.dwBytesRecorded = 0;
+	wave->whdr.lpNext = 0;
+	wave->whdr.reserved = 0;
+
+	// Play buffer
+	wave->waveFile.Read(wave->whdr.lpData, wave->whdr.dwBufferLength);
+
+	mmres = waveOutPrepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR));	
+	if (mmres != MMSYSERR_NOERROR) {
+		return mmres;
+	}
+
+	mmres = waveOutWrite(hwo, &wave->whdr, sizeof(WAVEHDR));	
+//	if (mmres != MMSYSERR_NOERROR) {
+		return mmres;
+//	}
+}
+
+#if 0
+void IdleWave(Wave& wave)
+{
+	// Wait for audio to finish playing
+	while (!(wave.whdr.dwFlags & WHDR_DONE)) {
+		WaitForSingleObject(wave.hDoneEvent, INFINITE);
+	}
+}
+#endif
+
+MMRESULT EndWave(SkOSSoundWave* wave)
+{
+	HWAVEOUT hwo = wave->hwo;
+	MMRESULT mmres;
+	// Clean up
+	mmres = waveOutUnprepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR));	
+	if (mmres != MMSYSERR_NOERROR) {
+		return mmres;
+	}
+
+	waveOutSetVolume(hwo, wave->dwOldVolume);
+	if (mmres != MMSYSERR_NOERROR) {
+		return mmres;
+	}
+
+	mmres = waveOutClose(hwo);
+	if (mmres != MMSYSERR_NOERROR) {
+		return mmres;
+	}
+
+	delete [] wave->whdr.lpData;
+	wave->waveFile.Close();
+
+	return MMSYSERR_NOERROR;
+}
+
+#endif	/* SK_BUILD_FOR_WIN */
+
diff --git a/src/views/SkParsePaint.cpp b/src/views/SkParsePaint.cpp
new file mode 100644
index 0000000..7bf0244
--- /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")) == nil)
+		return nil;
+
+	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")) != nil &&
+			SkParse::FindColor(str, &colors[0]) &&
+			(str = dom.findAttr(node, "c1")) != nil &&
+			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, nil, 2, mode);
+		}
+	}
+	else if (dom.hasAttr(node, "type", "bitmap"))
+	{
+		if ((str = dom.findAttr(node, "src")) == nil)
+			return nil;
+
+		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 nil;
+}
+
+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..21349a5
--- /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(nil);
+	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(nil);
+		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..a34c284
--- /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;
+	fOnShader->safeUnref();
+	fOffShader->safeUnref();
+}
+
+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/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..657f648
--- /dev/null
+++ b/src/views/SkTextBox.cpp
@@ -0,0 +1,216 @@
+/* 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 "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;
+    } 
+}
+
diff --git a/src/views/SkView.cpp b/src/views/SkView.cpp
new file mode 100644
index 0000000..a027744
--- /dev/null
+++ b/src/views/SkView.cpp
@@ -0,0 +1,760 @@
+#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::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 (canvas->quickReject(r, SkCanvas::kBW_EdgeType))
+			return;
+
+		SkAutoCanvasRestore	as(canvas, true);
+
+		canvas->clipRect(r);
+		canvas->translate(fLoc.fX, fLoc.fY);
+
+		this->onDraw(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)
+{
+	if (!this->isVisible())
+		return;
+
+	SkRect	bounds;
+
+	this->getLocalBounds(&bounds);
+	if (rect && !bounds.intersect(*rect))
+		return;
+
+	rect = &bounds;
+	SkView*	view = this;
+
+	for (;;)
+	{
+		if (view->handleInval(bounds))
+			break;
+
+		SkRect	parentR;
+		SkView* parent = view->fParent;
+
+		if (parent == NULL || !parent->isVisible())
+			break;
+
+		bounds.offset(view->fLoc.fX, view->fLoc.fY);
+		parent->getLocalBounds(&parentR);
+		if (!bounds.intersect(parentR))
+			return;
+
+		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;
+
+	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()
+{
+}
+
+SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y)
+{
+	return NULL;
+}
+
+bool SkView::onClick(Click*)
+{
+	return false;
+}
+
+bool SkView::handleInval(const SkRect& r)
+{
+	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::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..8d945f1
--- /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(nil, 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(nil), 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(nil);
+}
+
+/////////////////////////////////////////////////////////////////
+
+void SkTextView::getMargin(SkPoint* margin) const
+{
+	if (margin)
+		*margin = fMargin;
+}
+
+void SkTextView::setMargin(const SkPoint& margin)
+{
+	if (fMargin != margin)
+	{
+		fMargin = margin;
+		this->inval(nil);
+	}
+}
+
+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(nil, 0, &y, nil);
+	y = fMargin.fY - y;
+
+	if (fInterp)
+	{
+		if (fInterp->draw(canvas, fText, x, y, fPaint))
+			this->inval(nil);
+		else
+		{
+			delete fInterp;
+			fInterp = nil;
+		}
+	}
+	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(nil);
+	}
+}
+
+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(nil);
+
+			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, nil, 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..e7eb772
--- /dev/null
+++ b/src/views/SkWidgetViews.cpp
@@ -0,0 +1,570 @@
+#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 == nil && 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")) != nil)
+		fEvent.inflate(dom, node);
+}
+
+/*virtual*/ void SkWidgetView::onLabelChange(const char oldLabel[], const char newLabel[])
+{
+	this->inval(nil);
+}
+
+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(nil);
+}
+
+/*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(nil);
+		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(nil);
+			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;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTextBox.h"
+
+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(nil);
+}
+
+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(nil);
+	}
+}
+
+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 == nil)
+		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(nil);
+	}
+}
+
+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(nil);
+	}
+}
+
+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, "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")) != nil &&
+		(node = dom.getFirstChild(node, "screenplay")) != nil)
+	{
+		inflate_paint(dom, node, &fPaint);
+	}
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////
+
+SkView* SkWidgetFactory(const char name[])
+{
+	if (name == nil)
+		return nil;
+	
+	// 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 (int i = 0; i < SK_ARRAY_COUNT(gNames); i++)
+		if (!strcmp(gNames[i], name))
+			return SkWidgetFactory((SkWidgetEnum)i);
+
+	return nil;
+}
+
+#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 nil;
+}
diff --git a/src/views/SkWidgets.cpp b/src/views/SkWidgets.cpp
new file mode 100644
index 0000000..fe87cd6
--- /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] = nil;
+		}
+	}
+}
+
+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 == nil)
+		gSkinSuite = new SkinSuite;
+	return gSkinSuite->get(st);
+#else
+	return nil;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkWidget::Init()
+{
+}
+
+void SkWidget::Term()
+{
+	delete gSkinSuite;
+}
+
+void SkWidget::onEnabledChange()
+{
+	this->inval(nil);
+}
+
+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")) != nil)
+		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(nil);
+}
+
+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(nil);
+
+		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(), nil, nil);
+		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, nil, &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(nil);
+}
+
+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(nil);
+	}
+}
+
+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(nil);
+	}
+}
+
+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(nil);
+	}
+}
+
+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")) != nil)
+		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..5d00d4f
--- /dev/null
+++ b/src/views/SkWindow.cpp
@@ -0,0 +1,367 @@
+#include "SkWindow.h"
+#include "SkCanvas.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
+}
+
+SkWindow::~SkWindow()
+{
+	delete fClick;
+
+	fMenus.deleteAll();
+}
+
+void SkWindow::setConfig(SkBitmap::Config config)
+{
+	this->resize(fBitmap.width(), fBitmap.height(), config);
+}
+
+#include "SkImageDecoder.h"
+
+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();
+
+		this->setSize(SkIntToScalar(width), SkIntToScalar(height));
+		this->inval(NULL);
+	}
+
+	SkImageDecoder::SetDeviceConfig(fConfig);
+}
+
+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& r)
+{
+	SkIRect	ir;
+
+	r.round(&ir);
+	fDirtyRgn.op(ir, SkRegion::kUnion_Op);
+
+#ifdef SK_BUILD_FOR_WIN32
+	if (!fWaitingOnInval)
+	{
+		fWaitingOnInval = true;
+		(new SkEvent(SK_EventDelayInval))->post(this->getSinkID(), 10);
+	}
+#else
+	this->onHandleInval(ir);
+#endif
+	return true;
+}
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+	#include <windows.h>
+	#include <gx.h>
+	extern GXDisplayProperties gDisplayProps;
+#endif
+
+#ifdef SK_SIMULATE_FAILED_MALLOC
+extern bool gEnableControlledThrow;
+#endif
+
+bool SkWindow::update(SkIRect* updateArea)
+{
+	if (!fDirtyRgn.isEmpty())
+	{
+		SkBitmap bm = this->getBitmap();
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+		char* buffer = (char*)GXBeginDraw();
+		SkASSERT(buffer);
+
+		RECT	rect;
+		GetWindowRect((HWND)((SkOSWindow*)this)->getHWND(), &rect);
+		buffer += rect.top * gDisplayProps.cbyPitch + rect.left * gDisplayProps.cbxPitch;
+
+		bm.setPixels(buffer);
+#endif
+
+		SkCanvas	canvas(bm);
+
+		canvas.clipRegion(fDirtyRgn);
+		if (updateArea)
+			*updateArea = fDirtyRgn.getBounds();
+
+		// 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 = "";
+    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)
+{
+	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/xml/SkBML_Verbs.h b/src/xml/SkBML_Verbs.h
new file mode 100644
index 0000000..86bfede
--- /dev/null
+++ b/src/xml/SkBML_Verbs.h
@@ -0,0 +1,33 @@
+/* libs/graphics/xml/SkBML_Verbs.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 SkBML_Verbs_DEFINED
+#define SkBML_Verbs_DEFINED
+
+enum Verbs {
+    kStartElem_Value_Verb,
+    kStartElem_Index_Verb,
+    kEndElem_Verb,
+    kAttr_Value_Value_Verb,
+    kAttr_Value_Index_Verb,
+    kAttr_Index_Value_Verb,
+    kAttr_Index_Index_Verb,
+
+    kVerbCount
+};
+
+#endif // SkBML_Verbs_DEFINED
diff --git a/src/xml/SkBML_XMLParser.cpp b/src/xml/SkBML_XMLParser.cpp
new file mode 100644
index 0000000..53b7f61
--- /dev/null
+++ b/src/xml/SkBML_XMLParser.cpp
@@ -0,0 +1,192 @@
+/* libs/graphics/xml/SkBML_XMLParser.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 "SkBML_XMLParser.h"
+#include "SkBML_Verbs.h"
+#include "SkStream.h"
+#include "SkXMLWriter.h"
+
+static uint8_t rbyte(SkStream& s)
+{
+    uint8_t b;
+    size_t size = s.read(&b, 1);
+    SkASSERT(size == 1);
+    return b;
+}
+
+static int rdata(SkStream& s, int data)
+{
+    SkASSERT((data & ~31) == 0);
+    if (data == 31)
+    {
+        data = rbyte(s);
+        if (data == 0xFF)
+        {
+            data = rbyte(s);
+            data = (data << 8) | rbyte(s);
+        }
+    }
+    return data;
+}
+
+static void set(char* array[256], int index, SkStream& s, int data)
+{
+    SkASSERT((unsigned)index <= 255);
+
+    size_t size = rdata(s, data);
+
+    if (array[index] == NULL)
+        array[index] = (char*)sk_malloc_throw(size + 1);
+    else
+    {
+        if (strlen(array[index]) < size)
+            array[index] = (char*)sk_realloc_throw(array[index], size + 1);
+    }
+
+    s.read(array[index], size);
+    array[index][size] = 0;
+}
+
+static void freeAll(char* array[256])
+{
+    for (int i = 0; i < 256; i++)
+        sk_free(array[i]);
+}
+
+struct BMLW {
+    char*   fElems[256];
+    char*   fNames[256];
+    char*   fValues[256];
+
+    // important that these are uint8_t, so we get automatic wrap-around
+    uint8_t  fNextElem, fNextName, fNextValue;
+
+    BMLW()
+    {
+        memset(fElems, 0, sizeof(fElems));
+        memset(fNames, 0, sizeof(fNames));
+        memset(fValues, 0, sizeof(fValues));
+
+        fNextElem = fNextName = fNextValue = 0;
+    }
+    ~BMLW()
+    {
+        freeAll(fElems);
+        freeAll(fNames);
+        freeAll(fValues);
+    }
+};
+
+static void rattr(unsigned verb, SkStream& s, BMLW& rec, SkXMLWriter& writer)
+{
+    int data = verb & 31;
+    verb >>= 5;
+
+    int nameIndex, valueIndex;
+
+    switch (verb) {
+    case kAttr_Value_Value_Verb:
+        nameIndex = rec.fNextName;      // record before the ++
+        set(rec.fNames, rec.fNextName++, s, data);
+        valueIndex = rec.fNextValue;    // record before the ++
+        set(rec.fValues, rec.fNextValue++, s, 31);
+        break;
+    case kAttr_Value_Index_Verb:
+        nameIndex = rec.fNextName;      // record before the ++
+        set(rec.fNames, rec.fNextName++, s, data);
+        valueIndex = rbyte(s);
+        break;
+    case kAttr_Index_Value_Verb:
+        nameIndex = rdata(s, data);
+        valueIndex = rec.fNextValue;    // record before the ++
+        set(rec.fValues, rec.fNextValue++, s, 31);
+        break;
+    case kAttr_Index_Index_Verb:
+        nameIndex = rdata(s, data);
+        valueIndex = rbyte(s);
+        break;
+    default:
+        SkASSERT(!"bad verb");
+        return;
+    }
+    writer.addAttribute(rec.fNames[nameIndex], rec.fValues[valueIndex]);
+}
+
+static void relem(unsigned verb, SkStream& s, BMLW& rec, SkXMLWriter& writer)
+{
+    int data = verb & 31;
+    verb >>= 5;
+
+    int elemIndex;
+
+    if (verb == kStartElem_Value_Verb)
+    {
+        elemIndex = rec.fNextElem;      // record before the ++
+        set(rec.fElems, rec.fNextElem++, s, data);
+    }
+    else
+    {
+        SkASSERT(verb == kStartElem_Index_Verb);
+        elemIndex = rdata(s, data);
+    }
+
+    writer.startElement(rec.fElems[elemIndex]);
+
+    for (;;)
+    {
+        verb = rbyte(s);
+        switch (verb >> 5) {
+        case kAttr_Value_Value_Verb:
+        case kAttr_Value_Index_Verb:
+        case kAttr_Index_Value_Verb:
+        case kAttr_Index_Index_Verb:
+            rattr(verb, s, rec, writer);
+            break;
+        case kStartElem_Value_Verb:
+        case kStartElem_Index_Verb:
+            relem(verb, s, rec, writer);
+            break;
+        case kEndElem_Verb:
+            writer.endElement();
+            return;
+        default:
+            SkASSERT(!"bad verb");
+        }
+    }
+}
+
+void BML_XMLParser::Read(SkStream& s, SkXMLWriter& writer)
+{
+    BMLW rec;
+    writer.writeHeader();
+    relem(rbyte(s), s, rec, writer);
+}
+
+void BML_XMLParser::Read(SkStream& s, SkWStream& output)
+{
+    SkXMLStreamWriter writer(&output);
+    Read(s, writer);
+}
+
+void BML_XMLParser::Read(SkStream& s, SkXMLParser& output)
+{
+    SkXMLParserWriter writer(&output);
+    Read(s, writer);
+}
+
+
+
diff --git a/src/xml/SkDOM.cpp b/src/xml/SkDOM.cpp
new file mode 100644
index 0000000..a9fc31e
--- /dev/null
+++ b/src/xml/SkDOM.cpp
@@ -0,0 +1,512 @@
+/* libs/graphics/xml/SkDOM.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 "SkDOM.h"
+
+/////////////////////////////////////////////////////////////////////////
+
+#include "SkXMLParser.h"
+
+bool SkXMLParser::parse(const SkDOM& dom, const SkDOMNode* node)
+{
+    const char* elemName = dom.getName(node);
+
+    if (this->startElement(elemName))
+        return false;
+    
+    SkDOM::AttrIter iter(dom, node);
+    const char*     name, *value;
+    
+    while ((name = iter.next(&value)) != NULL)
+        if (this->addAttribute(name, value))
+            return false;
+
+    if ((node = dom.getFirstChild(node)) != NULL)
+        do {
+            if (!this->parse(dom, node))
+                return false;
+        } while ((node = dom.getNextSibling(node)) != NULL);
+    
+    return !this->endElement(elemName);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+struct SkDOMAttr {
+    const char* fName;
+    const char* fValue;
+};
+
+struct SkDOMNode {
+    const char* fName;
+    SkDOMNode*  fFirstChild;
+    SkDOMNode*  fNextSibling;
+    uint16_t    fAttrCount;
+    uint8_t     fType;
+    uint8_t     fPad;
+
+    const SkDOMAttr* attrs() const
+    {
+        return (const SkDOMAttr*)(this + 1);
+    }
+    SkDOMAttr* attrs()
+    {
+        return (SkDOMAttr*)(this + 1);
+    }
+};
+
+/////////////////////////////////////////////////////////////////////////
+
+#define kMinChunkSize   512
+
+SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(NULL)
+{
+}
+
+SkDOM::~SkDOM()
+{
+}
+
+const SkDOM::Node* SkDOM::getRootNode() const
+{
+    return fRoot;
+}
+
+const SkDOM::Node* SkDOM::getFirstChild(const Node* node, const char name[]) const
+{
+    SkASSERT(node);
+    const Node* child = node->fFirstChild;
+
+    if (name)
+    {
+        for (; child != NULL; child = child->fNextSibling)
+            if (!strcmp(name, child->fName))
+                break;
+    }
+    return child;
+}
+
+const SkDOM::Node* SkDOM::getNextSibling(const Node* node, const char name[]) const
+{
+    SkASSERT(node);
+    const Node* sibling = node->fNextSibling;
+    if (name)
+    {
+        for (; sibling != NULL; sibling = sibling->fNextSibling)
+            if (!strcmp(name, sibling->fName))
+                break;
+    }
+    return sibling;
+}
+
+SkDOM::Type SkDOM::getType(const Node* node) const
+{
+    SkASSERT(node);
+    return (Type)node->fType;
+}
+
+const char* SkDOM::getName(const Node* node) const
+{
+    SkASSERT(node);
+    return node->fName;
+}
+
+const char* SkDOM::findAttr(const Node* node, const char name[]) const
+{
+    SkASSERT(node);
+    const Attr* attr = node->attrs();
+    const Attr* stop = attr + node->fAttrCount;
+
+    while (attr < stop)
+    {
+        if (!strcmp(attr->fName, name))
+            return attr->fValue;
+        attr += 1;
+    }
+    return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const
+{
+    return node->fAttrCount ? node->attrs() : NULL;
+}
+
+const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const
+{
+    SkASSERT(node);
+    if (attr == NULL)
+        return NULL;
+    return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : NULL;
+}
+
+const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const
+{
+    SkASSERT(node);
+    SkASSERT(attr);
+    return attr->fName;
+}
+
+const char* SkDOM::getAttrValue(const Node* node, const Attr* attr) const
+{
+    SkASSERT(node);
+    SkASSERT(attr);
+    return attr->fValue;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node)
+{
+    SkASSERT(node);
+    fAttr = node->attrs();
+    fStop = fAttr + node->fAttrCount;
+}
+
+const char* SkDOM::AttrIter::next(const char** value)
+{
+    const char* name = NULL;
+
+    if (fAttr < fStop)
+    {
+        name = fAttr->fName;
+        if (value)
+            *value = fAttr->fValue;
+        fAttr += 1;
+    }
+    return name;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#include "SkXMLParser.h"
+#include "SkTDArray.h"
+
+static char* dupstr(SkChunkAlloc* chunk, const char src[])
+{
+    SkASSERT(chunk && src);
+    size_t  len = strlen(src);
+    char*   dst = (char*)chunk->alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType);
+    memcpy(dst, src, len + 1);
+    return dst;
+}
+
+class SkDOMParser : public SkXMLParser {
+    bool fNeedToFlush;
+public:
+    SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk)
+    {
+        fRoot = NULL;
+        fLevel = 0;
+        fNeedToFlush = true;
+    }
+    SkDOM::Node* getRoot() const { return fRoot; }
+    SkXMLParserError fParserError;
+protected:
+    void flushAttributes()
+    {
+        int attrCount = fAttrs.count();
+
+        SkDOM::Node* node = (SkDOM::Node*)fAlloc->alloc(sizeof(SkDOM::Node) + attrCount * sizeof(SkDOM::Attr),
+                                                        SkChunkAlloc::kThrow_AllocFailType);
+
+        node->fName = fElemName;
+        node->fFirstChild = NULL;
+        node->fAttrCount = SkToU16(attrCount);
+        node->fType = SkDOM::kElement_Type;
+
+        if (fRoot == NULL)
+        {
+            node->fNextSibling = NULL;
+            fRoot = node;
+        }
+        else    // this adds siblings in reverse order. gets corrected in onEndElement()
+        {
+            SkDOM::Node* parent = fParentStack.top();
+            SkASSERT(fRoot && parent);
+            node->fNextSibling = parent->fFirstChild;
+            parent->fFirstChild = node;
+        }
+        *fParentStack.push() = node;
+
+        memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr));
+        fAttrs.reset();
+
+    }
+    virtual bool onStartElement(const char elem[])
+    {
+        if (fLevel > 0 && fNeedToFlush)
+            this->flushAttributes();
+        fNeedToFlush = true;
+        fElemName = dupstr(fAlloc, elem);
+        ++fLevel;
+        return false;
+    }
+    virtual bool onAddAttribute(const char name[], const char value[])
+    {
+        SkDOM::Attr* attr = fAttrs.append();
+        attr->fName = dupstr(fAlloc, name);
+        attr->fValue = dupstr(fAlloc, value);
+        return false;
+    }
+    virtual bool onEndElement(const char elem[])
+    {
+        --fLevel;
+        if (fNeedToFlush)
+            this->flushAttributes();
+        fNeedToFlush = false;
+
+        SkDOM::Node* parent;
+
+        fParentStack.pop(&parent);
+
+        SkDOM::Node* child = parent->fFirstChild;
+        SkDOM::Node* prev = NULL;
+        while (child)
+        {
+            SkDOM::Node* next = child->fNextSibling;
+            child->fNextSibling = prev;
+            prev = child;
+            child = next;
+        }
+        parent->fFirstChild = prev;
+        return false;
+    }
+private:
+    SkTDArray<SkDOM::Node*> fParentStack;
+    SkChunkAlloc*   fAlloc;
+    SkDOM::Node*    fRoot;
+
+    // state needed for flushAttributes()
+    SkTDArray<SkDOM::Attr>  fAttrs;
+    char*                   fElemName;
+    int                     fLevel;
+};
+
+const SkDOM::Node* SkDOM::build(const char doc[], size_t len)
+{
+    fAlloc.reset();
+    SkDOMParser parser(&fAlloc);
+    if (!parser.parse(doc, len))
+    {
+        SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());)
+        fRoot = NULL;
+        fAlloc.reset();
+        return NULL;
+    }
+    fRoot = parser.getRoot();
+    return fRoot;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+static void walk_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLParser* parser)
+{
+    const char* elem = dom.getName(node);
+
+    parser->startElement(elem);
+    
+    SkDOM::AttrIter iter(dom, node);
+    const char*     name;
+    const char*     value;
+    while ((name = iter.next(&value)) != NULL)
+        parser->addAttribute(name, value);
+
+    node = dom.getFirstChild(node, NULL);
+    while (node)
+    {
+        walk_dom(dom, node, parser);
+        node = dom.getNextSibling(node, NULL);
+    }
+
+    parser->endElement(elem);
+}
+
+const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node)
+{
+    fAlloc.reset();
+    SkDOMParser parser(&fAlloc);
+
+    walk_dom(dom, node, &parser);
+
+    fRoot = parser.getRoot();
+    return fRoot;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+int SkDOM::countChildren(const Node* node, const char elem[]) const
+{
+    int count = 0;
+
+    node = this->getFirstChild(node, elem);
+    while (node)
+    {
+        count += 1;
+        node = this->getNextSibling(node, elem);
+    }
+    return count;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+#include "SkParse.h"
+
+bool SkDOM::findS32(const Node* node, const char name[], int32_t* value) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindS32(vstr, value);
+}
+
+bool SkDOM::findScalars(const Node* node, const char name[], SkScalar value[], int count) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindScalars(vstr, value, count);
+}
+
+bool SkDOM::findHex(const Node* node, const char name[], uint32_t* value) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindHex(vstr, value);
+}
+
+bool SkDOM::findBool(const Node* node, const char name[], bool* value) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && SkParse::FindBool(vstr, value);
+}
+
+int SkDOM::findList(const Node* node, const char name[], const char list[]) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr ? SkParse::FindList(vstr, list) : -1;
+}
+
+bool SkDOM::hasAttr(const Node* node, const char name[], const char value[]) const
+{
+    const char* vstr = this->findAttr(node, name);
+    return vstr && !strcmp(vstr, value);
+}
+
+bool SkDOM::hasS32(const Node* node, const char name[], int32_t target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    int32_t     value;
+    return vstr && SkParse::FindS32(vstr, &value) && value == target;
+}
+
+bool SkDOM::hasScalar(const Node* node, const char name[], SkScalar target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    SkScalar    value;
+    return vstr && SkParse::FindScalar(vstr, &value) && value == target;
+}
+
+bool SkDOM::hasHex(const Node* node, const char name[], uint32_t target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    uint32_t    value;
+    return vstr && SkParse::FindHex(vstr, &value) && value == target;
+}
+
+bool SkDOM::hasBool(const Node* node, const char name[], bool target) const
+{
+    const char* vstr = this->findAttr(node, name);
+    bool        value;
+    return vstr && SkParse::FindBool(vstr, &value) && value == target;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+static void tab(int level)
+{
+    while (--level >= 0)
+        SkDebugf("\t");
+}
+
+void SkDOM::dump(const Node* node, int level) const
+{
+    if (node == NULL)
+        node = this->getRootNode();
+    if (node)
+    {
+        tab(level);
+        SkDebugf("<%s", this->getName(node));
+
+        const Attr* attr = node->attrs();
+        const Attr* stop = attr + node->fAttrCount;
+        for (; attr < stop; attr++)
+            SkDebugf(" %s=\"%s\"", attr->fName, attr->fValue);
+
+        const Node* child = this->getFirstChild(node);
+        if (child)
+        {
+            SkDebugf(">\n");
+            while (child)
+            {
+                this->dump(child, level+1);
+                child = this->getNextSibling(child);
+            }
+            tab(level);
+            SkDebugf("</%s>\n", node->fName);
+        }
+        else
+            SkDebugf("/>\n");
+    }
+}
+
+void SkDOM::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    static const char gDoc[] = 
+        "<root a='1' b='2'>"
+            "<elem1 c='3' />"
+            "<elem2 d='4' />"
+            "<elem3 e='5'>"
+                "<subelem1/>"
+                "<subelem2 f='6' g='7'/>"
+            "</elem3>"
+            "<elem4 h='8'/>"
+        "</root>"
+        ;
+
+    SkDOM   dom;
+
+    SkASSERT(dom.getRootNode() == NULL);
+
+    const Node* root = dom.build(gDoc, sizeof(gDoc) - 1);
+    SkASSERT(root && dom.getRootNode() == root);
+
+    const char* v = dom.findAttr(root, "a");
+    SkASSERT(v && !strcmp(v, "1"));
+    v = dom.findAttr(root, "b");
+    SkASSERT(v && !strcmp(v, "2"));
+    v = dom.findAttr(root, "c");
+    SkASSERT(v == NULL);
+
+    SkASSERT(dom.getFirstChild(root, "elem1"));
+    SkASSERT(!dom.getFirstChild(root, "subelem1"));
+
+    dom.dump();
+#endif
+}
+
+#endif
+
diff --git a/src/xml/SkJS.cpp b/src/xml/SkJS.cpp
new file mode 100644
index 0000000..03ccba6
--- /dev/null
+++ b/src/xml/SkJS.cpp
@@ -0,0 +1,237 @@
+/* libs/graphics/xml/SkJS.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 <jsapi.h>
+
+#include "SkJS.h"
+#include "SkString.h"
+
+#ifdef _WIN32_WCE
+extern "C" {
+    void abort() {
+        SkASSERT(0);
+    }
+
+    unsigned int _control87(unsigned int _new, unsigned int mask ) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    time_t mktime(struct tm *timeptr ) {
+        SkASSERT(0);
+        return 0;
+    }
+
+//  int errno;
+
+    char *strdup(const char *) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    char *strerror(int errnum) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    int isatty(void* fd) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    int putenv(const char *envstring) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    char *getenv(const char *varname) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    void GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) {
+        SkASSERT(0);
+    }
+
+    struct tm * localtime(const time_t *timer) {
+        SkASSERT(0);
+        return 0;
+    }
+
+    size_t strftime(char *strDest, size_t maxsize, const char *format,
+        const struct tm *timeptr ) {
+        SkASSERT(0);
+        return 0;
+    }
+
+}
+#endif
+
+static JSBool
+global_enumerate(JSContext *cx, JSObject *obj)
+{
+#ifdef LAZY_STANDARD_CLASSES
+    return JS_EnumerateStandardClasses(cx, obj);
+#else
+    return JS_TRUE;
+#endif
+}
+
+static JSBool
+global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp)
+{
+#ifdef LAZY_STANDARD_CLASSES
+    if ((flags & JSRESOLVE_ASSIGNING) == 0) {
+        JSBool resolved;
+
+        if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
+            return JS_FALSE;
+        if (resolved) {
+            *objp = obj;
+            return JS_TRUE;
+        }
+    }
+#endif
+
+#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
+    if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) {
+        /*
+         * Do this expensive hack only for unoptimized Unix builds, which are
+         * not used for benchmarking.
+         */
+        char *path, *comp, *full;
+        const char *name;
+        JSBool ok, found;
+        JSFunction *fun;
+
+        if (!JSVAL_IS_STRING(id))
+            return JS_TRUE;
+        path = getenv("PATH");
+        if (!path)
+            return JS_TRUE;
+        path = JS_strdup(cx, path);
+        if (!path)
+            return JS_FALSE;
+        name = JS_GetStringBytes(JSVAL_TO_STRING(id));
+        ok = JS_TRUE;
+        for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
+            if (*comp != '\0') {
+                full = JS_smprintf("%s/%s", comp, name);
+                if (!full) {
+                    JS_ReportOutOfMemory(cx);
+                    ok = JS_FALSE;
+                    break;
+                }
+            } else {
+                full = (char *)name;
+            }
+            found = (access(full, X_OK) == 0);
+            if (*comp != '\0')
+                free(full);
+            if (found) {
+                fun = JS_DefineFunction(cx, obj, name, Exec, 0, JSPROP_ENUMERATE);
+                ok = (fun != NULL);
+                if (ok)
+                    *objp = obj;
+                break;
+            }
+        }
+        JS_free(cx, path);
+        return ok;
+    }
+#else
+    return JS_TRUE;
+#endif
+}
+
+JSClass global_class = {
+    "global", JSCLASS_NEW_RESOLVE,
+    JS_PropertyStub,  JS_PropertyStub,
+    JS_PropertyStub,  JS_PropertyStub,
+    global_enumerate, (JSResolveOp) global_resolve,
+    JS_ConvertStub,   JS_FinalizeStub
+};
+
+SkJS::SkJS(void* hwnd) : SkOSWindow(hwnd) {
+    if ((fRuntime = JS_NewRuntime(0x100000)) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    if ((fContext = JS_NewContext(fRuntime, 0x1000)) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    ;
+    if ((fGlobal = JS_NewObject(fContext, &global_class, NULL, NULL)) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    if (JS_InitStandardClasses(fContext, fGlobal) == NULL) {
+        SkASSERT(0);
+        return;
+    }
+    setConfig(SkBitmap::kARGB32_Config);
+    updateSize();
+    setVisibleP(true);
+    InitializeDisplayables(getBitmap(), fContext, fGlobal, NULL);
+}
+
+SkJS::~SkJS() {
+    DisposeDisplayables();
+    JS_DestroyContext(fContext);
+    JS_DestroyRuntime(fRuntime);
+    JS_ShutDown();
+}
+
+SkBool SkJS::EvaluateScript(const char* script, jsval* rVal) {
+    return JS_EvaluateScript(fContext, fGlobal, script, strlen(script),
+        "memory" /* no file name */, 0 /* no line number */, rVal);
+}
+
+SkBool SkJS::ValueToString(jsval value, SkString* string) {
+     JSString* str = JS_ValueToString(fContext, value);
+     if (str == NULL)
+         return false;
+     string->set(JS_GetStringBytes(str));
+     return true;
+}
+
+#ifdef SK_DEBUG
+void SkJS::Test(void* hwnd) {
+    SkJS js(hwnd);
+    jsval val;
+    SkBool success = js.EvaluateScript("22/7", &val);
+    SkASSERT(success);
+    SkString string;
+    success = js.ValueToString(val, &string);
+    SkASSERT(success);
+    SkASSERT(strcmp(string.c_str(), "3.142857142857143") == 0);
+    success = js.EvaluateScript(
+        "var rect = new rectangle();"
+        "rect.left = 4;"
+        "rect.top = 10;"
+        "rect.right = 20;"
+        "rect.bottom = 30;"
+        "rect.width = rect.height + 20;"
+        "rect.draw();"
+        , &val);
+    SkASSERT(success);
+    success = js.ValueToString(val, &string);
+    SkASSERT(success);
+}
+#endifASSERT(success);
+
diff --git a/src/xml/SkJSDisplayable.cpp b/src/xml/SkJSDisplayable.cpp
new file mode 100644
index 0000000..d52a7c9
--- /dev/null
+++ b/src/xml/SkJSDisplayable.cpp
@@ -0,0 +1,472 @@
+/* libs/graphics/xml/SkJSDisplayable.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 <jsapi.h>
+#include "SkJS.h"
+#include "SkDisplayType.h"
+//#include "SkAnimateColor.h"
+#include "SkAnimateMaker.h"
+#include "SkAnimateSet.h"
+//#include "SkAnimateTransform.h"
+#include "SkCanvas.h"
+//#include "SkDimensions.h"
+#include "SkDisplayAdd.h"
+#include "SkDisplayApply.h"
+//#include "SkDisplayBefore.h"
+#include "SkDisplayEvent.h"
+//#include "SkDisplayFocus.h"
+#include "SkDisplayInclude.h"
+#include "SkDisplayPost.h"
+#include "SkDisplayRandom.h"
+#include "SkDraw3D.h"
+#include "SkDrawBitmap.h"
+#include "SkDrawClip.h"
+#include "SkDrawDash.h"
+#include "SkDrawDiscrete.h"
+#include "SkDrawEmboss.h"
+//#include "SkDrawFont.h"
+#include "SkDrawFull.h"
+#include "SkDrawGradient.h"
+#include "SkDrawLine.h"
+//#include "SkDrawMaskFilter.h"
+#include "SkDrawMatrix.h"
+#include "SkDrawOval.h"
+#include "SkDrawPaint.h"
+#include "SkDrawPath.h"
+#include "SkDrawPoint.h"
+// #include "SkDrawStroke.h"
+#include "SkDrawText.h"
+#include "SkDrawTo.h"
+//#include "SkDrawTransferMode.h"
+#include "SkDrawTransparentShader.h"
+//#include "SkDrawUse.h"
+#include "SkMatrixParts.h"
+#include "SkPathParts.h"
+#include "SkPostParts.h"
+#include "SkScript.h"
+#include "SkSnapshot.h"
+#include "SkTextOnPath.h"
+#include "SkTextToPath.h"
+
+
+class SkJSDisplayable {
+public:
+    SkJSDisplayable() : fDisplayable(NULL) {}
+    ~SkJSDisplayable() { delete fDisplayable; }
+    static void Destructor(JSContext *cx, JSObject *obj);
+    static JSBool GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
+    static JSBool SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
+    static SkCanvas* gCanvas;
+    static SkPaint* gPaint;
+    static JSBool Draw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
+    SkDisplayable* fDisplayable;
+};
+
+SkCanvas* SkJSDisplayable::gCanvas;
+SkPaint* SkJSDisplayable::gPaint;
+
+JSBool SkJSDisplayable::Draw(JSContext *cx, JSObject *obj, uintN argc,
+                                    jsval *argv, jsval *rval)
+{
+    SkJSDisplayable *p = (SkJSDisplayable*) JS_GetPrivate(cx, obj);
+    SkASSERT(p->fDisplayable->isDrawable());
+    SkDrawable* drawable = (SkDrawable*) p->fDisplayable;
+    SkAnimateMaker maker(NULL, gCanvas, gPaint);
+    drawable->draw(maker);
+    return JS_TRUE;
+}
+
+
+JSFunctionSpec SkJSDisplayable_methods[] = 
+{
+    { "draw", SkJSDisplayable::Draw, 1, 0, 0 },
+    { 0 }
+};
+
+static JSPropertySpec* gDisplayableProperties[kNumberOfTypes];
+static JSClass gDisplayableClasses[kNumberOfTypes];
+
+#define JS_INIT(_prefix, _class) \
+static JSBool _class##Constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { \
+    SkJSDisplayable* jsDisplayable = new SkJSDisplayable(); \
+    jsDisplayable->fDisplayable = new _prefix##_class(); \
+    JS_SetPrivate(cx, obj, (void*) jsDisplayable); \
+    return JS_TRUE; \
+} \
+    \
+static JSObject* _class##Init(JSContext *cx, JSObject *obj, JSObject *proto) { \
+    JSObject *newProtoObj = JS_InitClass(cx, obj, proto, &gDisplayableClasses[SkType_##_class], \
+        _class##Constructor, 0, \
+        NULL, SkJSDisplayable_methods , \
+        NULL, NULL); \
+    JS_DefineProperties(cx, newProtoObj, gDisplayableProperties[SkType_##_class]); \
+    return newProtoObj; \
+}
+
+JS_INIT(Sk, Add)
+JS_INIT(Sk, AddCircle)
+JS_INIT(Sk, AddOval)
+JS_INIT(Sk, AddPath)
+JS_INIT(Sk, AddRectangle)
+JS_INIT(Sk, AddRoundRect)
+//JS_INIT(Sk, After)
+JS_INIT(Sk, Apply)
+// JS_INIT(Sk, Animate)
+//JS_INIT(Sk, AnimateColor)
+JS_INIT(Sk, AnimateField)
+//JS_INIT(Sk, AnimateRotate)
+//JS_INIT(Sk, AnimateScale)
+//JS_INIT(Sk, AnimateTranslate)
+JS_INIT(SkDraw, Bitmap)
+JS_INIT(Sk, BaseBitmap)
+//JS_INIT(Sk, Before)
+JS_INIT(SkDraw, BitmapShader)
+JS_INIT(SkDraw, Blur)
+JS_INIT(SkDraw, Clip)
+JS_INIT(SkDraw, Color)
+JS_INIT(Sk, CubicTo)
+JS_INIT(Sk, Dash)
+JS_INIT(Sk, Data)
+//JS_INIT(Sk, Dimensions)
+JS_INIT(Sk, Discrete)
+JS_INIT(Sk, DrawTo)
+JS_INIT(SkDraw, Emboss)
+JS_INIT(SkDisplay, Event)
+// JS_INIT(SkDraw, Font)
+// JS_INIT(Sk, Focus)
+JS_INIT(Sk, Image)
+JS_INIT(Sk, Include)
+// JS_INIT(Sk, Input)
+JS_INIT(Sk, Line)
+JS_INIT(Sk, LinearGradient)
+JS_INIT(Sk, LineTo)
+JS_INIT(SkDraw, Matrix)
+JS_INIT(Sk, Move)
+JS_INIT(Sk, MoveTo)
+JS_INIT(Sk, Oval)
+JS_INIT(SkDraw, Path)
+JS_INIT(SkDraw, Paint)
+JS_INIT(Sk, DrawPoint)
+JS_INIT(Sk, PolyToPoly)
+JS_INIT(Sk, Polygon)
+JS_INIT(Sk, Polyline)
+JS_INIT(Sk, Post)
+JS_INIT(Sk, QuadTo)
+JS_INIT(Sk, RadialGradient)
+JS_INIT(SkDisplay, Random)
+JS_INIT(Sk, RectToRect)
+JS_INIT(Sk, Rectangle)
+JS_INIT(Sk, Remove)
+JS_INIT(Sk, Replace)
+JS_INIT(Sk, Rotate)
+JS_INIT(Sk, RoundRect)
+JS_INIT(Sk, Scale)
+JS_INIT(Sk, Set)
+JS_INIT(Sk, Skew)
+// JS_INIT(Sk, 3D_Camera)
+// JS_INIT(Sk, 3D_Patch)
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+JS_INIT(Sk, Snapshot)
+#endif
+// JS_INIT(SkDraw, Stroke)
+JS_INIT(Sk, Text)
+JS_INIT(Sk, TextOnPath)
+JS_INIT(Sk, TextToPath)
+JS_INIT(Sk, Translate)
+//JS_INIT(Sk, Use)
+
+#if SK_USE_CONDENSED_INFO == 0
+static void GenerateTables() {
+    for (int index = 0; index < kTypeNamesSize; index++) {
+        int infoCount;
+        SkDisplayTypes type = gTypeNames[index].fType;
+        const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, type, &infoCount);
+        if (info == NULL)
+            continue;
+        gDisplayableProperties[type] = new JSPropertySpec[infoCount + 1];
+        JSPropertySpec* propertySpec = gDisplayableProperties[type];
+        memset(propertySpec, 0, sizeof (JSPropertySpec) * (infoCount + 1));
+        for (int inner = 0; inner < infoCount; inner++) {
+            if (info[inner].fType == SkType_BaseClassInfo)
+                continue;
+            propertySpec[inner].name = info[inner].fName;
+            propertySpec[inner].tinyid = inner;
+            propertySpec[inner].flags = JSPROP_ENUMERATE;
+        }
+        gDisplayableClasses[type].name = gTypeNames[index].fName;
+        gDisplayableClasses[type].flags = JSCLASS_HAS_PRIVATE;
+        gDisplayableClasses[type].addProperty = JS_PropertyStub;
+        gDisplayableClasses[type].delProperty = JS_PropertyStub;
+        gDisplayableClasses[type].getProperty = SkJSDisplayable::GetProperty;
+        gDisplayableClasses[type].setProperty = SkJSDisplayable::SetProperty;
+        gDisplayableClasses[type].enumerate = JS_EnumerateStub;
+        gDisplayableClasses[type].resolve = JS_ResolveStub;
+        gDisplayableClasses[type].convert = JS_ConvertStub;
+        gDisplayableClasses[type].finalize = SkJSDisplayable::Destructor;
+    }
+}
+#endif
+
+void SkJSDisplayable::Destructor(JSContext *cx, JSObject *obj) {
+    delete (SkJSDisplayable*) JS_GetPrivate(cx, obj);
+}
+
+JSBool SkJSDisplayable::GetProperty(JSContext *cx, JSObject *obj, jsval id,
+                                 jsval *vp)
+{
+    if (JSVAL_IS_INT(id) == 0)
+        return JS_TRUE; 
+    SkJSDisplayable *p = (SkJSDisplayable *) JS_GetPrivate(cx, obj);
+    SkDisplayable* displayable = p->fDisplayable;
+    SkDisplayTypes displayableType = displayable->getType();
+    int members;
+    const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, displayableType, &members);
+    int idIndex = JSVAL_TO_INT(id);
+    SkASSERT(idIndex >= 0 && idIndex < members);
+    info = &info[idIndex];
+    SkDisplayTypes infoType = (SkDisplayTypes) info->fType;
+    SkScalar scalar = 0;
+    S32 s32 = 0;
+    SkString* string= NULL;
+    JSString *str;
+    if (infoType == SkType_MemberProperty) {
+        infoType = info->propertyType();
+        switch (infoType) {
+            case SkType_Scalar: {
+                SkScriptValue scriptValue;
+                bool success = displayable->getProperty(info->propertyIndex(), &scriptValue);
+                SkASSERT(scriptValue.fType == SkType_Scalar);
+                scalar = scriptValue.fOperand.fScalar;
+                } break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    } else {
+        SkASSERT(info->fCount == 1);
+        switch (infoType) {
+            case SkType_Boolean:
+            case SkType_Color:
+            case SkType_S32:
+                s32 = *(S32*) info->memberData(displayable);
+                break;
+            case SkType_String:
+                info->getString(displayable, &string);
+                break;
+            case SkType_Scalar:
+                SkOperand operand;
+                info->getValue(displayable, &operand, 1);
+                scalar = operand.fScalar;
+                break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    }
+    switch (infoType) {
+        case SkType_Boolean:
+            *vp = BOOLEAN_TO_JSVAL(s32);
+            break;
+        case SkType_Color:
+        case SkType_S32:
+            *vp = INT_TO_JSVAL(s32);
+            break;
+        case SkType_Scalar:
+            if (SkScalarFraction(scalar) == 0)
+                *vp = INT_TO_JSVAL(SkScalarFloor(scalar));
+            else
+#ifdef SK_SCALAR_IS_FLOAT
+            *vp = DOUBLE_TO_JSVAL(scalar);
+#else
+            *vp = DOUBLE_TO_JSVAL(scalar / 65536.0f );
+#endif
+            break;
+        case SkType_String:
+            str = JS_NewStringCopyN(cx, string->c_str(), string->size());
+            *vp = STRING_TO_JSVAL(str);
+            break;
+        default:
+            SkASSERT(0); // !!! unimplemented
+    }
+    return JS_TRUE;
+}
+
+JSBool SkJSDisplayable::SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
+    if (JSVAL_IS_INT(id) == 0)
+        return JS_TRUE; 
+    SkJSDisplayable *p = (SkJSDisplayable *) JS_GetPrivate(cx, obj);
+    SkDisplayable* displayable = p->fDisplayable;
+    SkDisplayTypes displayableType = displayable->getType();
+    int members;
+    const SkMemberInfo* info = SkDisplayType::GetMembers(NULL /* fMaker */, displayableType, &members);
+    int idIndex = JSVAL_TO_INT(id);
+    SkASSERT(idIndex >= 0 && idIndex < members);
+    info = &info[idIndex];
+    SkDisplayTypes infoType = info->getType();
+    SkScalar scalar = 0;
+    S32 s32 = 0;
+    SkString string;
+    JSString* str;
+    jsval value = *vp;
+    switch (infoType) {
+        case SkType_Boolean:
+            s32 = JSVAL_TO_BOOLEAN(value);
+            break;
+        case SkType_Color:
+        case SkType_S32:
+            s32 = JSVAL_TO_INT(value);
+            break;
+        case SkType_Scalar:
+            if (JSVAL_IS_INT(value))
+                scalar = SkIntToScalar(JSVAL_TO_INT(value));
+            else {
+                SkASSERT(JSVAL_IS_DOUBLE(value));
+#ifdef SK_SCALAR_IS_FLOAT
+                scalar = (float) *(double*) JSVAL_TO_DOUBLE(value);
+#else
+                scalar = (SkFixed)  (*(double*)JSVAL_TO_DOUBLE(value) * 65536.0);
+#endif
+            }
+            break;
+        case SkType_String:
+            str = JS_ValueToString(cx, value);
+            string.set(JS_GetStringBytes(str));
+            break;
+        default:
+            SkASSERT(0); // !!! unimplemented
+    }
+    if (info->fType == SkType_MemberProperty) {
+        switch (infoType) {
+            case SkType_Scalar: {
+                SkScriptValue scriptValue;
+                scriptValue.fType = SkType_Scalar;
+                scriptValue.fOperand.fScalar = scalar;
+                displayable->setProperty(-1 - (int) info->fOffset, scriptValue);
+                } break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    } else {
+        SkASSERT(info->fCount == 1);
+        switch (infoType) {
+            case SkType_Boolean:
+            case SkType_Color:
+            case SkType_S32:
+                s32 = *(S32*) ((const char*) displayable + info->fOffset);
+                break;
+            case SkType_String:
+                info->setString(displayable, &string);
+                break;
+            case SkType_Scalar:
+                SkOperand operand;
+                operand.fScalar = scalar;
+                info->setValue(displayable, &operand, 1);
+                break;
+            default:
+                SkASSERT(0); // !!! unimplemented
+        }
+    }
+    return JS_TRUE;
+}
+
+void SkJS::InitializeDisplayables(const SkBitmap& bitmap, JSContext *cx, JSObject *obj, JSObject *proto) {
+    SkJSDisplayable::gCanvas = new SkCanvas(bitmap);
+    SkJSDisplayable::gPaint = new SkPaint();
+#if SK_USE_CONDENSED_INFO == 0
+    GenerateTables();
+#else
+    SkASSERT(0); // !!! compressed version hasn't been implemented
+#endif
+    AddInit(cx, obj, proto);
+    AddCircleInit(cx, obj, proto);
+    AddOvalInit(cx, obj, proto);
+    AddPathInit(cx, obj, proto);
+    AddRectangleInit(cx, obj, proto);
+    AddRoundRectInit(cx, obj, proto);
+//  AfterInit(cx, obj, proto);
+    ApplyInit(cx, obj, proto);
+    // AnimateInit(cx, obj, proto);
+//  AnimateColorInit(cx, obj, proto);
+    AnimateFieldInit(cx, obj, proto);
+//  AnimateRotateInit(cx, obj, proto);
+//  AnimateScaleInit(cx, obj, proto);
+//  AnimateTranslateInit(cx, obj, proto);
+    BitmapInit(cx, obj, proto);
+//  BaseBitmapInit(cx, obj, proto);
+//  BeforeInit(cx, obj, proto);
+    BitmapShaderInit(cx, obj, proto);
+    BlurInit(cx, obj, proto);
+    ClipInit(cx, obj, proto);
+    ColorInit(cx, obj, proto);
+    CubicToInit(cx, obj, proto);
+    DashInit(cx, obj, proto);
+    DataInit(cx, obj, proto);
+//  DimensionsInit(cx, obj, proto);
+    DiscreteInit(cx, obj, proto);
+    DrawToInit(cx, obj, proto);
+    EmbossInit(cx, obj, proto);
+    EventInit(cx, obj, proto);
+//  FontInit(cx, obj, proto);
+//  FocusInit(cx, obj, proto);
+    ImageInit(cx, obj, proto);
+    IncludeInit(cx, obj, proto);
+//  InputInit(cx, obj, proto);
+    LineInit(cx, obj, proto);
+    LinearGradientInit(cx, obj, proto);
+    LineToInit(cx, obj, proto);
+    MatrixInit(cx, obj, proto);
+    MoveInit(cx, obj, proto);
+    MoveToInit(cx, obj, proto);
+    OvalInit(cx, obj, proto);
+    PathInit(cx, obj, proto);
+    PaintInit(cx, obj, proto);
+    DrawPointInit(cx, obj, proto);
+    PolyToPolyInit(cx, obj, proto);
+    PolygonInit(cx, obj, proto);
+    PolylineInit(cx, obj, proto);
+    PostInit(cx, obj, proto);
+    QuadToInit(cx, obj, proto);
+    RadialGradientInit(cx, obj, proto);
+    RandomInit(cx, obj, proto);
+    RectToRectInit(cx, obj, proto);
+    RectangleInit(cx, obj, proto);
+    RemoveInit(cx, obj, proto);
+    ReplaceInit(cx, obj, proto);
+    RotateInit(cx, obj, proto);
+    RoundRectInit(cx, obj, proto);
+    ScaleInit(cx, obj, proto);
+    SetInit(cx, obj, proto);
+    SkewInit(cx, obj, proto);
+    // 3D_CameraInit(cx, obj, proto);
+    // 3D_PatchInit(cx, obj, proto);
+    #ifdef SK_SUPPORT_IMAGE_ENCODE
+    SnapshotInit(cx, obj, proto);
+    #endif
+//  StrokeInit(cx, obj, proto);
+    TextInit(cx, obj, proto);
+    TextOnPathInit(cx, obj, proto);
+    TextToPathInit(cx, obj, proto);
+    TranslateInit(cx, obj, proto);
+//  UseInit(cx, obj, proto);
+}
+
+void SkJS::DisposeDisplayables() {
+    delete SkJSDisplayable::gPaint;
+    delete SkJSDisplayable::gCanvas;
+    for (int index = 0; index < kTypeNamesSize; index++) {
+        SkDisplayTypes type = gTypeNames[index].fType;
+        delete[] gDisplayableProperties[type];
+    }
+}
diff --git a/src/xml/SkParse.cpp b/src/xml/SkParse.cpp
new file mode 100644
index 0000000..e740280
--- /dev/null
+++ b/src/xml/SkParse.cpp
@@ -0,0 +1,336 @@
+/* libs/graphics/xml/SkParse.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 "SkParse.h"
+
+static inline bool is_between(int c, int min, int max)
+{
+    return (unsigned)(c - min) <= (unsigned)(max - min);
+}
+
+static inline bool is_ws(int c)
+{
+    return is_between(c, 1, 32);
+}
+
+static inline bool is_digit(int c)
+{
+    return is_between(c, '0', '9');
+}
+
+static inline bool is_sep(int c)
+{
+    return is_ws(c) || c == ',' || c == ';';
+}
+
+static int to_hex(int c)
+{
+    if (is_digit(c))
+        return c - '0';
+
+    c |= 0x20;  // make us lower-case
+    if (is_between(c, 'a', 'f'))
+        return c + 10 - 'a';
+    else
+        return -1;
+}
+
+static inline bool is_hex(int c)
+{
+    return to_hex(c) >= 0;
+}
+
+static const char* skip_ws(const char str[])
+{
+    SkASSERT(str);
+    while (is_ws(*str))
+        str++;
+    return str;
+}
+
+static const char* skip_sep(const char str[])
+{
+    SkASSERT(str);
+    while (is_sep(*str))
+        str++;
+    return str;
+}
+
+int SkParse::Count(const char str[]) 
+{
+    char c;
+    int count = 0;
+    goto skipLeading;
+    do {
+        count++;
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (is_sep(c) == false);
+skipLeading:
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (is_sep(c));
+    } while (true);
+goHome:
+    return count;
+}
+
+int SkParse::Count(const char str[], char separator) 
+{
+    char c;
+    int count = 0;
+    goto skipLeading;
+    do {
+        count++;
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (c != separator);
+skipLeading:
+        do {
+            if ((c = *str++) == '\0')
+                goto goHome;
+        } while (c == separator);
+    } while (true);
+goHome:
+    return count;
+}
+
+const char* SkParse::FindHex(const char str[], uint32_t* value)
+{
+    SkASSERT(str);
+    str = skip_ws(str);
+
+    if (!is_hex(*str))
+        return NULL;
+
+    uint32_t n = 0;
+    int max_digits = 8;
+    int digit;
+
+    while ((digit = to_hex(*str)) >= 0)
+    {
+        if (--max_digits < 0)
+            return NULL;
+        n = (n << 4) | digit;
+        str += 1;
+    }
+
+    if (*str == 0 || is_ws(*str))
+    {
+        if (value)
+            *value = n;
+        return str;
+    }
+    return false;
+}
+
+const char* SkParse::FindS32(const char str[], int32_t* value)
+{
+    SkASSERT(str);
+    str = skip_ws(str);
+
+    int sign = 0;
+    if (*str == '-')
+    {
+        sign = -1;
+        str += 1;
+    }
+
+    if (!is_digit(*str))
+        return NULL;
+
+    int n = 0;
+    while (is_digit(*str))
+    {
+        n = 10*n + *str - '0';
+        str += 1;
+    }
+    if (value)
+        *value = (n ^ sign) - sign;
+    return str;
+}
+
+const char* SkParse::FindMSec(const char str[], SkMSec* value)
+{
+    SkASSERT(str);
+    str = skip_ws(str);
+
+    int sign = 0;
+    if (*str == '-')
+    {
+        sign = -1;
+        str += 1;
+    }
+
+    if (!is_digit(*str))
+        return NULL;
+
+    int n = 0;
+    while (is_digit(*str))
+    {
+        n = 10*n + *str - '0';
+        str += 1;
+    }
+    int remaining10s = 3;
+    if (*str == '.') {
+        str++;
+        while (is_digit(*str))
+        {
+            n = 10*n + *str - '0';
+            str += 1;
+            if (--remaining10s == 0)
+                break;
+        }
+    }
+    while (--remaining10s >= 0)
+        n *= 10;
+    if (value)
+        *value = (n ^ sign) - sign;
+    return str;
+}
+
+const char* SkParse::FindScalar(const char str[], SkScalar* value)
+{
+    SkASSERT(str);
+    str = skip_ws(str);
+
+    int sign = 0;
+    if (*str == '-')
+    {
+        sign = -1;
+        str += 1;
+    }
+
+    if (!is_digit(*str) && *str != '.')
+        return NULL;
+
+    int n = 0;
+    while (is_digit(*str))
+    {
+        n = 10*n + *str - '0';
+        if (n > 0x7FFF)
+            return NULL;
+        str += 1;
+    }
+    n <<= 16;
+
+    if (*str == '.')
+    {
+        static const int gFractions[] = { (1 << 24)  / 10, (1 << 24)  / 100, (1 << 24)  / 1000, 
+            (1 << 24)  / 10000, (1 << 24)  / 100000 };
+        str += 1;
+        int d = 0;
+        const int* fraction = gFractions;
+        const int* end = &fraction[SK_ARRAY_COUNT(gFractions)];
+        while (is_digit(*str) && fraction < end)
+            d += (*str++ - '0') * *fraction++;
+        d += 0x80; // round
+        n += d >> 8;
+    }
+    while (is_digit(*str))
+        str += 1;
+    if (value)
+    {
+        n = (n ^ sign) - sign;  // apply the sign
+        *value = SkFixedToScalar(n);
+    }
+    return str;
+}
+
+const char* SkParse::FindScalars(const char str[], SkScalar value[], int count)
+{
+    SkASSERT(count >= 0);
+
+    if (count > 0)
+    {
+        for (;;)
+        {
+            str = SkParse::FindScalar(str, value);
+            if (--count == 0 || str == NULL)
+                break;
+
+            // keep going
+            str = skip_sep(str);
+            if (value)
+                value += 1;
+        }
+    }
+    return str;
+}
+
+static bool lookup_str(const char str[], const char** table, int count)
+{
+    while (--count >= 0)
+        if (!strcmp(str, table[count]))
+            return true;
+    return false;
+}
+
+bool SkParse::FindBool(const char str[], bool* value)
+{
+    static const char* gYes[] = { "yes", "1", "true" };
+    static const char* gNo[] = { "no", "0", "false" };
+
+    if (lookup_str(str, gYes, SK_ARRAY_COUNT(gYes)))
+    {
+        if (value) *value = true;
+        return true;
+    }
+    else if (lookup_str(str, gNo, SK_ARRAY_COUNT(gNo)))
+    {
+        if (value) *value = false;
+        return true;
+    }
+    return false;
+}
+
+int SkParse::FindList(const char target[], const char list[])
+{
+    size_t  len = strlen(target);
+    int     index = 0;
+
+    for (;;)
+    {
+        const char* end = strchr(list, ',');
+        size_t      entryLen;
+
+        if (end == NULL) // last entry
+            entryLen = strlen(list);
+        else
+            entryLen = end - list;
+
+        if (entryLen == len && memcmp(target, list, len) == 0)
+            return index;
+        if (end == NULL)
+            break;
+
+        list = end + 1; // skip the ','
+        index += 1;
+    }
+    return -1;
+}
+
+#ifdef SK_SUPPORT_UNITTEST
+void SkParse::UnitTest() 
+{
+    // !!! additional parse tests go here
+    SkParse::TestColor();
+}
+#endif
diff --git a/src/xml/SkParseColor.cpp b/src/xml/SkParseColor.cpp
new file mode 100644
index 0000000..eca2e38
--- /dev/null
+++ b/src/xml/SkParseColor.cpp
@@ -0,0 +1,546 @@
+/* libs/graphics/xml/SkParseColor.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 "SkParse.h"
+
+#ifdef SK_DEBUG
+#include "SkString.h"
+
+    // compress names 6 chars per long (packed 5 bits/char )
+        // note: little advantage to splitting chars across longs, since 3 longs at 2 unused bits each 
+        // allow for one additional split char (vs. the 18 unsplit chars in the three longs)
+    // use extra two bits to represent:
+        // 00 : final 6 (or fewer) chars (if 'a' is 0x01, zero could have special meaning)
+        // 01 : not final 6 chars
+        // 10 : color
+        // 11 : unused, except as debugging sentinal? (could be -1 for easier test)
+    // !!! the bit to end the word (last) is at the low bit for binary search
+    // lookup first character in offset for quick start
+        // offset is 27-entry table of bytes(?) that trims linear search to at most 21 entries ('d')
+    // shift match into long; set bit 30 if it all doesn't fit
+    // while longs don't match, march forward
+        // if they do match, and bit 30 is set, advance match, clearing bit 30 if
+        // final chars, and advance to next test
+        // if they do match, and bit 30 is clear, get next long (color) and return it
+    // stop at lookup of first char + 1
+static const struct SkNameRGB {
+    const char* name;
+    int rgb;
+} colorNames[] = {
+    { "aliceblue",            0xF0F8FF },
+    { "antiquewhite",         0xFAEBD7 },
+    { "aqua",                 0x00FFFF },
+    { "aquamarine",           0x7FFFD4 },
+    { "azure",                0xF0FFFF },
+    { "beige",                0xF5F5DC },
+    { "bisque",               0xFFE4C4 },
+    { "black",                0x000000 },
+    { "blanchedalmond",       0xFFEBCD },
+    { "blue",                 0x0000FF },
+    { "blueviolet",           0x8A2BE2 },
+    { "brown",                0xA52A2A },
+    { "burlywood",            0xDEB887 },
+    { "cadetblue",            0x5F9EA0 },
+    { "chartreuse",           0x7FFF00 },
+    { "chocolate",            0xD2691E },
+    { "coral",                0xFF7F50 },
+    { "cornflowerblue",       0x6495ED },
+    { "cornsilk",             0xFFF8DC },
+    { "crimson",              0xDC143C },
+    { "cyan",                 0x00FFFF },
+    { "darkblue",             0x00008B },
+    { "darkcyan",             0x008B8B },
+    { "darkgoldenrod",        0xB8860B },
+    { "darkgray",             0xA9A9A9 },
+    { "darkgreen",            0x006400 },
+    { "darkkhaki",            0xBDB76B },
+    { "darkmagenta",          0x8B008B },
+    { "darkolivegreen",       0x556B2F },
+    { "darkorange",           0xFF8C00 },
+    { "darkorchid",           0x9932CC },
+    { "darkred",              0x8B0000 },
+    { "darksalmon",           0xE9967A },
+    { "darkseagreen",         0x8FBC8F },
+    { "darkslateblue",        0x483D8B },
+    { "darkslategray",        0x2F4F4F },
+    { "darkturquoise",        0x00CED1 },
+    { "darkviolet",           0x9400D3 },
+    { "deeppink",             0xFF1493 },
+    { "deepskyblue",          0x00BFFF },
+    { "dimgray",              0x696969 },
+    { "dodgerblue",           0x1E90FF },
+    { "firebrick",            0xB22222 },
+    { "floralwhite",          0xFFFAF0 },
+    { "forestgreen",          0x228B22 },
+    { "fuchsia",              0xFF00FF },
+    { "gainsboro",            0xDCDCDC },
+    { "ghostwhite",           0xF8F8FF },
+    { "gold",                 0xFFD700 },
+    { "goldenrod",            0xDAA520 },
+    { "gray",                 0x808080 },
+    { "green",                0x008000 },
+    { "greenyellow",          0xADFF2F },
+    { "honeydew",             0xF0FFF0 },
+    { "hotpink",              0xFF69B4 },
+    { "indianred",            0xCD5C5C },
+    { "indigo",               0x4B0082 },
+    { "ivory",                0xFFFFF0 },
+    { "khaki",                0xF0E68C },
+    { "lavender",             0xE6E6FA },
+    { "lavenderblush",        0xFFF0F5 },
+    { "lawngreen",            0x7CFC00 },
+    { "lemonchiffon",         0xFFFACD },
+    { "lightblue",            0xADD8E6 },
+    { "lightcoral",           0xF08080 },
+    { "lightcyan",            0xE0FFFF },
+    { "lightgoldenrodyellow", 0xFAFAD2 },
+    { "lightgreen",           0x90EE90 },
+    { "lightgrey",            0xD3D3D3 },
+    { "lightpink",            0xFFB6C1 },
+    { "lightsalmon",          0xFFA07A },
+    { "lightseagreen",        0x20B2AA },
+    { "lightskyblue",         0x87CEFA },
+    { "lightslategray",       0x778899 },
+    { "lightsteelblue",       0xB0C4DE },
+    { "lightyellow",          0xFFFFE0 },
+    { "lime",                 0x00FF00 },
+    { "limegreen",            0x32CD32 },
+    { "linen",                0xFAF0E6 },
+    { "magenta",              0xFF00FF },
+    { "maroon",               0x800000 },
+    { "mediumaquamarine",     0x66CDAA },
+    { "mediumblue",           0x0000CD },
+    { "mediumorchid",         0xBA55D3 },
+    { "mediumpurple",         0x9370DB },
+    { "mediumseagreen",       0x3CB371 },
+    { "mediumslateblue",      0x7B68EE },
+    { "mediumspringgreen",    0x00FA9A },
+    { "mediumturquoise",      0x48D1CC },
+    { "mediumvioletred",      0xC71585 },
+    { "midnightblue",         0x191970 },
+    { "mintcream",            0xF5FFFA },
+    { "mistyrose",            0xFFE4E1 },
+    { "moccasin",             0xFFE4B5 },
+    { "navajowhite",          0xFFDEAD },
+    { "navy",                 0x000080 },
+    { "oldlace",              0xFDF5E6 },
+    { "olive",                0x808000 },
+    { "olivedrab",            0x6B8E23 },
+    { "orange",               0xFFA500 },
+    { "orangered",            0xFF4500 },
+    { "orchid",               0xDA70D6 },
+    { "palegoldenrod",        0xEEE8AA },
+    { "palegreen",            0x98FB98 },
+    { "paleturquoise",        0xAFEEEE },
+    { "palevioletred",        0xDB7093 },
+    { "papayawhip",           0xFFEFD5 },
+    { "peachpuff",            0xFFDAB9 },
+    { "peru",                 0xCD853F },
+    { "pink",                 0xFFC0CB },
+    { "plum",                 0xDDA0DD },
+    { "powderblue",           0xB0E0E6 },
+    { "purple",               0x800080 },
+    { "red",                  0xFF0000 },
+    { "rosybrown",            0xBC8F8F },
+    { "royalblue",            0x4169E1 },
+    { "saddlebrown",          0x8B4513 },
+    { "salmon",               0xFA8072 },
+    { "sandybrown",           0xF4A460 },
+    { "seagreen",             0x2E8B57 },
+    { "seashell",             0xFFF5EE },
+    { "sienna",               0xA0522D },
+    { "silver",               0xC0C0C0 },
+    { "skyblue",              0x87CEEB },
+    { "slateblue",            0x6A5ACD },
+    { "slategray",            0x708090 },
+    { "snow",                 0xFFFAFA },
+    { "springgreen",          0x00FF7F },
+    { "steelblue",            0x4682B4 },
+    { "tan",                  0xD2B48C },
+    { "teal",                 0x008080 },
+    { "thistle",              0xD8BFD8 },
+    { "tomato",               0xFF6347 },
+    { "turquoise",            0x40E0D0 },
+    { "violet",               0xEE82EE },
+    { "wheat",                0xF5DEB3 },
+    { "white",                0xFFFFFF },
+    { "whitesmoke",           0xF5F5F5 },
+    { "yellow",               0xFFFF00 },
+    { "yellowgreen",          0x9ACD32 }
+};
+
+int colorNamesSize = sizeof(colorNames) / sizeof(colorNames[0]);
+
+static void CreateTable() {
+    SkString comment;
+    size_t originalSize = 0;
+    int replacement = 0;
+    for (int index = 0; index < colorNamesSize; index++) {
+        SkNameRGB nameRGB =  colorNames[index];
+        const char* name = nameRGB.name;
+        size_t len = strlen(name);
+        originalSize += len + 9;
+        bool first = true;
+        bool last = false;
+        do {
+            int compressed = 0;
+            const char* start = name;
+            for (int chIndex = 0; chIndex < 6; chIndex++) {
+                compressed <<= 5;
+                compressed |= *name ? *name++ - 'a' + 1 : 0 ;
+            }
+            replacement += sizeof(int);
+            compressed <<= 1;
+            compressed |= 1;
+            if (first) {
+                compressed |= 0x80000000;
+                first = false;
+            }
+            if (len <= 6) { // last
+                compressed &= ~1;
+                last = true;
+            }
+            len -= 6;
+            SkDebugf("0x%08x, ", compressed);
+            comment.append(start, name - start);
+        } while (last == false);
+        replacement += sizeof(int);
+        SkDebugf("0x%08x, ", nameRGB.rgb);
+        SkDebugf("// %s\n", comment.c_str());
+        comment.reset();
+    }
+    SkDebugf("// original = %d : replacement = %d\n", originalSize, replacement);
+    SkASSERT(0); // always stop after creating table
+}
+
+#endif
+
+static const unsigned int gColorNames[] = {
+0x85891945, 0x32a50000, 0x00f0f8ff, // aliceblue
+0x85d44c6b, 0x16e84d0a, 0x00faebd7, // antiquewhite
+0x86350800, 0x0000ffff, // aqua
+0x86350b43, 0x492e2800, 0x007fffd4, // aquamarine
+0x87559140, 0x00f0ffff, // azure
+0x88a93940, 0x00f5f5dc, // beige
+0x89338d4a, 0x00ffe4c4, // bisque
+0x89811ac0, 0x00000000, // black
+0x898170d1, 0x1481635f, 0x38800000, 0x00ffebcd, // blanchedalmond
+0x89952800, 0x000000ff, // blue
+0x89952d93, 0x3d85a000, 0x008a2be2, // blueviolet
+0x8a4fbb80, 0x00a52a2a, // brown
+0x8ab2666f, 0x3de40000, 0x00deb887, // burlywood
+0x8c242d05, 0x32a50000, 0x005f9ea0, // cadetblue
+0x8d019525, 0x16b32800, 0x007fff00, // chartreuse
+0x8d0f1bd9, 0x06850000, 0x00d2691e, // chocolate
+0x8df20b00, 0x00ff7f50, // coral
+0x8df27199, 0x3ee59099, 0x54a00000, 0x006495ed, // cornflowerblue
+0x8df274d3, 0x31600000, 0x00fff8dc, // cornsilk
+0x8e496cdf, 0x38000000, 0x00dc143c, // crimson
+0x8f217000, 0x0000ffff, // cyan
+0x90325899, 0x54a00000, 0x0000008b, // darkblue
+0x903258f3, 0x05c00000, 0x00008b8b, // darkcyan
+0x903259df, 0x3085749f, 0x10000000, 0x00b8860b, // darkgoldenrod
+0x903259e5, 0x07200000, 0x00a9a9a9, // darkgray
+0x903259e5, 0x14ae0000, 0x00006400, // darkgreen
+0x90325ad1, 0x05690000, 0x00bdb76b, // darkkhaki
+0x90325b43, 0x1caea040, 0x008b008b, // darkmagenta
+0x90325bd9, 0x26c53c8b, 0x15c00000, 0x00556b2f, // darkolivegreen
+0x90325be5, 0x05c72800, 0x00ff8c00, // darkorange
+0x90325be5, 0x0d092000, 0x009932cc, // darkorchid
+0x90325c8b, 0x10000000, 0x008b0000, // darkred
+0x90325cc3, 0x31af7000, 0x00e9967a, // darksalmon
+0x90325ccb, 0x04f2295c, 0x008fbc8f, // darkseagreen
+0x90325cd9, 0x0685132b, 0x14000000, 0x00483d8b, // darkslateblue
+0x90325cd9, 0x06853c83, 0x64000000, 0x002f4f4f, // darkslategray
+0x90325d2b, 0x4a357a67, 0x14000000, 0x0000ced1, // darkturquoise
+0x90325d93, 0x3d85a000, 0x009400d3, // darkviolet
+0x90a58413, 0x39600000, 0x00ff1493, // deeppink
+0x90a584d7, 0x644ca940, 0x0000bfff, // deepskyblue
+0x912d3c83, 0x64000000, 0x00696969, // dimgray
+0x91e43965, 0x09952800, 0x001e90ff, // dodgerblue
+0x993228a5, 0x246b0000, 0x00b22222, // firebrick
+0x998f9059, 0x5d09a140, 0x00fffaf0, // floralwhite
+0x99f22ce9, 0x1e452b80, 0x00228b22, // forestgreen
+0x9aa344d3, 0x04000000, 0x00ff00ff, // fuchsia
+0x9c2974c5, 0x3e4f0000, 0x00dcdcdc, // gainsboro
+0x9d0f9d2f, 0x21342800, 0x00f8f8ff, // ghostwhite
+0x9dec2000, 0x00ffd700, // gold
+0x9dec215d, 0x49e40000, 0x00daa520, // goldenrod
+0x9e41c800, 0x00808080, // gray
+0x9e452b80, 0x00008000, // green
+0x9e452bb3, 0x158c7dc0, 0x00adff2f, // greenyellow
+0xa1ee2e49, 0x16e00000, 0x00f0fff0, // honeydew
+0xa1f4825d, 0x2c000000, 0x00ff69b4, // hotpink
+0xa5c4485d, 0x48a40000, 0x00cd5c5c, // indianred
+0xa5c449de, 0x004b0082, // indigo
+0xa6cf9640, 0x00fffff0, // ivory
+0xad015a40, 0x00f0e68c, // khaki
+0xb0362b89, 0x16400000, 0x00e6e6fa, // lavender
+0xb0362b89, 0x16426567, 0x20000000, 0x00fff0f5, // lavenderblush
+0xb03771e5, 0x14ae0000, 0x007cfc00, // lawngreen
+0xb0ad7b87, 0x212633dc, 0x00fffacd, // lemonchiffon
+0xb1274505, 0x32a50000, 0x00add8e6, // lightblue
+0xb1274507, 0x3e416000, 0x00f08080, // lightcoral
+0xb1274507, 0x642e0000, 0x00e0ffff, // lightcyan
+0xb127450f, 0x3d842ba5, 0x3c992b19, 0x3ee00000, 0x00fafad2, // lightgoldenrodyellow
+0xb127450f, 0x48a57000, 0x0090ee90, // lightgreen
+0xb127450f, 0x48b90000, 0x00d3d3d3, // lightgrey
+0xb1274521, 0x25cb0000, 0x00ffb6c1, // lightpink
+0xb1274527, 0x058d7b80, 0x00ffa07a, // lightsalmon
+0xb1274527, 0x1427914b, 0x38000000, 0x0020b2aa, // lightseagreen
+0xb1274527, 0x2f22654a, 0x0087cefa, // lightskyblue
+0xb1274527, 0x303429e5, 0x07200000, 0x00778899, // lightslategray
+0xb1274527, 0x50a56099, 0x54a00000, 0x00b0c4de, // lightsteelblue
+0xb1274533, 0x158c7dc0, 0x00ffffe0, // lightyellow
+0xb12d2800, 0x0000ff00, // lime
+0xb12d29e5, 0x14ae0000, 0x0032cd32, // limegreen
+0xb12e2b80, 0x00faf0e6, // linen
+0xb4272ba9, 0x04000000, 0x00ff00ff, // magenta
+0xb4327bdc, 0x00800000, // maroon
+0xb4a44d5b, 0x06350b43, 0x492e2800, 0x0066cdaa, // mediumaquamarine
+0xb4a44d5b, 0x09952800, 0x000000cd, // mediumblue
+0xb4a44d5b, 0x3e434248, 0x00ba55d3, // mediumorchid
+0xb4a44d5b, 0x42b2830a, 0x009370db, // mediumpurple
+0xb4a44d5b, 0x4ca13c8b, 0x15c00000, 0x003cb371, // mediumseagreen
+0xb4a44d5b, 0x4d81a145, 0x32a50000, 0x007b68ee, // mediumslateblue
+0xb4a44d5b, 0x4e124b8f, 0x1e452b80, 0x0000fa9a, // mediumspringgreen
+0xb4a44d5b, 0x52b28d5f, 0x26650000, 0x0048d1cc, // mediumturquoise
+0xb4a44d5b, 0x592f6169, 0x48a40000, 0x00c71585, // mediumvioletred
+0xb524724f, 0x2282654a, 0x00191970, // midnightblue
+0xb52ea0e5, 0x142d0000, 0x00f5fffa, // mintcream
+0xb533a665, 0x3e650000, 0x00ffe4e1, // mistyrose
+0xb5e31867, 0x25c00000, 0x00ffe4b5, // moccasin
+0xb8360a9f, 0x5d09a140, 0x00ffdead, // navajowhite
+0xb836c800, 0x00000080, // navy
+0xbd846047, 0x14000000, 0x00fdf5e6, // oldlace
+0xbd89b140, 0x00808000, // olive
+0xbd89b149, 0x48220000, 0x006b8e23, // olivedrab
+0xbe4171ca, 0x00ffa500, // orange
+0xbe4171cb, 0x48a40000, 0x00ff4500, // orangered
+0xbe434248, 0x00da70d6, // orchid
+0xc02c29df, 0x3085749f, 0x10000000, 0x00eee8aa, // palegoldenrod
+0xc02c29e5, 0x14ae0000, 0x0098fb98, // palegreen
+0xc02c2d2b, 0x4a357a67, 0x14000000, 0x00afeeee, // paleturquoise
+0xc02c2d93, 0x3d85a48b, 0x10000000, 0x00db7093, // palevioletred
+0xc0300e43, 0x5d098000, 0x00ffefd5, // papayawhip
+0xc0a11a21, 0x54c60000, 0x00ffdab9, // peachpuff
+0xc0b2a800, 0x00cd853f, // peru
+0xc12e5800, 0x00ffc0cb, // pink
+0xc1956800, 0x00dda0dd, // plum
+0xc1f72165, 0x09952800, 0x00b0e0e6, // powderblue
+0xc2b2830a, 0x00800080, // purple
+0xc8a40000, 0x00ff0000, // red
+0xc9f3c8a5, 0x3eee0000, 0x00bc8f8f, // rosybrown
+0xc9f90b05, 0x32a50000, 0x004169e1, // royalblue
+0xcc24230b, 0x0a4fbb80, 0x008b4513, // saddlebrown
+0xcc2c6bdc, 0x00fa8072, // salmon
+0xcc2e2645, 0x49f77000, 0x00f4a460, // sandybrown
+0xcca13c8b, 0x15c00000, 0x002e8b57, // seagreen
+0xcca19a0b, 0x31800000, 0x00fff5ee, // seashell
+0xcd257382, 0x00a0522d, // sienna
+0xcd2cb164, 0x00c0c0c0, // silver
+0xcd79132b, 0x14000000, 0x0087ceeb, // skyblue
+0xcd81a145, 0x32a50000, 0x006a5acd, // slateblue
+0xcd81a14f, 0x48390000, 0x00708090, // slategray
+0xcdcfb800, 0x00fffafa, // snow
+0xce124b8f, 0x1e452b80, 0x0000ff7f, // springgreen
+0xce852b05, 0x32a50000, 0x004682b4, // steelblue
+0xd02e0000, 0x00d2b48c, // tan
+0xd0a16000, 0x00008080, // teal
+0xd1099d19, 0x14000000, 0x00d8bfd8, // thistle
+0xd1ed0d1e, 0x00ff6347, // tomato
+0xd2b28d5f, 0x26650000, 0x0040e0d0, // turquoise
+0xd92f6168, 0x00ee82ee, // violet
+0xdd050d00, 0x00f5deb3, // wheat
+0xdd09a140, 0x00ffffff, // white
+0xdd09a167, 0x35eb2800, 0x00f5f5f5, // whitesmoke
+0xe4ac63ee, 0x00ffff00, // yellow
+0xe4ac63ef, 0x1e452b80, 0x009acd32 // yellowgreen
+}; // original = 2505 : replacement = 1616
+
+
+const char* SkParse::FindNamedColor(const char* name, size_t len, SkColor* color) {
+    const char* namePtr = name;
+    unsigned int sixMatches[4];
+    unsigned int* sixMatchPtr = sixMatches;
+    bool first = true;
+    bool last = false;
+    char ch;
+    do {
+        unsigned int sixMatch = 0;
+        for (int chIndex = 0; chIndex < 6; chIndex++) {
+            sixMatch <<= 5;
+            ch = *namePtr  | 0x20;
+            if (ch < 'a' || ch > 'z')
+                ch = 0;
+            else {
+                ch = ch - 'a' + 1;
+                namePtr++;
+            }
+            sixMatch |= ch ;  // turn 'A' (0x41) into 'a' (0x61);
+        }
+        sixMatch <<= 1;
+        sixMatch |= 1;
+        if (first) {
+            sixMatch |= 0x80000000;
+            first = false;
+        }
+        ch = *namePtr | 0x20;
+        last = ch < 'a' || ch > 'z';
+        if (last) 
+            sixMatch &= ~1;
+        len -= 6;
+        *sixMatchPtr++ = sixMatch;
+    } while (last == false && len > 0);
+    const int colorNameSize = sizeof(gColorNames) / sizeof(unsigned int);
+    int lo = 0;
+    int hi = colorNameSize - 3; // back off to beginning of yellowgreen
+    while (lo <= hi) {
+        int mid = (hi + lo) >> 1;
+        while ((int) gColorNames[mid] >= 0)
+            --mid;
+        sixMatchPtr = sixMatches;
+        while (gColorNames[mid] == *sixMatchPtr) {
+            ++mid;
+            if ((*sixMatchPtr & 1) == 0) { // last
+                *color = gColorNames[mid] | 0xFF000000;
+                return namePtr;
+            }
+            ++sixMatchPtr;
+        }
+        int sixMask = *sixMatchPtr & ~0x80000000;
+        int midMask = gColorNames[mid] & ~0x80000000;
+        if (sixMask > midMask) {
+            lo = mid + 2;   // skip color
+            while ((int) gColorNames[lo] >= 0)
+                ++lo;
+        } else if (hi == mid)
+            return NULL;
+        else
+            hi = mid;
+    }
+    return NULL;
+}
+
+// !!! move to char utilities
+//static int count_separators(const char* str, const char* sep) {
+//  char c;
+//  int separators = 0;
+//  while ((c = *str++) != '\0') {
+//      if (strchr(sep, c) == NULL)
+//          continue;
+//      do {
+//          if ((c = *str++) == '\0')
+//              goto goHome;
+//      } while (strchr(sep, c) != NULL);
+//      separators++;
+//  }
+//goHome:
+//  return separators;
+//}
+
+static inline unsigned nib2byte(unsigned n)
+{
+    SkASSERT((n & ~0xF) == 0);
+    return (n << 4) | n;
+}
+
+const char* SkParse::FindColor(const char* value, SkColor* colorPtr) {
+    unsigned int oldAlpha = SkColorGetA(*colorPtr);
+    if (value[0] == '#') {
+        uint32_t    hex;
+        const char* end = SkParse::FindHex(value + 1, &hex);
+//      SkASSERT(end);
+        if (end == NULL)
+            return end;
+        size_t len = end - value - 1;
+        if (len == 3 || len == 4) {
+            unsigned a = len == 4 ? nib2byte(hex >> 12) : oldAlpha;
+            unsigned r = nib2byte((hex >> 8) & 0xF);
+            unsigned g = nib2byte((hex >> 4) & 0xF);
+            unsigned b = nib2byte(hex & 0xF);
+            *colorPtr = SkColorSetARGB(a, r, g, b);
+            return end;
+        } else if (len == 6 || len == 8) {
+            if (len == 6)
+                hex |= oldAlpha << 24;
+            *colorPtr = hex;
+            return end;
+        } else {
+//          SkASSERT(0);
+            return NULL;
+        }
+//  } else if (strchr(value, ',')) {
+//      SkScalar array[4];
+//      int count = count_separators(value, ",") + 1; // !!! count commas, add 1
+//      SkASSERT(count == 3 || count == 4);
+//      array[0] = SK_Scalar1 * 255;
+//      const char* end = SkParse::FindScalars(value, &array[4 - count], count);
+//      if (end == NULL)
+//          return NULL;
+        // !!! range check for errors?
+//      *colorPtr = SkColorSetARGB(SkScalarRound(array[0]), SkScalarRound(array[1]), 
+//          SkScalarRound(array[2]), SkScalarRound(array[3]));
+//      return end;
+    } else
+        return FindNamedColor(value, strlen(value), colorPtr);
+}
+
+#ifdef SK_DEBUG
+void SkParse::TestColor() {
+    if (false)
+        CreateTable();  // regenerates data table in the output window
+    SkColor result;
+    int index;
+    for (index = 0; index < colorNamesSize; index++) {
+        result = SK_ColorBLACK;
+        SkNameRGB nameRGB = colorNames[index];
+        SkASSERT(FindColor(nameRGB.name, &result) != NULL);
+        SkASSERT(result == (SkColor) (nameRGB.rgb | 0xFF000000));
+    }
+    for (index = 0; index < colorNamesSize; index++) {
+        result = SK_ColorBLACK;
+        SkNameRGB nameRGB = colorNames[index];
+        char bad[24];
+        size_t len = strlen(nameRGB.name);
+        memcpy(bad, nameRGB.name, len);
+        bad[len - 1] -= 1; 
+        SkASSERT(FindColor(bad, &result) == false);
+        bad[len - 1] += 2; 
+        SkASSERT(FindColor(bad, &result) == false);
+    }
+    result = SK_ColorBLACK;
+    SkASSERT(FindColor("lightGrey", &result));
+    SkASSERT(result == 0xffd3d3d3);
+//  SkASSERT(FindColor("12,34,56,78", &result));
+//  SkASSERT(result == ((12 << 24) | (34 << 16) | (56 << 8) | (78 << 0)));
+    result = SK_ColorBLACK;
+    SkASSERT(FindColor("#ABCdef", &result));
+    SkASSERT(result == 0XFFABCdef);
+    SkASSERT(FindColor("#12ABCdef", &result));
+    SkASSERT(result == 0X12ABCdef);
+    result = SK_ColorBLACK;
+    SkASSERT(FindColor("#123", &result));
+    SkASSERT(result == 0Xff112233);
+    SkASSERT(FindColor("#abcd", &result));
+    SkASSERT(result == 0Xaabbccdd);
+    result = SK_ColorBLACK;
+//  SkASSERT(FindColor("71,162,253", &result));
+//  SkASSERT(result == ((0xFF << 24) | (71 << 16) | (162 << 8) | (253 << 0)));
+}
+#endif
+
diff --git a/src/xml/SkXMLParser.cpp b/src/xml/SkXMLParser.cpp
new file mode 100644
index 0000000..da33307
--- /dev/null
+++ b/src/xml/SkXMLParser.cpp
@@ -0,0 +1,95 @@
+/* libs/graphics/xml/SkXMLParser.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 "SkXMLParser.h"
+
+static char const* const gErrorStrings[] = {
+    "empty or missing file ",
+    "unknown element ",
+    "unknown attribute name ",
+    "error in attribute value ",
+    "duplicate ID ",
+    "unknown error "
+};
+
+SkXMLParserError::SkXMLParserError() : fCode(kNoError), fLineNumber(-1),
+    fNativeCode(-1)
+{
+    reset();
+}
+
+SkXMLParserError::~SkXMLParserError()
+{
+    // need a virtual destructor for our subclasses
+}
+
+void SkXMLParserError::getErrorString(SkString* str) const
+{
+    SkASSERT(str);
+    SkString temp;
+    if (fCode != kNoError) {
+        if ((unsigned)fCode < SK_ARRAY_COUNT(gErrorStrings))
+            temp.set(gErrorStrings[fCode - 1]);
+        temp.append(fNoun);
+    } else
+        SkXMLParser::GetNativeErrorString(fNativeCode, &temp);
+    str->append(temp);
+}
+
+void SkXMLParserError::reset() {
+    fCode = kNoError;
+    fLineNumber = -1;
+    fNativeCode = -1;
+}
+
+
+////////////////
+
+SkXMLParser::SkXMLParser(SkXMLParserError* parserError) : fParser(NULL), fError(parserError)
+{
+}
+
+SkXMLParser::~SkXMLParser()
+{
+}
+
+bool SkXMLParser::startElement(const char elem[])
+{
+    return this->onStartElement(elem);
+}
+
+bool SkXMLParser::addAttribute(const char name[], const char value[])
+{
+    return this->onAddAttribute(name, value);
+}
+
+bool SkXMLParser::endElement(const char elem[])
+{
+    return this->onEndElement(elem);
+}
+
+bool SkXMLParser::text(const char text[], int len) 
+{
+    return this->onText(text, len);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool SkXMLParser::onStartElement(const char elem[]) {return false; }
+bool SkXMLParser::onAddAttribute(const char name[], const char value[]) {return false; }
+bool SkXMLParser::onEndElement(const char elem[]) { return false; }
+bool SkXMLParser::onText(const char text[], int len) {return false; }
diff --git a/src/xml/SkXMLPullParser.cpp b/src/xml/SkXMLPullParser.cpp
new file mode 100644
index 0000000..5df75b7
--- /dev/null
+++ b/src/xml/SkXMLPullParser.cpp
@@ -0,0 +1,132 @@
+#include "SkXMLParser.h"
+#include "SkStream.h"
+
+static void reset(SkXMLPullParser::Curr* curr)
+{
+    curr->fEventType = SkXMLPullParser::ERROR;
+    curr->fName = "";
+    curr->fAttrInfoCount = 0;
+    curr->fIsWhitespace = false;
+}
+
+SkXMLPullParser::SkXMLPullParser() : fStream(NULL)
+{
+    fCurr.fEventType = ERROR;
+    fDepth = -1;
+}
+
+SkXMLPullParser::SkXMLPullParser(SkStream* stream) : fStream(NULL)
+{
+    fCurr.fEventType = ERROR;
+    fDepth = 0;
+    
+    this->setStream(stream);
+}
+
+SkXMLPullParser::~SkXMLPullParser()
+{
+    this->setStream(NULL);
+}
+
+SkStream* SkXMLPullParser::setStream(SkStream* stream)
+{
+    if (fStream && !stream)
+        this->onExit();
+
+    SkRefCnt_SafeAssign(fStream, stream);
+
+    if (fStream)
+    {
+        fCurr.fEventType = START_DOCUMENT;
+        this->onInit();
+    }
+    else
+    {
+        fCurr.fEventType = ERROR;
+    }
+    fDepth = 0;
+
+    return fStream;
+}
+
+SkXMLPullParser::EventType SkXMLPullParser::nextToken()
+{
+    switch (fCurr.fEventType) {
+    case ERROR:
+    case END_DOCUMENT:
+        break;
+    case END_TAG:
+        fDepth -= 1;
+        // fall through
+    default:        
+        reset(&fCurr);
+        fCurr.fEventType = this->onNextToken();
+        break;
+    }
+    
+    switch (fCurr.fEventType) {
+    case START_TAG:
+        fDepth += 1;
+        break;
+    default:
+        break;
+    }
+
+    return fCurr.fEventType;
+}
+
+const char* SkXMLPullParser::getName()
+{
+    switch (fCurr.fEventType) {
+    case START_TAG:
+    case END_TAG:
+        return fCurr.fName;
+    default:
+        return NULL;
+    }
+}
+
+const char* SkXMLPullParser::getText()
+{
+    switch (fCurr.fEventType) {
+    case TEXT:
+    case IGNORABLE_WHITESPACE:
+        return fCurr.fName;
+    default:
+        return NULL;
+    }
+}
+
+bool SkXMLPullParser::isWhitespace()
+{
+    switch (fCurr.fEventType) {
+    case IGNORABLE_WHITESPACE:
+        return true;
+    case TEXT:
+    case CDSECT:
+        return fCurr.fIsWhitespace;
+    default:
+        return false;   // unknown/illegal
+    }
+}
+
+int SkXMLPullParser::getAttributeCount()
+{
+    return fCurr.fAttrInfoCount;
+}
+
+void SkXMLPullParser::getAttributeInfo(int index, AttrInfo* info)
+{
+    SkASSERT((unsigned)index < (unsigned)fCurr.fAttrInfoCount);
+    
+    if (info)
+        *info = fCurr.fAttrInfos[index];
+}
+    
+bool SkXMLPullParser::onEntityReplacement(const char name[],
+                                          SkString* replacement)
+{
+    // TODO: std 5 entities here
+    return false;
+}
+
diff --git a/src/xml/SkXMLWriter.cpp b/src/xml/SkXMLWriter.cpp
new file mode 100644
index 0000000..4685d81
--- /dev/null
+++ b/src/xml/SkXMLWriter.cpp
@@ -0,0 +1,342 @@
+/* libs/graphics/xml/SkXMLWriter.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 "SkXMLWriter.h"
+#include "SkStream.h"
+
+SkXMLWriter::SkXMLWriter(bool doEscapeMarkup) : fDoEscapeMarkup(doEscapeMarkup)
+{
+}
+
+SkXMLWriter::~SkXMLWriter()
+{
+    SkASSERT(fElems.count() == 0);
+}
+
+void SkXMLWriter::flush()
+{
+    while (fElems.count())
+        this->endElement();
+}
+
+void SkXMLWriter::addAttribute(const char name[], const char value[])
+{
+    this->addAttributeLen(name, value, strlen(value));
+}
+
+void SkXMLWriter::addS32Attribute(const char name[], int32_t value)
+{
+    SkString    tmp;
+    tmp.appendS32(value);
+    this->addAttribute(name, tmp.c_str());
+}
+
+void SkXMLWriter::addHexAttribute(const char name[], uint32_t value, int minDigits)
+{
+    SkString    tmp("0x");
+    tmp.appendHex(value, minDigits);
+    this->addAttribute(name, tmp.c_str());
+}
+
+void SkXMLWriter::addScalarAttribute(const char name[], SkScalar value)
+{
+    SkString    tmp;
+    tmp.appendScalar(value);
+    this->addAttribute(name, tmp.c_str());
+}
+
+void SkXMLWriter::doEnd(Elem* elem)
+{
+    delete elem;
+}
+
+bool SkXMLWriter::doStart(const char name[], size_t length)
+{
+    int level = fElems.count();
+    bool firstChild = level > 0 && !fElems[level-1]->fHasChildren;
+    if (firstChild)
+        fElems[level-1]->fHasChildren = true;
+    Elem** elem = fElems.push();
+    *elem = new Elem;
+    (*elem)->fName.set(name, length);
+    (*elem)->fHasChildren = 0;
+    return firstChild;
+}
+
+SkXMLWriter::Elem* SkXMLWriter::getEnd() 
+{
+    Elem* elem;
+    fElems.pop(&elem);
+    return elem;
+}
+
+const char* SkXMLWriter::getHeader()
+{
+    static const char gHeader[] = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
+    return gHeader;
+}
+
+void SkXMLWriter::startElement(const char name[])
+{
+    this->startElementLen(name, strlen(name));
+}
+
+static const char* escape_char(char c, char storage[2])
+{
+    static const char* gEscapeChars[] = {
+        "<&lt;",
+        ">&gt;",
+        //"\"&quot;",
+        //"'&apos;",
+        "&&amp;"
+    };
+
+    const char** array = gEscapeChars;
+    for (unsigned i = 0; i < SK_ARRAY_COUNT(gEscapeChars); i++)
+    {
+        if (array[i][0] == c)
+            return &array[i][1];
+    }
+    storage[0] = c;
+    storage[1] = 0;
+    return storage;
+}
+
+static size_t escape_markup(char dst[], const char src[], size_t length)
+{
+    size_t      extra = 0;
+    const char* stop = src + length;
+
+    while (src < stop)
+    {
+        char        orig[2];
+        const char* seq = escape_char(*src, orig);
+        size_t      seqSize = strlen(seq);
+
+        if (dst)
+        {
+            memcpy(dst, seq, seqSize);
+            dst += seqSize;
+        }
+
+        // now record the extra size needed
+        extra += seqSize - 1;   // minus one to subtract the original char
+
+        // bump to the next src char
+        src += 1;
+    }
+    return extra;
+}
+
+void SkXMLWriter::addAttributeLen(const char name[], const char value[], size_t length)
+{
+    SkString valueStr;
+
+    if (fDoEscapeMarkup)
+    {
+        size_t   extra = escape_markup(NULL, value, length);
+        if (extra)
+        {
+            valueStr.resize(length + extra);
+            (void)escape_markup(valueStr.writable_str(), value, length);
+            value = valueStr.c_str();
+            length += extra;
+        }
+    }
+    this->onAddAttributeLen(name, value, length);
+}
+
+void SkXMLWriter::startElementLen(const char elem[], size_t length)
+{
+    this->onStartElementLen(elem, length);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+static void write_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLWriter* w, bool skipRoot)
+{
+    if (!skipRoot)
+    {
+        w->startElement(dom.getName(node));
+
+        SkDOM::AttrIter iter(dom, node);
+        const char* name;
+        const char* value;
+        while ((name = iter.next(&value)) != NULL)
+            w->addAttribute(name, value);
+    }
+
+    node = dom.getFirstChild(node, NULL);
+    while (node)
+    {
+        write_dom(dom, node, w, false);
+        node = dom.getNextSibling(node, NULL);
+    }
+
+    if (!skipRoot)
+        w->endElement();
+}
+
+void SkXMLWriter::writeDOM(const SkDOM& dom, const SkDOM::Node* node, bool skipRoot)
+{
+    if (node)
+        write_dom(dom, node, this, skipRoot);
+}
+
+void SkXMLWriter::writeHeader()
+{
+}
+
+// SkXMLStreamWriter
+
+static void tab(SkWStream& stream, int level)
+{
+    for (int i = 0; i < level; i++)
+        stream.writeText("\t");
+}
+
+SkXMLStreamWriter::SkXMLStreamWriter(SkWStream* stream) : fStream(*stream)
+{
+}
+
+SkXMLStreamWriter::~SkXMLStreamWriter()
+{
+    this->flush();
+}
+
+void SkXMLStreamWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
+{
+    SkASSERT(!fElems.top()->fHasChildren);
+    fStream.writeText(" ");
+    fStream.writeText(name);
+    fStream.writeText("=\"");
+    fStream.write(value, length);
+    fStream.writeText("\"");
+}
+
+void SkXMLStreamWriter::onEndElement()
+{
+    Elem* elem = getEnd();
+    if (elem->fHasChildren)
+    {
+        tab(fStream, fElems.count());
+        fStream.writeText("</");
+        fStream.writeText(elem->fName.c_str());
+        fStream.writeText(">");
+    }
+    else
+        fStream.writeText("/>");
+    fStream.newline();
+    doEnd(elem);
+}
+
+void SkXMLStreamWriter::onStartElementLen(const char name[], size_t length)
+{
+    int level = fElems.count();
+    if (this->doStart(name, length))
+    {
+        // the first child, need to close with >
+        fStream.writeText(">");
+        fStream.newline();
+    }
+
+    tab(fStream, level);
+    fStream.writeText("<");
+    fStream.write(name, length);
+}
+
+void SkXMLStreamWriter::writeHeader()
+{
+    const char* header = getHeader();
+    fStream.write(header, strlen(header));
+    fStream.newline();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkXMLParser.h"
+
+SkXMLParserWriter::SkXMLParserWriter(SkXMLParser* parser)
+    : SkXMLWriter(false), fParser(*parser)
+{
+}
+
+SkXMLParserWriter::~SkXMLParserWriter()
+{
+    this->flush();
+}
+
+void SkXMLParserWriter::onAddAttributeLen(const char name[], const char value[], size_t length)
+{
+    SkASSERT(fElems.count() == 0 || !fElems.top()->fHasChildren);
+    SkString str(value, length);
+    fParser.addAttribute(name, str.c_str());
+}
+
+void SkXMLParserWriter::onEndElement()
+{
+    Elem* elem = this->getEnd();
+    fParser.endElement(elem->fName.c_str());
+    this->doEnd(elem);
+}
+
+void SkXMLParserWriter::onStartElementLen(const char name[], size_t length)
+{
+    (void)this->doStart(name, length);
+    SkString str(name, length);
+    fParser.startElement(str.c_str());
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkXMLStreamWriter::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+    SkDebugWStream  s;
+    SkXMLStreamWriter       w(&s);
+
+    w.startElement("elem0");
+    w.addAttribute("hello", "world");
+    w.addS32Attribute("dec", 42);
+    w.addHexAttribute("hex", 0x42, 3);
+#ifdef SK_SCALAR_IS_FLOAT
+    w.addScalarAttribute("scalar", -4.2f);
+#endif
+    w.startElement("elem1");
+        w.endElement();
+        w.startElement("elem1");
+        w.addAttribute("name", "value");
+        w.endElement();
+        w.startElement("elem1");
+            w.startElement("elem2");
+                w.startElement("elem3");
+                w.addAttribute("name", "value");
+                w.endElement();
+            w.endElement();
+            w.startElement("elem2");
+            w.endElement();
+        w.endElement();
+    w.endElement();
+#endif
+}
+
+#endif
+
diff --git a/xcode/core/core.xcodeproj/project.pbxproj b/xcode/core/core.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..daa04a3
--- /dev/null
+++ b/xcode/core/core.xcodeproj/project.pbxproj
@@ -0,0 +1,671 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 42;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		005F256F0EF94F7900582A90 /* ARGB32_Clamp_Bilinear_BitmapShader.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F24F60EF94F7900582A90 /* ARGB32_Clamp_Bilinear_BitmapShader.h */; };
+		005F25700EF94F7900582A90 /* Sk64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F24F70EF94F7900582A90 /* Sk64.cpp */; };
+		005F25710EF94F7900582A90 /* SkAlphaRuns.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F24F80EF94F7900582A90 /* SkAlphaRuns.cpp */; };
+		005F25720EF94F7900582A90 /* SkAntiRun.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F24F90EF94F7900582A90 /* SkAntiRun.h */; };
+		005F25730EF94F7900582A90 /* SkAutoKern.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F24FA0EF94F7900582A90 /* SkAutoKern.h */; };
+		005F25740EF94F7900582A90 /* SkBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F24FB0EF94F7900582A90 /* SkBitmap.cpp */; };
+		005F25750EF94F7900582A90 /* SkBitmap_scroll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F24FC0EF94F7900582A90 /* SkBitmap_scroll.cpp */; };
+		005F25760EF94F7900582A90 /* SkBitmapProcShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F24FD0EF94F7900582A90 /* SkBitmapProcShader.cpp */; };
+		005F25770EF94F7900582A90 /* SkBitmapProcShader.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F24FE0EF94F7900582A90 /* SkBitmapProcShader.h */; };
+		005F25780EF94F7900582A90 /* SkBitmapProcState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F24FF0EF94F7900582A90 /* SkBitmapProcState.cpp */; };
+		005F25790EF94F7900582A90 /* SkBitmapProcState.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25000EF94F7900582A90 /* SkBitmapProcState.h */; };
+		005F257A0EF94F7900582A90 /* SkBitmapProcState_matrix.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25010EF94F7900582A90 /* SkBitmapProcState_matrix.h */; };
+		005F257B0EF94F7900582A90 /* SkBitmapProcState_matrixProcs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25020EF94F7900582A90 /* SkBitmapProcState_matrixProcs.cpp */; };
+		005F257C0EF94F7900582A90 /* SkBitmapProcState_sample.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25030EF94F7900582A90 /* SkBitmapProcState_sample.h */; };
+		005F257D0EF94F7900582A90 /* SkBitmapSampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25040EF94F7900582A90 /* SkBitmapSampler.cpp */; };
+		005F257E0EF94F7900582A90 /* SkBitmapSampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25050EF94F7900582A90 /* SkBitmapSampler.h */; };
+		005F257F0EF94F7900582A90 /* SkBitmapSamplerTemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25060EF94F7900582A90 /* SkBitmapSamplerTemplate.h */; };
+		005F25800EF94F7900582A90 /* SkBitmapShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25070EF94F7900582A90 /* SkBitmapShader.cpp */; };
+		005F25810EF94F7900582A90 /* SkBitmapShader.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25080EF94F7900582A90 /* SkBitmapShader.h */; };
+		005F25820EF94F7900582A90 /* SkBitmapShader16BilerpTemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25090EF94F7900582A90 /* SkBitmapShader16BilerpTemplate.h */; };
+		005F25830EF94F7900582A90 /* SkBitmapShaderTemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F250A0EF94F7900582A90 /* SkBitmapShaderTemplate.h */; };
+		005F25840EF94F7900582A90 /* SkBlitBWMaskTemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F250B0EF94F7900582A90 /* SkBlitBWMaskTemplate.h */; };
+		005F25850EF94F7900582A90 /* SkBlitRow.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F250C0EF94F7900582A90 /* SkBlitRow.h */; };
+		005F25860EF94F7900582A90 /* SkBlitRow_D16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F250D0EF94F7900582A90 /* SkBlitRow_D16.cpp */; };
+		005F25870EF94F7900582A90 /* SkBlitRow_D4444.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F250E0EF94F7900582A90 /* SkBlitRow_D4444.cpp */; };
+		005F25880EF94F7900582A90 /* SkBlitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F250F0EF94F7900582A90 /* SkBlitter.cpp */; };
+		005F25890EF94F7900582A90 /* SkBlitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25100EF94F7900582A90 /* SkBlitter.h */; };
+		005F258A0EF94F7900582A90 /* SkBlitter_4444.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25110EF94F7900582A90 /* SkBlitter_4444.cpp */; };
+		005F258B0EF94F7900582A90 /* SkBlitter_A1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25120EF94F7900582A90 /* SkBlitter_A1.cpp */; };
+		005F258C0EF94F7900582A90 /* SkBlitter_A8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25130EF94F7900582A90 /* SkBlitter_A8.cpp */; };
+		005F258D0EF94F7900582A90 /* SkBlitter_ARGB32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25140EF94F7900582A90 /* SkBlitter_ARGB32.cpp */; };
+		005F258E0EF94F7900582A90 /* SkBlitter_RGB16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25150EF94F7900582A90 /* SkBlitter_RGB16.cpp */; };
+		005F258F0EF94F7900582A90 /* SkBlitter_Sprite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25160EF94F7900582A90 /* SkBlitter_Sprite.cpp */; };
+		005F25900EF94F7900582A90 /* SkBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25170EF94F7900582A90 /* SkBuffer.cpp */; };
+		005F25910EF94F7900582A90 /* SkCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25180EF94F7900582A90 /* SkCanvas.cpp */; };
+		005F25920EF94F7900582A90 /* SkChunkAlloc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25190EF94F7900582A90 /* SkChunkAlloc.cpp */; };
+		005F25930EF94F7900582A90 /* SkColor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F251A0EF94F7900582A90 /* SkColor.cpp */; };
+		005F25940EF94F7900582A90 /* SkColorFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F251B0EF94F7900582A90 /* SkColorFilter.cpp */; };
+		005F25950EF94F7900582A90 /* SkColorTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F251C0EF94F7900582A90 /* SkColorTable.cpp */; };
+		005F25960EF94F7900582A90 /* SkCordic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F251D0EF94F7900582A90 /* SkCordic.cpp */; };
+		005F25970EF94F7900582A90 /* SkCordic.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F251E0EF94F7900582A90 /* SkCordic.h */; };
+		005F25980EF94F7900582A90 /* SkCoreBlitters.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F251F0EF94F7900582A90 /* SkCoreBlitters.h */; };
+		005F25990EF94F7900582A90 /* SkDebug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25200EF94F7900582A90 /* SkDebug.cpp */; };
+		005F259A0EF94F7900582A90 /* SkDebug_stdio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25210EF94F7900582A90 /* SkDebug_stdio.cpp */; };
+		005F259B0EF94F7900582A90 /* SkDeque.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25220EF94F7900582A90 /* SkDeque.cpp */; };
+		005F259C0EF94F7900582A90 /* SkDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25230EF94F7900582A90 /* SkDevice.cpp */; };
+		005F259D0EF94F7900582A90 /* SkDither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25240EF94F7900582A90 /* SkDither.cpp */; };
+		005F259E0EF94F7900582A90 /* SkDraw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25250EF94F7900582A90 /* SkDraw.cpp */; };
+		005F259F0EF94F7900582A90 /* SkDrawProcs.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25260EF94F7900582A90 /* SkDrawProcs.h */; };
+		005F25A00EF94F7900582A90 /* SkEdge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25270EF94F7900582A90 /* SkEdge.cpp */; };
+		005F25A10EF94F7900582A90 /* SkEdge.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25280EF94F7900582A90 /* SkEdge.h */; };
+		005F25A20EF94F7900582A90 /* SkFilterProc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25290EF94F7900582A90 /* SkFilterProc.cpp */; };
+		005F25A30EF94F7900582A90 /* SkFilterProc.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F252A0EF94F7900582A90 /* SkFilterProc.h */; };
+		005F25A40EF94F7900582A90 /* SkFlattenable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F252B0EF94F7900582A90 /* SkFlattenable.cpp */; };
+		005F25A50EF94F7900582A90 /* SkFloat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F252C0EF94F7900582A90 /* SkFloat.cpp */; };
+		005F25A60EF94F7900582A90 /* SkFloat.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F252D0EF94F7900582A90 /* SkFloat.h */; };
+		005F25A70EF94F7900582A90 /* SkFloatBits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F252E0EF94F7900582A90 /* SkFloatBits.cpp */; };
+		005F25A80EF94F7900582A90 /* SkFP.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F252F0EF94F7900582A90 /* SkFP.h */; };
+		005F25A90EF94F7900582A90 /* SkGeometry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25300EF94F7900582A90 /* SkGeometry.cpp */; };
+		005F25AA0EF94F7900582A90 /* SkGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25310EF94F7900582A90 /* SkGeometry.h */; };
+		005F25AB0EF94F7900582A90 /* SkGlobals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25320EF94F7900582A90 /* SkGlobals.cpp */; };
+		005F25AC0EF94F7900582A90 /* SkGlyphCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25330EF94F7900582A90 /* SkGlyphCache.cpp */; };
+		005F25AD0EF94F7900582A90 /* SkGlyphCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25340EF94F7900582A90 /* SkGlyphCache.h */; };
+		005F25AE0EF94F7900582A90 /* SkGraphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25350EF94F7900582A90 /* SkGraphics.cpp */; };
+		005F25B00EF94F7900582A90 /* SkMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25370EF94F7900582A90 /* SkMask.cpp */; };
+		005F25B10EF94F7900582A90 /* SkMaskFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25380EF94F7900582A90 /* SkMaskFilter.cpp */; };
+		005F25B20EF94F7900582A90 /* SkMath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25390EF94F7900582A90 /* SkMath.cpp */; };
+		005F25B30EF94F7900582A90 /* SkMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F253A0EF94F7900582A90 /* SkMatrix.cpp */; };
+		005F25B40EF94F7900582A90 /* SkMemory_stdlib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F253B0EF94F7900582A90 /* SkMemory_stdlib.cpp */; };
+		005F25B50EF94F7900582A90 /* SkPackBits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F253C0EF94F7900582A90 /* SkPackBits.cpp */; };
+		005F25B70EF94F7900582A90 /* SkPaint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F253E0EF94F7900582A90 /* SkPaint.cpp */; };
+		005F25B80EF94F7900582A90 /* SkPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F253F0EF94F7900582A90 /* SkPath.cpp */; };
+		005F25B90EF94F7900582A90 /* SkPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25400EF94F7900582A90 /* SkPathEffect.cpp */; };
+		005F25BA0EF94F7900582A90 /* SkPathHeap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25410EF94F7900582A90 /* SkPathHeap.cpp */; };
+		005F25BB0EF94F7900582A90 /* SkPathHeap.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25420EF94F7900582A90 /* SkPathHeap.h */; };
+		005F25BC0EF94F7900582A90 /* SkPathMeasure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25430EF94F7900582A90 /* SkPathMeasure.cpp */; };
+		005F25BD0EF94F7900582A90 /* SkPicture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25440EF94F7900582A90 /* SkPicture.cpp */; };
+		005F25BE0EF94F7900582A90 /* SkPictureFlat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25450EF94F7900582A90 /* SkPictureFlat.cpp */; };
+		005F25BF0EF94F7900582A90 /* SkPictureFlat.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25460EF94F7900582A90 /* SkPictureFlat.h */; };
+		005F25C00EF94F7900582A90 /* SkPicturePlayback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25470EF94F7900582A90 /* SkPicturePlayback.cpp */; };
+		005F25C10EF94F7900582A90 /* SkPicturePlayback.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25480EF94F7900582A90 /* SkPicturePlayback.h */; };
+		005F25C20EF94F7900582A90 /* SkPictureRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25490EF94F7900582A90 /* SkPictureRecord.cpp */; };
+		005F25C30EF94F7900582A90 /* SkPictureRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F254A0EF94F7900582A90 /* SkPictureRecord.h */; };
+		005F25C40EF94F7900582A90 /* SkPixelRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F254B0EF94F7900582A90 /* SkPixelRef.cpp */; };
+		005F25C50EF94F7900582A90 /* SkPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F254C0EF94F7900582A90 /* SkPoint.cpp */; };
+		005F25C60EF94F7900582A90 /* SkProcSpriteBlitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F254D0EF94F7900582A90 /* SkProcSpriteBlitter.cpp */; };
+		005F25C70EF94F7900582A90 /* SkPtrRecorder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F254E0EF94F7900582A90 /* SkPtrRecorder.cpp */; };
+		005F25C80EF94F7900582A90 /* SkRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F254F0EF94F7900582A90 /* SkRasterizer.cpp */; };
+		005F25C90EF94F7900582A90 /* SkRect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25500EF94F7900582A90 /* SkRect.cpp */; };
+		005F25CA0EF94F7900582A90 /* SkRefCnt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25510EF94F7900582A90 /* SkRefCnt.cpp */; };
+		005F25CB0EF94F7900582A90 /* SkRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25520EF94F7900582A90 /* SkRegion.cpp */; };
+		005F25CC0EF94F7900582A90 /* SkRegion_path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25530EF94F7900582A90 /* SkRegion_path.cpp */; };
+		005F25CD0EF94F7900582A90 /* SkRegionPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25540EF94F7900582A90 /* SkRegionPriv.h */; };
+		005F25CE0EF94F7900582A90 /* SkScalerContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25550EF94F7900582A90 /* SkScalerContext.cpp */; };
+		005F25CF0EF94F7900582A90 /* SkScan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25560EF94F7900582A90 /* SkScan.cpp */; };
+		005F25D00EF94F7900582A90 /* SkScan.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25570EF94F7900582A90 /* SkScan.h */; };
+		005F25D10EF94F7900582A90 /* SkScan_Antihair.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25580EF94F7900582A90 /* SkScan_Antihair.cpp */; };
+		005F25D20EF94F7900582A90 /* SkScan_AntiPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25590EF94F7900582A90 /* SkScan_AntiPath.cpp */; };
+		005F25D30EF94F7900582A90 /* SkScan_Hairline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F255A0EF94F7900582A90 /* SkScan_Hairline.cpp */; };
+		005F25D40EF94F7900582A90 /* SkScan_Path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F255B0EF94F7900582A90 /* SkScan_Path.cpp */; };
+		005F25D50EF94F7900582A90 /* SkScanPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F255C0EF94F7900582A90 /* SkScanPriv.h */; };
+		005F25D60EF94F7900582A90 /* SkShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F255D0EF94F7900582A90 /* SkShader.cpp */; };
+		005F25D70EF94F7900582A90 /* SkSinTable.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F255E0EF94F7900582A90 /* SkSinTable.h */; };
+		005F25D80EF94F7900582A90 /* SkSpriteBlitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F255F0EF94F7900582A90 /* SkSpriteBlitter.h */; };
+		005F25D90EF94F7900582A90 /* SkSpriteBlitter_ARGB32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25600EF94F7900582A90 /* SkSpriteBlitter_ARGB32.cpp */; };
+		005F25DA0EF94F7900582A90 /* SkSpriteBlitter_RGB16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25610EF94F7900582A90 /* SkSpriteBlitter_RGB16.cpp */; };
+		005F25DB0EF94F7900582A90 /* SkSpriteBlitterTemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25620EF94F7900582A90 /* SkSpriteBlitterTemplate.h */; };
+		005F25DC0EF94F7900582A90 /* SkString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25630EF94F7900582A90 /* SkString.cpp */; };
+		005F25DD0EF94F7900582A90 /* SkStroke.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25640EF94F7900582A90 /* SkStroke.cpp */; };
+		005F25DE0EF94F7900582A90 /* SkStrokerPriv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25650EF94F7900582A90 /* SkStrokerPriv.cpp */; };
+		005F25DF0EF94F7900582A90 /* SkStrokerPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25660EF94F7900582A90 /* SkStrokerPriv.h */; };
+		005F25E00EF94F7900582A90 /* SkTemplatesPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25670EF94F7900582A90 /* SkTemplatesPriv.h */; };
+		005F25E10EF94F7900582A90 /* SkTSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F25680EF94F7900582A90 /* SkTSearch.cpp */; };
+		005F25E20EF94F7900582A90 /* SkTSort.h in Headers */ = {isa = PBXBuildFile; fileRef = 005F25690EF94F7900582A90 /* SkTSort.h */; };
+		005F25E30EF94F7900582A90 /* SkTypeface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F256A0EF94F7900582A90 /* SkTypeface.cpp */; };
+		005F25E40EF94F7900582A90 /* SkUnPreMultiply.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F256B0EF94F7900582A90 /* SkUnPreMultiply.cpp */; };
+		005F25E50EF94F7900582A90 /* SkUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F256C0EF94F7900582A90 /* SkUtils.cpp */; };
+		005F25E60EF94F7900582A90 /* SkWriter32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F256D0EF94F7900582A90 /* SkWriter32.cpp */; };
+		005F25E70EF94F7900582A90 /* SkXfermode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F256E0EF94F7900582A90 /* SkXfermode.cpp */; };
+		005F26960EF955D400582A90 /* SkComposeShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F26950EF955D400582A90 /* SkComposeShader.cpp */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		005F24F60EF94F7900582A90 /* ARGB32_Clamp_Bilinear_BitmapShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARGB32_Clamp_Bilinear_BitmapShader.h; path = ../../src/core/ARGB32_Clamp_Bilinear_BitmapShader.h; sourceTree = SOURCE_ROOT; };
+		005F24F70EF94F7900582A90 /* Sk64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sk64.cpp; path = ../../src/core/Sk64.cpp; sourceTree = SOURCE_ROOT; };
+		005F24F80EF94F7900582A90 /* SkAlphaRuns.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkAlphaRuns.cpp; path = ../../src/core/SkAlphaRuns.cpp; sourceTree = SOURCE_ROOT; };
+		005F24F90EF94F7900582A90 /* SkAntiRun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkAntiRun.h; path = ../../src/core/SkAntiRun.h; sourceTree = SOURCE_ROOT; };
+		005F24FA0EF94F7900582A90 /* SkAutoKern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkAutoKern.h; path = ../../src/core/SkAutoKern.h; sourceTree = SOURCE_ROOT; };
+		005F24FB0EF94F7900582A90 /* SkBitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBitmap.cpp; path = ../../src/core/SkBitmap.cpp; sourceTree = SOURCE_ROOT; };
+		005F24FC0EF94F7900582A90 /* SkBitmap_scroll.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBitmap_scroll.cpp; path = ../../src/core/SkBitmap_scroll.cpp; sourceTree = SOURCE_ROOT; };
+		005F24FD0EF94F7900582A90 /* SkBitmapProcShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBitmapProcShader.cpp; path = ../../src/core/SkBitmapProcShader.cpp; sourceTree = SOURCE_ROOT; };
+		005F24FE0EF94F7900582A90 /* SkBitmapProcShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitmapProcShader.h; path = ../../src/core/SkBitmapProcShader.h; sourceTree = SOURCE_ROOT; };
+		005F24FF0EF94F7900582A90 /* SkBitmapProcState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBitmapProcState.cpp; path = ../../src/core/SkBitmapProcState.cpp; sourceTree = SOURCE_ROOT; };
+		005F25000EF94F7900582A90 /* SkBitmapProcState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitmapProcState.h; path = ../../src/core/SkBitmapProcState.h; sourceTree = SOURCE_ROOT; };
+		005F25010EF94F7900582A90 /* SkBitmapProcState_matrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitmapProcState_matrix.h; path = ../../src/core/SkBitmapProcState_matrix.h; sourceTree = SOURCE_ROOT; };
+		005F25020EF94F7900582A90 /* SkBitmapProcState_matrixProcs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBitmapProcState_matrixProcs.cpp; path = ../../src/core/SkBitmapProcState_matrixProcs.cpp; sourceTree = SOURCE_ROOT; };
+		005F25030EF94F7900582A90 /* SkBitmapProcState_sample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitmapProcState_sample.h; path = ../../src/core/SkBitmapProcState_sample.h; sourceTree = SOURCE_ROOT; };
+		005F25040EF94F7900582A90 /* SkBitmapSampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBitmapSampler.cpp; path = ../../src/core/SkBitmapSampler.cpp; sourceTree = SOURCE_ROOT; };
+		005F25050EF94F7900582A90 /* SkBitmapSampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitmapSampler.h; path = ../../src/core/SkBitmapSampler.h; sourceTree = SOURCE_ROOT; };
+		005F25060EF94F7900582A90 /* SkBitmapSamplerTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitmapSamplerTemplate.h; path = ../../src/core/SkBitmapSamplerTemplate.h; sourceTree = SOURCE_ROOT; };
+		005F25070EF94F7900582A90 /* SkBitmapShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBitmapShader.cpp; path = ../../src/core/SkBitmapShader.cpp; sourceTree = SOURCE_ROOT; };
+		005F25080EF94F7900582A90 /* SkBitmapShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitmapShader.h; path = ../../src/core/SkBitmapShader.h; sourceTree = SOURCE_ROOT; };
+		005F25090EF94F7900582A90 /* SkBitmapShader16BilerpTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitmapShader16BilerpTemplate.h; path = ../../src/core/SkBitmapShader16BilerpTemplate.h; sourceTree = SOURCE_ROOT; };
+		005F250A0EF94F7900582A90 /* SkBitmapShaderTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitmapShaderTemplate.h; path = ../../src/core/SkBitmapShaderTemplate.h; sourceTree = SOURCE_ROOT; };
+		005F250B0EF94F7900582A90 /* SkBlitBWMaskTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBlitBWMaskTemplate.h; path = ../../src/core/SkBlitBWMaskTemplate.h; sourceTree = SOURCE_ROOT; };
+		005F250C0EF94F7900582A90 /* SkBlitRow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBlitRow.h; path = ../../src/core/SkBlitRow.h; sourceTree = SOURCE_ROOT; };
+		005F250D0EF94F7900582A90 /* SkBlitRow_D16.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitRow_D16.cpp; path = ../../src/core/SkBlitRow_D16.cpp; sourceTree = SOURCE_ROOT; };
+		005F250E0EF94F7900582A90 /* SkBlitRow_D4444.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitRow_D4444.cpp; path = ../../src/core/SkBlitRow_D4444.cpp; sourceTree = SOURCE_ROOT; };
+		005F250F0EF94F7900582A90 /* SkBlitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitter.cpp; path = ../../src/core/SkBlitter.cpp; sourceTree = SOURCE_ROOT; };
+		005F25100EF94F7900582A90 /* SkBlitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBlitter.h; path = ../../src/core/SkBlitter.h; sourceTree = SOURCE_ROOT; };
+		005F25110EF94F7900582A90 /* SkBlitter_4444.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitter_4444.cpp; path = ../../src/core/SkBlitter_4444.cpp; sourceTree = SOURCE_ROOT; };
+		005F25120EF94F7900582A90 /* SkBlitter_A1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitter_A1.cpp; path = ../../src/core/SkBlitter_A1.cpp; sourceTree = SOURCE_ROOT; };
+		005F25130EF94F7900582A90 /* SkBlitter_A8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitter_A8.cpp; path = ../../src/core/SkBlitter_A8.cpp; sourceTree = SOURCE_ROOT; };
+		005F25140EF94F7900582A90 /* SkBlitter_ARGB32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitter_ARGB32.cpp; path = ../../src/core/SkBlitter_ARGB32.cpp; sourceTree = SOURCE_ROOT; };
+		005F25150EF94F7900582A90 /* SkBlitter_RGB16.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitter_RGB16.cpp; path = ../../src/core/SkBlitter_RGB16.cpp; sourceTree = SOURCE_ROOT; };
+		005F25160EF94F7900582A90 /* SkBlitter_Sprite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitter_Sprite.cpp; path = ../../src/core/SkBlitter_Sprite.cpp; sourceTree = SOURCE_ROOT; };
+		005F25170EF94F7900582A90 /* SkBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBuffer.cpp; path = ../../src/core/SkBuffer.cpp; sourceTree = SOURCE_ROOT; };
+		005F25180EF94F7900582A90 /* SkCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkCanvas.cpp; path = ../../src/core/SkCanvas.cpp; sourceTree = SOURCE_ROOT; };
+		005F25190EF94F7900582A90 /* SkChunkAlloc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkChunkAlloc.cpp; path = ../../src/core/SkChunkAlloc.cpp; sourceTree = SOURCE_ROOT; };
+		005F251A0EF94F7900582A90 /* SkColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkColor.cpp; path = ../../src/core/SkColor.cpp; sourceTree = SOURCE_ROOT; };
+		005F251B0EF94F7900582A90 /* SkColorFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkColorFilter.cpp; path = ../../src/core/SkColorFilter.cpp; sourceTree = SOURCE_ROOT; };
+		005F251C0EF94F7900582A90 /* SkColorTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkColorTable.cpp; path = ../../src/core/SkColorTable.cpp; sourceTree = SOURCE_ROOT; };
+		005F251D0EF94F7900582A90 /* SkCordic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkCordic.cpp; path = ../../src/core/SkCordic.cpp; sourceTree = SOURCE_ROOT; };
+		005F251E0EF94F7900582A90 /* SkCordic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkCordic.h; path = ../../src/core/SkCordic.h; sourceTree = SOURCE_ROOT; };
+		005F251F0EF94F7900582A90 /* SkCoreBlitters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkCoreBlitters.h; path = ../../src/core/SkCoreBlitters.h; sourceTree = SOURCE_ROOT; };
+		005F25200EF94F7900582A90 /* SkDebug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkDebug.cpp; path = ../../src/core/SkDebug.cpp; sourceTree = SOURCE_ROOT; };
+		005F25210EF94F7900582A90 /* SkDebug_stdio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkDebug_stdio.cpp; path = ../../src/core/SkDebug_stdio.cpp; sourceTree = SOURCE_ROOT; };
+		005F25220EF94F7900582A90 /* SkDeque.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkDeque.cpp; path = ../../src/core/SkDeque.cpp; sourceTree = SOURCE_ROOT; };
+		005F25230EF94F7900582A90 /* SkDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkDevice.cpp; path = ../../src/core/SkDevice.cpp; sourceTree = SOURCE_ROOT; };
+		005F25240EF94F7900582A90 /* SkDither.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkDither.cpp; path = ../../src/core/SkDither.cpp; sourceTree = SOURCE_ROOT; };
+		005F25250EF94F7900582A90 /* SkDraw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkDraw.cpp; path = ../../src/core/SkDraw.cpp; sourceTree = SOURCE_ROOT; };
+		005F25260EF94F7900582A90 /* SkDrawProcs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDrawProcs.h; path = ../../src/core/SkDrawProcs.h; sourceTree = SOURCE_ROOT; };
+		005F25270EF94F7900582A90 /* SkEdge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkEdge.cpp; path = ../../src/core/SkEdge.cpp; sourceTree = SOURCE_ROOT; };
+		005F25280EF94F7900582A90 /* SkEdge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkEdge.h; path = ../../src/core/SkEdge.h; sourceTree = SOURCE_ROOT; };
+		005F25290EF94F7900582A90 /* SkFilterProc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkFilterProc.cpp; path = ../../src/core/SkFilterProc.cpp; sourceTree = SOURCE_ROOT; };
+		005F252A0EF94F7900582A90 /* SkFilterProc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFilterProc.h; path = ../../src/core/SkFilterProc.h; sourceTree = SOURCE_ROOT; };
+		005F252B0EF94F7900582A90 /* SkFlattenable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkFlattenable.cpp; path = ../../src/core/SkFlattenable.cpp; sourceTree = SOURCE_ROOT; };
+		005F252C0EF94F7900582A90 /* SkFloat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkFloat.cpp; path = ../../src/core/SkFloat.cpp; sourceTree = SOURCE_ROOT; };
+		005F252D0EF94F7900582A90 /* SkFloat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFloat.h; path = ../../src/core/SkFloat.h; sourceTree = SOURCE_ROOT; };
+		005F252E0EF94F7900582A90 /* SkFloatBits.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkFloatBits.cpp; path = ../../src/core/SkFloatBits.cpp; sourceTree = SOURCE_ROOT; };
+		005F252F0EF94F7900582A90 /* SkFP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFP.h; path = ../../src/core/SkFP.h; sourceTree = SOURCE_ROOT; };
+		005F25300EF94F7900582A90 /* SkGeometry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkGeometry.cpp; path = ../../src/core/SkGeometry.cpp; sourceTree = SOURCE_ROOT; };
+		005F25310EF94F7900582A90 /* SkGeometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkGeometry.h; path = ../../src/core/SkGeometry.h; sourceTree = SOURCE_ROOT; };
+		005F25320EF94F7900582A90 /* SkGlobals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkGlobals.cpp; path = ../../src/core/SkGlobals.cpp; sourceTree = SOURCE_ROOT; };
+		005F25330EF94F7900582A90 /* SkGlyphCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkGlyphCache.cpp; path = ../../src/core/SkGlyphCache.cpp; sourceTree = SOURCE_ROOT; };
+		005F25340EF94F7900582A90 /* SkGlyphCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkGlyphCache.h; path = ../../src/core/SkGlyphCache.h; sourceTree = SOURCE_ROOT; };
+		005F25350EF94F7900582A90 /* SkGraphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkGraphics.cpp; path = ../../src/core/SkGraphics.cpp; sourceTree = SOURCE_ROOT; };
+		005F25370EF94F7900582A90 /* SkMask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkMask.cpp; path = ../../src/core/SkMask.cpp; sourceTree = SOURCE_ROOT; };
+		005F25380EF94F7900582A90 /* SkMaskFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkMaskFilter.cpp; path = ../../src/core/SkMaskFilter.cpp; sourceTree = SOURCE_ROOT; };
+		005F25390EF94F7900582A90 /* SkMath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkMath.cpp; path = ../../src/core/SkMath.cpp; sourceTree = SOURCE_ROOT; };
+		005F253A0EF94F7900582A90 /* SkMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkMatrix.cpp; path = ../../src/core/SkMatrix.cpp; sourceTree = SOURCE_ROOT; };
+		005F253B0EF94F7900582A90 /* SkMemory_stdlib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkMemory_stdlib.cpp; path = ../../src/core/SkMemory_stdlib.cpp; sourceTree = SOURCE_ROOT; };
+		005F253C0EF94F7900582A90 /* SkPackBits.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPackBits.cpp; path = ../../src/core/SkPackBits.cpp; sourceTree = SOURCE_ROOT; };
+		005F253E0EF94F7900582A90 /* SkPaint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPaint.cpp; path = ../../src/core/SkPaint.cpp; sourceTree = SOURCE_ROOT; };
+		005F253F0EF94F7900582A90 /* SkPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPath.cpp; path = ../../src/core/SkPath.cpp; sourceTree = SOURCE_ROOT; };
+		005F25400EF94F7900582A90 /* SkPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPathEffect.cpp; path = ../../src/core/SkPathEffect.cpp; sourceTree = SOURCE_ROOT; };
+		005F25410EF94F7900582A90 /* SkPathHeap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPathHeap.cpp; path = ../../src/core/SkPathHeap.cpp; sourceTree = SOURCE_ROOT; };
+		005F25420EF94F7900582A90 /* SkPathHeap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPathHeap.h; path = ../../src/core/SkPathHeap.h; sourceTree = SOURCE_ROOT; };
+		005F25430EF94F7900582A90 /* SkPathMeasure.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPathMeasure.cpp; path = ../../src/core/SkPathMeasure.cpp; sourceTree = SOURCE_ROOT; };
+		005F25440EF94F7900582A90 /* SkPicture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPicture.cpp; path = ../../src/core/SkPicture.cpp; sourceTree = SOURCE_ROOT; };
+		005F25450EF94F7900582A90 /* SkPictureFlat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPictureFlat.cpp; path = ../../src/core/SkPictureFlat.cpp; sourceTree = SOURCE_ROOT; };
+		005F25460EF94F7900582A90 /* SkPictureFlat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPictureFlat.h; path = ../../src/core/SkPictureFlat.h; sourceTree = SOURCE_ROOT; };
+		005F25470EF94F7900582A90 /* SkPicturePlayback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPicturePlayback.cpp; path = ../../src/core/SkPicturePlayback.cpp; sourceTree = SOURCE_ROOT; };
+		005F25480EF94F7900582A90 /* SkPicturePlayback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPicturePlayback.h; path = ../../src/core/SkPicturePlayback.h; sourceTree = SOURCE_ROOT; };
+		005F25490EF94F7900582A90 /* SkPictureRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPictureRecord.cpp; path = ../../src/core/SkPictureRecord.cpp; sourceTree = SOURCE_ROOT; };
+		005F254A0EF94F7900582A90 /* SkPictureRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPictureRecord.h; path = ../../src/core/SkPictureRecord.h; sourceTree = SOURCE_ROOT; };
+		005F254B0EF94F7900582A90 /* SkPixelRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPixelRef.cpp; path = ../../src/core/SkPixelRef.cpp; sourceTree = SOURCE_ROOT; };
+		005F254C0EF94F7900582A90 /* SkPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPoint.cpp; path = ../../src/core/SkPoint.cpp; sourceTree = SOURCE_ROOT; };
+		005F254D0EF94F7900582A90 /* SkProcSpriteBlitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkProcSpriteBlitter.cpp; path = ../../src/core/SkProcSpriteBlitter.cpp; sourceTree = SOURCE_ROOT; };
+		005F254E0EF94F7900582A90 /* SkPtrRecorder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPtrRecorder.cpp; path = ../../src/core/SkPtrRecorder.cpp; sourceTree = SOURCE_ROOT; };
+		005F254F0EF94F7900582A90 /* SkRasterizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkRasterizer.cpp; path = ../../src/core/SkRasterizer.cpp; sourceTree = SOURCE_ROOT; };
+		005F25500EF94F7900582A90 /* SkRect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkRect.cpp; path = ../../src/core/SkRect.cpp; sourceTree = SOURCE_ROOT; };
+		005F25510EF94F7900582A90 /* SkRefCnt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkRefCnt.cpp; path = ../../src/core/SkRefCnt.cpp; sourceTree = SOURCE_ROOT; };
+		005F25520EF94F7900582A90 /* SkRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkRegion.cpp; path = ../../src/core/SkRegion.cpp; sourceTree = SOURCE_ROOT; };
+		005F25530EF94F7900582A90 /* SkRegion_path.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkRegion_path.cpp; path = ../../src/core/SkRegion_path.cpp; sourceTree = SOURCE_ROOT; };
+		005F25540EF94F7900582A90 /* SkRegionPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRegionPriv.h; path = ../../src/core/SkRegionPriv.h; sourceTree = SOURCE_ROOT; };
+		005F25550EF94F7900582A90 /* SkScalerContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkScalerContext.cpp; path = ../../src/core/SkScalerContext.cpp; sourceTree = SOURCE_ROOT; };
+		005F25560EF94F7900582A90 /* SkScan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkScan.cpp; path = ../../src/core/SkScan.cpp; sourceTree = SOURCE_ROOT; };
+		005F25570EF94F7900582A90 /* SkScan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkScan.h; path = ../../src/core/SkScan.h; sourceTree = SOURCE_ROOT; };
+		005F25580EF94F7900582A90 /* SkScan_Antihair.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkScan_Antihair.cpp; path = ../../src/core/SkScan_Antihair.cpp; sourceTree = SOURCE_ROOT; };
+		005F25590EF94F7900582A90 /* SkScan_AntiPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkScan_AntiPath.cpp; path = ../../src/core/SkScan_AntiPath.cpp; sourceTree = SOURCE_ROOT; };
+		005F255A0EF94F7900582A90 /* SkScan_Hairline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkScan_Hairline.cpp; path = ../../src/core/SkScan_Hairline.cpp; sourceTree = SOURCE_ROOT; };
+		005F255B0EF94F7900582A90 /* SkScan_Path.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkScan_Path.cpp; path = ../../src/core/SkScan_Path.cpp; sourceTree = SOURCE_ROOT; };
+		005F255C0EF94F7900582A90 /* SkScanPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkScanPriv.h; path = ../../src/core/SkScanPriv.h; sourceTree = SOURCE_ROOT; };
+		005F255D0EF94F7900582A90 /* SkShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkShader.cpp; path = ../../src/core/SkShader.cpp; sourceTree = SOURCE_ROOT; };
+		005F255E0EF94F7900582A90 /* SkSinTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkSinTable.h; path = ../../src/core/SkSinTable.h; sourceTree = SOURCE_ROOT; };
+		005F255F0EF94F7900582A90 /* SkSpriteBlitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkSpriteBlitter.h; path = ../../src/core/SkSpriteBlitter.h; sourceTree = SOURCE_ROOT; };
+		005F25600EF94F7900582A90 /* SkSpriteBlitter_ARGB32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkSpriteBlitter_ARGB32.cpp; path = ../../src/core/SkSpriteBlitter_ARGB32.cpp; sourceTree = SOURCE_ROOT; };
+		005F25610EF94F7900582A90 /* SkSpriteBlitter_RGB16.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkSpriteBlitter_RGB16.cpp; path = ../../src/core/SkSpriteBlitter_RGB16.cpp; sourceTree = SOURCE_ROOT; };
+		005F25620EF94F7900582A90 /* SkSpriteBlitterTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkSpriteBlitterTemplate.h; path = ../../src/core/SkSpriteBlitterTemplate.h; sourceTree = SOURCE_ROOT; };
+		005F25630EF94F7900582A90 /* SkString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkString.cpp; path = ../../src/core/SkString.cpp; sourceTree = SOURCE_ROOT; };
+		005F25640EF94F7900582A90 /* SkStroke.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkStroke.cpp; path = ../../src/core/SkStroke.cpp; sourceTree = SOURCE_ROOT; };
+		005F25650EF94F7900582A90 /* SkStrokerPriv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkStrokerPriv.cpp; path = ../../src/core/SkStrokerPriv.cpp; sourceTree = SOURCE_ROOT; };
+		005F25660EF94F7900582A90 /* SkStrokerPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkStrokerPriv.h; path = ../../src/core/SkStrokerPriv.h; sourceTree = SOURCE_ROOT; };
+		005F25670EF94F7900582A90 /* SkTemplatesPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTemplatesPriv.h; path = ../../src/core/SkTemplatesPriv.h; sourceTree = SOURCE_ROOT; };
+		005F25680EF94F7900582A90 /* SkTSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkTSearch.cpp; path = ../../src/core/SkTSearch.cpp; sourceTree = SOURCE_ROOT; };
+		005F25690EF94F7900582A90 /* SkTSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTSort.h; path = ../../src/core/SkTSort.h; sourceTree = SOURCE_ROOT; };
+		005F256A0EF94F7900582A90 /* SkTypeface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkTypeface.cpp; path = ../../src/core/SkTypeface.cpp; sourceTree = SOURCE_ROOT; };
+		005F256B0EF94F7900582A90 /* SkUnPreMultiply.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkUnPreMultiply.cpp; path = ../../src/core/SkUnPreMultiply.cpp; sourceTree = SOURCE_ROOT; };
+		005F256C0EF94F7900582A90 /* SkUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkUtils.cpp; path = ../../src/core/SkUtils.cpp; sourceTree = SOURCE_ROOT; };
+		005F256D0EF94F7900582A90 /* SkWriter32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkWriter32.cpp; path = ../../src/core/SkWriter32.cpp; sourceTree = SOURCE_ROOT; };
+		005F256E0EF94F7900582A90 /* SkXfermode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkXfermode.cpp; path = ../../src/core/SkXfermode.cpp; sourceTree = SOURCE_ROOT; };
+		005F26950EF955D400582A90 /* SkComposeShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkComposeShader.cpp; path = ../../src/core/SkComposeShader.cpp; sourceTree = SOURCE_ROOT; };
+		D2AAC046055464E500DB518D /* libcore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcore.a; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		D289987405E68DCB004EDB86 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		08FB7794FE84155DC02AAC07 /* core */ = {
+			isa = PBXGroup;
+			children = (
+				08FB7795FE84155DC02AAC07 /* src */,
+				C6A0FF2B0290797F04C91782 /* Documentation */,
+				1AB674ADFE9D54B511CA2CBB /* Products */,
+			);
+			name = core;
+			sourceTree = "<group>";
+		};
+		08FB7795FE84155DC02AAC07 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				005F26950EF955D400582A90 /* SkComposeShader.cpp */,
+				005F24F60EF94F7900582A90 /* ARGB32_Clamp_Bilinear_BitmapShader.h */,
+				005F24F70EF94F7900582A90 /* Sk64.cpp */,
+				005F24F80EF94F7900582A90 /* SkAlphaRuns.cpp */,
+				005F24F90EF94F7900582A90 /* SkAntiRun.h */,
+				005F24FA0EF94F7900582A90 /* SkAutoKern.h */,
+				005F24FB0EF94F7900582A90 /* SkBitmap.cpp */,
+				005F24FC0EF94F7900582A90 /* SkBitmap_scroll.cpp */,
+				005F24FD0EF94F7900582A90 /* SkBitmapProcShader.cpp */,
+				005F24FE0EF94F7900582A90 /* SkBitmapProcShader.h */,
+				005F24FF0EF94F7900582A90 /* SkBitmapProcState.cpp */,
+				005F25000EF94F7900582A90 /* SkBitmapProcState.h */,
+				005F25010EF94F7900582A90 /* SkBitmapProcState_matrix.h */,
+				005F25020EF94F7900582A90 /* SkBitmapProcState_matrixProcs.cpp */,
+				005F25030EF94F7900582A90 /* SkBitmapProcState_sample.h */,
+				005F25040EF94F7900582A90 /* SkBitmapSampler.cpp */,
+				005F25050EF94F7900582A90 /* SkBitmapSampler.h */,
+				005F25060EF94F7900582A90 /* SkBitmapSamplerTemplate.h */,
+				005F25070EF94F7900582A90 /* SkBitmapShader.cpp */,
+				005F25080EF94F7900582A90 /* SkBitmapShader.h */,
+				005F25090EF94F7900582A90 /* SkBitmapShader16BilerpTemplate.h */,
+				005F250A0EF94F7900582A90 /* SkBitmapShaderTemplate.h */,
+				005F250B0EF94F7900582A90 /* SkBlitBWMaskTemplate.h */,
+				005F250C0EF94F7900582A90 /* SkBlitRow.h */,
+				005F250D0EF94F7900582A90 /* SkBlitRow_D16.cpp */,
+				005F250E0EF94F7900582A90 /* SkBlitRow_D4444.cpp */,
+				005F250F0EF94F7900582A90 /* SkBlitter.cpp */,
+				005F25100EF94F7900582A90 /* SkBlitter.h */,
+				005F25110EF94F7900582A90 /* SkBlitter_4444.cpp */,
+				005F25120EF94F7900582A90 /* SkBlitter_A1.cpp */,
+				005F25130EF94F7900582A90 /* SkBlitter_A8.cpp */,
+				005F25140EF94F7900582A90 /* SkBlitter_ARGB32.cpp */,
+				005F25150EF94F7900582A90 /* SkBlitter_RGB16.cpp */,
+				005F25160EF94F7900582A90 /* SkBlitter_Sprite.cpp */,
+				005F25170EF94F7900582A90 /* SkBuffer.cpp */,
+				005F25180EF94F7900582A90 /* SkCanvas.cpp */,
+				005F25190EF94F7900582A90 /* SkChunkAlloc.cpp */,
+				005F251A0EF94F7900582A90 /* SkColor.cpp */,
+				005F251B0EF94F7900582A90 /* SkColorFilter.cpp */,
+				005F251C0EF94F7900582A90 /* SkColorTable.cpp */,
+				005F251D0EF94F7900582A90 /* SkCordic.cpp */,
+				005F251E0EF94F7900582A90 /* SkCordic.h */,
+				005F251F0EF94F7900582A90 /* SkCoreBlitters.h */,
+				005F25200EF94F7900582A90 /* SkDebug.cpp */,
+				005F25210EF94F7900582A90 /* SkDebug_stdio.cpp */,
+				005F25220EF94F7900582A90 /* SkDeque.cpp */,
+				005F25230EF94F7900582A90 /* SkDevice.cpp */,
+				005F25240EF94F7900582A90 /* SkDither.cpp */,
+				005F25250EF94F7900582A90 /* SkDraw.cpp */,
+				005F25260EF94F7900582A90 /* SkDrawProcs.h */,
+				005F25270EF94F7900582A90 /* SkEdge.cpp */,
+				005F25280EF94F7900582A90 /* SkEdge.h */,
+				005F25290EF94F7900582A90 /* SkFilterProc.cpp */,
+				005F252A0EF94F7900582A90 /* SkFilterProc.h */,
+				005F252B0EF94F7900582A90 /* SkFlattenable.cpp */,
+				005F252C0EF94F7900582A90 /* SkFloat.cpp */,
+				005F252D0EF94F7900582A90 /* SkFloat.h */,
+				005F252E0EF94F7900582A90 /* SkFloatBits.cpp */,
+				005F252F0EF94F7900582A90 /* SkFP.h */,
+				005F25300EF94F7900582A90 /* SkGeometry.cpp */,
+				005F25310EF94F7900582A90 /* SkGeometry.h */,
+				005F25320EF94F7900582A90 /* SkGlobals.cpp */,
+				005F25330EF94F7900582A90 /* SkGlyphCache.cpp */,
+				005F25340EF94F7900582A90 /* SkGlyphCache.h */,
+				005F25350EF94F7900582A90 /* SkGraphics.cpp */,
+				005F25370EF94F7900582A90 /* SkMask.cpp */,
+				005F25380EF94F7900582A90 /* SkMaskFilter.cpp */,
+				005F25390EF94F7900582A90 /* SkMath.cpp */,
+				005F253A0EF94F7900582A90 /* SkMatrix.cpp */,
+				005F253B0EF94F7900582A90 /* SkMemory_stdlib.cpp */,
+				005F253C0EF94F7900582A90 /* SkPackBits.cpp */,
+				005F253E0EF94F7900582A90 /* SkPaint.cpp */,
+				005F253F0EF94F7900582A90 /* SkPath.cpp */,
+				005F25400EF94F7900582A90 /* SkPathEffect.cpp */,
+				005F25410EF94F7900582A90 /* SkPathHeap.cpp */,
+				005F25420EF94F7900582A90 /* SkPathHeap.h */,
+				005F25430EF94F7900582A90 /* SkPathMeasure.cpp */,
+				005F25440EF94F7900582A90 /* SkPicture.cpp */,
+				005F25450EF94F7900582A90 /* SkPictureFlat.cpp */,
+				005F25460EF94F7900582A90 /* SkPictureFlat.h */,
+				005F25470EF94F7900582A90 /* SkPicturePlayback.cpp */,
+				005F25480EF94F7900582A90 /* SkPicturePlayback.h */,
+				005F25490EF94F7900582A90 /* SkPictureRecord.cpp */,
+				005F254A0EF94F7900582A90 /* SkPictureRecord.h */,
+				005F254B0EF94F7900582A90 /* SkPixelRef.cpp */,
+				005F254C0EF94F7900582A90 /* SkPoint.cpp */,
+				005F254D0EF94F7900582A90 /* SkProcSpriteBlitter.cpp */,
+				005F254E0EF94F7900582A90 /* SkPtrRecorder.cpp */,
+				005F254F0EF94F7900582A90 /* SkRasterizer.cpp */,
+				005F25500EF94F7900582A90 /* SkRect.cpp */,
+				005F25510EF94F7900582A90 /* SkRefCnt.cpp */,
+				005F25520EF94F7900582A90 /* SkRegion.cpp */,
+				005F25530EF94F7900582A90 /* SkRegion_path.cpp */,
+				005F25540EF94F7900582A90 /* SkRegionPriv.h */,
+				005F25550EF94F7900582A90 /* SkScalerContext.cpp */,
+				005F25560EF94F7900582A90 /* SkScan.cpp */,
+				005F25570EF94F7900582A90 /* SkScan.h */,
+				005F25580EF94F7900582A90 /* SkScan_Antihair.cpp */,
+				005F25590EF94F7900582A90 /* SkScan_AntiPath.cpp */,
+				005F255A0EF94F7900582A90 /* SkScan_Hairline.cpp */,
+				005F255B0EF94F7900582A90 /* SkScan_Path.cpp */,
+				005F255C0EF94F7900582A90 /* SkScanPriv.h */,
+				005F255D0EF94F7900582A90 /* SkShader.cpp */,
+				005F255E0EF94F7900582A90 /* SkSinTable.h */,
+				005F255F0EF94F7900582A90 /* SkSpriteBlitter.h */,
+				005F25600EF94F7900582A90 /* SkSpriteBlitter_ARGB32.cpp */,
+				005F25610EF94F7900582A90 /* SkSpriteBlitter_RGB16.cpp */,
+				005F25620EF94F7900582A90 /* SkSpriteBlitterTemplate.h */,
+				005F25630EF94F7900582A90 /* SkString.cpp */,
+				005F25640EF94F7900582A90 /* SkStroke.cpp */,
+				005F25650EF94F7900582A90 /* SkStrokerPriv.cpp */,
+				005F25660EF94F7900582A90 /* SkStrokerPriv.h */,
+				005F25670EF94F7900582A90 /* SkTemplatesPriv.h */,
+				005F25680EF94F7900582A90 /* SkTSearch.cpp */,
+				005F25690EF94F7900582A90 /* SkTSort.h */,
+				005F256A0EF94F7900582A90 /* SkTypeface.cpp */,
+				005F256B0EF94F7900582A90 /* SkUnPreMultiply.cpp */,
+				005F256C0EF94F7900582A90 /* SkUtils.cpp */,
+				005F256D0EF94F7900582A90 /* SkWriter32.cpp */,
+				005F256E0EF94F7900582A90 /* SkXfermode.cpp */,
+			);
+			name = src;
+			sourceTree = "<group>";
+		};
+		1AB674ADFE9D54B511CA2CBB /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				D2AAC046055464E500DB518D /* libcore.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		C6A0FF2B0290797F04C91782 /* Documentation */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			name = Documentation;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		D2AAC043055464E500DB518D /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				005F256F0EF94F7900582A90 /* ARGB32_Clamp_Bilinear_BitmapShader.h in Headers */,
+				005F25720EF94F7900582A90 /* SkAntiRun.h in Headers */,
+				005F25730EF94F7900582A90 /* SkAutoKern.h in Headers */,
+				005F25770EF94F7900582A90 /* SkBitmapProcShader.h in Headers */,
+				005F25790EF94F7900582A90 /* SkBitmapProcState.h in Headers */,
+				005F257A0EF94F7900582A90 /* SkBitmapProcState_matrix.h in Headers */,
+				005F257C0EF94F7900582A90 /* SkBitmapProcState_sample.h in Headers */,
+				005F257E0EF94F7900582A90 /* SkBitmapSampler.h in Headers */,
+				005F257F0EF94F7900582A90 /* SkBitmapSamplerTemplate.h in Headers */,
+				005F25810EF94F7900582A90 /* SkBitmapShader.h in Headers */,
+				005F25820EF94F7900582A90 /* SkBitmapShader16BilerpTemplate.h in Headers */,
+				005F25830EF94F7900582A90 /* SkBitmapShaderTemplate.h in Headers */,
+				005F25840EF94F7900582A90 /* SkBlitBWMaskTemplate.h in Headers */,
+				005F25850EF94F7900582A90 /* SkBlitRow.h in Headers */,
+				005F25890EF94F7900582A90 /* SkBlitter.h in Headers */,
+				005F25970EF94F7900582A90 /* SkCordic.h in Headers */,
+				005F25980EF94F7900582A90 /* SkCoreBlitters.h in Headers */,
+				005F259F0EF94F7900582A90 /* SkDrawProcs.h in Headers */,
+				005F25A10EF94F7900582A90 /* SkEdge.h in Headers */,
+				005F25A30EF94F7900582A90 /* SkFilterProc.h in Headers */,
+				005F25A60EF94F7900582A90 /* SkFloat.h in Headers */,
+				005F25A80EF94F7900582A90 /* SkFP.h in Headers */,
+				005F25AA0EF94F7900582A90 /* SkGeometry.h in Headers */,
+				005F25AD0EF94F7900582A90 /* SkGlyphCache.h in Headers */,
+				005F25BB0EF94F7900582A90 /* SkPathHeap.h in Headers */,
+				005F25BF0EF94F7900582A90 /* SkPictureFlat.h in Headers */,
+				005F25C10EF94F7900582A90 /* SkPicturePlayback.h in Headers */,
+				005F25C30EF94F7900582A90 /* SkPictureRecord.h in Headers */,
+				005F25CD0EF94F7900582A90 /* SkRegionPriv.h in Headers */,
+				005F25D00EF94F7900582A90 /* SkScan.h in Headers */,
+				005F25D50EF94F7900582A90 /* SkScanPriv.h in Headers */,
+				005F25D70EF94F7900582A90 /* SkSinTable.h in Headers */,
+				005F25D80EF94F7900582A90 /* SkSpriteBlitter.h in Headers */,
+				005F25DB0EF94F7900582A90 /* SkSpriteBlitterTemplate.h in Headers */,
+				005F25DF0EF94F7900582A90 /* SkStrokerPriv.h in Headers */,
+				005F25E00EF94F7900582A90 /* SkTemplatesPriv.h in Headers */,
+				005F25E20EF94F7900582A90 /* SkTSort.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		D2AAC045055464E500DB518D /* core */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 1DEB91EB08733DB70010E9CD /* Build configuration list for PBXNativeTarget "core" */;
+			buildPhases = (
+				D2AAC043055464E500DB518D /* Headers */,
+				D2AAC044055464E500DB518D /* Sources */,
+				D289987405E68DCB004EDB86 /* Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = core;
+			productName = core;
+			productReference = D2AAC046055464E500DB518D /* libcore.a */;
+			productType = "com.apple.product-type.library.static";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		08FB7793FE84155DC02AAC07 /* Project object */ = {
+			isa = PBXProject;
+			buildConfigurationList = 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "core" */;
+			compatibilityVersion = "Xcode 2.4";
+			hasScannedForEncodings = 1;
+			mainGroup = 08FB7794FE84155DC02AAC07 /* core */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				D2AAC045055464E500DB518D /* core */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+		D2AAC044055464E500DB518D /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				005F25700EF94F7900582A90 /* Sk64.cpp in Sources */,
+				005F25710EF94F7900582A90 /* SkAlphaRuns.cpp in Sources */,
+				005F25740EF94F7900582A90 /* SkBitmap.cpp in Sources */,
+				005F25750EF94F7900582A90 /* SkBitmap_scroll.cpp in Sources */,
+				005F25760EF94F7900582A90 /* SkBitmapProcShader.cpp in Sources */,
+				005F25780EF94F7900582A90 /* SkBitmapProcState.cpp in Sources */,
+				005F257B0EF94F7900582A90 /* SkBitmapProcState_matrixProcs.cpp in Sources */,
+				005F257D0EF94F7900582A90 /* SkBitmapSampler.cpp in Sources */,
+				005F25800EF94F7900582A90 /* SkBitmapShader.cpp in Sources */,
+				005F25860EF94F7900582A90 /* SkBlitRow_D16.cpp in Sources */,
+				005F25870EF94F7900582A90 /* SkBlitRow_D4444.cpp in Sources */,
+				005F25880EF94F7900582A90 /* SkBlitter.cpp in Sources */,
+				005F258A0EF94F7900582A90 /* SkBlitter_4444.cpp in Sources */,
+				005F258B0EF94F7900582A90 /* SkBlitter_A1.cpp in Sources */,
+				005F258C0EF94F7900582A90 /* SkBlitter_A8.cpp in Sources */,
+				005F258D0EF94F7900582A90 /* SkBlitter_ARGB32.cpp in Sources */,
+				005F258E0EF94F7900582A90 /* SkBlitter_RGB16.cpp in Sources */,
+				005F258F0EF94F7900582A90 /* SkBlitter_Sprite.cpp in Sources */,
+				005F25900EF94F7900582A90 /* SkBuffer.cpp in Sources */,
+				005F25910EF94F7900582A90 /* SkCanvas.cpp in Sources */,
+				005F25920EF94F7900582A90 /* SkChunkAlloc.cpp in Sources */,
+				005F25930EF94F7900582A90 /* SkColor.cpp in Sources */,
+				005F25940EF94F7900582A90 /* SkColorFilter.cpp in Sources */,
+				005F25950EF94F7900582A90 /* SkColorTable.cpp in Sources */,
+				005F25960EF94F7900582A90 /* SkCordic.cpp in Sources */,
+				005F25990EF94F7900582A90 /* SkDebug.cpp in Sources */,
+				005F259A0EF94F7900582A90 /* SkDebug_stdio.cpp in Sources */,
+				005F259B0EF94F7900582A90 /* SkDeque.cpp in Sources */,
+				005F259C0EF94F7900582A90 /* SkDevice.cpp in Sources */,
+				005F259D0EF94F7900582A90 /* SkDither.cpp in Sources */,
+				005F259E0EF94F7900582A90 /* SkDraw.cpp in Sources */,
+				005F25A00EF94F7900582A90 /* SkEdge.cpp in Sources */,
+				005F25A20EF94F7900582A90 /* SkFilterProc.cpp in Sources */,
+				005F25A40EF94F7900582A90 /* SkFlattenable.cpp in Sources */,
+				005F25A50EF94F7900582A90 /* SkFloat.cpp in Sources */,
+				005F25A70EF94F7900582A90 /* SkFloatBits.cpp in Sources */,
+				005F25A90EF94F7900582A90 /* SkGeometry.cpp in Sources */,
+				005F25AB0EF94F7900582A90 /* SkGlobals.cpp in Sources */,
+				005F25AC0EF94F7900582A90 /* SkGlyphCache.cpp in Sources */,
+				005F25AE0EF94F7900582A90 /* SkGraphics.cpp in Sources */,
+				005F25B00EF94F7900582A90 /* SkMask.cpp in Sources */,
+				005F25B10EF94F7900582A90 /* SkMaskFilter.cpp in Sources */,
+				005F25B20EF94F7900582A90 /* SkMath.cpp in Sources */,
+				005F25B30EF94F7900582A90 /* SkMatrix.cpp in Sources */,
+				005F25B40EF94F7900582A90 /* SkMemory_stdlib.cpp in Sources */,
+				005F25B50EF94F7900582A90 /* SkPackBits.cpp in Sources */,
+				005F25B70EF94F7900582A90 /* SkPaint.cpp in Sources */,
+				005F25B80EF94F7900582A90 /* SkPath.cpp in Sources */,
+				005F25B90EF94F7900582A90 /* SkPathEffect.cpp in Sources */,
+				005F25BA0EF94F7900582A90 /* SkPathHeap.cpp in Sources */,
+				005F25BC0EF94F7900582A90 /* SkPathMeasure.cpp in Sources */,
+				005F25BD0EF94F7900582A90 /* SkPicture.cpp in Sources */,
+				005F25BE0EF94F7900582A90 /* SkPictureFlat.cpp in Sources */,
+				005F25C00EF94F7900582A90 /* SkPicturePlayback.cpp in Sources */,
+				005F25C20EF94F7900582A90 /* SkPictureRecord.cpp in Sources */,
+				005F25C40EF94F7900582A90 /* SkPixelRef.cpp in Sources */,
+				005F25C50EF94F7900582A90 /* SkPoint.cpp in Sources */,
+				005F25C60EF94F7900582A90 /* SkProcSpriteBlitter.cpp in Sources */,
+				005F25C70EF94F7900582A90 /* SkPtrRecorder.cpp in Sources */,
+				005F25C80EF94F7900582A90 /* SkRasterizer.cpp in Sources */,
+				005F25C90EF94F7900582A90 /* SkRect.cpp in Sources */,
+				005F25CA0EF94F7900582A90 /* SkRefCnt.cpp in Sources */,
+				005F25CB0EF94F7900582A90 /* SkRegion.cpp in Sources */,
+				005F25CC0EF94F7900582A90 /* SkRegion_path.cpp in Sources */,
+				005F25CE0EF94F7900582A90 /* SkScalerContext.cpp in Sources */,
+				005F25CF0EF94F7900582A90 /* SkScan.cpp in Sources */,
+				005F25D10EF94F7900582A90 /* SkScan_Antihair.cpp in Sources */,
+				005F25D20EF94F7900582A90 /* SkScan_AntiPath.cpp in Sources */,
+				005F25D30EF94F7900582A90 /* SkScan_Hairline.cpp in Sources */,
+				005F25D40EF94F7900582A90 /* SkScan_Path.cpp in Sources */,
+				005F25D60EF94F7900582A90 /* SkShader.cpp in Sources */,
+				005F25D90EF94F7900582A90 /* SkSpriteBlitter_ARGB32.cpp in Sources */,
+				005F25DA0EF94F7900582A90 /* SkSpriteBlitter_RGB16.cpp in Sources */,
+				005F25DC0EF94F7900582A90 /* SkString.cpp in Sources */,
+				005F25DD0EF94F7900582A90 /* SkStroke.cpp in Sources */,
+				005F25DE0EF94F7900582A90 /* SkStrokerPriv.cpp in Sources */,
+				005F25E10EF94F7900582A90 /* SkTSearch.cpp in Sources */,
+				005F25E30EF94F7900582A90 /* SkTypeface.cpp in Sources */,
+				005F25E40EF94F7900582A90 /* SkUnPreMultiply.cpp in Sources */,
+				005F25E50EF94F7900582A90 /* SkUtils.cpp in Sources */,
+				005F25E60EF94F7900582A90 /* SkWriter32.cpp in Sources */,
+				005F25E70EF94F7900582A90 /* SkXfermode.cpp in Sources */,
+				005F26960EF955D400582A90 /* SkComposeShader.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		1DEB91EC08733DB70010E9CD /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_ENABLE_FIX_AND_CONTINUE = YES;
+				GCC_MODEL_TUNING = G5;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				INSTALL_PATH = /usr/local/lib;
+				PRODUCT_NAME = core;
+				ZERO_LINK = YES;
+			};
+			name = Debug;
+		};
+		1DEB91ED08733DB70010E9CD /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_MODEL_TUNING = G5;
+				INSTALL_PATH = /usr/local/lib;
+				PRODUCT_NAME = core;
+			};
+			name = Release;
+		};
+		1DEB91F008733DB70010E9CD /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				GCC_PREPROCESSOR_DEFINITIONS = SK_DEBUG;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				PREBINDING = NO;
+				USER_HEADER_SEARCH_PATHS = ../../include/core;
+			};
+			name = Debug;
+		};
+		1DEB91F108733DB70010E9CD /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = (
+					ppc,
+					i386,
+				);
+				GCC_PREPROCESSOR_DEFINITIONS = SK_RELEASE;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				PREBINDING = NO;
+				USER_HEADER_SEARCH_PATHS = ../../include/core;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		1DEB91EB08733DB70010E9CD /* Build configuration list for PBXNativeTarget "core" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1DEB91EC08733DB70010E9CD /* Debug */,
+				1DEB91ED08733DB70010E9CD /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Debug;
+		};
+		1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "core" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1DEB91F008733DB70010E9CD /* Debug */,
+				1DEB91F108733DB70010E9CD /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Debug;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}