grab from latest android



git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
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));
+}
+