grab from latest android



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