add gpu backend (not hooked up yet)



git-svn-id: http://skia.googlecode.com/svn/trunk@649 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/FlingState.h b/gpu/include/FlingState.h
new file mode 100644
index 0000000..a1da4fb
--- /dev/null
+++ b/gpu/include/FlingState.h
@@ -0,0 +1,59 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef SkFlingState_DEFINED
+#define SkFlingState_DEFINED
+
+#include "SkScalar.h"
+#include "SkPoint.h"
+
+class SkMatrix;
+
+struct FlingState {
+    FlingState() : fActive(false) {}
+
+    bool isActive() const { return fActive; }
+    void stop() { fActive = false; }
+
+    void reset(float sx, float sy);
+    bool evaluateMatrix(SkMatrix* matrix);
+
+private:
+    SkPoint     fDirection;
+    SkScalar    fSpeed0;
+    double      fTime0;
+    bool        fActive;
+};
+
+class GrAnimateFloat {
+public:
+    GrAnimateFloat();
+
+    void start(float v0, float v1, float duration);
+    bool isActive() const { return fTime0 != 0; }
+    void stop() { fTime0 = 0; }
+
+    float evaluate();
+
+private:
+    float   fValue0, fValue1, fDuration;
+    SkMSec  fTime0;
+};
+
+#endif
+
+
diff --git a/gpu/include/GrAPI.h b/gpu/include/GrAPI.h
new file mode 100644
index 0000000..b660e8d
--- /dev/null
+++ b/gpu/include/GrAPI.h
@@ -0,0 +1,37 @@
+
+
+class GrAPI {
+public:
+
+    void setRenderTarget(GrRenderTarget* target);
+
+    void setMatrix(const GrMatrix&);
+
+    void setClip(rect, bool aa);
+    void setClip(rect[], bool aa);
+    void setClip(path, bool aa);
+    void setClip(rect, texture/key, state, matrix);
+    void setClip(path, texture/key, state, matrix);
+    
+    void setColor(color);
+    void setTexture(texture/key, sampler, const GrMatrix* = NULL);
+    void setBlend(src, dst);
+    
+    void drawRect(const GrRect&, stroke, join);
+    void drawOval(const GrRect&, stroke);
+    void drawRoundRect(const GrRect&, GrScalar ovalW, GrScalar ovalH, stroke);
+    void drawPath(const GrPathIter&, GrPathFill);
+    void drawVertices(...);
+    void drawGlyphs(const uint16_t[], int count, const GrPoint[], GrFontScaler*);
+
+///
+
+    void save();
+    void restore();
+    void concatMatrix(const GrMatrix&);
+    void concatClipRect(const GrRect&);
+    void concatClipPath(const GrPathIter&, bool aa);
+
+};
+
+
diff --git a/gpu/include/GrAllocPool.h b/gpu/include/GrAllocPool.h
new file mode 100644
index 0000000..46359e5
--- /dev/null
+++ b/gpu/include/GrAllocPool.h
@@ -0,0 +1,71 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrAllocPool_DEFINED
+#define GrAllocPool_DEFINED
+
+#include "GrNoncopyable.h"
+
+class GrAllocPool : GrNoncopyable {
+public:
+    GrAllocPool(size_t blockSize = 0);
+    ~GrAllocPool();
+
+    /**
+     *  Frees all blocks that have been allocated with alloc().
+     */
+    void reset();
+
+    /**
+     *  Returns a block of memory bytes size big. This address must not be
+     *  passed to realloc/free/delete or any other function that assumes the
+     *  address was allocated by malloc or new (becuase it hasn't).
+     */
+    void* alloc(size_t bytes);
+    
+    /**
+     * Releases the most recently allocated bytes back to allocpool.
+     */
+    void release(size_t bytes);
+
+private:
+    struct Block;
+
+    Block*  fBlock;
+    size_t  fMinBlockSize;
+
+#if GR_DEBUG
+    int fBlocksAllocated;
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+};
+
+template <typename T> class GrTAllocPool {
+public:
+    GrTAllocPool(int count) : fPool(count * sizeof(T)) {}
+
+    void reset() { fPool.reset(); }
+    T* alloc() { return (T*)fPool.alloc(sizeof(T)); }
+
+private:
+    GrAllocPool fPool;
+};
+
+#endif
+
diff --git a/gpu/include/GrAllocator.h b/gpu/include/GrAllocator.h
new file mode 100755
index 0000000..da02ba4
--- /dev/null
+++ b/gpu/include/GrAllocator.h
@@ -0,0 +1,230 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrAllocator_DEFINED
+#define GrAllocator_DEFINED
+
+#include "GrConfig.h"
+#include "GrTArray.h"
+
+class GrAllocator {
+public:
+    virtual ~GrAllocator() {
+        reset();
+    }
+
+    /**
+     * Create an allocator
+     *
+     * @param   itemSize        the size of each item to allocate
+     * @param   itemsPerBlock   the number of items to allocate at once
+     * @param   initialBlock    optional memory to use for the first block.
+     *                          Must be at least itemSize*itemsPerBlock sized.
+     *                          Caller is responsible for freeing this memory.
+     */
+    GrAllocator(size_t itemSize, uint32_t itemsPerBlock, void* initialBlock) :
+            fBlocks(fBlockInitialStorage, NUM_INIT_BLOCK_PTRS),
+            fItemSize(itemSize),
+            fItemsPerBlock(itemsPerBlock),
+            fOwnFirstBlock(NULL == initialBlock),
+            fCount(0) {
+        fBlockSize = fItemSize * fItemsPerBlock;
+        fBlocks.push_back() = initialBlock;
+        GR_DEBUGCODE(if (!fOwnFirstBlock) {*((char*)initialBlock+fBlockSize-1)='a';} );
+    }
+
+    /**
+     * Adds an item and returns pointer to it.
+     *
+     * @return pointer to the added item.
+     */
+    void* push_back() {        
+        uint32_t indexInBlock = fCount % fItemsPerBlock;
+        // we always have at least one block
+        if (0 == indexInBlock) {
+            if (0 != fCount) {
+                fBlocks.push_back() = GrMalloc(fBlockSize);
+            } else if (fOwnFirstBlock) {
+                fBlocks[0] = GrMalloc(fBlockSize);
+            }
+        }        
+        void* ret = (char*)fBlocks[fCount/fItemsPerBlock] + 
+                    fItemSize * indexInBlock;
+        ++fCount;
+        return ret;
+    }
+
+    /**
+     * removes all added items
+     */
+    void reset() {        
+        uint32_t blockCount = GrMax((unsigned)1, 
+                                    GrUIDivRoundUp(fCount, fItemsPerBlock));
+        for (uint32_t i = 1; i < blockCount; ++i) {
+            GrFree(fBlocks[i]);
+        }
+        if (fOwnFirstBlock) {
+            GrFree(fBlocks[0]);
+            fBlocks[0] = NULL;
+        }
+        fBlocks.pop_back_n(blockCount-1);
+        fCount = 0;
+    }
+
+    /**
+     * count of items
+     */
+    uint32_t count() const {
+        return fCount;
+    }
+    
+    /**
+     * is the count 0
+     */
+    bool empty() const { return fCount == 0; }
+    
+    /**
+     * access last item, only call if count() != 0
+     */
+    void* back() {
+        GrAssert(fCount);
+        return (*this)[fCount-1];
+    }
+    
+    /**
+     * access last item, only call if count() != 0
+     */
+    const void* back() const {
+        GrAssert(fCount);
+        return (*this)[fCount-1];
+    }
+    
+    /**
+     * access item by index.
+     */    
+    void* operator[] (uint32_t i) {
+        GrAssert(i < fCount);
+        return (char*)fBlocks[i / fItemsPerBlock] + 
+               fItemSize * (i % fItemsPerBlock);
+    }
+
+    /**
+     * access item by index.
+     */  
+    const void* operator[] (uint32_t i) const {
+        GrAssert(i < fCount);
+        return (const char*)fBlocks[i / fItemsPerBlock] + 
+               fItemSize * (i % fItemsPerBlock);
+    }
+
+private:
+    static const uint32_t NUM_INIT_BLOCK_PTRS = 8;
+    
+    GrTArray<void*> fBlocks;
+    size_t          fBlockSize;    
+    char            fBlockInitialStorage[NUM_INIT_BLOCK_PTRS*sizeof(void*)];    
+    size_t          fItemSize;
+    uint32_t        fItemsPerBlock;
+    bool            fOwnFirstBlock;
+    uint32_t        fCount;    
+};
+
+template <typename T>
+class GrTAllocator {
+private:
+    GrAllocator fAllocator;
+    
+public:
+    virtual ~GrTAllocator() {};
+    
+    /**
+     * Create an allocator
+     *
+     * @param   itemsPerBlock   the number of items to allocate at once
+     * @param   initialBlock    optional memory to use for the first block.
+     *                          Must be at least size(T)*itemsPerBlock sized.
+     *                          Caller is responsible for freeing this memory.
+     */
+    GrTAllocator(uint32_t itemsPerBlock, void* initialBlock) :
+        fAllocator(sizeof(T), itemsPerBlock, initialBlock)
+    {}
+    
+    /**
+     * Adds an item and returns it.
+     *
+     * @return the added item.
+     */
+    T& push_back() {
+        void* item = fAllocator.push_back();
+        GrAssert(NULL != item);
+        new (item) T;
+        return *(T*)item;
+    }
+    
+    /**
+     * removes all added items
+     */
+    void reset() {
+        uint32_t c = fAllocator.count();
+        for (uint32_t i = 0; i < c; ++i) {
+            ((T*)fAllocator[i])->~T();
+        }
+        fAllocator.reset();
+    }
+    
+    /**
+     * count of items
+     */
+    uint32_t count() const {
+        return fAllocator.count();
+    }
+    
+    /**
+     * is the count 0
+     */
+    bool empty() const { return fAllocator.empty(); }
+    
+    /**
+     * access last item, only call if count() != 0
+     */
+    T& back() {
+        return *(T*)fAllocator.back();
+    }
+
+    /**
+     * access last item, only call if count() != 0
+     */
+    const T& back() const {
+        return *(const T*)fAllocator.back();
+    }
+
+    /**
+     * access item by index.
+     */  
+    T& operator[] (uint32_t i) {
+        return *(T*)(fAllocator[i]);
+    }
+    
+    /**
+     * access item by index.
+     */
+    const T& operator[] (uint32_t i) const {
+        return *(const T*)(fAllocator[i]);
+    }    
+};
+
+#endif
diff --git a/gpu/include/GrAtlas.h b/gpu/include/GrAtlas.h
new file mode 100644
index 0000000..9526e0b
--- /dev/null
+++ b/gpu/include/GrAtlas.h
@@ -0,0 +1,88 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrAtlas_DEFINED
+#define GrAtlas_DEFINED
+
+#include "GrPoint.h"
+#include "GrTexture.h"
+#include "GrTDArray.h"
+
+class GrGpu;
+class GrRectanizer;
+class GrAtlasMgr;
+
+class GrAtlas {
+public:
+    GrAtlas(GrAtlasMgr*, int plotX, int plotY);
+
+    int getPlotX() const { return fPlot.fX; }
+    int getPlotY() const { return fPlot.fY; }
+
+    GrTexture* texture() const { return fTexture; }
+
+    bool addSubImage(int width, int height, const void*, GrIPoint16*);
+
+    static void FreeLList(GrAtlas* atlas) {
+        while (atlas) {
+            GrAtlas* next = atlas->fNext;
+            delete atlas;
+            atlas = next;
+        }
+    }
+
+    // testing
+    GrAtlas* nextAtlas() const { return fNext; }
+
+private:
+    ~GrAtlas(); // does not try to delete the fNext field
+
+    GrAtlas*        fNext;
+    GrTexture*      fTexture;
+    GrRectanizer*   fRects;
+    GrAtlasMgr*     fAtlasMgr;
+    GrIPoint16      fPlot;
+
+    friend class GrAtlasMgr;
+};
+
+class GrPlotMgr;
+
+class GrAtlasMgr {
+public:
+    GrAtlasMgr(GrGpu*);
+    ~GrAtlasMgr();
+
+    GrAtlas* addToAtlas(GrAtlas*, int width, int height, const void*,
+                        GrIPoint16*);
+
+    GrTexture* getTexture() const { return fTexture; }
+
+    // to be called by ~GrAtlas()
+    void freePlot(int x, int y);
+
+    void abandonAll();
+
+private:
+    GrGpu*      fGpu;
+    GrTexture*  fTexture;
+    GrPlotMgr*  fPlotMgr;
+};
+
+#endif
+
+
diff --git a/gpu/include/GrClip.h b/gpu/include/GrClip.h
new file mode 100644
index 0000000..8e3030c
--- /dev/null
+++ b/gpu/include/GrClip.h
@@ -0,0 +1,115 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrClip_DEFINED
+#define GrClip_DEFINED
+
+#include "GrClipIterator.h"
+#include "GrRect.h"
+#include "GrTDArray.h"
+
+class GrClip {
+public:
+    GrClip();
+    GrClip(const GrClip& src);
+    GrClip(GrClipIterator* iter);
+    ~GrClip();
+
+    GrClip& operator=(const GrClip& src);
+
+    bool isEmpty() const { return fBounds.isEmpty(); }
+    bool isComplex() const { return fList.count() > 0; }
+    bool isRect() const {
+        return !this->isEmpty() && !this->isComplex();
+    }
+    
+    const GrIRect& getBounds() const { return fBounds; }
+
+    /**
+     *  Resets this clip to be empty (fBounds is empty, and fList is empty)
+     */
+    void setEmpty();
+
+    /**
+     *  Resets this clip to have fBounds == rect, and fList is empty.
+     */
+    void setRect(const GrIRect& rect);
+
+    /**
+     *  Append a rect to an existing clip. The call must ensure that rect does
+     *  not overlap with any previous rect in this clip (either from setRect
+     *  or addRect). fBounds is automatically updated to reflect the union of
+     *  all rects that have been added.
+     */
+    void addRect(const GrIRect&);
+
+    void setFromIterator(GrClipIterator* iter);
+
+    friend bool operator==(const GrClip& a, const GrClip& b) {
+        return a.fBounds == b.fBounds && a.fList == b.fList;
+    }
+    friend bool operator!=(const GrClip& a, const GrClip& b) {
+        return !(a == b);
+    }
+
+    /**
+     *  Return the number of rects in this clip: 0 for empty, 1 for a rect,
+     *  or N for a complex clip.
+     */
+    int countRects() const {
+        return this->isEmpty() ? 0 : GrMax<int>(1, fList.count());
+    }
+
+    /**
+     *  Return an array of rects for this clip. Use countRects() to know the
+     *  number of entries.
+     */
+    const GrIRect* getRects() const {
+        return fList.count() > 0 ? fList.begin() : &fBounds;
+    }
+
+#if GR_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+private:
+    GrTDArray<GrIRect>  fList;
+    GrIRect             fBounds;
+};
+
+class GrClipIter : public GrClipIterator {
+public:
+    GrClipIter(const GrClip& clip) : fClip(&clip), fIndex(0) {}
+    GrClipIter() : fClip(NULL), fIndex(0) {}
+    
+    void reset(const GrClip& clip);
+    
+    virtual bool isDone();
+    virtual void rewind();
+    virtual void getRect(GrIRect* r);
+    virtual void next();
+    virtual void computeBounds(GrIRect* r);
+    
+private:
+    const GrClip*   fClip;
+    int             fIndex;
+};
+
+#endif
+
diff --git a/gpu/include/GrClipIterator.h b/gpu/include/GrClipIterator.h
new file mode 100644
index 0000000..d1fe4dd
--- /dev/null
+++ b/gpu/include/GrClipIterator.h
@@ -0,0 +1,81 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrClipIterator_DEFINED
+#define GrClipIterator_DEFINED
+
+#include "GrRect.h"
+
+class GrClipIterator {
+public:
+    GrClipIterator() : fNeedBounds(true) {}
+    virtual ~GrClipIterator() {}
+
+    /**
+     *  Returns true if there are no more rects to process
+     */
+    virtual bool isDone() = 0;
+
+    /**
+     *  Rewind the iterate to replay the set of rects again
+     */
+    virtual void rewind() = 0;
+
+    /**
+     *  Return the current rect. It is an error to call this when done() is true
+     */
+    virtual void getRect(GrIRect*) = 0;
+
+    /**
+     *  Call to move to the next rect in the set
+     */
+    virtual void next() = 0;
+
+    /**
+     *  Set bounds to be the bounds of the clip.
+     */
+    virtual void computeBounds(GrIRect* bounds) = 0;
+
+    /**
+     *  Subclass should call this whenever their underlying bounds has changed.
+     */
+    void invalidateBoundsCache() { fNeedBounds = true; }
+
+    const GrIRect& getBounds() {
+        if (fNeedBounds) {
+            this->computeBounds(&fBounds);
+            fNeedBounds = false;
+        }
+        return fBounds;
+    }
+
+private:
+    GrIRect fBounds;
+    bool    fNeedBounds;
+};
+
+/**
+ *  Call to rewind iter, first checking to see if iter is NULL
+ */
+static inline void GrSafeRewind(GrClipIterator* iter) {
+    if (iter) {
+        iter->rewind();
+    }
+}
+
+#endif
+
diff --git a/gpu/include/GrColor.h b/gpu/include/GrColor.h
new file mode 100644
index 0000000..8dc03d2
--- /dev/null
+++ b/gpu/include/GrColor.h
@@ -0,0 +1,72 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrColor_DEFINED
+#define GrColor_DEFINED
+
+#include "GrTypes.h"
+
+/**
+ *  GrColor is 4 bytes for R, G, B, A, in a compile-time specific order. The
+ *  components are stored premultiplied.
+ */
+typedef uint32_t GrColor;
+
+// indices for address a GrColor as an array of bytes
+
+#define GrColor_INDEX_R     0
+#define GrColor_INDEX_G     1
+#define GrColor_INDEX_B     2
+#define GrColor_INDEX_A     3
+
+// shfit amount to assign a component to a GrColor int
+
+#define GrColor_SHIFT_R     0
+#define GrColor_SHIFT_G     8
+#define GrColor_SHIFT_B     16
+#define GrColor_SHIFT_A     24
+
+/**
+ *  Pack 4 components (RGBA) into a GrColor int
+ */
+static inline GrColor GrColorPackRGBA(unsigned r, unsigned g,
+                                      unsigned b, unsigned a) {
+    GrAssert((uint8_t)r == r);
+    GrAssert((uint8_t)g == g);
+    GrAssert((uint8_t)b == b);
+    GrAssert((uint8_t)a == a);
+    return  (r << GrColor_SHIFT_R) |
+            (g << GrColor_SHIFT_G) |
+            (b << GrColor_SHIFT_B) |
+            (a << GrColor_SHIFT_A);
+}
+
+// extract a component (byte) from a GrColor int
+
+#define GrColorUnpackR(color)   (((color) >> GrColor_SHIFT_R) & 0xFF)
+#define GrColorUnpackG(color)   (((color) >> GrColor_SHIFT_G) & 0xFF)
+#define GrColorUnpackB(color)   (((color) >> GrColor_SHIFT_B) & 0xFF)
+#define GrColorUnpackA(color)   (((color) >> GrColor_SHIFT_A) & 0xFF)
+
+/**
+ *  Since premultiplied means that alpha >= color, we construct a color with
+ *  each component==255 and alpha == 0 to be "illegal"
+ */
+#define GrColor_ILLEGAL     (~(0xFF << GrColor_SHIFT_A))
+
+#endif
+
diff --git a/gpu/include/GrConfig.h b/gpu/include/GrConfig.h
new file mode 100644
index 0000000..9c18f60
--- /dev/null
+++ b/gpu/include/GrConfig.h
@@ -0,0 +1,344 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrConfig_DEFINED
+#define GrConfig_DEFINED
+
+///////////////////////////////////////////////////////////////////////////////
+// preconfig section:
+//
+// All the work before including GrUserConfig.h should center around guessing
+// what platform we're on, and defining low-level symbols based on that.
+//
+// A build environment may have already defined symbols, so we first check
+// for that
+//
+
+// hack to ensure we know what sort of Apple platform we're on
+#if defined(__APPLE_CPP__) || defined(__APPLE_CC__)
+    #include <TargetConditionals.h>
+#endif
+
+/**
+ *  Gr defines are set to 0 or 1, rather than being undefined or defined
+ */
+
+#if !defined(GR_ANDROID_BUILD)
+    #define GR_ANDROID_BUILD    0
+#endif
+#if !defined(GR_IOS_BUILD)
+    #define GR_IOS_BUILD        0
+#endif
+#if !defined(GR_LINUX_BUILD)
+    #define GR_LINUX_BUILD      0
+#endif
+#if !defined(GR_MAC_BUILD)
+    #define GR_MAC_BUILD        0
+#endif
+#if !defined(GR_WIN32_BUILD)
+    #define GR_WIN32_BUILD      0
+#endif
+#if !defined(GR_QNX_BUILD)
+    #define GR_QNX_BUILD        0
+#endif
+
+/**
+ *  If no build target has been defined, attempt to infer.
+ */
+#if !GR_ANDROID_BUILD && !GR_IOS_BUILD && !GR_LINUX_BUILD && !GR_MAC_BUILD && !GR_WIN32_BUILD && !GR_QNX_BUILD
+    #if defined(_WIN32)
+        #undef GR_WIN32_BUILD
+        #define GR_WIN32_BUILD      1
+//      #error "WIN"
+    #elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
+        #undef GR_IOS_BUILD
+        #define GR_IOS_BUILD        1
+//      #error "IOS"
+    #elif ANDROID_NDK || defined(ANDROID)
+        #undef GR_ANDROID_BUILD
+        #define GR_ANDROID_BUILD    1
+//      #error "ANDROID"
+    #elif TARGET_OS_MAC
+        #undef GR_MAC_BUILD
+        #define GR_MAC_BUILD        1
+//      #error "MAC"
+    #elif TARGET_OS_QNX || defined(__QNXNTO__)
+        #undef GR_QNX_BUILD
+        #define GR_QNX_BUILD        1
+//      #error "QNX"
+    #else
+        #undef GR_LINUX_BUILD
+        #define GR_LINUX_BUILD      1
+//      #error "LINUX"
+    #endif    
+#endif
+
+#if !defined(GR_DEBUG) && !defined(GR_RELEASE)
+    #ifdef NDEBUG
+        #define GR_DEBUG    0
+    #else
+        #define GR_DEBUG    1
+    #endif
+    #define GR_RELEASE  !GR_DEBUG
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ *  Pull stdint.h in before user-config, to be sure our __STDC... macros are
+ *  defined before anyone else might try to include stdint.h
+ */
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+#include <stdint.h>
+
+/*
+ *  The "user config" file can be empty, and everything should work. It is 
+ *  meant to store a given platform/client's overrides of our guess-work.
+ *
+ *  A alternate user config file can be specified by defining 
+ *  GR_USER_CONFIG_FILE. It should be defined relative to GrConfig.h
+ *
+ *  e.g. it can specify GR_DEBUG/GR_RELEASE as it please, change the BUILD
+ *  target, or supply its own defines for anything else (e.g. GR_SCALAR)
+ */
+#if !defined(GR_USER_CONFIG_FILE)
+    #include "GrUserConfig.h"
+#else 
+    #include GR_USER_CONFIG_FILE
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// postconfig section:
+//
+// By now we must have a GR_..._BUILD symbol set to 1, and a decision about
+// debug -vs- release
+//
+
+extern void GrPrintf(const char format[], ...);
+
+/**
+ *  GR_STRING makes a string of X where X is expanded before conversion to a string
+ *  if X itself contains macros.
+ */
+#define GR_STRING(X) GR_STRING_IMPL(X)
+#define GR_STRING_IMPL(X) #X
+
+/**
+ *  GR_CONCAT concatenates X and Y  where each is expanded before 
+ *  contanenation if either contains macros.
+ */
+#define GR_CONCAT(X,Y) GR_CONCAT_IMPL(X,Y)
+#define GR_CONCAT_IMPL(X,Y) X##Y
+
+/**
+ *  Creates a string of the form "<filename>(<linenumber>) : "
+ */
+#define GR_FILE_AND_LINE_STR __FILE__ "(" GR_STRING(__LINE__) ") : "
+
+/**
+ *  Compilers have different ways of issuing warnings. This macro 
+ *  attempts to abstract them, but may need to be specialized for your 
+ *  particular compiler.
+ *  To insert compiler warnings use "#pragma message GR_WARN(<string>)"
+ */
+#if _MSC_VER
+    #define GR_WARN(MSG) (GR_FILE_AND_LINE_STR "WARNING: " MSG)
+#else//__GNUC__ - may need other defines for different compilers
+    #define GR_WARN(MSG) ("WARNING: " MSG)
+#endif
+
+/**
+ *  GR_ALWAYSBREAK is an unconditional break in all builds.
+ */
+#if !defined(GR_ALWAYSBREAK)
+    #if     GR_WIN32_BUILD
+        #define GR_ALWAYSBREAK __debugbreak()
+    #else 
+        // TODO: do other platforms really not have continuable breakpoints?
+        // sign extend for 64bit architectures to be sure this is 
+        // in the high address range
+        #define GR_ALWAYSBREAK *((int*)(int64_t)(int32_t)0xbeefcafe) = 0;
+    #endif
+#endif
+
+/**
+ *  GR_DEBUGBREAK is an unconditional break in debug builds.
+ */
+#if !defined(GR_DEBUGBREAK)
+    #if GR_DEBUG
+        #define GR_DEBUGBREAK GR_ALWAYSBREAK
+    #else
+        #define GR_DEBUGBREAK
+    #endif
+#endif
+
+/**
+ *  GR_ALWAYSASSERT is an assertion in all builds.
+ */
+#if !defined(GR_ALWAYSASSERT)
+    #define GR_ALWAYSASSERT(COND)                                        \
+        do {                                                             \
+            if (!(COND)) {                                               \
+                GrPrintf("%s %s failed\n", GR_FILE_AND_LINE_STR, #COND); \
+                GR_ALWAYSBREAK;                                          \
+            }                                                            \
+        } while (false)
+#endif
+
+/**
+ *  GR_DEBUGASSERT is an assertion in debug builds only.
+ */
+#if !defined(GR_DEBUGASSERT)
+    #if GR_DEBUG
+        #define GR_DEBUGASSERT(COND) GR_ALWAYSASSERT(COND)
+    #else
+        #define GR_DEBUGASSERT(COND)
+    #endif
+#endif
+
+/**
+ *  Prettier forms of the above macros.
+ */
+#define GrAssert(COND) GR_DEBUGASSERT(COND)
+#define GrAlwaysAssert(COND) GR_ALWAYSASSERT(COND)
+
+/**
+ *  GR_DEBUGCODE compiles the code X in debug builds only 
+ */
+#if !defined(GR_DEBUGCODE)
+    #if GR_DEBUG
+        #define GR_DEBUGCODE(X) X
+    #else
+        #define GR_DEBUGCODE(X)
+    #endif
+#endif
+
+/**
+ *  GR_STATIC_ASSERT is a compile time assertion. Depending on the platform
+ *  it may print the message in the compiler log. Obviously, the condition must
+ *  be evaluatable at compile time.
+ */
+// VS 2010 and GCC compiled with c++0x or gnu++0x support the new 
+// static_assert.
+#if !defined(GR_STATIC_ASSERT)
+    #if (_MSC_VER >= 1600) || __GXX_EXPERIMENTAL_CXX0X__
+        #define GR_STATIC_ASSERT(CONDITION) static_assert(CONDITION, "bug")
+    #else
+        template <bool> class GR_STATIC_ASSERT_FAILURE;
+        template <> class GR_STATIC_ASSERT_FAILURE<true> {};
+        #define GR_STATIC_ASSERT(CONDITION) \
+            enum {GR_CONCAT(X,__LINE__) = \
+            sizeof(GR_STATIC_ASSERT_FAILURE<CONDITION>)}
+    #endif
+#endif
+
+#if !defined(GR_SCALAR_IS_FLOAT)
+    #define GR_SCALAR_IS_FLOAT   0
+#endif
+#if !defined(GR_SCALAR_IS_FIXED)
+    #define GR_SCALAR_IS_FIXED   0
+#endif
+
+#if !defined(GR_TEXT_SCALAR_TYPE_IS_USHORT)
+    #define GR_TEXT_SCALAR_TYPE_IS_USHORT  0
+#endif    
+#if !defined(GR_TEXT_SCALAR_TYPE_IS_FLOAT)
+    #define GR_TEXT_SCALAR_TYPE_IS_FLOAT   0
+#endif
+#if !defined(GR_TEXT_SCALAR_TYPE_IS_FIXED)
+    #define GR_TEXT_SCALAR_TYPE_IS_FIXED   0
+#endif
+
+#ifndef GR_DUMP_TEXTURE_UPLOAD
+    #define GR_DUMP_TEXTURE_UPLOAD  0
+#endif
+
+/**
+ *  GR_COLLECT_STATS controls whether the GrGpu class collects stats.
+ *  If not already defined then collect in debug build but not release.
+ */
+#if !defined(GR_COLLECT_STATS)
+    #define GR_COLLECT_STATS GR_DEBUG
+#endif
+
+/**
+ *  GR_GL_LOG_CALLS controls whether each GL call is logged.
+ */
+#if !defined(GR_GL_LOG_CALLS)
+    #define GR_GL_LOG_CALLS 0 
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// tail section:
+//
+// Now we just assert if we are missing some required define, or if we detect
+// and inconsistent combination of defines
+//
+
+
+/**
+ *  Only one build target macro should be 1 and the rest should be 0.
+ */
+#define GR_BUILD_SUM    (GR_WIN32_BUILD + GR_MAC_BUILD + GR_IOS_BUILD + GR_ANDROID_BUILD + GR_LINUX_BUILD + GR_QNX_BUILD)
+#if 0 == GR_BUILD_SUM
+    #error "Missing a GR_BUILD define"
+#elif 1 != GR_BUILD_SUM
+    #error "More than one GR_BUILD defined"
+#endif
+
+
+#if !GR_SCALAR_IS_FLOAT && !GR_SCALAR_IS_FIXED
+    #undef  GR_SCALAR_IS_FLOAT
+    #define GR_SCALAR_IS_FLOAT              1
+    #pragma message GR_WARN("Scalar type not defined, defaulting to float")
+#endif
+
+#if !GR_TEXT_SCALAR_IS_FLOAT && \
+    !GR_TEXT_SCALAR_IS_FIXED && \
+    !GR_TEXT_SCALAR_IS_USHORT
+    #undef  GR_TEXT_SCALAR_IS_FLOAT
+    #define GR_TEXT_SCALAR_IS_FLOAT         1
+    #pragma message GR_WARN("Text scalar type not defined, defaulting to float")
+#endif
+
+#if 0
+#if GR_WIN32_BUILD
+//    #pragma message GR_WARN("GR_WIN32_BUILD")
+#endif
+#if GR_MAC_BUILD
+//    #pragma message GR_WARN("GR_MAC_BUILD")
+#endif
+#if GR_IOS_BUILD
+//    #pragma message GR_WARN("GR_IOS_BUILD")
+#endif
+#if GR_ANDROID_BUILD
+//    #pragma message GR_WARN("GR_ANDROID_BUILD")
+#endif
+#if GR_LINUX_BUILD
+//    #pragma message GR_WARN("GR_LINUX_BUILD")
+#endif
+#if GR_QNX_BUILD
+//    #pragma message GR_WARN("GR_QNX_BUILD")
+#endif
+#endif
+
+#endif
+
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
new file mode 100644
index 0000000..f9d5ed5
--- /dev/null
+++ b/gpu/include/GrContext.h
@@ -0,0 +1,322 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+#ifndef GrContext_DEFINED

+#define GrContext_DEFINED

+

+#include "GrClip.h"

+#include "GrGpu.h"

+#include "GrSamplerState.h"

+#include "GrTextureCache.h"

+#include "GrInOrderDrawBuffer.h"

+#include "GrVertexBufferAllocPool.h"

+

+class GrFontCache;

+class GrPathIter;

+

+//TODO: move GrGpu enums/nested types here

+

+class GrContext : public GrRefCnt {

+public:

+    /**

+     * Creates a GrContext from within a 3D context.

+     */

+    static GrContext* Create(GrGpu::Engine engine,

+                             GrGpu::Platform3DContext context3D);

+

+    virtual ~GrContext();

+

+    /**

+     * The GrContext normally assumes that no outsider is setting state

+     * within the underlying 3D API's context/device/whatever. This call informs

+     * the context that the state was modified and it should resend. Shouldn't

+     * be called frequently for good performance.

+     */

+    void resetContext();

+

+    /**

+     *  Abandons all textures. Call this if you have lost the associated GPU

+     *  context, and thus internal texture references/IDs are now invalid.

+     */

+    void abandonAllTextures();

+

+    /**

+     *  Search for an entry with the same Key. If found, "lock" it and return it.

+     *  If not found, return null.

+     */

+    GrTextureEntry* findAndLockTexture(GrTextureKey*,

+                                       const GrSamplerState&);

+

+

+    /**

+     *  Create a new entry, based on the specified key and texture, and return

+     *  its "locked" entry.

+     *

+     *  Ownership of the texture is transferred to the Entry, which will unref()

+     *  it when we are purged or deleted.

+     */

+    GrTextureEntry* createAndLockTexture(GrTextureKey* key,

+                                         const GrSamplerState&,

+                                         const GrGpu::TextureDesc&,

+                                         void* srcData, size_t rowBytes);

+

+    /**

+     *  When done with an entry, call unlockTexture(entry) on it, which returns

+     *  it to the cache, where it may be purged.

+     */

+    void unlockTexture(GrTextureEntry* entry);

+

+    /**

+     *  Removes an texture from the cache. This prevents the texture from

+     *  being found by a subsequent findAndLockTexture() until it is

+     *  reattached. The entry still counts against the cache's budget and should

+     *  be reattached when exclusive access is no longer needed.

+     */

+    void detachCachedTexture(GrTextureEntry*);

+

+    /**

+     * Reattaches a texture to the cache and unlocks it. Allows it to be found

+     * by a subsequent findAndLock or be purged (provided its lock count is

+     * now 0.)

+     */

+    void reattachAndUnlockCachedTexture(GrTextureEntry*);

+

+    /**

+     * Creates a texture that is outside the cache. Does not count against

+     * cache's budget.

+     */

+    GrTexture* createUncachedTexture(const GrGpu::TextureDesc&,

+                                     void* srcData,

+                                     size_t rowBytes);

+

+    /**

+     * Wraps an externally-created rendertarget in a GrRenderTarget.

+     * e.g. in GL platforamRenderTarget is an FBO id.

+     */

+    GrRenderTarget* createPlatformRenderTarget(intptr_t platformRenderTarget,

+                                               int width, int height);

+

+    /**

+     *  Returns true if the specified use of an indexed texture is supported.

+     */

+    bool supportsIndex8PixelConfig(const GrSamplerState&, int width, int height);

+

+    ///////////////////////////////////////////////////////////////////////////

+

+    GrRenderTarget* currentRenderTarget() const;

+    void getViewMatrix(GrMatrix* m) const;

+    const GrClip& getClip() const { return fGpu->getClip(); }

+

+    void setRenderTarget(GrRenderTarget* target);

+    void setDefaultRenderTargetSize(uint32_t width, uint32_t height);

+    GrRenderTarget* defaultRenderTarget() { return fGpu->defaultRenderTarget(); }

+

+    void setTexture(GrTexture* texture);

+    void setSamplerState(const GrSamplerState&);

+    void setTextureMatrix(const GrMatrix& m);

+

+    void setAntiAlias(bool);

+    void setDither(bool);

+    void setAlpha(uint8_t alpha);

+    void setColor(GrColor color);

+    void setPointSize(float size);

+    void setBlendFunc(GrGpu::BlendCoeff srcCoef, GrGpu::BlendCoeff dstCoef);

+    void setViewMatrix(const GrMatrix& m);

+    void setClip(const GrClip&);

+

+    /**

+     *  Erase the entire render target, ignoring any clips/scissors.

+     */

+    void eraseColor(GrColor color);

+

+    /**

+     *  Draw everywhere (respecting the clip) with the current color.

+     */

+    void drawFull(bool useTexture);

+

+    /**

+     *  Draw the rect, respecting the current texture if useTexture is true.

+     *  If strokeWidth < 0, then the rect is filled, else the rect is stroked

+     *  based on strokeWidth. If strokeWidth == 0, then the stroke is always

+     *  a single pixel thick.

+     */

+    void drawRect(const GrRect&, bool useTexture, GrScalar strokeWidth);

+

+    void fillRect(const GrRect& rect, bool useTexture) {

+        this->drawRect(rect, useTexture, -1);

+    }

+

+    /**

+     * Path filling rules

+     */

+    enum PathFills {

+        kWinding_PathFill,

+        kEvenOdd_PathFill,

+        kInverseWinding_PathFill,

+        kInverseEvenOdd_PathFill,

+        kHairLine_PathFill,

+

+        kPathFillCount

+    };

+

+    /**

+     * Tessellates and draws a path.

+     *

+     * @param path          the path to draw

+     * @param paint         the paint to set before drawing

+     * @param useTexture    if true the path vertices will also be used as

+     *                      texture coorindates referencing last texture passed

+     *                      to setTexture.

+     */

+    void drawPath(GrPathIter* path,

+                  PathFills fill,

+                  bool useTexture,

+                  const GrPoint* translate = NULL);

+

+    /**

+     * Call to ensure all drawing to the context has been issued to the

+     * underlying 3D API.

+     * if flushRenderTarget is true then after the call the last

+     * rendertarget set will be current in the underlying 3D API, otherwise

+     * it may not be. It is useful to set if the caller plans to use the 3D

+     * context outside of Ganesh to render into the current RT.

+     */

+    void flush(bool flushRenderTarget);

+

+    /**

+     *  Return true on success, i.e. if we could copy the specified range of

+     *  pixels from the current render-target into the buffer, converting into

+     *  the specified pixel-config.

+     */

+    bool readPixels(int left, int top, int width, int height,

+                    GrTexture::PixelConfig, void* buffer);

+

+    /**

+     *  Copy the src pixels [buffer, stride, pixelconfig] into the current

+     *  render-target at the specified rectangle.

+     */

+    void writePixels(int left, int top, int width, int height,

+                     GrTexture::PixelConfig, const void* buffer, size_t stride);

+

+    /* -------------------------------------------------------

+     * Mimicking the GrGpu interface for now

+     * TODO: define appropriate higher-level API for context

+     */

+

+    GrVertexBuffer* createVertexBuffer(uint32_t size, bool dynamic);

+

+    GrIndexBuffer* createIndexBuffer(uint32_t size, bool dynamic);

+

+    bool reserveAndLockGeometry(GrVertexLayout    vertexLayout,

+                                uint32_t          vertexCount,

+                                uint32_t          indexCount,

+                                void**            vertices,

+                                void**            indices);

+

+    void drawIndexed(GrGpu::PrimitiveType type,

+                     uint32_t startVertex,

+                     uint32_t startIndex,

+                     uint32_t vertexCount,

+                     uint32_t indexCount);

+

+    void drawNonIndexed(GrGpu::PrimitiveType type,

+                        uint32_t startVertex,

+                        uint32_t vertexCount);

+

+    void setVertexSourceToArray(const void* array,

+                                GrVertexLayout vertexLayout);

+    void setIndexSourceToArray(const void* array);

+    void setVertexSourceToBuffer(GrVertexBuffer* buffer,

+                                GrVertexLayout vertexLayout);

+    void setIndexSourceToBuffer(GrIndexBuffer* buffer);

+

+    void releaseReservedGeometry();

+

+    void resetStats();

+

+    const GrGpu::Stats& getStats() const;

+

+    void printStats() const;

+

+    class AutoRenderTarget : ::GrNoncopyable {

+    public:

+        AutoRenderTarget(GrContext* context, GrRenderTarget* target) {

+            fContext = NULL;

+            fPrevTarget = context->currentRenderTarget();

+            if (fPrevTarget != target) {

+                context->setRenderTarget(target);

+                fContext = context;

+            }

+        }

+        ~AutoRenderTarget() {

+            if (fContext) {

+                fContext->setRenderTarget(fPrevTarget);

+            }

+        }

+    private:

+        GrContext*      fContext;

+        GrRenderTarget* fPrevTarget;

+    };

+

+    /* -------------------------------------------------------

+     */

+

+    // Intended only to be used within Ganesh:

+    GrGpu* getGpu() { return fGpu; }

+    GrFontCache* getFontCache() { return fFontCache; }

+    GrDrawTarget* getTextTarget();

+    void flushText();

+

+    const GrIndexBuffer* quadIndexBuffer() const;

+    int maxQuadsInIndexBuffer() const;

+

+private:

+    GrGpu*          fGpu;

+    GrTextureCache* fTextureCache;

+    GrFontCache*    fFontCache;

+

+    GrVertexBufferAllocPool fVBAllocPool;

+    GrInOrderDrawBuffer     fTextDrawBuffer;

+

+    GrContext(GrGpu* gpu);

+    bool finalizeTextureKey(GrTextureKey*, const GrSamplerState&) const;

+

+    void drawClipIntoStencil();

+};

+

+/**

+ *  Save/restore the view-matrix in the context.

+ */

+class GrAutoViewMatrix : GrNoncopyable {

+public:

+    GrAutoViewMatrix(GrContext* ctx) : fContext(ctx) {

+        ctx->getViewMatrix(&fMatrix);

+    }

+    GrAutoViewMatrix(GrContext* ctx, const GrMatrix& matrix) : fContext(ctx) {

+        ctx->getViewMatrix(&fMatrix);

+        ctx->setViewMatrix(matrix);

+    }

+    ~GrAutoViewMatrix() {

+        fContext->setViewMatrix(fMatrix);

+    }

+

+private:

+    GrContext*  fContext;

+    GrMatrix    fMatrix;

+};

+

+#endif

+
diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h
new file mode 100644
index 0000000..fee13a6
--- /dev/null
+++ b/gpu/include/GrDrawTarget.h
@@ -0,0 +1,736 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrDrawTarget_DEFINED
+#define GrDrawTarget_DEFINED
+
+#include "GrScalar.h"
+#include "GrMatrix.h"
+#include "GrColor.h"
+#include "GrRefCnt.h"
+#include "GrSamplerState.h"
+#include "GrClip.h"
+
+class GrTexture;
+class GrRenderTarget;
+class GrClipIterator;
+class GrVertexBuffer;
+class GrIndexBuffer;
+
+class GrDrawTarget : public GrRefCnt {
+public:
+    /**
+     * Geometric primitives used for drawing.
+     */
+    enum PrimitiveType {
+        kTriangles_PrimitiveType,
+        kTriangleStrip_PrimitiveType,
+        kTriangleFan_PrimitiveType,
+        kPoints_PrimitiveType,
+        kLines_PrimitiveType,
+        kLineStrip_PrimitiveType
+    };
+
+    /**
+     *  Flags that affect rendering. Controlled using enable/disableState(). All
+     *  default to disabled.
+     */
+    enum StateBits {
+        kDither_StateBit          = 0x1,//<! Perform color dithering
+        kAntialias_StateBit       = 0x2,//<! Perform anti-aliasing. The render-
+                                        //   target must support some form of AA
+                                        //   (msaa, coverage sampling, etc). For
+                                        //   GrGpu-created rendertarget/textures
+                                        //   this is controlled by parameters
+                                        //   passed to createTexture.
+        kClip_StateBit            = 0x4,//<! Controls whether drawing is clipped
+                                        //   against the region specified by
+                                        //   setClip.
+    };
+
+    /**
+     * Coeffecients for alpha-blending.
+     */
+    enum BlendCoeff {
+        kZero_BlendCoeff,    //<! 0
+        kOne_BlendCoeff,     //<! 1
+        kSC_BlendCoeff,      //<! src color
+        kISC_BlendCoeff,     //<! one minus src color
+        kDC_BlendCoeff,      //<! dst color
+        kIDC_BlendCoeff,     //<! one minus dst color
+        kSA_BlendCoeff,      //<! src alpha
+        kISA_BlendCoeff,     //<! one minus src alpha
+        kDA_BlendCoeff,      //<! dst alpha
+        kIDA_BlendCoeff,     //<! one minus dst alpha
+    };
+
+    /**
+     * StencilPass
+     *
+     * Sets the stencil state for subsequent draw calls. Used to fill paths.
+     *
+     * Winding requires two passes when the GPU/API doesn't support separate
+     * stencil.
+     *
+     * The color pass for path fill is used to zero out stencil bits used for
+     * path filling. Every pixel covere by a winding/EO stencil pass must get
+     * covered by the color pass in order to leave stencil buffer in the correct
+     * state for the next path draw.
+     *
+     * NOTE: Stencil-based Winding fill has alias-to-zero problems. (e.g. A
+     * winding count of 128,256,512,etc with a 8 bit stencil buffer
+     * will be unfilled)
+     */
+    enum StencilPass {
+        kNone_StencilPass,            //<! Not drawing a path or clip.
+        kEvenOddStencil_StencilPass,  //<! records in/out in stencil buffer
+                                      //   using the Even/Odd fill rule.
+        kEvenOddColor_StencilPass,    //<! writes colors to color target in
+                                      //   pixels marked inside the fill by
+                                      //   kEOFillStencil_StencilPass. Clears
+                                      //   stencil in pixels covered by
+                                      //   geometry.
+        kWindingStencil1_StencilPass, //<! records in/out in stencil buffer
+                                      //   using the Winding fill rule.
+        kWindingStencil2_StencilPass, //<! records in/out in stencil buffer
+                                      //   using the Winding fill rule.
+                                      //   Run when single-stencil-pass winding
+                                      //   not supported (i.e. no separate
+                                      //   stencil support)
+        kWindingColor_StencilPass,    //<! writes colors to color target in
+                                      //   pixels marked inside the fill by
+                                      //   kWindFillStencil_StencilPass. Clears
+                                      //   stencil in pixels covered by
+                                      //   geometry.
+        kDrawTargetCount_StencilPass  //<! Subclass may extend this enum to use
+                                      //   the stencil for other purposes (e.g.
+                                      //   to do stencil-based clipping)
+                                      //   This value is provided as basis for
+                                      //   defining these extended enum values.
+    };
+
+protected:
+    enum MatrixMode {
+        kModelView_MatrixMode = 0,
+        kTexture_MatrixMode,
+
+        kMatrixModeCount
+    };
+
+    struct DrawState {
+        uint32_t                fFlagBits;
+        BlendCoeff          	fSrcBlend;
+        BlendCoeff          	fDstBlend;
+        GrTexture*          	fTexture;
+        GrSamplerState        	fSamplerState;
+        GrRenderTarget*     	fRenderTarget;
+        GrColor             	fColor;
+        float               	fPointSize;
+        StencilPass             fStencilPass;
+        bool                    fReverseFill;
+        GrMatrix                fMatrixModeCache[kMatrixModeCount];
+        bool operator ==(const DrawState& s) const {
+            return 0 == memcmp(this, &s, sizeof(DrawState));
+        }
+        bool operator !=(const DrawState& s) const { return !(*this == s); }
+    };
+
+public:
+    ///////////////////////////////////////////////////////////////////////////
+
+    GrDrawTarget();
+
+    /**
+     * Sets the current clip to the region specified by clip. All draws will be
+     * clipped against this clip if kClip_StateBit is enabled.
+     *
+     * @param description of the clipping region
+     */
+    void setClip(const GrClip& clip);
+
+    /**
+     * Gets the current clip.
+     *
+     * @return the clip.
+     */
+    const GrClip& getClip() const;
+
+    /**
+     * Sets the texture used at the next drawing call
+     *
+     * @param texture The texture to set. Can be NULL though there is no advantage
+     * to settings a NULL texture if doing non-textured drawing
+     */
+    void setTexture(GrTexture* texture);
+
+    /**
+     * Retrieves the currently set texture.
+     *
+     * @return    The currently set texture. The return value will be NULL if no
+     *            texture has been set, NULL was most recently passed to
+     *            setTexture, or the last setTexture was destroyed.
+     */
+    GrTexture* currentTexture() const;
+
+    /**
+     * Sets the rendertarget used at the next drawing call
+     *
+     * @param target  The render target to set. Must be a valid rendertarget.
+     *                That is it is a value that was returned by
+     *                currentRenderTarget() or GrTexture::asRenderTarget().
+     */
+    void setRenderTarget(GrRenderTarget* target);
+
+    /**
+     * Retrieves the currently set rendertarget.
+     *
+     * @return    The currently set render target.
+     */
+    GrRenderTarget* currentRenderTarget() const;
+
+    /**
+     * Sets the sampler state for the next draw.
+     *
+     * The sampler state determines the address wrap modes and
+     * filtering
+     *
+     * @param samplerState    Specifies the sampler state.
+     */
+    void setSamplerState(const GrSamplerState& samplerState);
+
+    /**
+     * Sets the matrix applied to texture coordinates.
+     *
+     * The post-matrix texture coordinates in the square [0,1]^2 cover the
+     * entire area of the texture. This means the full POT width when a NPOT
+     * texture is embedded in a POT width texture to meet the 3D API
+     * requirements. The texture matrix is applied both when the texture
+     * coordinates are explicit and when vertex positions are used as texture
+     * coordinates. In the latter case the texture matrix is applied to the
+     * pre-modelview position values.
+     *
+     * @param m the matrix used to transform the texture coordinates.
+     */
+    void setTextureMatrix(const GrMatrix& m) {
+        this->loadMatrix(m, kTexture_MatrixMode);
+    }
+
+    /**
+     * Sets the matrix applied to veretx positions.
+     *
+     * In the post-view-matrix space the rectangle [0,w]x[0,h]
+     * fully covers the render target. (w and h are the width and height of the
+     * the rendertarget.)
+     *
+     * @param m the matrix used to transform the vertex positions.
+     */
+    void setViewMatrix(const GrMatrix& m) {
+        this->loadMatrix(m, kModelView_MatrixMode);
+    }
+
+    /**
+     *  Multiplies the current view matrix by a matrix
+     *
+     *  After this call V' = V*m where V is the old view matrix,
+     *  m is the parameter to this function, and V' is the new view matrix.
+     *  (We consider positions to be column vectors so position vector p is
+     *  transformed by matrix X as p' = X*p.)
+     *
+     *  @param m the matrix used to modify the modelview matrix.
+     */
+    void concatViewMatrix(const GrMatrix& m);
+
+    /**
+     *  Sets color for next draw to a premultiplied-alpha color.
+     *
+     *  @param the color to set.
+     */
+    void setColor(GrColor);
+
+    /**
+     *  Sets the color to be used for the next draw to be
+     *  (r,g,b,a) = (alpha, alpha, alpha, alpha).
+     *
+     *  @param alpha The alpha value to set as the color.
+     */
+    void setAlpha(uint8_t alpha);
+
+    /**
+     * Sets pass for path rendering
+     *
+     * @param pass of path rendering
+     */
+    void setStencilPass(StencilPass pass);
+
+    /**
+     * Reveses the in/out decision of the fill rule for path rendering.
+     * Only affects kEOFillColor_StencilPass and kWindingFillColor_StencilPass
+     *
+     * @param reverse true to reverse, false otherwise
+     */
+    void setReverseFill(bool reverse);
+
+    /**
+     * Enable render state settings.
+     *
+     * @param flags   bitfield of StateBits specifing the states to enable
+     */
+    void enableState(uint32_t stateBits);
+
+    /**
+     * Disable render state settings.
+     *
+     * @param flags   bitfield of StateBits specifing the states to disable
+     */
+    void disableState(uint32_t stateBits);
+
+    bool isDitherState() const {
+        return fCurrDrawState.fFlagBits & kDither_StateBit;
+    }
+
+    /**
+     * Sets the size of points used the next time points are drawn.
+     *
+     * @param the point size
+     */
+    void setPointSize(float size);
+
+    /**
+     * Sets the blending function coeffecients.
+     *
+     * The blend function will be:
+     *    D' = sat(S*srcCoef + D*dstCoef)
+     *
+     *   where D is the existing destination color, S is the incoming source
+     *   color, and D' is the new destination color that will be written. sat()
+     *   is the saturation function.
+     *
+     * @param srcCoef coeffecient applied to the src color.
+     * @param dstCoef coeffecient applied to the dst color.
+     */
+    void setBlendFunc(BlendCoeff srcCoef, BlendCoeff dstCoef);
+
+    /**
+     * Retrieves the current view matrix
+     * @param matrix will be the current view matrix after return.
+     */
+    void getViewMatrix(GrMatrix* matrix) const;
+
+    /**
+     *  Retrieves the inverse of the current view matrix.
+     *
+     *  If the current view matrix is invertible, return true, and if matrix
+     *  is non-null, copy the inverse into it. If the current view matrix is
+     *  non-invertible, return false and ignore the matrix parameter.
+     *
+     * @param matrix if not null, will receive a copy of the current inverse.
+     */
+    bool getViewInverse(GrMatrix* matrix) const;
+
+    /**
+     * Used to save and restore the GrGpu's drawing state
+     */
+    struct SavedDrawState {
+    private:
+        DrawState fState;
+        friend class GrDrawTarget;
+    };
+
+    /**
+     * Saves the current draw state. The state can be restored at a later time
+     * with restoreDrawState.
+     *
+     * See also AutoStateRestore class.
+     *
+     * @param   state will hold the state after the function returns.
+     */
+    void saveCurrentDrawState(SavedDrawState* state) const;
+
+    /**
+     * Restores previously saved draw state. The client guarantees that state
+     * was previously passed to saveCurrentDrawState and that the rendertarget
+     * and texture set at save are still valid.
+     *
+     * See also AutoStateRestore class.
+     *
+     * @param   state the previously saved state to restore.
+     */
+    void restoreDrawState(const SavedDrawState& state);
+
+    /**
+     * Copies the draw state from another target to this target.
+     *
+     * @param srcTarget     draw target used as src of the draw state.
+     */
+    void copyDrawState(const GrDrawTarget& srcTarget);
+
+    /**
+     * Flags that indicate the layout of vertex data.
+     *
+     * kSeparateTexCoord_VertexLayoutBit is incompatible with
+     * kPositionAsTexCoord_VertexLayoutBit. kTextFormat_VertexLayoutBit is
+     * incompatible with any other flags.
+     *
+     * When kTextFormat_VertexLayoutBit is set:
+     *      Texture coordinates are separate.
+     *      Positions and Texture coordinates are SkGpuTextVertex.
+     * For non-text vertices:
+     *      Position and texture coordinates are GrPoints.
+     *      Colors are GrColors.
+     *
+     * The order is always positions, texture coords, colors.
+     */
+    enum VertexLayoutBits {
+        kSeparateTexCoord_VertexLayoutBit   = 0x1, //<! vertices have texture
+                                                   //   coords that are not
+                                                   //   inferred from the
+                                                   //   positions
+        kPositionAsTexCoord_VertexLayoutBit = 0x2, //<! vertices use positions
+                                                   //   as texture coords.
+        kColor_VertexLayoutBit              = 0x4, //<! vertices have colors
+        kTextFormat_VertexLayoutBit         = 0x8, //<! vertices represent glyphs
+                                                   //   and therefore contain
+                                                   //   two GrGpuTextVertexs.
+                                                   //   One for pos and one for
+                                                   //   text coords.
+        // for below assert
+        kDummy,
+        kHighVertexLayoutBit = kDummy - 1
+    };
+    GR_STATIC_ASSERT(kHighVertexLayoutBit < (1 << 8*sizeof(GrVertexLayout)));
+
+    /**
+     * Reserves space for vertices and/or indices. Draw target will use
+     * reserved vertices / indices at next draw.
+     *
+     * If succeeds:
+     *          if vertexCount is nonzero, *vertices will be the array
+     *          of vertices to be filled by caller. The next draw will read
+     *          these vertices.
+     *
+     *          if indecCount is nonzero, *indices will be the array of indices
+     *          to be filled by caller. The next indexed draw will read from
+     *          these indices.
+     *
+     * If a client does not already have a vertex buffer or cpu arrays then this
+     * is the preferred way to allocate vertex/index array. It allows the
+     * subclass of GrDrawTarget to decide whether to put data in buffers, to
+     * group vertex data that uses the same state (e.g. for deferred rendering),
+     * etc.
+     *
+     * This must be matched with a releaseReservedGeometry call after all
+     * draws that reference the reserved geometry data have been called.
+     *
+     * AutoGeometryRelease can be used to automatically call the release.
+     *
+     * @param vertexCount  the number of vertices to reserve space for. Can be 0.
+     * @param indexCount   the number of indices to reserve space for. Can be 0.
+     * @param vertexLayout the format of vertices (ignored if vertexCount == 0).
+     * @param vertices     will point to reserved vertex space if vertexCount is
+     *                     non-zero. Illegal to pass NULL if vertexCount > 0.
+     * @param indices      will point to reserved index space if indexCount is
+     *                     non-zero. Illegal to pass NULL if indexCount > 0.
+     *
+     * @return  true if succeeded in allocating space for the vertices and false
+     *               if not.
+     */
+    bool reserveAndLockGeometry(GrVertexLayout    vertexLayout,
+                                uint32_t          vertexCount,
+                                uint32_t          indexCount,
+                                void**            vertices,
+                                void**            indices);
+    /**
+     * Provides hints to caller about the number of vertices and indices
+     * that can be allocated cheaply. This can be useful if caller is reserving
+     * space but doesn't know exactly how much geometry is needed.
+     *
+     * Also may hint whether the draw target should be flushed first. This is
+     * useful for deferred targets.
+     *
+     * @param vertexLayout layout of vertices caller would like to reserve
+     * @param vertexCount  in: hint about how many vertices the caller would
+     *                     like to allocate.
+     *                     out: a hint about the number of vertices that can be
+     *                     allocated cheaply. Negative means no hint.
+     *                     Ignored if NULL.
+     * @param indexCount   in: hint about how many indices the caller would
+     *                     like to allocate.
+     *                     out: a hint about the number of indices that can be
+     *                     allocated cheaply. Negative means no hint.
+     *                     Ignored if NULL.
+     *
+     * @return  true if target should be flushed based on the input values.
+     */
+    virtual bool geometryHints(GrVertexLayout vertexLayout,
+                               int32_t*       vertexCount,
+                               int32_t*       indexCount) const;
+
+    /**
+     * Releases reserved vertex/index data from reserveAndLockGeometry().
+     */
+    void releaseReservedGeometry();
+
+    /**
+     * Sets source of vertex data for the next draw. Data does not have to be
+     * in the array until drawIndexed or drawNonIndexed.
+     *
+     * @param array         cpu array containing vertex data.
+     * @param vertexLayout  layout of the vertex data in the array.
+     */
+    void setVertexSourceToArray(const void* array, GrVertexLayout vertexLayout);
+
+    /**
+     * Sets source of index data for the next indexed draw. Data does not have
+     * to be in the array until drawIndexed or drawNonIndexed.
+     *
+     * @param array cpu array containing index data.
+     */
+    void setIndexSourceToArray(const void* array);
+
+    /**
+     * Sets source of vertex data for the next draw. Data does not have to be
+     * in the buffer until drawIndexed or drawNonIndexed.
+     *
+     * @param buffer        vertex buffer containing vertex data. Must be
+     *                      unlocked before draw call.
+     * @param vertexLayout  layout of the vertex data in the buffer.
+     */
+    void setVertexSourceToBuffer(const GrVertexBuffer* buffer,
+                                 GrVertexLayout vertexLayout);
+
+    /**
+     * Sets source of index data for the next indexed draw. Data does not have
+     * to be in the buffer until drawIndexed or drawNonIndexed.
+     *
+     * @param buffer index buffer containing indices. Must be unlocked
+     *               before indexed draw call.
+     */
+    void setIndexSourceToBuffer(const GrIndexBuffer* buffer);
+
+    /**
+     * Draws indexed geometry using the current state and current vertex / index
+     * sources.
+     *
+     * @param type         The type of primitives to draw.
+     * @param startVertex  the vertex in the vertex array/buffer corresponding
+     *                     to index 0
+     * @param startIndex   first index to read from index src.
+     * @param vertexCount  one greater than the max index.
+     * @param indexCount   the number of index elements to read. The index count
+     *                     is effectively trimmed to the last completely
+     *                     specified primitive.
+     */
+    virtual void drawIndexed(PrimitiveType type,
+                             uint32_t startVertex,
+                             uint32_t startIndex,
+                             uint32_t vertexCount,
+                             uint32_t indexCount) = 0;
+
+    /**
+     * Draws non-indexed geometry using the current state and current vertex
+     * sources.
+     *
+     * @param type         The type of primitives to draw.
+     * @param startVertex  the vertex in the vertex array/buffer corresponding
+     *                     to index 0
+     * @param vertexCount  one greater than the max index.
+     */
+    virtual void drawNonIndexed(PrimitiveType type,
+                                uint32_t startVertex,
+                                uint32_t vertexCount)  = 0;
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class AutoStateRestore : ::GrNoncopyable {
+    public:
+        AutoStateRestore(GrDrawTarget* target);
+        ~AutoStateRestore();
+
+    private:
+        GrDrawTarget*       fDrawTarget;
+        SavedDrawState      fDrawState;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class AutoReleaseGeometry : ::GrNoncopyable {
+    public:
+        AutoReleaseGeometry(GrDrawTarget*  target,
+                            GrVertexLayout vertexLayout,
+                            uint32_t       vertexCount,
+                            uint32_t       indexCount) {
+            fTarget = target;
+            fSuccess = fTarget->reserveAndLockGeometry(vertexLayout,
+                                                       vertexCount,
+                                                       indexCount,
+                                                       &fVertices,
+                                                       &fIndices);
+        }
+        ~AutoReleaseGeometry() {
+            if (fSuccess) {
+                fTarget->releaseReservedGeometry();
+            }
+        }
+
+        bool succeeded() const { return fSuccess; }
+        void* vertices() const { return fVertices; }
+        void* indices() const { return fIndices; }
+
+        GrPoint* positions() const {
+            return static_cast<GrPoint*>(fVertices);
+        }
+
+    private:
+        GrDrawTarget* fTarget;
+        bool          fSuccess;
+        void*         fVertices;
+        void*         fIndices;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class AutoClipRestore : ::GrNoncopyable {
+    public:
+        AutoClipRestore(GrDrawTarget* target) {
+            fTarget = target;
+            fClip = fTarget->getClip();
+        }
+
+        ~AutoClipRestore() {
+            fTarget->setClip(fClip);
+        }
+    private:
+        GrDrawTarget* fTarget;
+        GrClip        fClip;
+    };
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Helper function to compute the size of a vertex from a vertex layout
+     * @return size of a single vertex.
+     */
+    static size_t VertexSize(GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to compute the offset of texture coordinates in a vertex
+     * @return offset of texture coordinates in vertex layout or -1 if the
+     *         layout has no texture coordinates.
+     */
+    static int VertexTexCoordOffset(GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to compute the offset of the color in a vertex
+     * @return offset of color in vertex layout or -1 if the
+     *         layout has no color.
+     */
+    static int VertexColorOffset(GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to compute vertex size and component offsets.
+     * @param texCoordOffset  after return it is the offset of texture coords
+     *                        in vertex layout or -1 if the layout has no
+     *                        texture coords.
+     * @param colorOffset     after return it is the offset of color in vertex
+     *                        layout or -1 if the layout has no color.
+     * @return size of a single vertex.
+     */
+    static int VertexSizeAndOffsets(GrVertexLayout vertexLayout,
+                                    int* texCoordOffset,
+                                    int* colorOffset);
+    /**
+     * Helper function to determine if vertex layout contains either explicit or
+     * implicit texture coordinates.
+     *
+     * @return true if vertex specifies texture coordinates, false otherwise.
+     */
+    static bool VertexHasTexCoords(GrVertexLayout vertexLayout);
+
+protected:
+
+    // Helpers for GrDrawTarget subclasses that won't have private access to
+    // SavedDrawState but need to peek at the state values.
+    static DrawState& accessSavedDrawState(SavedDrawState& sds)
+                                                        { return sds.fState; }
+    static const DrawState& accessSavedDrawState(const SavedDrawState& sds)
+                                                        { return sds.fState; }
+
+    // implemented by subclass
+    virtual bool acquireGeometryHelper(GrVertexLayout vertexLayout,
+                                       void** vertices,
+                                       void** indices) = 0;
+
+    virtual void releaseGeometryHelper() = 0;
+
+    virtual void clipWillChange(const GrClip& clip) = 0;
+
+    enum GeometrySrcType {
+        kArray_GeometrySrcType,
+        kReserved_GeometrySrcType,
+        kBuffer_GeometrySrcType
+    };
+
+    struct {
+        bool            fLocked;
+        uint32_t        fVertexCount;
+        uint32_t        fIndexCount;
+    } fReservedGeometry;
+
+    struct GeometrySrc {
+        GeometrySrcType             fVertexSrc;
+        union {
+            const GrVertexBuffer*   fVertexBuffer;
+            const void*             fVertexArray;
+        };
+        GeometrySrcType             fIndexSrc;
+        union {
+            const GrIndexBuffer*    fIndexBuffer;
+            const void*             fIndexArray;
+        };
+        GrVertexLayout              fVertexLayout;
+    } fGeometrySrc;
+
+    GrClip fClip;
+
+    DrawState fCurrDrawState;
+
+    // set texture or modelview matrix
+    void loadMatrix(const GrMatrix&, MatrixMode);
+
+    // not meant for outside usage. Could cause problems if calls between
+    // the save and restore mess with reserved geometry state.
+    class AutoGeometrySrcRestore {
+    public:
+        AutoGeometrySrcRestore(GrDrawTarget* target) {
+            fTarget = target;
+            fGeometrySrc = fTarget->fGeometrySrc;
+        }
+        ~AutoGeometrySrcRestore() {
+            fTarget->fGeometrySrc = fGeometrySrc;
+        }
+    private:
+        GrDrawTarget *fTarget;
+        GeometrySrc  fGeometrySrc;
+
+        AutoGeometrySrcRestore();
+        AutoGeometrySrcRestore(const AutoGeometrySrcRestore&);
+        AutoGeometrySrcRestore& operator =(AutoGeometrySrcRestore&);
+    };
+
+};
+
+#endif
diff --git a/gpu/include/GrFontScaler.h b/gpu/include/GrFontScaler.h
new file mode 100644
index 0000000..6baa56f
--- /dev/null
+++ b/gpu/include/GrFontScaler.h
@@ -0,0 +1,43 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrFontScaler_DEFINED
+#define GrFontScaler_DEFINED
+
+#include "GrGlyph.h"
+#include "GrKey.h"
+
+class GrPath;
+
+/**
+ *  This is a virtual base class which Gr's interface to the host platform's
+ *  font scaler.
+ *
+ *  The client is responsible for subclassing, and instantiating this. The
+ *  instance is create for a specific font+size+matrix.
+ */
+class GrFontScaler : public GrRefCnt {
+public:
+    virtual const GrKey* getKey() = 0;
+    virtual bool getPackedGlyphBounds(GrGlyph::PackedID, GrIRect* bounds) = 0;
+    virtual bool getPackedGlyphImage(GrGlyph::PackedID, int width, int height,
+                                     int rowBytes, void* image) = 0;
+    virtual bool getGlyphPath(uint16_t glyphID, GrPath*) = 0;
+};
+
+#endif
+
diff --git a/gpu/include/GrGLConfig.h b/gpu/include/GrGLConfig.h
new file mode 100644
index 0000000..90763ab
--- /dev/null
+++ b/gpu/include/GrGLConfig.h
@@ -0,0 +1,323 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGLConfig_DEFINED
+#define GrGLConfig_DEFINED
+
+#include "GrTypes.h"
+
+#if GR_WIN32_BUILD
+    // glew has to be included before gl
+    #define GR_INCLUDE_GLDESKTOP    <GL/glew.h>
+    #define GR_INCLUDE_GLDESKTOPext <GL/gl.h>
+    #define GR_GL_FUNC __stdcall
+    // undo stupid windows defines
+    #undef near
+    #undef far
+#elif GR_MAC_BUILD
+    #define GR_INCLUDE_GLDESKTOP    <OpenGL/gl.h>
+    #define GR_INCLUDE_GLDESKTOPext <OpenGL/glext.h>
+    #define GR_GL_FUNC
+#elif GR_IOS_BUILD
+    #define GR_INCLUDE_GLES1       <OpenGLES/ES1/gl.h>
+    #define GR_INCLUDE_GLES1ext    <OpenGLES/ES1/glext.h>
+    #define GR_INCLUDE_GLES2       <OpenGLES/ES2/gl.h>
+    #define GR_INCLUDE_GLES2ext    <OpenGLES/ES2/glext.h>
+    #define GR_GL_FUNC
+#elif GR_ANDROID_BUILD
+    #ifndef GL_GLEXT_PROTOTYPES
+        #define GL_GLEXT_PROTOTYPES
+    #endif
+    #define GR_INCLUDE_GLES2        <GLES2/gl2.h>
+    #define GR_INCLUDE_GLES2ext     <GLES2/gl2ext.h>
+    #define GR_GL_FUNC
+#elif GR_LINUX_BUILD
+    // need to distinguish between ES and Deskop versions for linux
+    #ifndef GL_GLEXT_PROTOTYPES
+        #define GL_GLEXT_PROTOTYPES
+    #endif
+    #define GR_INCLUDE_GLES1        <GLES/gl.h>
+    #define GR_INCLUDE_GLES1ext     <GLES/glext.h>
+    #define GR_INCLUDE_GLES2        <GLES2/gl2.h>
+    #define GR_INCLUDE_GLES2ext     <GLES2/gl2ext.h>
+    #define GR_GL_FUNC
+#elif GR_QNX_BUILD
+    #ifndef GL_GLEXT_PROTOTYPES
+        #define GL_GLEXT_PROTOTYPES
+    #endif
+    // This is needed by the QNX GLES2 headers
+    #define GL_API_EXT
+    #define GR_INCLUDE_GLES2        <GLES2/gl2.h>
+    #define GR_INCLUDE_GLES2ext     <GLES2/gl2ext.h>
+    #define GR_INCLUDE_EGL          <EGL/egl.h>
+    #define GR_GL_FUNC
+#else
+    #error "unsupported GR_???_BUILD"
+#endif
+
+// Ensure we're at least defined
+//
+
+#ifndef GR_SUPPORT_GLES1
+    #if defined(GR_INCLUDE_GLES1)
+        #define GR_SUPPORT_GLES1        1
+    #else
+        #define GR_SUPPORT_GLES1        0
+    #endif
+#endif
+
+#ifndef GR_SUPPORT_GLES2
+    #if defined(GR_INCLUDE_GLES2)
+        #define GR_SUPPORT_GLES2        1
+    #else
+        #define GR_SUPPORT_GLES2        0
+    #endif
+#endif
+
+#ifndef GR_SUPPORT_GLDESKTOP
+    #if defined(GR_INCLUDE_GLDESKTOP)
+        #define GR_SUPPORT_GLDESKTOP    1
+    #else
+        #define GR_SUPPORT_GLDESKTOP    0
+    #endif
+#endif
+
+#ifndef GR_SUPPORT_EGL
+    #if defined(GR_INCLUDE_EGL)
+        #define GR_SUPPORT_EGL          1
+    #else
+        #define GR_SUPPORT_EGL          0
+    #endif
+#endif
+// Filter the includes based on what we support
+//
+
+#if !GR_SUPPORT_GLES1
+    #undef GR_INCLUDE_GLES1
+    #undef GR_INCLUDE_GLES1ext
+#endif
+
+#if !GR_SUPPORT_GLES2
+    #undef GR_INCLUDE_GLES2
+    #undef GR_INCLUDE_GLES2ext
+#endif
+
+#if !GR_SUPPORT_GLDESKTOP
+    #undef GR_INCLUDE_GLDESKTOP
+    #undef GR_INCLUDE_GLDESKTOPext
+#endif
+
+#if !GR_SUPPORT_EGL
+    #undef GR_INCLUDE_EGL
+#endif
+
+// Begin including GL headers
+//
+
+#ifdef GR_INCLUDE_GLES1
+    #include GR_INCLUDE_GLES1
+#endif
+#ifdef GR_INCLUDE_GLES1ext
+    #include GR_INCLUDE_GLES1ext
+#endif
+#ifdef GR_INCLUDE_GLES2
+    #include GR_INCLUDE_GLES2
+#endif
+#ifdef GR_INCLUDE_GLES2ext
+    #include GR_INCLUDE_GLES2ext
+#endif
+#ifdef GR_INCLUDE_GLDESKTOP
+    #include GR_INCLUDE_GLDESKTOP
+#endif
+#ifdef GR_INCLUDE_GLDESKTOPext
+    #include GR_INCLUDE_GLDESKTOPext
+#endif
+#ifdef GR_INCLUDE_EGL
+    #include GR_INCLUDE_EGL
+#endif
+
+//
+// End including GL headers
+
+#if GL_VERSION_1_1
+    #define GR_GL_DESKTOP   1
+    #define GR_GL_ES        0
+#else
+    #ifndef GL_ES_VERSION_2_0
+        GR_STATIC_ASSERT(GL_VERSION_ES_CM_1_0 || 
+                         GL_VERSION_ES_CL_1_0 || 
+                         GL_VERSION_ES_CM_1_1 ||
+                         GL_VERSION_ES_CL_1_1);
+    #endif
+    #define GR_GL_DESKTOP   0
+    #define GR_GL_ES        1
+#endif
+
+#if GR_SCALAR_IS_FIXED
+    #define GrGLType   GL_FIXED
+#elif GR_SCALAR_IS_FLOAT
+    #define GrGLType   GL_FLOAT
+#else
+    #error "unknown GR_SCALAR type"
+#endif
+
+#if GR_TEXT_SCALAR_IS_USHORT
+    #define GrGLTextType                    GL_UNSIGNED_SHORT
+    #define GR_GL_TEXT_TEXTURE_NORMALIZED   1
+#elif GR_TEXT_SCALAR_IS_FLOAT
+    #define GrGLTextType                    GL_FLOAT
+    #define GR_GL_TEXT_TEXTURE_NORMALIZED   0
+#elif GR_TEXT_SCALAR_IS_FIXED
+    #define GrGLTextType                    GL_FIXED
+    #define GR_GL_TEXT_TEXTURE_NORMALIZED   0
+#else 
+    #error "unknown GR_TEXT_SCALAR type"
+#endif
+
+// Pick a pixel config for 32bit bitmaps. Our default is GL_RGBA
+#ifndef SK_GL_32BPP_COLOR_FORMAT    
+    #define SK_GL_32BPP_COLOR_FORMAT    GL_RGBA    
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// setup for opengl ES/desktop extensions
+// we make a struct of function pointers so that each GL context
+// can have it's own struct. (Some environments may have different proc 
+// addresses for different contexts).
+
+extern "C" {
+struct GrGLExts {
+// FBO
+    GLvoid (GR_GL_FUNC *GenFramebuffers)(GLsizei n, GLuint *framebuffers);
+    GLvoid (GR_GL_FUNC *BindFramebuffer)(GLenum target, GLuint framebuffer);
+    GLvoid (GR_GL_FUNC *FramebufferTexture2D)(GLenum target, GLenum attachment,
+                                              GLenum textarget, GLuint texture, 
+                                              GLint level);
+    GLenum (GR_GL_FUNC *CheckFramebufferStatus)(GLenum target);
+    GLvoid (GR_GL_FUNC *DeleteFramebuffers)(GLsizei n, const 
+                                            GLuint *framebuffers);
+    GLvoid (GR_GL_FUNC *RenderbufferStorage)(GLenum target, 
+                                             GLenum internalformat,
+                                             GLsizei width, GLsizei height);
+    GLvoid (GR_GL_FUNC *GenRenderbuffers)(GLsizei n, GLuint *renderbuffers);
+    GLvoid (GR_GL_FUNC *DeleteRenderbuffers)(GLsizei n, 
+                                             const GLuint *renderbuffers);
+    GLvoid (GR_GL_FUNC *FramebufferRenderbuffer)(GLenum target, 
+                                                 GLenum attachment,
+                                                 GLenum renderbuffertarget, 
+                                                 GLuint renderbuffer);
+    GLvoid (GR_GL_FUNC *BindRenderbuffer)(GLenum target, GLuint renderbuffer);
+
+// Multisampling
+    // same prototype for ARB_FBO, EXT_FBO, GL 3.0, & Apple ES extension
+    GLvoid (GR_GL_FUNC *RenderbufferStorageMultisample)(GLenum target, 
+                                                        GLsizei samples,
+                                                        GLenum internalformat,
+                                                        GLsizei width, 
+                                                        GLsizei height);
+    // desktop: ext_fbo_blit, arb_fbo, gl 3.0
+    GLvoid (GR_GL_FUNC *BlitFramebuffer)(GLint srcX0, GLint srcY0, 
+                                         GLint srcX1, GLint srcY1,
+                                         GLint dstX0, GLint dstY0, 
+                                         GLint dstX1, GLint dstY1,
+                                         GLbitfield mask, GLenum filter);
+    // apple's es extension
+    GLvoid (GR_GL_FUNC *ResolveMultisampleFramebuffer)();
+
+    // IMG'e es extension
+    GLvoid (GR_GL_FUNC *FramebufferTexture2DMultisample)(GLenum target, 
+                                                         GLenum attachment,
+                                                         GLenum textarget, 
+                                                         GLuint texture, 
+                                                         GLint level, 
+                                                         GLsizei samples);
+
+// Buffer mapping (extension in ES).
+    GLvoid* (GR_GL_FUNC *MapBuffer)(GLenum target, GLenum access);
+    GLboolean (GR_GL_FUNC *UnmapBuffer)(GLenum target);
+};
+}
+
+// FBO
+#define GR_FRAMEBUFFER              0x8D40
+#define GR_FRAMEBUFFER_COMPLETE     0x8CD5
+#define GR_COLOR_ATTACHMENT0        0x8CE0
+#define GR_FRAMEBUFFER_BINDING      0x8CA6          
+#define GR_RENDERBUFFER             0x8D41
+#define GR_STENCIL_ATTACHMENT       0x8D20
+#define GR_STENCIL_INDEX8           0x8D48
+#define GR_STENCIL_INDEX16          0x8D49
+#define GR_MAX_RENDERBUFFER_SIZE    0x84E8
+#define GR_DEPTH_STENCIL_ATTACHMENT 0x821A
+#define GR_UNSIGNED_INT_24_8        0x84FA
+#define GR_DEPTH_STENCIL            0x84F9
+#define GR_RGBA8                    0x8058
+#define GR_RGB565                   0x8D62
+
+
+// Multisampling
+
+// IMG MAX_SAMPLES uses a different value than desktop, Apple ES extension.
+#define GR_MAX_SAMPLES              0x8D57
+#define GR_MAX_SAMPLES_IMG          0x9135
+#define GR_READ_FRAMEBUFFER         0x8CA8
+#define GR_DRAW_FRAMEBUFFER         0x8CA9
+
+// Buffer mapping
+#define GR_WRITE_ONLY               0x88B9
+#define GR_BUFFER_MAPPED            0x88BC
+
+// Palette texture
+#define GR_PALETTE8_RGBA8           0x8B91
+
+extern void GrGLInitExtensions(GrGLExts* exts);
+////////////////////////////////////////////////////////////////////////////////
+          
+extern void GrGLCheckErr(const char* location, const char* call);
+
+static inline void GrGLClearErr() {
+    while (GL_NO_ERROR != glGetError()) {} 
+}
+
+// GR_FORCE_GLCHECKERR can be defined by GrUserConfig.h
+#if defined(GR_FORCE_GLCHECKERR)
+    #define GR_LOCAL_CALL_CHECKERR GR_FORCE_GLCHECKERR
+#else
+    #define GR_LOCAL_CALL_CHECKERR GR_DEBUG
+#endif
+static inline void GrDebugGLCheckErr(const char* location, const char* call) {
+#if GR_LOCAL_CALL_CHECKERR
+    GrGLCheckErr(location, call);
+#endif
+}
+#undef GR_LOCAL_CALL_CHECKERR
+
+#if GR_GL_LOG_CALLS
+    extern bool gPrintGL;
+    #define GR_GL(X)                 gl ## X; GrDebugGLCheckErr(GR_FILE_AND_LINE_STR, #X); if (gPrintGL) GrPrintf(GR_FILE_AND_LINE_STR "GL: " #X "\n")
+    #define GR_GL_NO_ERR(X)          GrGLClearErr(); gl ## X; if (gPrintGL) GrPrintf(GR_FILE_AND_LINE_STR "GL: " #X "\n")
+    #define GR_GLEXT(exts, X)        exts. X; GrDebugGLCheckErr(GR_FILE_AND_LINE_STR, #X); if (gPrintGL) GrPrintf(GR_FILE_AND_LINE_STR "GL: " #X "\n")
+    #define GR_GLEXT_NO_ERR(exts, X) GrGLClearErr(); exts. X; if (gPrintGL) GrPrintf(GR_FILE_AND_LINE_STR "GL: " #X "\n")
+#else
+    #define GR_GL(X)                 gl ## X; GrDebugGLCheckErr(GR_FILE_AND_LINE_STR, #X)
+    #define GR_GL_NO_ERR(X)          GrGLClearErr(); gl ## X
+    #define GR_GLEXT(exts, X)        exts. X; GrDebugGLCheckErr(GR_FILE_AND_LINE_STR, #X)
+    #define GR_GLEXT_NO_ERR(exts, X) GrGLClearErr(); exts. X
+#endif
+
+#endif
+
diff --git a/gpu/include/GrGLIndexBuffer.h b/gpu/include/GrGLIndexBuffer.h
new file mode 100644
index 0000000..5177b4b
--- /dev/null
+++ b/gpu/include/GrGLIndexBuffer.h
@@ -0,0 +1,53 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGLIndexBuffer_DEFINED
+#define GrGLIndexBuffer_DEFINED
+
+#include "GrIndexBuffer.h"
+#include "GrGLConfig.h"
+
+class GrGpuGL;
+
+class GrGLIndexBuffer : public GrIndexBuffer {
+protected:
+    GrGLIndexBuffer(GLuint id,
+                    GrGpuGL* gl,
+                    uint32_t sizeInBytes,
+                    bool dynamic);
+public:
+    virtual ~GrGLIndexBuffer();
+
+    GLuint bufferID() const;
+
+    // overrides of GrIndexBuffer
+    virtual void abandon();
+    virtual void* lock();
+    virtual void unlock();
+    virtual bool isLocked() const;
+    virtual bool updateData(const void* src, uint32_t srcSizeInBytes);
+private:
+    GrGpuGL*     fGL;
+    GLuint       fBufferID;
+    void*        fLockPtr;
+    
+    friend class GrGpuGL;
+
+    typedef GrIndexBuffer INHERITED;
+};
+
+#endif
diff --git a/gpu/include/GrGLTexture.h b/gpu/include/GrGLTexture.h
new file mode 100644
index 0000000..ada3151
--- /dev/null
+++ b/gpu/include/GrGLTexture.h
@@ -0,0 +1,166 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGLTexture_DEFINED
+#define GrGLTexture_DEFINED
+
+#include "GrGLConfig.h"
+#include "GrGpu.h"
+#include "GrTexture.h"
+#include "GrRect.h"
+
+class GrGpuGL;
+class GrGLTexture;
+
+class GrGLRenderTarget : public GrRenderTarget {
+protected:
+    
+    struct GLRenderTargetIDs {
+        GLuint      fRTFBOID;
+        GLuint      fTexFBOID;
+        GLuint      fStencilRenderbufferID;
+        GLuint      fMSColorRenderbufferID;
+        bool        fOwnIDs;
+    };
+    
+    GrGLRenderTarget(const GLRenderTargetIDs& ids, 
+                     const GrIRect& fViewport,
+                     GrGLTexture* texture,
+                     GrGpuGL* gl);
+    
+    void setViewport(const GrIRect& rect) { GrAssert(rect.height() <= 0); 
+                                            fViewport = rect;}
+    
+    virtual uint32_t width() const { return fViewport.width(); }
+    virtual uint32_t height() const { return -fViewport.height(); }
+    
+public:
+    virtual ~GrGLRenderTarget();
+    
+    bool resolveable() const { return fRTFBOID != fTexFBOID; }
+    bool needsResolve() const { return fNeedsResolve; }
+    void setDirty(bool dirty) { fNeedsResolve = resolveable() && dirty; }
+    
+    GLuint renderFBOID() const { return fRTFBOID; }
+    GLuint textureFBOID() const { return fTexFBOID; }
+
+    const GrIRect& viewport() const { return fViewport; }
+    void   abandon();
+
+private:
+    GrGpuGL*    fGL;
+    GLuint      fRTFBOID;
+    GLuint      fTexFBOID;    
+    GLuint      fStencilRenderbufferID;
+    GLuint      fMSColorRenderbufferID;
+   
+    // Should this object delete IDs when it is destroyed or does someone
+    // else own them.
+    bool        fOwnIDs;
+    
+    // If there separate Texture and RenderTarget FBO IDs then the rendertarget
+    // must be resolved to the texture FBO before it is used as a texture.
+    bool fNeedsResolve;
+    
+    // when we switch to this rendertarget we want to set the viewport to 
+    // only render to to content area (as opposed to the whole allocation) and
+    // we want the rendering to be at top left (GL has origin in bottom left) 
+    GrIRect fViewport;
+    
+    friend class GrGpuGL;
+    friend class GrGLTexture;
+    
+    typedef GrRenderTarget INHERITED;
+};
+
+class GrGLTexture : public GrTexture {
+public:
+    enum Orientation {
+        kBottomUp_Orientation,
+        kTopDown_Orientation,
+    };
+
+protected:
+    struct GLTextureDesc {
+        uint32_t    fContentWidth;
+        uint32_t    fContentHeight;
+        uint32_t    fAllocWidth;
+        uint32_t 	fAllocHeight;
+        PixelConfig fFormat;
+        GLuint      fTextureID;
+        GLenum      fUploadFormat;
+        GLenum      fUploadByteCount;
+        GLenum      fUploadType;
+        Orientation fOrientation;
+    };
+    typedef GrGLRenderTarget::GLRenderTargetIDs GLRenderTargetIDs;
+    GrGLTexture(const GLTextureDesc& textureDesc,
+                const GLRenderTargetIDs& rtIDs,
+                GrGpuGL* gl);
+
+public:
+    virtual ~GrGLTexture();
+    
+    // overloads of GrTexture
+    virtual void abandon();
+    virtual bool isRenderTarget() const;
+    virtual GrRenderTarget* asRenderTarget();
+    virtual void removeRenderTarget();
+    virtual void uploadTextureData(uint32_t x,
+                                   uint32_t y,
+                                   uint32_t width,
+                                   uint32_t height,
+                                   const void* srcData);
+    virtual intptr_t getTextureHandle();
+
+    const GrSamplerState& samplerState() const { return fSamplerState; }
+    void setSamplerState(const GrSamplerState& state) 
+                                                    { fSamplerState = state; }
+    GLuint textureID() const { return fTextureID; }
+
+    GLenum uploadFormat() const { return fUploadFormat; }
+    GLenum uploadByteCount() const { return fUploadByteCount; }
+    GLenum uploadType() const { return fUploadType; }
+
+    // Ganesh assumes texture coordinates have their origin
+    // in the top-left corner of the image. OpenGL, however,
+    // has the origin in the lower-left corner. For content that
+    // is loaded by Ganesh we just push the content "upside down"
+    // (by GL's understanding of the world ) in glTex*Image and the 
+    // addressing just works out. However, content generated by GL 
+    // (FBO or externally imported texture) will be updside down 
+    // and it is up to the GrGpuGL derivative to handle y-mirroing.
+    Orientation orientation() const { return fOrientation; }
+
+private:
+    GrSamplerState      fSamplerState;
+    GLuint              fTextureID;
+    GLenum              fUploadFormat;
+    GLenum              fUploadByteCount;
+    GLenum              fUploadType;
+    Orientation         fOrientation;
+    GrGLRenderTarget*   fRenderTarget;    
+    GrGpuGL*            fGpuGL;
+    
+    static const GLenum gWrapMode2GLWrap[];
+
+    friend class GrGpuGL;
+
+    typedef GrTexture INHERITED;
+};
+
+#endif
diff --git a/gpu/include/GrGLVertexBuffer.h b/gpu/include/GrGLVertexBuffer.h
new file mode 100644
index 0000000..6b99f57
--- /dev/null
+++ b/gpu/include/GrGLVertexBuffer.h
@@ -0,0 +1,55 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGLVertexBuffer_DEFINED
+#define GrGLVertexBuffer_DEFINED
+
+#include "GrVertexBuffer.h"
+#include "GrGLConfig.h"
+
+class GrGpuGL;
+
+class GrGLVertexBuffer : public GrVertexBuffer {
+protected:
+    GrGLVertexBuffer(GLuint id,
+                     GrGpuGL* gl,
+                     uint32_t sizeInBytes,
+                     bool dynamic);
+
+public:
+    virtual ~GrGLVertexBuffer();
+    
+    // overrides of GrVertexBuffer
+    virtual void abandon();
+    virtual void* lock();
+    virtual void unlock();
+    virtual bool isLocked() const;
+    virtual bool updateData(const void* src, uint32_t srcSizeInBytes);
+
+    GLuint bufferID() const;
+
+private:
+    GrGpuGL*     fGL;
+    GLuint       fBufferID;
+    void*        fLockPtr;
+
+    friend class GrGpuGL;
+
+    typedef GrVertexBuffer INHERITED;
+};
+
+#endif
diff --git a/gpu/include/GrGlyph.h b/gpu/include/GrGlyph.h
new file mode 100644
index 0000000..4a3b307
--- /dev/null
+++ b/gpu/include/GrGlyph.h
@@ -0,0 +1,89 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGlyph_DEFINED
+#define GrGlyph_DEFINED
+
+#include "GrPath.h"
+#include "GrRect.h"
+
+class GrAtlas;
+
+/*  Need this to be quad-state:
+    - complete w/ image
+    - just metrics
+    - failed to get image, but has metrics
+    - failed to get metrics
+ */
+struct GrGlyph {
+    typedef uint32_t PackedID;
+
+    GrAtlas*    fAtlas;
+    GrPath*     fPath;
+    PackedID    fPackedID;
+    GrIRect16   fBounds;
+    GrIPoint16  fAtlasLocation;
+
+    void init(GrGlyph::PackedID packed, const GrIRect& bounds) {
+        fAtlas = NULL;
+        fPath = NULL;
+        fPackedID = packed;
+        fBounds.set(bounds);
+        fAtlasLocation.set(0, 0);
+    }
+    
+    void free() {
+        if (fPath) {
+            delete fPath;
+            fPath = NULL;
+        }
+    }
+    
+    int width() const { return fBounds.width(); }
+    int height() const { return fBounds.height(); }
+    bool isEmpty() const { return fBounds.isEmpty(); }
+    uint16_t glyphID() const { return UnpackID(fPackedID); }
+
+    ///////////////////////////////////////////////////////////////////////////
+    
+    static inline unsigned ExtractSubPixelBitsFromFixed(GrFixed pos) {
+        // two most significant fraction bits from fixed-point
+        return (pos >> 14) & 3;
+    }
+    
+    static inline PackedID Pack(uint16_t glyphID, GrFixed x, GrFixed y) {
+        x = ExtractSubPixelBitsFromFixed(x);
+        y = ExtractSubPixelBitsFromFixed(y);
+        return (x << 18) | (y << 16) | glyphID;
+    }
+    
+    static inline GrFixed UnpackFixedX(PackedID packed) {
+        return ((packed >> 18) & 3) << 14;
+    }
+    
+    static inline GrFixed UnpackFixedY(PackedID packed) {
+        return ((packed >> 16) & 3) << 14;
+    }
+    
+    static inline uint16_t UnpackID(PackedID packed) {
+        return (uint16_t)packed;
+    }
+};
+
+
+#endif
+
diff --git a/gpu/include/GrGpu.h b/gpu/include/GrGpu.h
new file mode 100644
index 0000000..f1fdf01
--- /dev/null
+++ b/gpu/include/GrGpu.h
@@ -0,0 +1,446 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGpu_DEFINED
+#define GrGpu_DEFINED
+
+#include "GrRect.h"
+#include "GrRefCnt.h"
+#include "GrDrawTarget.h"
+#include "GrGpuVertex.h"
+#include "GrTexture.h"
+#include "GrMemory.h"
+
+
+class GrGpu : public GrDrawTarget {
+
+public:
+    /**
+     * Possible 3D APIs that may be used by Ganesh.
+     */
+    enum Engine {
+        kOpenGL_Shaders_Engine,
+        kOpenGL_Fixed_Engine,
+        kDirect3D9_Engine
+    };
+
+    /**
+     * Platform specific 3D context.
+     * For
+     *    kOpenGL_Shaders_Engine use NULL
+     *    kOpenGL_Fixed_Engine   use NULL
+     *    kDirect3D9_Engine      use an IDirect3DDevice9*
+     */
+    typedef void* Platform3DContext;
+
+    /**
+     *  Create an instance of GrGpu that matches the specified Engine backend.
+     *  If the requested engine is not supported (at compile-time or run-time)
+     *  this returns NULL.
+     */
+    static GrGpu* Create(Engine, Platform3DContext context3D);
+
+    /**
+     * Describes levels of support for non-power-of-two textures.
+     */
+    enum NPOTTextureTypes {
+        /**
+         *  no support for NPOT textures
+         */
+        kNone_NPOTTextureType,
+        /**
+         *  only clamp is supported for textures
+         */
+        kNoRepeat_NPOTTextureType,
+        /**
+         *  no texture restrictions at all, but rendertargets must be POW2
+         */
+        kNonRendertarget_NPOTTextureType,
+        /**
+         * no POW2 restrictions at all
+         */
+        kFull_NPOTTextureType
+    };
+
+    /**
+     * Used to control the level of antialiasing available for a rendertarget.
+     * Anti-alias quality levels depend on the underlying API/GPU capabilities.
+     */
+    enum AALevels {
+        kNone_AALevel, //<! No antialiasing available.
+        kLow_AALevel,  //<! Low quality antialiased rendering. Actual
+                       //   interpretation is platform-dependent.
+        kMed_AALevel,  //<! Medium quality antialiased rendering. Actual
+                       //   interpretation is platform-dependent.
+        kHigh_AALevel, //<! High quality antialiased rendering. Actual
+                       //   interpretation is platform-dependent.
+    };
+
+
+    /**
+     * Optional bitfield flags that can be passed to createTexture.
+     */
+    enum TextureFlags {
+        kRenderTarget_TextureFlag  = 0x1,   //<! Creates a texture that can be
+                                            //   rendered to by calling
+                                            //   GrGpu::setRenderTarget() with
+                                            //   GrTexture::asRenderTarget().
+        kNoPathRendering_TextureFlag = 0x2, //<! If the texture is used as a
+                                            //   rendertarget but paths will not
+                                            //   be rendered to it.
+        kDynamicUpdate_TextureFlag = 0x4    //!< Hint that the CPU may modify
+                                            // this texture after creation
+    };
+
+    enum {
+        /**
+         *  For Index8 pixel config, the colortable must be 256 entries
+         */
+        kColorTableSize = 256 * sizeof(GrColor)
+    };
+    /**
+     * Describes a texture to be created.
+     */
+    struct TextureDesc {
+        uint32_t               fFlags;  //!< bitfield of TextureFlags
+        GrGpu::AALevels        fAALevel;//!< The level of antialiasing available
+                                        //   for a rendertarget texture. Only
+                                        //   flags contains
+                                        //   kRenderTarget_TextureFlag.
+        uint32_t               fWidth;  //!< Width of the texture
+        uint32_t               fHeight; //!< Height of the texture
+        GrTexture::PixelConfig fFormat; //!< Format of source data of the
+                                        //   texture. Not guaraunteed to be the
+                                        //   same as internal format used by
+                                        //   3D API.
+    };
+
+    /**
+     * Gpu usage statistics.
+     */
+    struct Stats {
+        uint32_t fVertexCnt;  //<! Number of vertices drawn
+        uint32_t fIndexCnt;   //<! Number of indices drawn
+        uint32_t fDrawCnt;    //<! Number of draws
+
+        uint32_t fProgChngCnt;//<! Number of program changes (N/A for fixed)
+
+        /*
+         *  Number of times the texture is set in 3D API
+         */
+        uint32_t fTextureChngCnt;
+        /*
+         *  Number of times the render target is set in 3D API
+         */
+        uint32_t fRenderTargetChngCnt;
+        /*
+         *  Number of textures created (includes textures that are rendertargets).
+         */
+        uint32_t fTextureCreateCnt;
+        /*
+         *  Number of rendertargets created.
+         */
+        uint32_t fRenderTargetCreateCnt;
+    };
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    GrGpu();
+    virtual ~GrGpu();
+
+    /**
+     * The GrGpu object normally assumes that no outsider is setting state
+     * within the underlying 3D API's context/device/whatever. This call informs
+     * the GrGpu that the state was modified and it should resend. Shouldn't
+     * be called frequently for good performance.
+     */
+    virtual void resetContext();
+
+    void unimpl(const char[]);
+
+    /**
+     * Creates a texture object
+     *
+     * @param desc        describes the texture to be created.
+     * @param srcData     texel data to load texture. Begins with full-size
+     *                    palette data for paletted textures. Contains width*
+     *                    height texels. If NULL texture data is uninitialized.
+     *
+     * @return    The texture object if successful, otherwise NULL.
+     */
+    virtual GrTexture* createTexture(const TextureDesc& desc,
+                                     const void* srcData, size_t rowBytes) = 0;
+    /**
+     * Wraps an externally-created rendertarget in a GrRenderTarget.
+     * @param platformRenderTarget  handle to the the render target in the
+     *                              underlying 3D API. Interpretation depends on
+     *                              GrGpu subclass in use.
+     * @param width                 width of the render target
+     * @param height                height of the render target
+     */
+    virtual GrRenderTarget* createPlatformRenderTarget(
+                                                intptr_t platformRenderTarget,
+                                                int width, int height) = 0;
+
+    /**
+     * Creates a vertex buffer.
+     *
+     * @param size    size in bytes of the vertex buffer
+     * @param dynamic hints whether the data will be frequently changed
+     *                by either GrVertexBuffer::lock or
+     *                GrVertexBuffer::updateData.
+     *
+     * @return    The vertex buffer if successful, otherwise NULL.
+     */
+    virtual GrVertexBuffer* createVertexBuffer(uint32_t size, bool dynamic) = 0;
+
+    /**
+     * Creates an index buffer.
+     *
+     * @param size    size in bytes of the index buffer
+     * @param dynamic hints whether the data will be frequently changed
+     *                by either GrIndexBuffer::lock or
+     *                GrIndexBuffer::updateData.
+     *
+     * @return The index buffer if successful, otherwise NULL.
+     */
+    virtual GrIndexBuffer* createIndexBuffer(uint32_t size, bool dynamic) = 0;
+
+    /**
+     * Gets the default render target. This is the render target set in the
+     * 3D API at the time the GrGpu was created.
+     */
+    virtual GrRenderTarget* defaultRenderTarget() = 0;
+
+    /**
+     * At construction time the GrGpu infers the render target and viewport from
+     * the state of the underlying 3D API. However, a platform-specific resize
+     * event may occur.
+     * @param width     new width of the default rendertarget
+     * @param height    new height of the default rendertarget
+     */
+    virtual void setDefaultRenderTargetSize(uint32_t width, uint32_t height) = 0;
+
+    /**
+     * Erase the entire render target, ignoring any clips/scissors.
+     *
+     * This is issued to the GPU driver immediately.
+     */
+    virtual void eraseColor(GrColor color) = 0;
+
+    /**
+     * Are 8 bit paletted textures supported.
+     *
+     * @return    true if 8bit palette textures are supported, false otherwise
+     */
+    bool supports8BitPalette() const { return f8bitPaletteSupport; }
+
+    /**
+     * If single stencil pass winding is supported then one stencil pass
+     * (kWindingStencil1_PathPass) is required to do winding rule path filling
+     * (or inverse winding rule). Otherwise, two passes are required
+     * (kWindingStencil1_PathPass followed by kWindingStencil2_PathPass).
+     *
+     * @return    true if only a single stencil pass is needed.
+     */
+    bool supportsSingleStencilPassWinding() const
+                                        { return fSingleStencilPassForWinding; }
+
+    /**
+     * Checks whether locking vertex and index buffers is supported.
+     *
+     * @return true if locking is supported.
+     */
+    bool supportsBufferLocking() const { return fBufferLockSupport; }
+
+    /**
+     * Gets the minimum width of a render target. If a texture/rt is created
+     * with a width less than this size the GrGpu object will clamp it to this
+     * value.
+     */
+    int minRenderTargetWidth() const { return fMinRenderTargetWidth; }
+
+    /**
+     * Gets the minimum width of a render target. If a texture/rt is created
+     * with a height less than this size the GrGpu object will clamp it to this
+     * value.
+     */
+    int minRenderTargetHeight() const  { return fMinRenderTargetHeight; }
+
+    /**
+     * Retrieves the level of NPOT texture support. Regardless of support level
+     * NPOT textures can always be created, but internally they may be imbedded
+     * in a POT texture. An exception is paletted textures which must be
+     * specified as a POT when npotTextureSupport() is not Full.
+     *
+     * @return    the level of NPOT texture support.
+     */
+    NPOTTextureTypes npotTextureSupport() const { return fNPOTTextureSupport; }
+
+    // GrDrawTarget overrides
+    virtual void drawIndexed(PrimitiveType type,
+                             uint32_t startVertex,
+                             uint32_t startIndex,
+                             uint32_t vertexCount,
+                             uint32_t indexCount);
+
+    virtual void drawNonIndexed(PrimitiveType type,
+                                uint32_t startVertex,
+                                uint32_t vertexCount);
+
+    /**
+     * Determines if blend is effectively disabled.
+     *
+     * @return true if blend can be disabled without changing the rendering
+     *  result given the current state including the vertex layout specified
+     *  with the vertex source.
+     */
+    bool canDisableBlend() const;
+
+    /**
+     * Returns an index buffer that can be used to render quads.
+     * Indices are 0, 1, 2, 0, 2, 3, etc.
+     * Draw with kTriangles_PrimitiveType
+     */
+    const GrIndexBuffer* quadIndexBuffer() const;
+    /**
+     * Gets the number of quads that can be rendered using quadIndexBuffer.
+     */
+    int maxQuadsInIndexBuffer() const;
+
+    /**
+     * Ensures that the current render target is actually set in the
+     * underlying 3D API. Used when client wants to use 3D API to directly
+     * render to the RT.
+     */
+    virtual void forceRenderTargetFlush() = 0;
+
+    virtual bool readPixels(int left, int top, int width, int height,
+                            GrTexture::PixelConfig, void* buffer) = 0;
+
+
+    const Stats& getStats() const;
+    void resetStats();
+    void printStats() const;
+
+protected:
+    /**
+     * Extensions to GrDrawTarget::StencilPass to implement stencil clipping
+     */
+    enum GpuStencilPass {
+        kSetClip_StencilPass = kDrawTargetCount_StencilPass,
+                                        /* rendering a hard clip to the stencil
+                                           buffer. Subsequent draws with other
+                                           StencilPass values will be clipped
+                                           if kStencilClip_StateBit is set. */
+        kGpuCount_StencilPass
+    };
+
+    /**
+     * Extensions to GrDrawTarget::StateBits to implement stencil clipping
+     */
+    struct ClipState {
+        bool            fClipInStencil;
+        bool            fClipIsDirty;
+        GrRenderTarget* fStencilClipTarget;
+    } fClipState;
+
+    virtual void clipWillChange(const GrClip& clip);
+    bool setupClipAndFlushState(PrimitiveType type);
+
+    struct BoundsState {
+        bool    fScissorEnabled;
+        GrIRect fScissorRect;
+        GrIRect fViewportRect;
+    };
+
+    // defaults to false, subclass can set true to support palleted textures
+    bool f8bitPaletteSupport;
+
+    // defaults to false, subclass can set higher support level
+    NPOTTextureTypes fNPOTTextureSupport;
+
+    // True if only one stencil pass is required to implement the winding path
+    // fill rule. Subclass responsible for setting this value.
+    bool fSingleStencilPassForWinding;
+
+    // set by subclass to true if index and vertex buffers can be locked, false
+    // otherwise.
+    bool fBufferLockSupport;
+
+    // set by subclass
+    int fMinRenderTargetWidth;
+    int fMinRenderTargetHeight;
+
+    // overridden by API specific GrGpu-derived class to perform the draw call.
+    virtual void drawIndexedHelper(PrimitiveType type,
+                                   uint32_t startVertex,
+                                   uint32_t startIndex,
+                                   uint32_t vertexCount,
+                                   uint32_t indexCount) = 0;
+
+    virtual void drawNonIndexedHelper(PrimitiveType type,
+                                      uint32_t vertexCount,
+                                      uint32_t numVertices) = 0;
+
+    // called to program the vertex data, indexCount will be 0 if drawing non-
+    // indexed geometry.
+    virtual void setupGeometry(uint32_t startVertex,
+                               uint32_t startIndex,
+                               uint32_t vertexCount,
+                               uint32_t indexCount) = 0;
+
+
+    // The GrGpu typically records the clients requested state and then flushes
+    // deltas from previous state at draw time. This function does the
+    // API-specific flush of the state
+    // returns false if current state is unsupported.
+    virtual bool flushGraphicsState(PrimitiveType type) = 0;
+
+    // Sets the scissor rect, or disables if rect is NULL.
+    virtual void flushScissor(const GrIRect* rect) = 0;
+
+    // GrGpu subclass removes the clip from the stencil buffer
+    virtual void eraseStencilClip() = 0;
+
+    // GrDrawTarget overrides
+    virtual bool acquireGeometryHelper(GrVertexLayout vertexLayout,
+                                       void**         vertices,
+                                       void**         indices);
+    virtual void releaseGeometryHelper();
+
+private:
+    mutable GrIndexBuffer* fQuadIndexBuffer; // mutable so it can be
+                                             // created on-demand
+
+    static const int MAX_VERTEX_SIZE = GR_CT_MAX(2*sizeof(GrPoint) + sizeof(GrColor),
+                                                 2*sizeof(GrGpuTextVertex));
+    static const int VERTEX_STORAGE = 16 * MAX_VERTEX_SIZE;
+    static const int INDEX_STORAGE = 32 * sizeof(uint16_t);
+
+protected:
+    GrAutoSMalloc<VERTEX_STORAGE> fVertices;
+    GrAutoSMalloc<INDEX_STORAGE>  fIndices;
+
+    Stats           fStats;
+
+private:
+    typedef GrRefCnt INHERITED;
+};
+
+#endif
+
diff --git a/gpu/include/GrGpuD3D9.h b/gpu/include/GrGpuD3D9.h
new file mode 100644
index 0000000..c3d3e1a
--- /dev/null
+++ b/gpu/include/GrGpuD3D9.h
@@ -0,0 +1,259 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGpuD3D9_DEFINED
+#define GrGpuD3D9_DEFINED
+
+#include <Windows.h>
+#include <d3d9.h>
+
+#include "GrGpu.h"
+
+class GrD3D9VertexBuffer;
+class GrD3D9IndexBuffer;
+class GrD3D9Texture;
+
+// For D3D9 GrRenderTarget casts to a (GrD3D9RenderTarget*)
+struct GrD3D9RenderTarget {
+    IDirect3DSurface9* fColor;
+    IDirect3DSurface9* fStencil;
+    bool fClearStencil;
+};
+
+// GrGpu implementation for D3D9 fixed pipeline. 
+// Known needed improvements:
+//      vertex/index buffers need to be better managed:
+//          use no_overwrite and walk down VB/IB until reach end and wrap
+//      take advantage of the redrawHint and don't recopy vertex/idx data
+//      User created vertex buffers must have position Z values 
+//          (required for fixed pipeline) but there is no way to communicate 
+//          this now
+//      We create a temporary sysmem surface for each texture update.
+//      split this out into fixed/shader subclasses (use vdecls for shaders)
+class GrGpuD3D9 : public GrGpu {
+public:
+            GrGpuD3D9(IDirect3DDevice9* device);
+    virtual ~GrGpuD3D9();
+
+    // overrides from GrGpu
+    virtual GrTexture* createTexture(const TextureDesc& desc,
+                                     const void* srcData);
+    virtual GrVertexBuffer* createVertexBuffer(uint32_t size, bool dynamic);
+    virtual GrIndexBuffer* createIndexBuffer(uint32_t size, bool dynamic);
+
+    virtual void eraseColor(GrColor color);
+    virtual void eraseStencil();
+
+protected:
+    // overrides from GrGpu
+    virtual bool flushGraphicsState(PrimitiveTypes type);
+    virtual void drawIndexArrayApi(PrimitiveTypes type,
+                                   int baseVertex,
+                                   int vertexCount,
+                                   int indexCount,
+                                   const uint16_t* indexArray,
+                                   bool redrawHint);
+    virtual void drawIndexBufferApi(PrimitiveTypes type,
+                                    int baseVertex,
+                                    int startIndex,
+                                    int vertexCount,
+                                    int indexCount,
+                                    GrIndexBuffer* indexBuffer,
+                                    bool redrawHint);
+    virtual void drawNonIndexedApi(PrimitiveTypes type,
+                                   int baseVertex,
+                                   int indexCount,
+                                   bool redrawHint);
+    virtual void flushScissor();
+
+private:
+
+    // baseVertex may be modified while setting up the stage
+    GrD3D9VertexBuffer* setupVBufferStage(int vsize, int* baseVertex, 
+                                          int vertexCount, DrawModes mode);
+    GrD3D9IndexBuffer* setupIBufferStage(int* startIndex, int indexCount, 
+                                         const uint16_t* indices);
+    static int vertexSize(int vertFlagBits, GrGpu::DrawModes mode);
+    static bool positionsOnly(int vertFlagBits);
+
+    // notify callbacks to update state tracking when related
+    // objects are bound to the device or deleted outside of the class
+    void notifyVertexBufferBind(GrD3D9VertexBuffer* buffer);
+    void notifyVertexBufferDelete(GrD3D9VertexBuffer* buffer);
+    void notifyIndexBufferBind(GrD3D9IndexBuffer* buffer);
+    void notifyIndexBufferDelete(GrD3D9IndexBuffer* buffer);
+    void notifyTextureDelete(GrD3D9Texture* texture);
+    void notifyTextureRemoveRenderTarget(GrD3D9Texture* texture);
+
+    IDirect3DSurface9* createStencil(uint32_t width, 
+                                     uint32_t height,
+                                     D3DMULTISAMPLE_TYPE msType,
+                                     DWORD msQual);
+
+    void setRenderTargetImm();
+
+    friend class GrD3D9VertexBuffer;
+    friend class GrD3D9IndexBuffer;
+    friend class GrD3D9Texture;
+
+    GrIndexBuffer*                  fLastIndexBuffer;
+    
+    // used to track the COLORARG1 value for tex stage 0
+    // needs to use ALPHAREPLICATE when using alpha-only textures
+    DWORD                           fLastColorArg1;
+
+    IDirect3DDevice9*               fDevice;
+    // We may use Ex functionality if this is a Ex device
+    IDirect3DDevice9Ex*             fDeviceEx;
+
+    enum VertDecls {
+        kInvalid_VertDecl = -1,
+        kPosOnly_VertDecl = 0,
+        kTex_VertDecl,
+        kColors_VertDecl,
+        kTexAndColors_VertDecl,
+        kPosAsTex_VertDecl,
+        kPosAsTexAndColors_VertDecl,
+        kVertDeclCount
+    };
+
+    static const VertDecls gVertFlags2VertDeclIdx[];
+    static const DWORD gDeclToFVFs[];
+    static const DWORD gTextFVF;
+    
+    DWORD fLastVertFVF;
+
+    bool fLastBlendOff;
+
+    // D3D allows user pointers in place of buffers for vertex/index data
+    // but it doesn't allow:
+    //  -multiple streams (non-interleaved) ~ this will be resolved when we 
+    //                                        go AoS with our verts
+    //  -mixing user pointer verts with index buffer (or vice versa)
+    // So we use these staging buffers
+    GrD3D9VertexBuffer* fStageVBuffer;
+    GrD3D9IndexBuffer*  fStageIBuffer;
+
+    // did we use texture coordinate generation at the last flush
+    bool fLastTexGen;
+
+    GrD3D9RenderTarget fDefaultRenderTarget;
+
+    // We use texture stage 0 to set a constant color
+    // D3D disables the stage if NULL is bound (even when the ops don't
+    // reference the texture). So we have a 1x1 dummy texture that
+    // gets set when drawing constant color with no texture
+    GrD3D9Texture* fDummyTexture;
+};
+
+class GrD3D9Texture : public GrTexture {
+protected:
+    GrD3D9Texture(uint32_t width, 
+                  uint32_t height, 
+                  PixelConfig config,
+                  IDirect3DTexture9* texture,
+                  IDirect3DSurface9* stencil,
+                  bool clearStencil,
+                  GrGpuD3D9* gpuD3D9);
+public:
+    virtual ~GrD3D9Texture();
+    
+    // overloads of GrTexture
+    virtual void abandon();
+    virtual bool isRenderTarget();
+    virtual GrRenderTarget* asRenderTarget() 
+                                    { return (GrRenderTarget*) &fRenderTarget; }
+    virtual void removeRenderTarget();
+    virtual void uploadTextureData(uint32_t x,
+                                   uint32_t y,
+                                   uint32_t width,
+                                   uint32_t height,
+                                   const void* srcData);
+    IDirect3DTexture9* texture() const { return fTexture; }
+    IDirect3DSurface9* stencil() const { return fStencil; }
+    D3DFORMAT format() const { return fDesc.Format; }
+private:
+    IDirect3DTexture9*      fTexture;
+    GrD3D9RenderTarget      fRenderTarget;
+    IDirect3DSurface9*      fStencil;
+    D3DSURFACE_DESC         fDesc;
+    GrGpuD3D9*              fGpuD3D9;
+
+    friend class GrGpuD3D9;
+
+    typedef GrTexture INHERITED;
+};
+
+class GrD3D9VertexBuffer : public GrVertexBuffer {
+protected:
+    GrD3D9VertexBuffer(uint32_t size, 
+                       bool dynamic,
+                       IDirect3DVertexBuffer9* vbuffer,
+                       GrGpuD3D9* gpuD3D9);
+public:
+    virtual ~GrD3D9VertexBuffer();
+    
+    IDirect3DVertexBuffer9* buffer() const { return fBuffer; }
+
+    // overrides of GrVertexBuffer
+    virtual void abandon();
+    virtual void* lock();
+    virtual void unlock();
+    virtual bool isLocked();
+    virtual bool updateData(const void* src, uint32_t srcSizeInBytes);
+
+private:
+    IDirect3DVertexBuffer9* fBuffer;
+    D3DVERTEXBUFFER_DESC    fDesc;
+    bool                    fLocked;
+    GrGpuD3D9*              fGpuD3D9;
+
+    friend class GrGpuD3D9;
+
+    typedef GrVertexBuffer INHERITED;
+};
+
+class GrD3D9IndexBuffer : public GrIndexBuffer {
+protected:
+    GrD3D9IndexBuffer(uint32_t size, 
+                      bool dynamic,
+                      IDirect3DIndexBuffer9* vbuffer,
+                      GrGpuD3D9* gpuD3D9);
+public:
+    virtual ~GrD3D9IndexBuffer();
+
+    IDirect3DIndexBuffer9* buffer() const { return fBuffer; }
+
+    // overrides of GrIndexBuffer
+    virtual void abandon();
+    virtual void* lock();
+    virtual void unlock();
+    virtual bool isLocked();
+    virtual bool updateData(const void* src, uint32_t srcSizeInBytes);
+private:
+    IDirect3DIndexBuffer9*  fBuffer;
+    D3DINDEXBUFFER_DESC     fDesc;
+    bool                    fLocked;
+    GrGpuD3D9*              fGpuD3D9;
+
+    friend class GrGpuD3D9;
+
+    typedef GrIndexBuffer INHERITED;
+};
+
+#endif
+
diff --git a/gpu/include/GrGpuVertex.h b/gpu/include/GrGpuVertex.h
new file mode 100644
index 0000000..1e3293a
--- /dev/null
+++ b/gpu/include/GrGpuVertex.h
@@ -0,0 +1,104 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGpuVertex_DEFINED
+#define GrGpuVertex_DEFINED
+
+#include "GrGLConfig.h"
+#include "GrPoint.h"
+
+#if GR_TEXT_SCALAR_IS_USHORT
+    typedef uint16_t                GrTextScalar;  
+    #define GrIntToTextScalar(x)    ((uint16_t)x)
+    #define GrFixedToTextScalar(x)  (x)
+#elif GR_TEXT_SCALAR_IS_FIXED
+    typedef GrFixed                 GrTextScalar;
+    #define GrIntToTextScalar(x)    GrIntToFixed(x)
+    #define GrFixedToTextScalar(x)  (x)
+#elif GR_TEXT_SCALAR_IS_FLOAT
+    typedef float                   GrTextScalar;    
+    #define GrIntToTextScalar(x)    ((GrTextScalar)x)
+    #define GrFixedToTextScalar(x)  GrFixedToFloat(x)
+#else
+    #error "Text scalar type not defined"
+#endif
+
+// text has its own vertex class, since it may want to be in fixed point (given)
+// that it starts with all integers) even when the default vertices are floats
+struct GrGpuTextVertex {
+    GrTextScalar fX;
+    GrTextScalar fY;
+
+    void set(GrTextScalar x, GrTextScalar y) {
+        fX = x;
+        fY = y;
+    }
+
+    void setI(int x, int y) {
+        fX = GrIntToTextScalar(x);
+        fY = GrIntToTextScalar(y);
+    }
+    
+    void setX(GrFixed x, GrFixed y) {
+        fX = GrFixedToTextScalar(x);
+        fY = GrFixedToTextScalar(y);
+    }
+    
+    // rect fan is counter-clockwise
+
+    void setRectFan(GrTextScalar l, GrTextScalar t, GrTextScalar r,
+                    GrTextScalar b) {
+        GrGpuTextVertex* v = this;
+        v[0].set(l, t);
+        v[1].set(l, b);
+        v[2].set(r, b);
+        v[3].set(r, t);
+    }
+
+    void setIRectFan(int l, int t, int r, int b) {
+        this->setRectFan(GrIntToTextScalar(l), GrIntToTextScalar(t),
+                         GrIntToTextScalar(r), GrIntToTextScalar(b));
+    }
+
+    void setIRectFan(int l, int t, int r, int b, size_t stride) {
+        GrAssert(stride > sizeof(GrGpuTextVertex));
+        char* v = (char*)this;
+        ((GrGpuTextVertex*)(v + 0*stride))->setI(l, t);
+        ((GrGpuTextVertex*)(v + 1*stride))->setI(l, b);
+        ((GrGpuTextVertex*)(v + 2*stride))->setI(r, b);
+        ((GrGpuTextVertex*)(v + 3*stride))->setI(r, t);
+    }
+
+    // counter-clockwise fan
+    void setXRectFan(GrFixed l, GrFixed t, GrFixed r, GrFixed b) {
+        this->setRectFan(GrFixedToTextScalar(l), GrFixedToTextScalar(t),
+                         GrFixedToTextScalar(r), GrFixedToTextScalar(b));
+    }
+
+    void setXRectFan(GrFixed l, GrFixed t, GrFixed r, GrFixed b, size_t stride) {
+        GrAssert(stride > sizeof(GrGpuTextVertex));
+        char* v = (char*)this;
+        ((GrGpuTextVertex*)(v + 0*stride))->setX(l, t);
+        ((GrGpuTextVertex*)(v + 1*stride))->setX(l, b);
+        ((GrGpuTextVertex*)(v + 2*stride))->setX(r, b);
+        ((GrGpuTextVertex*)(v + 3*stride))->setX(r, t);
+    }
+
+};
+
+#endif
+
diff --git a/gpu/include/GrIPoint.h b/gpu/include/GrIPoint.h
new file mode 100644
index 0000000..b979a09
--- /dev/null
+++ b/gpu/include/GrIPoint.h
@@ -0,0 +1,35 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrIPoint_DEFINED
+#define GrIPoint_DEFINED
+
+#include "GrTypes.h"
+
+struct GrIPoint {
+public:
+    int32_t fX, fY;
+    
+    GrIPoint(int32_t x, int32_t y) : fX(x), fY(y) {}
+   
+    void set(int32_t x, int32_t y) {
+        fX = x;
+        fY = y;
+    }
+};
+
+#endif
diff --git a/gpu/include/GrInOrderDrawBuffer.h b/gpu/include/GrInOrderDrawBuffer.h
new file mode 100644
index 0000000..805861a
--- /dev/null
+++ b/gpu/include/GrInOrderDrawBuffer.h
@@ -0,0 +1,131 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrInOrderDrawBuffer_DEFINED
+#define GrInOrderDrawBuffer_DEFINED
+
+#include "GrDrawTarget.h"
+#include "GrAllocPool.h"
+#include "GrAllocator.h"
+#include "GrClip.h"
+
+class GrVertexBufferAllocPool;
+
+// TODO: don't save clip per draw
+class GrInOrderDrawBuffer : public GrDrawTarget {
+public:
+
+    GrInOrderDrawBuffer(GrVertexBufferAllocPool* pool = NULL);
+
+    virtual ~GrInOrderDrawBuffer();
+
+    void initializeDrawStateAndClip(const GrDrawTarget& target);
+
+    virtual void drawIndexed(PrimitiveType type,
+                             uint32_t startVertex,
+                             uint32_t startIndex,
+                             uint32_t vertexCount,
+                             uint32_t indexCount);
+
+    virtual void drawNonIndexed(PrimitiveType type,
+                                uint32_t startVertex,
+                                uint32_t vertexCount);
+
+    virtual bool geometryHints(GrVertexLayout vertexLayout,
+                               int32_t*       vertexCount,
+                               int32_t*       indexCount) const;
+
+    void reset();
+
+    void playback(GrDrawTarget* target);
+
+private:
+
+    struct Draw {
+        PrimitiveType   fType;
+        uint32_t        fStartVertex;
+        uint32_t        fStartIndex;
+        uint32_t        fVertexCount;
+        uint32_t        fIndexCount;
+        bool            fStateChange;
+        GrVertexLayout  fVertexLayout;
+        bool            fUseVertexBuffer;
+        bool            fClipChanged;
+        union {
+            const GrVertexBuffer*   fVertexBuffer;
+            const void*             fVertexArray;
+        };
+        bool            fUseIndexBuffer;
+        union {
+            const GrIndexBuffer*    fIndexBuffer;
+            const void*             fIndexArray;
+        };
+    };
+
+    virtual bool acquireGeometryHelper(GrVertexLayout vertexLayout,
+                                       void**         vertices,
+                                       void**         indices);
+    virtual void releaseGeometryHelper();
+    virtual void clipWillChange(const GrClip& clip);
+
+
+    bool grabState();
+    bool grabClip();
+
+    GrTAllocator<Draw>              fDraws;
+    // HACK: We hold refs on textures in saved state but not RTs, VBs, and IBs.
+    // a) RTs aren't ref counted (yet)
+    // b) we are only using this class for text which doesn't use VBs or IBs
+    // This should be fixed by either refcounting them all or having some
+    // notification occur if a cache is purging an object we have a ptr to.
+    GrTAllocator<SavedDrawState>    fStates;
+
+    GrTAllocator<GrClip>            fClips;
+    bool                            fClipChanged;
+
+    // vertices are either queued in cpu arrays or some vertex buffer pool
+    // that knows about a specific GrGpu object.
+    GrAllocPool                     fCPUVertices;
+    GrVertexBufferAllocPool*        fBufferVertices;
+    GrAllocPool                     fIndices;
+    void*                           fCurrReservedVertices;
+    void*                       	fCurrReservedIndices;
+    // valid if we're queueing vertices in fBufferVertices
+    GrVertexBuffer*                 fCurrVertexBuffer;
+    uint32_t                        fCurrStartVertex;
+
+    // caller may conservatively over allocate vertices / indices.
+    // we release unused space back to allocator if possible
+    size_t                          fReservedVertexBytes;
+    size_t                          fReservedIndexBytes;
+    size_t                          fUsedReservedVertexBytes;
+    size_t                          fUsedReservedIndexBytes;
+
+    static const uint32_t           STATES_BLOCK_SIZE = 8;
+    static const uint32_t           DRAWS_BLOCK_SIZE  = 8;
+    static const uint32_t           CLIPS_BLOCK_SIZE  = 8;
+    static const uint32_t           VERTEX_BLOCK_SIZE = 1 << 12;
+    static const uint32_t           INDEX_BLOCK_SIZE  = 1 << 10;
+    int8_t                          fDrawsStorage[sizeof(Draw) *
+                                                  DRAWS_BLOCK_SIZE];
+    int8_t                          fStatesStorage[sizeof(SavedDrawState) *
+                                                   STATES_BLOCK_SIZE];
+    int8_t                          fClipsStorage[sizeof(GrClip) *
+                                                  CLIPS_BLOCK_SIZE];
+};
+
+#endif
diff --git a/gpu/include/GrIndexBuffer.h b/gpu/include/GrIndexBuffer.h
new file mode 100644
index 0000000..0f4c4d6
--- /dev/null
+++ b/gpu/include/GrIndexBuffer.h
@@ -0,0 +1,92 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrIndexBuffer_DEFINED
+#define GrIndexBuffer_DEFINED
+
+#include "GrRefCnt.h"
+
+class GrIndexBuffer : public GrRefCnt {
+protected:
+    GrIndexBuffer(uint32_t sizeInBytes, bool dynamic) : 
+                  fSizeInBytes(sizeInBytes),
+                  fDynamic(dynamic) {}
+public:
+    virtual ~GrIndexBuffer() {}
+    
+    /**
+     Retrieves the size of the index buffer
+
+     @return the size of the index buffer in bytes
+     */
+    uint32_t size() const { return fSizeInBytes; }
+   
+    /**
+     Retrieves whether the index buffer was created with the dynamic flag
+
+     @return true if the index buffer was created with the dynamic flag
+     */
+    bool dynamic() const { return fDynamic; }
+
+    /**
+     Indicates that GPU context in which this veretx buffer was created is 
+     destroyed and that Ganesh should not attempt to free the texture with the
+     underlying API.
+     */
+    virtual void abandon() = 0;
+
+    /**
+     Locks the index buffer to be written by the CPU.
+     
+     The previous content of the index buffer is invalidated. It is an error to
+     draw whil the buffer is locked.  It is an error to call lock on an already
+     locked index buffer.
+     
+     @return a pointer to the index data or NULL if the lock fails.
+     */
+    virtual void* lock() = 0;
+
+    /** 
+     Unlocks the index buffer. 
+     
+     The pointer returned by the previous lock call will no longer be valid.
+     */
+    virtual void unlock() = 0;
+
+    /** 
+     Queries whether the index buffer has been locked.
+     
+     @return true if the index buffer is locked, false otherwise.
+     */
+    virtual bool isLocked() const = 0;
+
+    /**
+     Updates the index buffer data. 
+     
+     The size of the index buffer will be preserved. However, only the updated 
+     region will have defined contents.
+
+     @return returns true if the update succeeds, false otherwise.
+     */
+    virtual bool updateData(const void* src, uint32_t srcSizeInBytes) = 0;
+
+private:
+    uint32_t fSizeInBytes;
+    bool     fDynamic;
+};
+
+#endif
diff --git a/gpu/include/GrInstanceCounter.h b/gpu/include/GrInstanceCounter.h
new file mode 100644
index 0000000..11cec2b
--- /dev/null
+++ b/gpu/include/GrInstanceCounter.h
@@ -0,0 +1,47 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrInstanceCounter_DEFINED
+#define GrInstanceCounter_DEFINED
+
+#include "GrTypes.h"
+
+template <typename T> class GrInstanceCounter {
+public:
+    GrInstanceCounter() {
+        ++gCounter;
+        GrPrintf("+ %s %d\n", T::InstanceCounterClassName(), gCounter);
+    }
+
+    ~GrInstanceCounter() {
+        --gCounter;
+        GrPrintf("- %s %d\n", T::InstanceCounterClassName(), gCounter);
+    }
+
+private:
+    static int gCounter;
+};
+
+template <typename T> int GrInstanceCounter<T>::gCounter;
+
+#define DECLARE_INSTANCE_COUNTER(T)                                 \
+    static const char* InstanceCounterClassName() { return #T; }    \
+    friend class GrInstanceCounter<T>;                              \
+    GrInstanceCounter<T> fInstanceCounter
+
+#endif
+
diff --git a/gpu/include/GrKey.h b/gpu/include/GrKey.h
new file mode 100644
index 0000000..19133ae
--- /dev/null
+++ b/gpu/include/GrKey.h
@@ -0,0 +1,47 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrKey_DEFINED
+#define GrKey_DEFINED
+
+#include "GrRefCnt.h"
+
+class GrKey : public GrRefCnt {
+public:
+    typedef intptr_t Hash;
+
+    explicit GrKey(Hash hash) : fHash(hash) {}
+
+    intptr_t getHash() const { return fHash; }
+
+    bool operator<(const GrKey& rh) const {
+        return fHash < rh.fHash || (fHash == rh.fHash && this->lt(rh));
+    }
+    bool operator==(const GrKey& rh) const {
+        return fHash == rh.fHash && this->eq(rh);
+    }
+
+protected:
+    virtual bool lt(const GrKey& rh) const = 0;
+    virtual bool eq(const GrKey& rh) const = 0;
+
+private:
+    const Hash fHash;
+};
+
+#endif
+
diff --git a/gpu/include/GrMatrix.h b/gpu/include/GrMatrix.h
new file mode 100644
index 0000000..43fd4a5
--- /dev/null
+++ b/gpu/include/GrMatrix.h
@@ -0,0 +1,370 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+

+#ifndef GrMatrix_DEFINED

+#define GrMatrix_DEFINED

+

+#include "GrPoint.h"

+

+struct GrRect;

+

+/*

+ * 3x3 matrix

+ */

+class GrMatrix {

+public:

+    static const GrMatrix& I();

+    static const GrScalar gRESCALE;

+    /** 

+     * Handy index constants

+     */

+    enum {

+        kScaleX,

+        kSkewX,

+        kTransX,

+        kSkewY,

+        kScaleY,

+        kTransY,

+        kPersp0,

+        kPersp1,

+        kPersp2

+    };

+    

+    /**

+     * Create an uninitialized matrix

+     */

+    GrMatrix() {

+        fTypeMask = 0;

+    }

+    

+    /**

+     * Create a matrix from an array of values

+     * @param values    row-major array of matrix components

+     */

+    explicit GrMatrix(GrScalar* values) {

+        setToArray(values);

+    }

+    

+    /**

+     * Create a matrix from values

+     * @param scaleX    (0,0) matrix element

+     * @param skewX     (0,1) matrix element

+     * @param transX    (0,2) matrix element

+     * @param skewY     (1,0) matrix element

+     * @param scaleY    (1,1) matrix element

+     * @param transY    (1,2) matrix element

+     * @param persp0    (2,0) matrix element

+     * @param persp1    (2,1) matrix element

+     * @param persp2    (2,2) matrix element

+     */

+    GrMatrix(GrScalar scaleX,

+             GrScalar skewX,

+             GrScalar transX,

+             GrScalar skewY,

+             GrScalar scaleY,

+             GrScalar transY,

+             GrScalar persp0,

+             GrScalar persp1,

+             GrScalar persp2) {

+        setAll(scaleX, skewX,  transX,

+               skewY,  scaleY, transY,

+               persp0, persp1, persp2);

+    }

+

+    /**

+     * access matrix component

+     * @return matrix component value

+     */

+    const GrScalar& operator[] (int idx) const {

+        GrAssert((unsigned)idx < 9);

+        return fM[idx];

+    }

+

+    /**

+     * Set a matrix from an array of values

+     * @param values    row-major array of matrix components

+     */

+    void setToArray(GrScalar* values) {

+        for (int i = 0; i < 9; ++i) {

+            fM[i] = values[i];

+        }

+        setTypeMask();

+    }

+    

+    /**

+     * Create a matrix from values

+     * @param scaleX    (0,0) matrix element

+     * @param skewX     (0,1) matrix element

+     * @param transX    (0,2) matrix element

+     * @param skewY     (1,0) matrix element

+     * @param scaleY    (1,1) matrix element

+     * @param transY    (1,2) matrix element

+     * @param persp0    (2,0) matrix element

+     * @param persp1    (2,1) matrix element

+     * @param persp2    (2,2) matrix element

+     */

+    void setAll(GrScalar scaleX,

+                GrScalar skewX,

+                GrScalar transX,

+                GrScalar skewY,

+                GrScalar scaleY,

+                GrScalar transY,

+                GrScalar persp0,

+                GrScalar persp1,                

+                GrScalar persp2) {

+        fM[kScaleX] = scaleX;

+        fM[kSkewX]  = skewX;

+        fM[kTransX] = transX;

+        fM[kSkewY]  = skewY;

+        fM[kScaleY] = scaleY;

+        fM[kTransY] = transY;

+        fM[kPersp0] = persp0;

+        fM[kPersp1] = persp1;

+        fM[kPersp2] = persp2;

+        

+        setTypeMask();

+    }

+    

+    /**

+     * set matrix component

+     * @param idx    index of component to set

+     * @param value  value to set component to

+     */

+    inline void set(int idx, GrScalar value);

+    

+    /**

+     * make this matrix an identity matrix

+     */

+    void setIdentity();

+

+    /**

+     * overwrite entire matrix to be a translation matrix

+     * @param dx    amount to translate by in x

+     * @param dy    amount to translate by in y

+     */

+    void setTranslate(GrScalar dx, GrScalar dy);

+

+    /**

+     * overwrite entire matrix to be a scaling matrix

+     * @param sx    x scale factor

+     * @param sy    y scale factor

+     */

+    void setScale(GrScalar sx, GrScalar sy);

+

+    /**

+     * overwrite entire matrix to be a skew matrix

+     * @param skx   x skew factor

+     * @param sky   y skew factor

+     */

+    void setSkew(GrScalar skx, GrScalar sky);

+

+    /**

+     * set this matrix to be a concantenation of two

+     * matrices (a*b). Either a, b, or both can be this matrix.

+     * @param a     first matrix to multiply

+     * @param b     second matrix to multiply

+     */

+    void setConcat(const GrMatrix& a, const GrMatrix& b);

+

+    /**

+     * Set this matrix to this*m

+     * @param m     matrix to concatenate

+     */

+    void preConcat(const GrMatrix& m);

+

+    /**

+     * Set this matrix to m*this

+     * @param m     matrix to concatenate

+     */

+    void postConcat(const GrMatrix& m);

+

+    /**

+     *  Compute the inverse of this matrix, and return true if it is invertible,

+     *  or false if not.

+     *

+     *  If inverted is not null, and the matrix is invertible, then the inverse

+     *  is written into it. If the matrix is not invertible (this method returns

+     *  false) then inverted is left unchanged.

+     */

+    bool invert(GrMatrix* inverted) const;

+    

+    /**

+     * Transforms a point by the matrix

+     *

+     * @param src   the point to transform

+     * @return the transformed point

+     */

+    GrPoint mapPoint(const GrPoint& src) const {

+        GrPoint result;

+        (this->*gMapProcs[fTypeMask])(&result, &src, 1);

+        return result;

+    }

+    

+    /**

+     * Transforms an array of points by the matrix.

+     *

+     * @param dstPts the array to write transformed points into

+     * @param srcPts the array of points to transform

+     @ @param count the number of points to transform

+     */

+    void mapPoints(GrPoint dstPts[], 

+                   const GrPoint srcPts[], 

+                   uint32_t count) const {

+        (this->*gMapProcs[fTypeMask])(dstPts, srcPts, count);

+    }

+

+    /**

+     * Transforms pts with arbitrary stride in place.

+     *

+     * @param start  pointer to first point to transform

+     * @param stride distance in bytes between consecutive points

+     @ @param count the number of points to transform

+     */

+    void mapPointsWithStride(GrPoint* start, 

+                             size_t stride, 

+                             uint32_t count) const {

+        for (uint32_t i = 0; i < count; ++i) {            

+            this->mapPoints(start, start, 1);

+            start = (GrPoint*)((intptr_t)start + stride);

+        }

+    }

+    

+    /**

+     *  Transform the 4 corners of the src rect, and return the bounding rect

+     *  in the dst rect. Note: src and dst may point to the same memory.

+     */

+    void mapRect(GrRect* dst, const GrRect& src) const;

+

+    /**

+     *  Transform the 4 corners of the rect, and return their bounds in the rect

+     */

+    void mapRect(GrRect* rect) const {

+        this->mapRect(rect, *rect);

+    }

+

+    /**

+     * Checks if matrix is a perspective matrix.

+     * @return true if third row is not (0, 0, 1)

+     */

+    bool hasPerspective() const;

+    

+    /**

+     * Checks whether matrix is identity

+     * @return true if matrix is idenity

+     */

+    bool isIdentity() const;

+    

+    /**

+     * Calculates the maximum stretching factor of the matrix. Only defined if

+     * the matrix does not have perspective.

+     *

+     * @return maximum strecthing factor or negative if matrix has perspective.

+     */

+    GrScalar getMaxStretch() const;

+

+    /**

+     * Checks for matrix equality. Test is element-by-element equality,

+     * not a homogeneous test.

+     * @return true if matrices are equal, false otherwise

+     */

+    bool operator == (const GrMatrix& m) const;

+

+    /**

+     * Checks for matrix inequality. Test is element-by-element inequality,

+     * not a homogeneous test.

+     * @return true if matrices are not equal, false otherwise

+     */

+    bool operator != (const GrMatrix& m) const;

+    

+    static void UnitTest();

+

+private:

+    

+    void setTypeMask();

+    

+    double determinant() const;

+    

+    enum TypeBits {

+        kScale_TypeBit       = 1 << 0, // set if scales are not both 1

+        kTranslate_TypeBit   = 1 << 1, // set if translates are not both 0

+        kSkew_TypeBit        = 1 << 2, // set if skews are not both 0

+        kPerspective_TypeBit = 1 << 3, // set if perspective

+        kZeroScale_TypeBit   = 1 << 4, // set if scales are both zero

+    };

+

+    void mapIdentity(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapScale(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapScaleAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapSkew(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapScaleAndSkew(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapSkewAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapNonPerspective(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapPerspective(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapZero(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapSetToTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapSwappedScale(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    void mapSwappedScaleAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    

+    void mapInvalid(GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    

+    typedef void (GrMatrix::*MapProc) (GrPoint* dst, const GrPoint* src, uint32_t count) const;

+    static const MapProc gMapProcs[];

+

+    int      fTypeMask;

+    

+    GrScalar fM[9];

+};

+

+void GrMatrix::set(int idx, GrScalar value) {

+    GrAssert((unsigned)idx < 9);

+    fM[idx] = value;

+    if (idx > 5) {

+        if (0 != fM[kPersp0] || 0 != fM[kPersp1] ||

+            gRESCALE != fM[kPersp2]) {

+            fTypeMask |= kPerspective_TypeBit;

+        } else {

+            fTypeMask &= ~kPerspective_TypeBit;

+        }

+    } else if (!(idx % 4)) {

+        if ((GR_Scalar1 == fM[kScaleX] && GR_Scalar1 == fM[kScaleY])) {

+            fTypeMask &= ~kScale_TypeBit;

+            fTypeMask &= ~kZeroScale_TypeBit;

+        } else {

+            fTypeMask |= kScale_TypeBit;

+            if ((0 == fM[kScaleX] && 0 == fM[kScaleY])) {

+                fTypeMask |= kZeroScale_TypeBit;

+            } else {

+                fTypeMask &= ~kZeroScale_TypeBit;

+            }

+        }

+    } else if (2 == (idx % 3)) {

+        if (0 != fM[kTransX] || 0 != fM[kTransY]) {

+            fTypeMask |= kTranslate_TypeBit;

+        } else {

+            fTypeMask &= ~kTranslate_TypeBit;

+        }

+    } else {

+        if (0 != fM[kSkewX] || 0 != fM[kSkewY]) {

+            fTypeMask |= kSkew_TypeBit;

+        } else {

+            fTypeMask &= ~kSkew_TypeBit;

+        }

+    }

+}

+

+#endif
diff --git a/gpu/include/GrMemory.h b/gpu/include/GrMemory.h
new file mode 100644
index 0000000..673d0ab
--- /dev/null
+++ b/gpu/include/GrMemory.h
@@ -0,0 +1,151 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrMemory_DEFINED
+#define GrMemory_DEFINED
+
+#include "GrNoncopyable.h"
+
+class GrAutoMalloc : GrNoncopyable {
+public:
+    GrAutoMalloc(size_t bytes) : fPtr(GrMalloc(bytes)) {}
+    ~GrAutoMalloc() { GrFree(fPtr); }
+
+    /**
+     *  Return the allocated memory, or NULL if it has already been freed or
+     *  detached.
+     */
+    void* get() const { return fPtr; }
+
+    /**
+     *  transfer ownership of the memory to the caller. It must be freed with
+     *  a call to GrFree()
+     */
+    void* detach() {
+        void* ptr = fPtr;
+        fPtr = NULL;    // we no longer own the block
+        return ptr;
+    }
+    
+    /**
+     *  free the block now. get() will now return NULL
+     */
+    void free() {
+        GrFree(fPtr);
+        fPtr = NULL;
+    }
+
+private:
+    void* fPtr;
+};
+
+/**
+ *  Variant of GrAutoMalloc with a compile-time specified byte size that is
+ *  pre-allocated in the class object, avoiding a call to to GrMalloc if
+ *  possible.
+ */
+template <size_t SIZE> class GrAutoSMalloc : GrNoncopyable {
+public:
+    GrAutoSMalloc() {
+        fPtr = fStorage;
+        fAllocatedBytes = SIZE;
+    }
+
+    explicit GrAutoSMalloc(size_t bytes) {
+        if (bytes > SIZE) {
+            fPtr = GrMalloc(bytes);
+            fAllocatedBytes = bytes;
+        } else {
+            fPtr = fStorage;
+            fAllocatedBytes = SIZE;
+        }
+    }
+
+    ~GrAutoSMalloc() {
+        if (fPtr != (void*)fStorage) {
+            GrFree(fPtr);
+        }
+    }
+
+    /**
+     *  Return the allocated memory, or NULL if it has already been freed or
+     *  detached.
+     */
+    void* get() const { return fPtr; }
+    
+    /**
+     *  Reallocates to a new size. May or may not call malloc. The contents 
+     *  are not preserved. If growOnly is true it will never reduce the 
+     *  allocated size.
+     */
+    void* realloc(size_t newSize, bool growOnly = false) {
+        if (newSize <= SIZE) {
+            if (NULL == fPtr) {
+                fPtr = fStorage;
+                fAllocatedBytes = SIZE;
+            } else if (!growOnly && fPtr != (void*)fStorage) {
+                GrFree(fPtr);
+                fPtr = fStorage;
+                fAllocatedBytes = SIZE;
+            }            
+        } else if ((newSize > fAllocatedBytes) ||
+                   (!growOnly && newSize < (fAllocatedBytes >> 1))) {
+            if (NULL != fPtr && fPtr != (void*)fStorage) {
+                GrFree(fPtr);
+            }
+            fPtr = GrMalloc(newSize);
+            fAllocatedBytes = newSize;
+        }
+        GrAssert(fAllocatedBytes >= newSize);
+        GrAssert((fPtr == fStorage) == (fAllocatedBytes == SIZE));
+        GR_DEBUGCODE(memset(fPtr, 0xEF, fAllocatedBytes));
+        return fPtr;
+    }
+   
+    /**
+     *  free the block now. get() will now return NULL
+     */
+    void free() {
+        if (fPtr != (void*)fStorage) {
+            GrFree(fPtr);
+        }
+        fAllocatedBytes = 0;
+        fPtr = NULL;
+    }
+    
+private:
+    void*    fPtr;
+    uint32_t fAllocatedBytes;
+    uint32_t fStorage[GrALIGN4(SIZE) >> 2];
+};
+
+/**
+ *  Variant of GrAutoMalloc with a compile-time specified byte size that is
+ *  pre-allocated in the class object, avoiding a call to to GrMalloc if
+ *  possible.
+ */
+template <int COUNT, typename T>
+class GrAutoSTMalloc : public GrAutoSMalloc<COUNT * sizeof(T)> {
+public:
+    GrAutoSTMalloc(int count) : GrAutoSMalloc<COUNT * sizeof(T)>(count * sizeof(T)) {}
+
+    operator T*() { return (T*)this->get(); }
+};
+
+
+#endif
+
diff --git a/gpu/include/GrMesh.h b/gpu/include/GrMesh.h
new file mode 100644
index 0000000..4d904e4
--- /dev/null
+++ b/gpu/include/GrMesh.h
@@ -0,0 +1,42 @@
+#ifndef GrMesh_DEFINED
+#define GrMesh_DEFINED
+
+#include "SkRect.h"
+#include "SkPoint.h"
+
+class SkCanvas;
+class SkPaint;
+
+class GrMesh {
+public:
+    GrMesh();
+    ~GrMesh();
+
+    GrMesh& operator=(const GrMesh& src);
+
+    void init(const SkRect& bounds, int rows, int cols,
+              const SkRect& texture);
+
+    const SkRect& bounds() const { return fBounds; }
+
+    int rows() const { return fRows; }
+    int cols() const { return fCols; }
+    SkPoint& pt(int row, int col) {
+        return fPts[row * (fRows + 1) + col];
+    }
+
+    void draw(SkCanvas*, const SkPaint&);
+    void drawWireframe(SkCanvas* canvas, const SkPaint& paint);
+
+private:
+    SkRect      fBounds;
+    int         fRows, fCols;
+    SkPoint*    fPts;
+    SkPoint*    fTex;   // just points into fPts, not separately allocated
+    int         fCount;
+    uint16_t*   fIndices;
+    int         fIndexCount;
+};
+
+#endif
+
diff --git a/gpu/include/GrNoncopyable.h b/gpu/include/GrNoncopyable.h
new file mode 100644
index 0000000..888e3b1
--- /dev/null
+++ b/gpu/include/GrNoncopyable.h
@@ -0,0 +1,38 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrNoncopyable_DEFINED
+#define GrNoncopyable_DEFINED
+
+#include "GrTypes.h"
+
+/**
+ *  Base for classes that want to disallow copying themselves. It makes its
+ *  copy-constructor and assignment operators private (and unimplemented).
+ */
+class GrNoncopyable {
+public:
+    GrNoncopyable() {}
+
+private:
+    // illegal
+    GrNoncopyable(const GrNoncopyable&);
+    GrNoncopyable& operator=(const GrNoncopyable&);
+};
+
+#endif
+
diff --git a/gpu/include/GrPath.h b/gpu/include/GrPath.h
new file mode 100644
index 0000000..a9b7566
--- /dev/null
+++ b/gpu/include/GrPath.h
@@ -0,0 +1,84 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrPath_DEFINED
+#define GrPath_DEFINED
+
+#include "GrPathSink.h"
+#include "GrPathIter.h"
+#include "GrTDArray.h"
+#include "GrPoint.h"
+
+class GrPath : public GrPathSink {
+public:
+    GrPath();
+    GrPath(const GrPath&);
+    explicit GrPath(GrPathIter&);
+    virtual ~GrPath();
+
+    GrPathIter::ConvexHint getConvexHint() const { return fConvexHint; }
+    void setConvexHint(GrPathIter::ConvexHint hint) { fConvexHint = hint; }
+
+    void resetFromIter(GrPathIter*);
+
+    // overrides from GrPathSink
+
+    virtual void moveTo(GrScalar x, GrScalar y);
+    virtual void lineTo(GrScalar x, GrScalar y);
+    virtual void quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1);
+    virtual void cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1,
+                         GrScalar x2, GrScalar y2);
+    virtual void close();
+
+    class Iter : public GrPathIter {
+    public:
+        Iter(const GrPath& path);
+
+        // overrides from GrPathIter
+        virtual Command next(GrPoint points[]);
+        virtual ConvexHint hint() const;
+        virtual Command next();
+        virtual void rewind();
+    private:
+        const GrPath& fPath;
+        GrPoint       fLastPt;
+        int           fVerbIndex;
+        int           fPtIndex;
+    };
+
+private:
+    enum Verb {
+        kMove, kLine, kQuad, kCubic, kClose
+    };
+
+    GrTDArray<uint8_t>      fVerbs;
+    GrTDArray<GrPoint>      fPts;
+    GrPathIter::ConvexHint  fConvexHint;
+
+    // this ensures we have a moveTo at the start of each contour
+    inline void ensureMoveTo();
+
+    bool wasLastVerb(Verb verb) const {
+        int count = fVerbs.count();
+        return count > 0 && verb == fVerbs[count - 1];
+    }
+
+    friend class Iter;
+};
+
+#endif
+
diff --git a/gpu/include/GrPathIter.h b/gpu/include/GrPathIter.h
new file mode 100644
index 0000000..028faaa
--- /dev/null
+++ b/gpu/include/GrPathIter.h
@@ -0,0 +1,110 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrPathIter_DEFINED
+#define GrPathIter_DEFINED
+
+#include "GrTypes.h"
+
+struct GrPoint;
+
+/**
+ 2D Path iterator. Porting layer creates a subclass of this. It allows Ganesh to
+ parse the top-level API's 2D paths. Supports lines, quadratics, and cubic
+ pieces and moves (multi-part paths).
+ */
+class GrPathIter {
+public:
+    /**
+     Returned by next(). Indicates the next piece of the path. 
+     */
+    enum Command {
+        kMove_Command,      //!< next() returns 1 pt
+                            //   Starts a new subpath at
+                            //   at the returned point
+        kLine_Command,      //!< next() returns 2 pts
+                            //   Adds a line segment
+        kQuadratic_Command, //!< next() returns 3 pts
+                            //   Adds a quadratic segment
+        kCubic_Command,     //!< next() returns 4 pts
+                            //   Adds a cubic segment
+        kClose_Command,     //!< next() returns 0 pts
+        kEnd_Command        //!< next() returns 0 pts
+                            //   Implictly closes the last 
+                            //   point
+    };
+    
+    enum ConvexHint {
+        kNone_ConvexHint,                         //<! No hint about convexity 
+                                                  //   of the path
+        kConvex_ConvexHint,                       //<! Path is one convex piece
+        kNonOverlappingConvexPieces_ConvexHint,   //<! Multiple convex pieces, 
+                                                  //   pieces are known to be
+                                                  //   disjoint
+        kSameWindingConvexPieces_ConvexHint,      //<! Multiple convex pieces, 
+                                                  //   may or may not intersect,
+                                                  //   either all wind cw or all 
+                                                  //   wind ccw.
+        kConcave_ConvexHint                       //<! Path is known to be 
+                                                  //   concave
+    };
+    
+    static int NumCommandPoints(Command cmd) {
+        static const int numPoints[] = {
+            1, 2, 3, 4, 0, 0
+        };
+        return numPoints[cmd];
+    }
+    
+    virtual ~GrPathIter() {};
+
+    /** 
+     Iterates through the path. Should not be called after
+     kEnd_Command has been returned once. This version retrieves the
+     points for the command.
+     @param points  The points relevant to returned commend. See Command
+                    enum for number of points valid for each command.
+     @return The next command of the path.
+     */
+    virtual Command next(GrPoint points[4]) = 0;
+
+    /**
+     * If the host API has knowledge of the convexity of the path
+     * it can be communicated by this hint. Ganesh can make these
+     * determinations itself. So it is not necessary to compute 
+     * convexity status if it isn't already determined.
+     *
+     * @return a hint about the convexity of the path.
+     */     
+    virtual ConvexHint hint() const { return kNone_ConvexHint; }
+
+     /** 
+     Iterates through the path. Should not be called after
+     kEnd_Command has been returned once. This version does not retrieve the
+     points for the command.
+     @return The next command of the path.
+     */
+     virtual Command next() = 0;
+
+    /**
+     Restarts iteration from the beginning.
+     */
+    virtual void rewind() = 0;
+
+};
+
+#endif
diff --git a/gpu/include/GrPathSink.h b/gpu/include/GrPathSink.h
new file mode 100644
index 0000000..4e8a0c2
--- /dev/null
+++ b/gpu/include/GrPathSink.h
@@ -0,0 +1,36 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrPathSink_DEFINED
+#define GrPathSink_DEFINED
+
+#include "GrScalar.h"
+
+class GrPathSink {
+public:
+    virtual ~GrPathSink() {}
+
+    virtual void moveTo(GrScalar x, GrScalar y) = 0;
+    virtual void lineTo(GrScalar x, GrScalar y) = 0;
+    virtual void quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1) = 0;
+    virtual void cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1,
+                         GrScalar x2, GrScalar y2) = 0;
+    virtual void close() = 0;
+};
+
+#endif
+
diff --git a/gpu/include/GrPlotMgr.h b/gpu/include/GrPlotMgr.h
new file mode 100644
index 0000000..cd60bde
--- /dev/null
+++ b/gpu/include/GrPlotMgr.h
@@ -0,0 +1,84 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrPlotMgr_DEFINED
+#define GrPlotMgr_DEFINED
+
+#include "GrTypes.h"
+#include "GrPoint.h"
+
+class GrPlotMgr : GrNoncopyable {
+public:
+    GrPlotMgr(int width, int height) {
+        fDim.set(width, height);
+        size_t needed = width * height;
+        if (needed <= sizeof(fStorage)) {
+            fBusy = fStorage;
+        } else {
+            fBusy = new char[needed];
+        }
+        this->reset();
+    }
+
+    ~GrPlotMgr() {
+        if (fBusy != fStorage) {
+            delete[] fBusy;
+        }
+    }
+    
+    void reset() {
+        Gr_bzero(fBusy, fDim.fX * fDim.fY);
+    }
+
+    bool newPlot(GrIPoint16* loc) {
+        char* busy = fBusy;
+        for (int y = 0; y < fDim.fY; y++) {
+            for (int x = 0; x < fDim.fX; x++) {
+                if (!*busy) {
+                    *busy = true;
+                    loc->set(x, y);
+                    return true;
+                }
+                busy++;
+            }
+        }
+        return false;
+    }
+
+    bool isBusy(int x, int y) const {
+        GrAssert((unsigned)x < (unsigned)fDim.fX);
+        GrAssert((unsigned)y < (unsigned)fDim.fY);
+        return fBusy[y * fDim.fX + x] != 0;
+    }
+
+    void freePlot(int x, int y) {
+        GrAssert((unsigned)x < (unsigned)fDim.fX);
+        GrAssert((unsigned)y < (unsigned)fDim.fY);
+        fBusy[y * fDim.fX + x] = false;
+    }
+
+private:
+    enum {
+        STORAGE = 64
+    };
+    char fStorage[STORAGE];
+    char* fBusy;
+    GrIPoint16  fDim;
+};
+
+#endif
+
diff --git a/gpu/include/GrPoint.h b/gpu/include/GrPoint.h
new file mode 100644
index 0000000..bb24959
--- /dev/null
+++ b/gpu/include/GrPoint.h
@@ -0,0 +1,287 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrPoint_DEFINED
+#define GrPoint_DEFINED
+
+#include "GrTypes.h"
+#include "GrScalar.h"
+
+/**
+ *  2D Point struct
+ */
+struct GrPoint {
+public:
+    GrScalar fX, fY;
+
+    GrPoint() {}
+    GrPoint(GrScalar x, GrScalar y) { fX = x; fY = y; }
+    
+    GrScalar x() const { return fX; }
+    GrScalar y() const { return fY; }
+
+    void set(GrScalar x, GrScalar y) {
+        fX = x;
+        fY = y;
+    }
+    
+    void setAsMidPoint(const GrPoint& a, const GrPoint& b) {
+        fX = GrScalarAve(a.fX, b.fX);
+        fY = GrScalarAve(a.fY, b.fY);
+    }
+
+    void offset(GrScalar dx, GrScalar dy) {
+        fX += dx;
+        fY += dy;
+    }
+
+    GrScalar distanceToSqd(const GrPoint& p) const {
+        GrScalar dx = (p.fX - fX);
+        GrScalar dy = (p.fY - fY);
+        return GrMul(dx, dx) + GrMul(dy, dy);
+    }
+    
+    GrScalar distanceTo(const GrPoint& p) const {
+        // TODO: fixed point sqrt
+        return GrFloatToScalar(sqrtf(GrScalarToFloat(distanceToSqd(p))));
+    }
+    
+    GrScalar distanceToOriginSqd() const {
+        return GrMul(fX, fX) + GrMul(fY, fY);
+    }
+
+    GrScalar distanceToOrigin() const {
+        return GrFloatToScalar(sqrtf(GrScalarToFloat(distanceToOriginSqd())));
+    }
+    
+    inline GrScalar distanceToLineBetweenSqd(const GrPoint& a, 
+                                             const GrPoint& b) const;
+
+    inline GrScalar distanceToLineBetween(const GrPoint& a, 
+                                          const GrPoint& b) const;
+    
+    inline GrScalar distanceToLineSegmentBetweenSqd(const GrPoint& a, 
+                                                    const GrPoint& b) const;
+    
+    inline GrScalar distanceToLineSegmentBetween(const GrPoint& a, 
+                                                 const GrPoint& b) const;
+    
+    // counter-clockwise fan
+    void setRectFan(GrScalar l, GrScalar t, GrScalar r, GrScalar b) {
+        GrPoint* v = this;
+        v[0].set(l, t);
+        v[1].set(l, b);
+        v[2].set(r, b);
+        v[3].set(r, t);
+    }
+    
+    void setRectFan(GrScalar l, GrScalar t, GrScalar r, GrScalar b, size_t stride) {
+        GrAssert(stride >= sizeof(GrPoint));
+        ((GrPoint*)((intptr_t)this + 0 * stride))->set(l, t);
+        ((GrPoint*)((intptr_t)this + 1 * stride))->set(l, b);
+        ((GrPoint*)((intptr_t)this + 2 * stride))->set(r, b);
+        ((GrPoint*)((intptr_t)this + 3 * stride))->set(r, t);
+    }
+    
+    // counter-clockwise fan
+    void setIRectFan(int l, int t, int r, int b) {
+        GrPoint* v = this;
+        v[0].set(GrIntToScalar(l), GrIntToScalar(t));
+        v[1].set(GrIntToScalar(l), GrIntToScalar(b));
+        v[2].set(GrIntToScalar(r), GrIntToScalar(b));
+        v[3].set(GrIntToScalar(r), GrIntToScalar(t));
+    }
+    
+    void setIRectFan(int l, int t, int r, int b, size_t stride) {
+        GrAssert(stride >= sizeof(GrPoint));
+        ((GrPoint*)((intptr_t)this + 0 * stride))->set(GrIntToScalar(l), 
+                                                      GrIntToScalar(t));
+        ((GrPoint*)((intptr_t)this + 1 * stride))->set(GrIntToScalar(l), 
+                                                      GrIntToScalar(b));
+        ((GrPoint*)((intptr_t)this + 2 * stride))->set(GrIntToScalar(r), 
+                                                      GrIntToScalar(b));
+        ((GrPoint*)((intptr_t)this + 3 * stride))->set(GrIntToScalar(r), 
+                                                      GrIntToScalar(t));
+    }
+    
+    bool operator ==(const GrPoint& p) const {
+        return fX == p.fX && fY == p.fY;
+    }
+    
+    bool operator !=(const GrPoint& p) const {
+        return fX != p.fX || fY != p.fY;
+    }
+};
+
+struct GrIPoint16 {
+    int16_t fX, fY;
+    
+    void set(intptr_t x, intptr_t y) {
+        fX = GrToS16(x);
+        fY = GrToS16(y);
+    }
+};
+
+struct GrVec {
+public:
+    GrScalar fX, fY;
+    
+    GrVec() {}
+    GrVec(GrScalar x, GrScalar y) { fX = x; fY = y; }
+    
+    GrScalar x() const { return fX; }
+    GrScalar y() const { return fY; }
+    
+    /**
+     * set x and y length of the vector.
+     */
+    void set(GrScalar x, GrScalar y) {
+        fX = x;
+        fY = y;
+    }
+    
+    /**
+     * set vector to point from a to b.
+     */
+    void setBetween(const GrPoint& a, const GrPoint& b) {
+        fX = b.fX - a.fX;
+        fY = b.fY - a.fY;
+    }
+      
+    /**
+     * length of the vector squared.
+     */
+    GrScalar lengthSqd() const {
+        return GrMul(fX, fX) + GrMul(fY, fY);
+    }
+    
+    /**
+     * length of the vector.
+     */
+    GrScalar length() const {
+        // TODO: fixed point sqrt
+        return GrFloatToScalar(sqrtf(GrScalarToFloat(lengthSqd())));
+    }
+    
+    /**
+     * normalizes the vector if it's length is not 0.
+     * @return true if normalized, otherwise false.
+     */
+    bool normalize() {
+        GrScalar l = lengthSqd();
+        if (l) {
+            // TODO: fixed point sqrt and invert
+            l = 1 / sqrtf(l);
+            fX *= l;
+            fY *= l;
+            return true;
+        }
+        return false;
+    }
+    
+    /**
+     * Dot product of this with vec.
+     */
+    GrScalar dot(const GrVec& vec) const {
+        return GrMul(vec.fX, fX) + GrMul(vec.fY, fY);
+    }
+   
+    /**
+     * z-value of this cross vec.
+     */
+    GrScalar cross(const GrVec& vec) const {
+        return GrMul(fX, vec.fY) - GrMul(fY, vec.fX);
+    }
+    
+    bool operator ==(const GrPoint& p) const {
+        return fX == p.fX && fY == p.fY;
+    }
+    
+    bool operator !=(const GrPoint& p) const {
+        return fX != p.fX || fY != p.fY;
+    }
+};
+
+GrScalar GrPoint::distanceToLineBetweenSqd(const GrPoint& a, 
+                                           const GrPoint& b) const {
+    // Let d be the distance between c (this) and line ab.
+    // The area of the triangle defined by a, b, and c is 
+    // A = |b-a|*d/2. Let u = b-a and v = c-a. The cross product of
+    // u and v is aligned with the z axis and its magnitude is 2A. 
+    // So d = |u x v| / |u|.
+    GrVec u, v;
+    u.setBetween(a,b);
+    v.setBetween(a,*this);
+    
+    GrScalar det = u.cross(v);
+    return (GrMul(det, det)) / u.lengthSqd();
+}
+
+GrScalar GrPoint::distanceToLineBetween(const GrPoint& a, 
+                                        const GrPoint& b) const {
+    GrVec u, v;
+    u.setBetween(a,b);
+    v.setBetween(a,*this);
+    
+    GrScalar det = u.cross(v);
+    return (GrScalarAbs(det)) / u.length();
+}
+
+GrScalar GrPoint::distanceToLineSegmentBetweenSqd(const GrPoint& a, 
+                                                  const GrPoint& b) const {
+    // See comments to distanceToLineBetweenSqd. If the projection of c onto
+    // u is between a and b then this returns the same result as that 
+    // function. Otherwise, it returns the distance to the closer of a and
+    // b. Let the projection of v onto u be v'.  There are three cases:
+    //    1. v' points opposite to u. c is not between a and b and is closer
+    //       to a than b.
+    //    2. v' points along u and has magnitude less than y. c is between
+    //       a and b and the distance to the segment is the same as distance
+    //       to the line ab.
+    //    3. v' points along u and has greater magnitude than u. c is not
+    //       not between a and b and is closer to b than a.
+    // v' = (u dot v) * u / |u|. So if (u dot v)/|u| is less than zero we're 
+    // in case 1. If (u dot v)/|u| is > |u| we are in case 3. Otherwise
+    // we're in case 2. We actually compare (u dot v) to 0 and |u|^2 to 
+    // avoid a sqrt to compute |u|.
+    
+    GrVec u, v;
+    u.setBetween(a,b);
+    v.setBetween(a,*this);
+    
+    GrScalar uLengthSqd = u.lengthSqd();
+    GrScalar uDotV = u.dot(v);
+    
+    if (uDotV <= 0) {
+        return v.lengthSqd();
+    } else if (uDotV > uLengthSqd) {
+        return b.distanceToSqd(*this);
+    } else {
+        GrScalar det = u.cross(v);
+        return (GrMul(det, det)) / uLengthSqd;
+    }
+}
+
+GrScalar GrPoint::distanceToLineSegmentBetween(const GrPoint& a, 
+                                               const GrPoint& b) const {
+    // TODO: fixed point sqrt
+    return GrFloatToScalar(sqrtf(GrScalarToFloat(distanceToLineSegmentBetweenSqd(a,b))));
+}
+
+
+#endif
+
diff --git a/gpu/include/GrRandom.h b/gpu/include/GrRandom.h
new file mode 100644
index 0000000..408f61d
--- /dev/null
+++ b/gpu/include/GrRandom.h
@@ -0,0 +1,62 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrRandom_DEFINED
+#define GrRandom_DEFINED
+
+class GrRandom {
+public:
+    GrRandom() : fSeed(0) {}
+    GrRandom(uint32_t seed) : fSeed(seed) {}
+
+    uint32_t seed() const { return fSeed; }
+
+    uint32_t nextU() {
+        fSeed = fSeed * kMUL + kADD;
+        return fSeed;
+    }
+
+    int32_t nextS() { return (int32_t)this->nextU(); }
+
+    /**
+     *  Returns value [0...1) as a float
+     */
+    float nextF() {
+        // const is 1 / (2^32 - 1)
+        return (float)(this->nextU() * 2.32830644e-10);
+    }
+
+    /**
+     *  Returns value [min...max) as a float
+     */
+    float nextF(float min, float max) {
+        return min + this->nextF() * (max - min);
+    }
+
+private:
+    /*
+     *  These constants taken from "Numerical Recipes in C", reprinted 1999
+     */
+    enum {
+        kMUL = 1664525,
+        kADD = 1013904223
+    };
+    uint32_t    fSeed;
+};
+
+#endif
+
diff --git a/gpu/include/GrRect.h b/gpu/include/GrRect.h
new file mode 100644
index 0000000..5192ebd
--- /dev/null
+++ b/gpu/include/GrRect.h
@@ -0,0 +1,284 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrRect_DEFINED
+#define GrRect_DEFINED
+
+#include "GrPoint.h"
+
+struct GrIRect {
+    int32_t fLeft, fTop, fRight, fBottom;
+    
+    GrIRect() {}
+    GrIRect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
+        fLeft = left;
+        fTop = top;
+        fRight = right;
+        fBottom = bottom;
+    }
+
+    int32_t x() const { return fLeft; }
+    int32_t y() const { return fTop; }
+    int32_t width() const { return fRight - fLeft; }
+    int32_t height() const { return fBottom - fTop; }
+
+    bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
+    bool isInverted() const { return fLeft > fRight || fTop > fBottom; }
+
+    void setEmpty() { fLeft = fTop = fRight = fBottom = 0; }
+
+    void setXYWH(int32_t x, int32_t y, int32_t w, int32_t h) {
+        fLeft = x;
+        fTop = y;
+        fRight = x + w;
+        fBottom = y + h;
+    }
+    
+    void setLTRB(int32_t l, int32_t t, int32_t r, int32_t b) {
+        fLeft = l;
+        fTop = t;
+        fRight = r;
+        fBottom = b;
+    }
+    
+    /**
+     *  Make the largest representable rectangle
+
+     */
+    void setLargest() {
+        fLeft = fTop = GR_Int32Min;
+        fRight = fBottom = GR_Int32Max;
+    }
+    
+    bool quickReject(int l, int t, int r, int b) const {
+        return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b;
+    }
+
+    void unionWith(const GrIRect& r) {
+        if (fLeft > r.fLeft) fLeft = r.fLeft;
+        if (fTop > r.fTop) fTop = r.fTop;
+        if (fRight < r.fRight) fRight = r.fRight;
+        if (fBottom < r.fBottom) fBottom = r.fBottom;
+    }
+
+    friend bool operator==(const GrIRect& a, const GrIRect& b) {
+        return 0 == memcmp(&a, &b, sizeof(a));
+    }
+    
+    friend bool operator!=(const GrIRect& a, const GrIRect& b) {
+        return 0 != memcmp(&a, &b, sizeof(a));
+    }
+
+    bool equalsLTRB(int l, int t, int r, int b) const {
+        return fLeft == l && fTop == t &&
+               fRight == r && fBottom == b;
+    }
+    bool equalsXYWH(int x, int y, int w, int h) const {
+        return fLeft == x && fTop == y &&
+               this->width() == w && this->height() == h;
+    }
+    
+    bool contains(const GrIRect& r) const {
+        return fLeft   <= r.fLeft &&
+               fRight  >= r.fRight &&
+               fTop    <= r.fTop &&
+               fBottom >= r.fBottom;
+    }
+};
+
+struct GrIRect16 {
+    int16_t fLeft, fTop, fRight, fBottom;
+    
+    int width() const { return fRight - fLeft; }
+    int height() const { return fBottom - fTop; }
+    int area() const { return this->width() * this->height(); }
+    bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
+    
+    void set(const GrIRect& r) {
+        fLeft   = GrToS16(r.fLeft);
+        fTop    = GrToS16(r.fTop);
+        fRight  = GrToS16(r.fRight);
+        fBottom = GrToS16(r.fBottom);
+    }
+};
+
+/** 
+ *  2D Rect struct
+ */
+struct GrRect {
+    GrScalar fLeft, fTop, fRight, fBottom;
+    
+    /**
+     *  Uninitialized rectangle.
+     */
+    GrRect() {}
+
+    /**
+     *  Initialize a rectangle to a point.
+     *  @param pt the point used to initialize the rectanglee.
+     */
+    GrRect(GrPoint pt) {
+        setToPoint(pt);
+    }
+
+    GrRect(GrScalar left, GrScalar top, GrScalar right, GrScalar bottom) {
+        fLeft = left;
+        fTop = top;
+        fRight = right;
+        fBottom = bottom;
+    }
+
+    explicit GrRect(const GrIRect& src) {
+        fLeft = GrIntToScalar(src.fLeft);
+        fTop = GrIntToScalar(src.fTop);
+        fRight = GrIntToScalar(src.fRight);
+        fBottom = GrIntToScalar(src.fBottom);
+    }
+
+    GrScalar x() const { return fLeft; }
+    GrScalar y() const { return fTop; }
+    GrScalar width() const { return fRight - fLeft; }
+    GrScalar height() const { return fBottom - fTop; }
+
+    GrScalar left() const { return fLeft; }
+    GrScalar top() const { return fTop; }
+    GrScalar right() const { return fRight; }
+    GrScalar bottom() const { return fBottom; }
+    
+    GrScalar diagonalLengthSqd() const {
+        GrScalar w = width();
+        GrScalar h = height();
+        return GrMul(w, w) + GrMul(h, h);
+    }
+
+    GrScalar diagonalLength() const {
+        // TODO: fixed point sqrt
+        return GrFloatToScalar(sqrtf(GrScalarToFloat(diagonalLengthSqd())));
+    }
+    
+    /**
+     *  Returns true if the width or height is <= 0
+     */
+    bool isEmpty() const {
+        return fLeft >= fRight || fTop >= fBottom;
+    }
+    
+    void setEmpty() {
+        fLeft = fTop = fRight = fBottom = 0;
+    }
+    
+    /**
+     *  returns true if the rectangle is inverted either in x or y
+     */
+    bool isInverted() const {
+        return (fLeft > fRight) || (fTop > fBottom);
+    }
+    
+    /**
+     *  Initialize a rectangle to a point.
+     *  @param pt the point used to initialize the rectangle.
+     */
+    void setToPoint(const GrPoint& pt) {
+        fLeft = pt.fX;
+        fTop = pt.fY;
+        fRight = pt.fX;
+        fBottom = pt.fY;
+    }
+
+    void set(const GrIRect& r) {
+        fLeft = GrIntToScalar(r.fLeft);
+        fTop = GrIntToScalar(r.fTop);
+        fRight = GrIntToScalar(r.fRight);
+        fBottom = GrIntToScalar(r.fBottom);
+    }
+
+    void roundOut(GrIRect* r) const {
+        r->setLTRB(GrScalarFloorToInt(fLeft),
+                   GrScalarFloorToInt(fTop),
+                   GrScalarCeilToInt(fRight),
+                   GrScalarCeilToInt(fBottom));
+    }
+
+    /**
+     *  Set the rect to the union of the array of points. If the array is empty
+     *  the rect will be empty [0,0,0,0]
+     */
+    void setBounds(const GrPoint pts[], int count);
+
+    /**
+     *  Make the largest representable rectangle
+     *  Set the rect to fLeft = fTop = GR_ScalarMin and 
+     *  fRight = fBottom = GR_ScalarMax.
+     */
+    void setLargest() {
+        fLeft = fTop = GR_ScalarMin;
+        fRight = fBottom = GR_ScalarMax;
+    }
+    
+    /**
+     Set the rect to fLeft = fTop = GR_ScalarMax and 
+     fRight = fBottom = GR_ScalarMin.
+     Useful for initializing a bounding rectangle.
+     */
+    void setLargestInverted() {
+        fLeft = fTop = GR_ScalarMax;
+        fRight = fBottom = GR_ScalarMin;
+    }
+    
+    void setLTRB(GrScalar left, 
+                 GrScalar top, 
+                 GrScalar right, 
+                 GrScalar bottom) {
+        fLeft = left;
+        fTop = top;
+        fRight = right;
+        fBottom = bottom;
+    }
+    
+    void setXYWH(GrScalar x, GrScalar y, GrScalar width, GrScalar height) {
+        fLeft = x;
+        fTop = y;
+        fRight = x + width;
+        fBottom = y + height;
+    }
+    
+    /**
+     Expand the edges of the rectangle to include a point.
+     Useful for constructing a bounding rectangle.
+     @param pt  the point used to grow the rectangle.
+     */
+    void growToInclude(const GrPoint& pt) {
+        fLeft  = GrMin(pt.fX, fLeft);
+        fRight = GrMax(pt.fX, fRight);
+        
+        fTop    = GrMin(pt.fY, fTop);
+        fBottom = GrMax(pt.fY, fBottom);
+    }
+
+    /**
+     *  Assigns 4 sequential points in order to construct a counter-clockwise
+     *  triangle fan, given the corners of this rect. Returns the address of
+     *  the next point, treating pts as an array.
+     */
+    GrPoint* setRectFan(GrPoint pts[4]) const {
+        pts->setRectFan(fLeft, fTop, fRight, fBottom);
+        return pts + 4;
+    }
+};
+
+#endif
+
diff --git a/gpu/include/GrRectanizer.h b/gpu/include/GrRectanizer.h
new file mode 100644
index 0000000..50bb8fe
--- /dev/null
+++ b/gpu/include/GrRectanizer.h
@@ -0,0 +1,64 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrRectanizer_DEFINED
+#define GrRectanizer_DEFINED
+
+#include "GrRect.h"
+#include "GrTDArray.h"
+
+class GrRectanizerPurgeListener {
+public:
+    virtual ~GrRectanizerPurgeListener() {}
+
+    virtual void notifyPurgeStrip(void*, int yCoord) = 0;
+};
+
+class GrRectanizer {
+public:
+    GrRectanizer(int width, int height) : fWidth(width), fHeight(height) {
+        GrAssert(width >= 0);
+        GrAssert(height >= 0);
+    }
+
+    virtual ~GrRectanizer() {}
+
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+
+    virtual bool addRect(int width, int height, GrIPoint16* loc) = 0;
+    virtual float percentFull() const = 0;
+
+    // return the Y-coordinate of a strip that should be purged, given height
+    // i.e. return the oldest such strip, or some other criteria. Return -1
+    // if there is no candidate
+    virtual int stripToPurge(int height) const = 0;
+    virtual void purgeStripAtY(int yCoord) = 0;
+
+    /**
+     *  Our factory, which returns the subclass du jour
+     */
+    static GrRectanizer* Factory(int width, int height);
+
+private:
+    int fWidth;
+    int fHeight;
+};
+
+#endif
+
+
diff --git a/gpu/include/GrRefCnt.h b/gpu/include/GrRefCnt.h
new file mode 100644
index 0000000..7204aff
--- /dev/null
+++ b/gpu/include/GrRefCnt.h
@@ -0,0 +1,125 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrRefCnt_DEFINED
+#define GrRefCnt_DEFINED
+
+#include "GrTypes.h"
+#include "GrNoncopyable.h"
+
+/**
+ *  Base class for reference counting. When an object is first instantiated,
+ *  its reference count is 1. If the object may be null, use GrSafeRef() and
+ *  GrSafeUnref().
+ *
+ *  It is an error (though only checked for in the debug build) to call unref()
+ *  such that the reference count becomes 0.
+ */
+class GrRefCnt : GrNoncopyable {
+public:
+            GrRefCnt() : fRefCnt(1) {}
+    virtual ~GrRefCnt() {
+        GrAssert(1 == fRefCnt);
+#if GR_DEBUG
+        fRefCnt = 0;    // force validate() to trigger if called afterwards
+#endif
+    }
+
+    int32_t refcnt() const { return fRefCnt; }
+
+    void ref() const {
+        GrAssert(fRefCnt > 0);
+        ++fRefCnt;
+    }
+
+    void unref() const {
+        GrAssert(fRefCnt > 0);
+        if (1 == fRefCnt) {
+            delete this;
+        } else {
+            --fRefCnt;
+        }
+    }
+
+#if GR_DEBUG
+    void validate() const {
+        GrAssert(fRefCnt > 0);
+    }
+#else
+    void validate() const {}
+#endif
+
+private:
+    mutable int32_t fRefCnt;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  Call with instance/subclass of GrRefCnt. This does nothing if obj is null,
+ *  but otherwise it calls ref().
+ */
+static inline void GrSafeRef(const GrRefCnt* obj) {
+    if (obj) {
+        obj->ref();
+    }
+}
+
+/**
+ *  Call with instance/subclass of GrRefCnt. This does nothing if obj is null,
+ *  but otherwise it calls unref().
+ */
+static inline void GrSafeUnref(const GrRefCnt* obj) {
+    if (obj) {
+        obj->unref();
+    }
+}
+
+/**
+ *  Assigns src to dst, checking for NULLs in each, and correctly incrementing
+ *  the reference count of src, and decrementing the reference count of dst
+ */
+static inline void GrSafeAssign(GrRefCnt*& dst, GrRefCnt* src) {
+    if (src) {
+        src->ref();
+    }
+    if (dst) {
+        dst->unref();
+    }
+    dst = src;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrAutoRef : GrNoncopyable {
+public:
+    GrAutoRef(GrRefCnt* obj) : fObj(obj) { GrSafeRef(obj); }
+    ~GrAutoRef() { GrSafeUnref(fObj); }
+private:
+    GrRefCnt* fObj;
+};
+
+class GrAutoUnref : GrNoncopyable {
+public:
+    GrAutoUnref(GrRefCnt* obj) : fObj(obj) {}
+    ~GrAutoUnref() { GrSafeUnref(fObj); }
+private:
+    GrRefCnt* fObj;
+};
+
+#endif
+
diff --git a/gpu/include/GrSamplerState.h b/gpu/include/GrSamplerState.h
new file mode 100644
index 0000000..06c2346
--- /dev/null
+++ b/gpu/include/GrSamplerState.h
@@ -0,0 +1,130 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrSamplerState_DEFINED
+#define GrSamplerState_DEFINED
+
+#include "GrTypes.h"
+
+class GrSamplerState {
+public:
+    enum SampleMode {
+        kNormal_SampleMode,     //!< sample color directly
+        kAlphaMod_SampleMode,   //!< modulate with alpha only
+        kRadial_SampleMode,     //!< treat as radial gradient
+        kRadial2_SampleMode,    //!< treat as 2-point radial gradient
+        kSweep_SampleMode,      //!< treat as sweep gradient
+    };
+
+    /**
+     * Describes how a texture is sampled when coordinates are outside the
+     * texture border
+     */
+    enum WrapMode {
+        kClamp_WrapMode,
+        kRepeat_WrapMode,
+        kMirror_WrapMode
+    };
+
+    /**
+     *  Default sampler state is set to kClamp and no-filter
+     */
+    GrSamplerState() {
+        this->setClampNoFilter();
+    }
+
+    GrSamplerState(bool filter) {
+        fWrapX = kClamp_WrapMode;
+        fWrapY = kClamp_WrapMode;
+        fSampleMode = kNormal_SampleMode;
+        fFilter = filter;
+    }
+    
+    GrSamplerState(WrapMode wx, WrapMode wy, bool filter) {
+        fWrapX = wx;
+        fWrapY = wy;
+        fSampleMode = kNormal_SampleMode;
+        fFilter = filter;
+    }
+    
+    GrSamplerState(WrapMode wx, WrapMode wy, SampleMode sample, bool filter) {
+        fWrapX = wx;
+        fWrapY = wy;
+        fSampleMode = sample;
+        fFilter = filter;
+    }
+    
+    WrapMode getWrapX() const { return fWrapX; }
+    WrapMode getWrapY() const { return fWrapY; }
+    SampleMode getSampleMode() const { return fSampleMode; }
+    bool isFilter() const { return fFilter; }
+
+    bool isGradient() const {
+        return  kRadial_SampleMode == fSampleMode ||
+                kRadial2_SampleMode == fSampleMode ||
+                kSweep_SampleMode == fSampleMode;
+    }
+
+    void setWrapX(WrapMode mode) { fWrapX = mode; }
+    void setWrapY(WrapMode mode) { fWrapY = mode; }
+    void setSampleMode(SampleMode mode) { fSampleMode = mode; }
+    void setFilter(bool filter) { fFilter = filter; }
+
+    void setClampNoFilter() {
+        fWrapX = kClamp_WrapMode;
+        fWrapY = kClamp_WrapMode;
+        fSampleMode = kNormal_SampleMode;
+        fFilter = false;
+    }
+
+    GrScalar getRadial2CenterX1() const { return fRadial2CenterX1; }
+    GrScalar getRadial2Radius0() const { return fRadial2Radius0; }
+    bool     isRadial2PosRoot() const { return fRadial2PosRoot; }
+
+    /**
+     * Sets the parameters for kRadial2_SampleMode. The texture 
+     * matrix must be set so that the first point is at (0,0) and the second 
+     * point lies on the x-axis. The second radius minus the first is 1 unit.
+     * The additional parameters to define the gradient are specified by this
+     * function.
+     */
+    void setRadial2Params(GrScalar centerX1, GrScalar radius0, bool posRoot) {
+        fRadial2CenterX1 = centerX1;
+        fRadial2Radius0 = radius0;
+        fRadial2PosRoot = posRoot;
+    }
+
+    static const GrSamplerState& ClampNoFilter() {
+        return gClampNoFilter;
+    }
+
+private:
+    WrapMode    fWrapX;
+    WrapMode    fWrapY;
+    SampleMode  fSampleMode;
+    bool        fFilter;
+
+    // these are undefined unless fSampleMode == kRadial2_SampleMode
+    GrScalar    fRadial2CenterX1;
+    GrScalar    fRadial2Radius0;
+    bool        fRadial2PosRoot;
+
+    static const GrSamplerState gClampNoFilter;
+};
+
+#endif
+
diff --git a/gpu/include/GrScalar.h b/gpu/include/GrScalar.h
new file mode 100644
index 0000000..1353fb2
--- /dev/null
+++ b/gpu/include/GrScalar.h
@@ -0,0 +1,116 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrScalar_DEFINED
+#define GrScalar_DEFINED
+
+#include "GrTypes.h"
+
+#include <float.h>
+#include <math.h>
+
+#define GR_Int32Max            (0x7fffffff)
+#define GR_Int32Min            (0x80000000)
+
+/**
+ *  Convert an int to fixed point 
+ */
+#if GR_DEBUG
+    inline GrFixed GrIntToFixed(int i) {
+        GrAssert(((i & 0xffff0000) == 0xffff0000) || ((i & 0xffff0000) == 0x0));
+        return i << 16;
+    }
+#else
+    #define GrIntToFixed(i) (GrFixed)((i) << 16)
+#endif
+
+#define GR_Fixed1              (1 << 16)
+#define GR_FixedHalf           (1 << 15)
+#define GR_FixedMax            GR_Int32Max
+#define GR_FixedMin            GR_Int32Min
+
+#define GrFixedFloorToFixed(x)  ((x) & ~0xFFFF)
+#define GrFixedFloorToInt(x)    ((x) >> 16)
+
+/**
+ *  Convert fixed point to floating point
+ */
+#define GrFixedToFloat(x)      ((x) * 0.0000152587890625f)
+
+/**
+ *  Convert floating point to fixed point
+ */
+#define GrFloatToFixed(x)      ((GrFixed)((x) * GR_Fixed1))
+
+inline GrFixed GrFixedAbs(GrFixed x) {
+    int32_t s = (x & 0x80000000) >> 31;
+    return (GrFixed)(((int32_t)x ^ s) - s);  
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_SCALAR_IS_FIXED
+    typedef GrFixed                 GrScalar;
+    #define GrIntToScalar(x)        GrIntToFixed(x)
+    #define GrFixedToScalar(x)      (x)
+    #define GrScalarToFloat(x)      GrFixedToFloat(x)
+    #define GrFloatToScalar(x)      GrFloatToFixed(x)
+    #define GrScalarHalf(x)         ((x) >> 1)
+    #define GrScalarAve(x,y)        (((x)+(y)) >> 1)
+    #define GrScalarAbs(x)          GrFixedAbs(x)
+    #define GR_Scalar1              GR_Fixed1
+    #define GR_ScalarHalf           GR_FixedHalf
+    #define GR_ScalarMax            GR_FixedMax
+    #define GR_ScalarMin            GR_FixedMin
+#elif GR_SCALAR_IS_FLOAT
+    typedef float                   GrScalar;
+    #define GrIntToScalar(x)        ((GrScalar)x)
+    #define GrFixedToScalar(x)      GrFixedToFloat(x)
+    #define GrScalarToFloat(x)      (x)
+    #define GrFloatToScalar(x)      (x)
+    #define GrScalarHalf(x)         ((x) * 0.5f)
+    #define GrScalarAbs(x)          fabsf(x)
+    #define GrScalarAve(x,y)        (((x) + (y)) * 0.5f)
+    #define GR_Scalar1              1.f    
+    #define GR_ScalarHalf           0.5f
+    #define GR_ScalarMax            (FLT_MAX)
+    #define GR_ScalarMin            (-FLT_MAX)
+
+    static inline int32_t GrScalarFloorToInt(float x) {
+        return (int32_t)::floorf(x);
+    }
+    static inline int32_t GrScalarCeilToInt(float x) {
+        return (int32_t)::ceilf(x);
+    }
+#else
+    #error "Scalar type not defined"
+#endif
+
+/**
+ *  Multiply two GrScalar values
+ */
+static inline GrScalar GrMul(GrScalar a, GrScalar b) {
+#if GR_SCALAR_IS_FLOAT
+    return a * b;
+#else
+    int64_t tmp = (int64_t)a * b;
+    return (tmp + GR_FixedHalf) >> 16;
+#endif
+}
+
+#endif
+
diff --git a/gpu/include/GrStopwatch.h b/gpu/include/GrStopwatch.h
new file mode 100644
index 0000000..4945897
--- /dev/null
+++ b/gpu/include/GrStopwatch.h
@@ -0,0 +1,135 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrStopwatch_DEFINED
+#define GrStopwatch_DEFINED
+
+#include "GrTypes.h"
+
+template <typename PLATFORM_TIMER>
+/**
+ * Base class for stopwatch. Relies on PLATFORM_TIMER for platform-specific
+ * timer functions. PLATFORM_TIMER provides:
+ *      - typename TIMESTAMP : a timestamp value that can be used with Diff()
+ *      - static TIMESTAMP Now() : gets current timestamp
+ *      - static double Diff(const TIMESTAMP& begin, const TIMESTAMP& end) : 
+ *           computes delta in seconds between two timestamps
+ */
+class GrStopwatchBase {
+public:
+    /**
+     * Contructor - implicit reset()
+     */
+    GrStopwatchBase() {
+        fRunning = false;
+        fTotalElapsed = 0.0;        
+    }
+
+    /**
+     * begins a new lap
+     */
+    void start() {
+        double lastLap = lapTime();
+        fTotalElapsed += lastLap;
+        fRunning = true;
+        fLastStart = PLATFORM_TIMER::Now();
+    }
+    
+    /**
+     * ends current lap (or no effect if lap not started)
+     */
+    void stop() {
+        double lastLap = lapTime();
+        fTotalElapsed += lastLap;
+        fRunning = false;
+    }
+
+    /**
+     * ends current lap, resets total time
+     */
+    void reset() {
+        fRunning = false;
+        fTotalElapsed = 0.f;
+    }
+    
+    /**
+     * Computes the time of all laps since last reset() including current lap
+     * if lap is still running.
+     *
+     * @return the sum time in seconds of all laps since last reset().
+     */
+    double totalTime() const {
+        return fTotalElapsed + lapTime();
+    }
+
+    /**
+     * Current lap time.
+     *
+     * @return time in seconds of current lap if one is running otherwise 0.
+     */
+    double lapTime() const {
+        if (fRunning) {
+            PLATFORM_TIMER::Timestamp now = PLATFORM_TIMER::Now();
+            return PLATFORM_TIMER::Elapsed(fLastStart, now);
+        }
+        return 0.0;
+    }
+
+private:
+    double fTotalElapsed;
+
+    typename PLATFORM_TIMER::Timestamp fLastStart;
+    bool                               fRunning;
+};
+
+#if GR_WIN32_BUILD
+
+    #include <Windows.h>
+
+    class GrWin32Timer {
+    public:
+        typedef LARGE_INTEGER Timestamp;  
+
+        static Timestamp Now() {
+            LARGE_INTEGER now;
+            QueryPerformanceCounter(&now);
+            return now;
+        }
+
+        static double Elapsed(const Timestamp& begin, const Timestamp& end) {
+            double diff = (double)(end.QuadPart - begin.QuadPart);
+            return diff * Scale();
+        }
+    private:
+        static double Scale() {
+            static double scale;
+            if (0.0 == scale) {
+                LARGE_INTEGER freq;
+                QueryPerformanceFrequency(&freq);
+                GrAssert(0 != freq.QuadPart);
+                scale = 1 / (double) freq.QuadPart;
+            }
+            return scale;
+        }
+    };
+    typedef GrStopwatchBase<GrWin32Timer> GrStopwatch;
+#else
+    #error "Implement platform timer for stopwatch"
+#endif
+
+
+#endif
diff --git a/gpu/include/GrStringBuilder.h b/gpu/include/GrStringBuilder.h
new file mode 100644
index 0000000..bcf124f
--- /dev/null
+++ b/gpu/include/GrStringBuilder.h
@@ -0,0 +1,182 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrStringBuilder_DEFINED
+#define GrStringBuilder_DEFINED
+
+#include <GrTArray.h>
+#include <stdio.h>
+
+// Class used to concat strings together into a single string
+// See below for GrSStringBuilder subclass that has a pool of
+// stack storage (to avoid malloc).
+class GrStringBuilder {
+public:
+    GrStringBuilder() :
+            fChars() {
+        fChars.push_back() = '\0';
+    }
+
+    GrStringBuilder(const GrStringBuilder& s) :
+            fChars(s.fChars) {
+        GrAssert('\0' == s.fChars.back());
+    }
+
+    GrStringBuilder(const char* s) :
+            fChars(s, strlen(s)+1) {
+    }
+
+    GrStringBuilder(const GrStringBuilder& a, const GrStringBuilder& b) {
+        GrAssert('\0' == a.fChars.back());
+        GrAssert('\0' == b.fChars.back());
+
+        fChars.push_back_n(a.fChars.count() + b.fChars.count() - 1);
+        char* s = &fChars.front();
+        memcpy(s, &a.fChars.front(), a.fChars.count() - 1);
+        s += a.fChars.count() - 1;
+        memcpy(s, &b.fChars.front(), b.fChars.count());
+    }
+
+    GrStringBuilder& operator =(const GrStringBuilder& s) {
+        fChars = s.fChars;
+        return *this;
+    }
+
+    GrStringBuilder& operator =(const char* s) {
+        GrAssert('\0' == fChars.back());
+
+        int l = strlen(s);
+        fChars.resize_back(l + 1);
+        memcpy(&fChars.front(), s, l + 1);
+        return *this;
+    }
+
+    GrStringBuilder& operator +=(const GrStringBuilder& s) {
+        GrAssert('\0' == fChars.back());
+        GrAssert('\0' == s.fChars.back());
+        fChars.push_back_n(s.length());
+        memcpy(&fChars.fromBack(s.length()), &s.fChars.front(), s.fChars.count());
+        return *this;
+    }
+
+    GrStringBuilder& operator +=(const char* s) {
+        GrAssert('\0' == fChars.back());
+        int l = strlen(s);
+        fChars.push_back_n(l);
+        memcpy(&fChars.fromBack(l), s, l + 1);
+        return *this;
+    }
+
+    GrStringBuilder& operator +=(char c) {
+        GrAssert('\0' == fChars.back());
+        fChars.back() = c;
+        fChars.push_back() = '\0';
+        return *this;
+    }
+
+    void appendInt(int x) {
+        GR_STATIC_ASSERT(4 == sizeof(int));
+        // -, 10 digits, null char
+        char temp[12];
+        sprintf(temp, "%d", x);
+        *this += temp;
+    }
+
+    char& operator [](int i) {
+        GrAssert(i < length());
+        return fChars[i];
+    }
+
+    char operator [](int i) const {
+        GrAssert(i < length());
+        return fChars[i];
+    }
+
+    const char* cstr() const { return &fChars.front(); }
+
+    int length() const { return fChars.count() - 1; }
+
+protected:
+    // helpers for GrSStringBuilder (with storage on the stack)
+
+    GrStringBuilder(void* stackChars, int stackCount) :
+            fChars(stackCount ? stackChars : NULL,
+                   stackCount) {
+        fChars.push_back() = '\0';
+    }
+
+    GrStringBuilder(void* stackChars,
+                    int stackCount,
+                    const GrStringBuilder& s) :
+            fChars(s.fChars,
+                   (stackCount ? stackChars : NULL),
+                   stackCount) {
+    }
+
+    GrStringBuilder(void* stackChars,
+                    int stackCount,
+                    const char* s) :
+            fChars(s,
+                   strlen(s)+1,
+                   stackCount ? stackChars : NULL,
+                   stackCount) {
+    }
+
+    GrStringBuilder(void* stackChars,
+                    int stackCount,
+                    const GrStringBuilder& a,
+                    const GrStringBuilder& b) :
+            fChars(stackCount ? stackChars : NULL,
+                   stackCount) {
+        GrAssert('\0' == a.fChars.back());
+        GrAssert('\0' == b.fChars.back());
+
+        fChars.push_back_n(a.fChars.count() + b.fChars.count() - 1);
+        char* s = &fChars.front();
+        memcpy(s, &a.fChars.front(), a.fChars.count() - 1);
+        s += a.fChars.count() - 1;
+        memcpy(s, &b.fChars.front(), b.fChars.count());
+    }
+
+private:
+    GrTArray<char, true>  fChars;
+};
+
+template <int STACK_COUNT = 128>
+class GrSStringBuilder : public GrStringBuilder {
+public:
+    GrSStringBuilder() : GrStringBuilder(fStackChars, STACK_COUNT) {}
+
+    GrSStringBuilder(const GrStringBuilder& s) : GrStringBuilder(fStackChars,
+                                                                 STACK_COUNT,
+                                                                 s) {
+    }
+
+    GrSStringBuilder(const char* s) : GrStringBuilder(fStackChars,
+                                                      STACK_COUNT,
+                                                      s) {
+    }
+
+    GrSStringBuilder(const GrStringBuilder& a, const GrStringBuilder& b) :
+            GrStringBuilder(fStackChars, STACK_COUNT, a, b) {
+    }
+private:
+    char fStackChars[STACK_COUNT];
+};
+
+#endif
+
diff --git a/gpu/include/GrTArray.h b/gpu/include/GrTArray.h
new file mode 100644
index 0000000..f0d9494
--- /dev/null
+++ b/gpu/include/GrTArray.h
@@ -0,0 +1,298 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrTArray_DEFINED
+#define GrTArray_DEFINED
+
+#include <new>
+#include "GrTypes.h"
+
+// TODO: convert from uint32_t to int.
+
+// DATA_TYPE indicates that T has a trivial cons, destructor
+// and can be shallow-copied
+template <typename T, bool DATA_TYPE = false> class GrTArray {
+public:
+    GrTArray() {
+        fCount = 0;
+        fReserveCount = MIN_ALLOC_COUNT;
+        fAllocCount = 0;
+        fMemArray = NULL;
+        fPreAllocMemArray = NULL;
+    }
+
+    GrTArray(uint32_t reserveCount) {
+        fCount = 0;
+        fReserveCount = GrMax(reserveCount, (uint32_t)MIN_ALLOC_COUNT);
+        fAllocCount = fReserveCount;
+        fMemArray = GrMalloc(sizeof(T) * fReserveCount);
+        fPreAllocMemArray = NULL;
+    }
+
+    GrTArray(void* preAllocStorage, uint32_t preAllocCount) {
+        // we allow NULL,0 args and revert to the default cons. behavior
+        // this makes it possible for a owner-object to use same constructor
+        // to get either prealloc or nonprealloc behavior based using same line
+        GrAssert((NULL == preAllocStorage) == !preAllocCount);
+
+        fCount              = 0;
+        fReserveCount       = preAllocCount > 0 ? preAllocCount :
+                                                  MIN_ALLOC_COUNT;
+        fAllocCount         = preAllocCount;
+        fMemArray           = preAllocStorage;
+        fPreAllocMemArray   = preAllocStorage;
+    }
+
+    GrTArray(const GrTArray& array) {
+        fCount              = array.count();
+        fReserveCount       = MIN_ALLOC_COUNT;
+        fAllocCount         = GrMax(fReserveCount, fCount);
+        fMemArray           = GrMalloc(sizeof(T) * fAllocCount);
+        fPreAllocMemArray   = NULL;
+        if (DATA_TYPE) {
+            memcpy(fMemArray, array.fMemArray, sizeof(T) * fCount);
+        } else {
+            for (uint32_t i = 0; i < fCount; ++i) {
+                new (fItemArray + i) T(array[i]);
+            }
+        }
+    }
+
+    GrTArray(const T* array, uint32_t count) {
+        fCount              = count;
+        fReserveCount       = MIN_ALLOC_COUNT;
+        fAllocCount         = GrMax(fReserveCount, fCount);
+        fMemArray           = GrMalloc(sizeof(T) * fAllocCount);
+        fPreAllocMemArray   = NULL;
+        if (DATA_TYPE) {
+            memcpy(fMemArray, array, sizeof(T) * fCount);
+        } else {
+            for (uint32_t i = 0; i < fCount; ++i) {
+                new (fItemArray + i) T(array[i]);
+            }
+        }
+    }
+
+    GrTArray(const GrTArray& array,
+             void* preAllocStorage, uint32_t preAllocCount) {
+
+        // for same reason as non-copying cons we allow NULL, 0 for prealloc
+        GrAssert((NULL == preAllocStorage) == !preAllocCount);
+
+        fCount              = array.count();
+        fReserveCount       = preAllocCount > 0 ? preAllocCount :
+                                                  MIN_ALLOC_COUNT;
+        fPreAllocMemArray   = preAllocStorage;
+
+        if (fReserveCount >= fCount && preAllocCount) {
+            fAllocCount = fReserveCount;
+            fMemArray = preAllocStorage;
+        } else {
+            fAllocCount = GrMax(fCount, fReserveCount);
+            fMemArray = GrMalloc(fAllocCount * sizeof(T));
+        }
+
+        if (DATA_TYPE) {
+            memcpy(fMemArray, array.fMemArray, sizeof(T) * fCount);
+        } else {
+            for (uint32_t i = 0; i < fCount; ++i) {
+                new (fItemArray + i) T(array[i]);
+            }
+        }
+    }
+
+    GrTArray(const T* array, uint32_t count,
+             void* preAllocStorage, uint32_t preAllocCount) {
+
+        // for same reason as non-copying cons we allow NULL, 0 for prealloc
+        GrAssert((NULL == preAllocStorage) == !preAllocCount);
+
+        fCount              = count;
+        fReserveCount       = (preAllocCount > 0) ? preAllocCount :
+                                                    MIN_ALLOC_COUNT;
+        fPreAllocMemArray   = preAllocStorage;
+
+        if (fReserveCount >= fCount && preAllocCount) {
+            fAllocCount = fReserveCount;
+            fMemArray = preAllocStorage;
+        } else {
+            fAllocCount = GrMax(fCount, fReserveCount);
+            fMemArray = GrMalloc(fAllocCount * sizeof(T));
+        }
+
+        if (DATA_TYPE) {
+            memcpy(fMemArray, array, sizeof(T) * fCount);
+        } else {
+            for (uint32_t i = 0; i < fCount; ++i) {
+                new (fItemArray + i) T(array[i]);
+            }
+        }
+    }
+
+    GrTArray& operator =(const GrTArray& array) {
+        for (uint32_t i = 0; i < fCount; ++i) {
+            fItemArray[i].~T();
+        }
+        fCount = 0;
+        checkRealloc((int)array.count());
+        fCount = array.count();
+        if (DATA_TYPE) {
+            memcpy(fMemArray, array.fMemArray, sizeof(T) * fCount);
+        } else {
+            for (uint32_t i = 0; i < fCount; ++i) {
+                new (fItemArray + i) T(array[i]);
+            }
+        }
+        return *this;
+    }
+
+    ~GrTArray() {
+        for (uint32_t i = 0; i < fCount; ++i) {
+            fItemArray[i].~T();
+        }
+        if (fMemArray != fPreAllocMemArray) {
+            GrFree(fMemArray);
+        }
+    }
+
+    uint32_t count() const { return fCount; }
+
+    bool empty() const { return !fCount; }
+
+    T& push_back() {
+        checkRealloc(1);
+        new ((char*)fMemArray+sizeof(T)*fCount) T;
+        ++fCount;
+        return fItemArray[fCount-1];
+    }
+
+    void push_back_n(uint32_t n) {
+        checkRealloc(n);
+        for (uint32_t i = 0; i < n; ++i) {
+            new (fItemArray + fCount + i) T;
+        }
+        fCount += n;
+    }
+
+    void pop_back() {
+        GrAssert(0 != fCount);
+        --fCount;
+        fItemArray[fCount].~T();
+        checkRealloc(0);
+    }
+
+    void pop_back_n(uint32_t n) {
+        GrAssert(fCount >= n);
+        fCount -= n;
+        for (uint32_t i = 0; i < n; ++i) {
+            fItemArray[i].~T();
+        }
+        checkRealloc(0);
+    }
+
+    // pushes or pops from the back to resize
+    void resize_back(uint32_t newCount) {
+        if (newCount > fCount) {
+            push_back_n(newCount - fCount);
+        } else if (newCount < fCount) {
+            pop_back_n(fCount - newCount);
+        }
+    }
+
+    T& operator[] (uint32_t i) {
+        GrAssert(i < fCount);
+        return fItemArray[i];
+    }
+
+    const T& operator[] (uint32_t i) const {
+        GrAssert(i < fCount);
+        return fItemArray[i];
+    }
+
+    T& front() { GrAssert(fCount); return fItemArray[0];}
+
+    const T& front() const { GrAssert(fCount); return fItemArray[0];}
+
+    T& back() { GrAssert(fCount); return fItemArray[fCount - 1];}
+
+    const T& back() const { GrAssert(fCount); return fItemArray[fCount - 1];}
+
+    T& fromBack(uint32_t i) {
+        GrAssert(i < fCount);
+        return fItemArray[fCount - i - 1];
+    }
+
+    const T& fromBack(uint32_t i) const {
+        GrAssert(i < fCount);
+        return fItemArray[fCount - i - 1];
+    }
+
+private:
+    static const uint32_t MIN_ALLOC_COUNT = 8;
+
+    inline void checkRealloc(int32_t delta) {
+        GrAssert(-delta <= (int32_t)fCount);
+
+        uint32_t newCount = fCount + delta;
+        uint32_t fNewAllocCount = fAllocCount;
+
+        if (newCount > fAllocCount) {
+            fNewAllocCount = GrMax(newCount + ((newCount + 1) >> 1),
+                                   fReserveCount);
+        } else if (newCount < fAllocCount / 3) {
+            fNewAllocCount = GrMax(fAllocCount / 2, fReserveCount);
+        }
+
+        if (fNewAllocCount != fAllocCount) {
+
+            fAllocCount = fNewAllocCount;
+            char* fNewMemArray;
+
+            if (fAllocCount == fReserveCount && NULL != fPreAllocMemArray) {
+                fNewMemArray = (char*) fPreAllocMemArray;
+            } else {
+                fNewMemArray = (char*) GrMalloc(fAllocCount*sizeof(T));
+            }
+
+            if (DATA_TYPE) {
+                memcpy(fNewMemArray, fMemArray, fCount * sizeof(T));
+            } else {
+                for (uint32_t i = 0; i < fCount; ++i) {
+                    new (fNewMemArray + sizeof(T) * i) T(fItemArray[i]);
+                    fItemArray[i].~T();
+                }
+            }
+
+            if (fMemArray != fPreAllocMemArray) {
+                GrFree(fMemArray);
+            }
+            fMemArray = fNewMemArray;
+        }
+    }
+
+    uint32_t fReserveCount;
+    uint32_t fCount;
+    uint32_t fAllocCount;
+    void*    fPreAllocMemArray;
+    union {
+        T*       fItemArray;
+        void*    fMemArray;
+    };
+};
+
+#endif
+
diff --git a/gpu/include/GrTBSearch.h b/gpu/include/GrTBSearch.h
new file mode 100644
index 0000000..264ccb0
--- /dev/null
+++ b/gpu/include/GrTBSearch.h
@@ -0,0 +1,53 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrTBSearch_DEFINED
+#define GrTBSearch_DEFINED
+
+template <typename ELEM, typename KEY>
+int GrTBSearch(const ELEM array[], int count, KEY target) {
+    GrAssert(count >= 0);
+    if (0 == count) {
+        // we should insert it at 0
+        return ~0;
+    }
+    
+    int high = count - 1;
+    int low = 0;
+    while (high > low) {
+        int index = (low + high) >> 1;
+        if (LT(array[index], target)) {
+            low = index + 1;
+        } else {
+            high = index;
+        }
+    }
+    
+    // check if we found it
+    if (EQ(array[high], target)) {
+        return high;
+    }
+    
+    // now return the ~ of where we should insert it
+    if (LT(array[high], target)) {
+        high += 1;
+    }
+    return ~high;
+}
+
+#endif
+
diff --git a/gpu/include/GrTDArray.h b/gpu/include/GrTDArray.h
new file mode 100644
index 0000000..092242e
--- /dev/null
+++ b/gpu/include/GrTDArray.h
@@ -0,0 +1,222 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrTDArray_DEFINED
+#define GrTDArray_DEFINED
+
+#include "GrTypes.h"
+
+static int GrInitialArrayAllocationCount() {
+    return 4;
+}
+
+static int GrNextArrayAllocationCount(int count) {
+    return count + ((count + 1) >> 1);
+}
+
+template <typename T> class GrTDArray {
+public:
+    GrTDArray() : fArray(NULL), fAllocated(0), fCount(0) {}
+    GrTDArray(const GrTDArray& src) {
+        fCount = fAllocated = src.fCount;
+        fArray = (T*)GrMalloc(fAllocated * sizeof(T));
+        memcpy(fArray, src.fArray, fCount * sizeof(T));
+    }
+    ~GrTDArray() {
+        if (fArray) {
+            GrFree(fArray);
+        }
+    }
+
+    bool isEmpty() const { return 0 == fCount; }
+    int count() const { return fCount; }
+
+    const T& at(int index) const {
+        GrAssert((unsigned)index < (unsigned)fCount);
+        return fArray[index];
+    }
+    T& at(int index) {
+        GrAssert((unsigned)index < (unsigned)fCount);
+        return fArray[index];
+    }
+
+    const T& operator[](int index) const { return this->at(index); }
+    T& operator[](int index) { return this->at(index); }
+
+    GrTDArray& operator=(const GrTDArray& src) {
+        if (fAllocated < src.fCount) {
+            fAllocated = src.fCount;
+            GrFree(fArray);
+            fArray = (T*)GrMalloc(fAllocated * sizeof(T));
+        }
+        fCount = src.fCount;
+        memcpy(fArray, src.fArray, fCount * sizeof(T));
+        return *this;
+    }
+
+    void reset() {
+        if (fArray) {
+            GrFree(fArray);
+            fArray = NULL;
+        }
+        fAllocated = fCount = 0;
+    }
+
+    T* begin() const { return fArray; }
+    T* end() const { return fArray + fCount; }
+    T* back() const { GrAssert(fCount); return fArray + (fCount - 1); } 
+
+    T* prepend() {
+        this->growAt(0);
+        return fArray;
+    }
+
+    T* append() {
+        this->growAt(fCount);
+        return fArray + fCount - 1;
+    }
+
+    /**
+     *  index may be [0..count], so that you can insert at the end (like append)
+     */
+    T* insert(int index) {
+        GrAssert((unsigned)index <= (unsigned)fCount);
+        this->growAt(index);
+        return fArray + index;
+    }
+
+    void remove(int index) {
+        GrAssert((unsigned)index < (unsigned)fCount);
+        fCount -= 1;
+        if (index < fCount) {
+            int remaining = fCount - index;
+            memmove(fArray + index, fArray + index + 1, remaining * sizeof(T));
+        }
+    }
+
+    void removeShuffle(int index) {
+        GrAssert((unsigned)index < (unsigned)fCount);
+        fCount -= 1;
+        if (index < fCount) {
+            memmove(fArray + index, fArray + fCount, sizeof(T));
+        }
+    }
+
+    // Utility iterators
+
+    /**
+     *  Calls GrFree() on each element. Assumes each is NULL or was allocated
+     *  with GrMalloc().
+     */
+    void freeAll() {
+        T* stop = this->end();
+        for (T* curr = this->begin(); curr < stop; curr++) {
+            GrFree(*curr);
+        }
+        this->reset();
+    }
+
+    /**
+     *  Calls delete on each element. Assumes each is NULL or was allocated
+     *  with new.
+     */
+    void deleteAll() {
+        T* stop = this->end();
+        for (T* curr = this->begin(); curr < stop; curr++) {
+            delete *curr;
+        }
+        this->reset();
+    }
+
+    /**
+     *  Calls GrSafeUnref() on each element. Assumes each is NULL or is a
+     *  subclass of GrRefCnt.
+     */
+    void unrefAll() {
+        T* stop = this->end();
+        for (T* curr = this->begin(); curr < stop; curr++) {
+            GrSafeUnref(*curr);
+        }
+        this->reset();
+    }
+
+    void visit(void visitor(T&)) const {
+        T* stop = this->end();
+        for (T* curr = this->begin(); curr < stop; curr++) {
+            if (*curr) {
+                visitor(*curr);
+            }
+        }
+    }
+
+    int find(const T& elem) const {
+        int count = this->count();
+        T* curr = this->begin();
+        for (int i = 0; i < count; i++) {
+            if (elem == curr[i]) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    friend bool operator==(const GrTDArray<T>& a, const GrTDArray<T>& b) {
+        return a.count() == b.count() &&
+               (0 == a.count() ||
+                0 == memcmp(a.begin(), b.begin(), a.count() * sizeof(T)));
+    }
+    friend bool operator!=(const GrTDArray<T>& a, const GrTDArray<T>& b) {
+        return !(a == b);
+    }
+
+private:
+    T*  fArray;
+    int fAllocated, fCount;
+
+    // growAt will increment fCount, reallocate fArray (as needed), and slide
+    // the contents of fArray to make a hole for new data at index.
+    void growAt(int index) {
+        GrAssert(fCount <= fAllocated);
+        if (0 == fAllocated) {
+            fAllocated = GrInitialArrayAllocationCount();
+            fArray = (T*)GrMalloc(fAllocated * sizeof(T));
+        } else if (fCount == fAllocated) {
+            fAllocated = GrNextArrayAllocationCount(fAllocated);
+            T* newArray = (T*)GrMalloc(fAllocated * sizeof(T));
+            memcpy(newArray, fArray, index * sizeof(T));
+            memcpy(newArray + index + 1, fArray + index,
+                   (fCount - index) * sizeof(T));
+            GrFree(fArray);
+            fArray = newArray;
+        } else {
+            // check that we're not just appending
+            if (index < fCount) {
+                memmove(fArray + index + 1, fArray + index,
+                        (fCount - index) * sizeof(T));
+            }
+        }
+        GrAssert(fCount < fAllocated);
+        fCount += 1;
+    }
+};
+
+extern void* GrTDArray_growAt(void*, int* allocated, int& count, int index,
+                              size_t);
+
+
+#endif
+
diff --git a/gpu/include/GrTHashCache.h b/gpu/include/GrTHashCache.h
new file mode 100644
index 0000000..510f9ab
--- /dev/null
+++ b/gpu/include/GrTHashCache.h
@@ -0,0 +1,226 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrTHashCache_DEFINED
+#define GrTHashCache_DEFINED
+
+#include "GrTDArray.h"
+
+/**
+ *  Key needs
+ *      static bool EQ(const Entry&, const HashKey&);
+ *      static bool LT(const Entry&, const HashKey&);
+ *      uint32_t getHash() const;
+ *
+ *  Allows duplicate key entries but on find you may get
+ *  any of the duplicate entries returned.
+ */
+template <typename T, typename Key, size_t kHashBits> class GrTHashTable {
+public:
+    GrTHashTable() { Gr_bzero(fHash, sizeof(fHash)); }
+    ~GrTHashTable() {}
+
+    int count() const { return fSorted.count(); }
+    T*  find(const Key&) const;
+    // return true if key was unique when inserted.
+    bool insert(const Key&, T*);
+    void remove(const Key&, const T*);
+    T* removeAt(int index, uint32_t hash);
+    void removeAll();
+    void deleteAll();
+    void unrefAll();
+
+    /**
+     *  Return the index for the element, using a linear search.
+     */
+    int slowFindIndex(T* elem) const { return fSorted.find(elem); }
+
+#if GR_DEBUG
+    void validate() const;
+    bool contains(T*) const;
+#endif
+
+    // testing
+    const GrTDArray<T*>& getArray() const { return fSorted; }
+private:
+    enum {
+        kHashCount = 1 << kHashBits,
+        kHashMask  = kHashCount - 1
+    };
+    static unsigned hash2Index(uint32_t hash) {
+        hash ^= hash >> 16;
+        if (kHashBits <= 8) {
+            hash ^= hash >> 8;
+        }
+        return hash & kHashMask;
+    }
+
+    mutable T* fHash[kHashCount];
+    GrTDArray<T*> fSorted;
+
+    // search fSorted, and return the found index, or ~index of where it
+    // should be inserted
+    int searchArray(const Key&) const;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T, typename Key, size_t kHashBits>
+int GrTHashTable<T, Key, kHashBits>::searchArray(const Key& key) const {
+    int count = fSorted.count();
+    if (0 == count) {
+        // we should insert it at 0
+        return ~0;
+    }
+
+    const T* const* array = fSorted.begin();
+    int high = count - 1;
+    int low = 0;
+    while (high > low) {
+        int index = (low + high) >> 1;
+        if (Key::LT(*array[index], key)) {
+            low = index + 1;
+        } else {
+            high = index;
+        }
+    }
+
+    // check if we found it
+    if (Key::EQ(*array[high], key)) {
+        // above search should have found the first occurrence if there
+        // are multiple.
+        GrAssert(0 == high || Key::LT(*array[high - 1], key));
+        return high;
+    }
+
+    // now return the ~ of where we should insert it
+    if (Key::LT(*array[high], key)) {
+        high += 1;
+    }
+    return ~high;
+}
+
+template <typename T, typename Key, size_t kHashBits>
+T* GrTHashTable<T, Key, kHashBits>::find(const Key& key) const {
+    int hashIndex = hash2Index(key.getHash());
+    T* elem = fHash[hashIndex];
+
+    if (NULL == elem || !Key::EQ(*elem, key)) {
+        // bsearch for the key in our sorted array
+        int index = this->searchArray(key);
+        if (index < 0) {
+            return NULL;
+        }
+        elem = fSorted[index];
+        // update the hash
+        fHash[hashIndex] = elem;
+    }
+    return elem;
+}
+
+template <typename T, typename Key, size_t kHashBits>
+bool GrTHashTable<T, Key, kHashBits>::insert(const Key& key, T* elem) {
+    int index = this->searchArray(key);
+    bool first = index < 0;
+    if (first) {
+        // turn it into the actual index
+        index = ~index;
+    }
+    // add it to our array
+    *fSorted.insert(index) = elem;
+    // update our hash table (overwrites any dupe's position in the hash)
+    fHash[hash2Index(key.getHash())] = elem;
+    return first;
+}
+
+template <typename T, typename Key, size_t kHashBits>
+void GrTHashTable<T, Key, kHashBits>::remove(const Key& key, const T* elem) {
+    int index = hash2Index(key.getHash());
+    if (fHash[index] == elem) {
+        fHash[index] = NULL;
+    }
+
+    // remove from our sorted array
+    index = this->searchArray(key);
+    GrAssert(index >= 0);
+    // if there are multiple matches searchArray will give us the first match
+    // march forward until we find elem.
+    while (elem != fSorted[index]) {
+        ++index;
+        GrAssert(index < fSorted.count());
+    }
+    GrAssert(elem == fSorted[index]);
+    fSorted.remove(index);
+}
+
+template <typename T, typename Key, size_t kHashBits>
+T* GrTHashTable<T, Key, kHashBits>::removeAt(int elemIndex, uint32_t hash) {
+    int hashIndex = hash2Index(hash);
+    if (fHash[hashIndex] == fSorted[elemIndex]) {
+        fHash[hashIndex] = NULL;
+    }
+    // remove from our sorted array
+    T* elem = fSorted[elemIndex];
+    fSorted.remove(elemIndex);
+    return elem;
+}
+
+template <typename T, typename Key, size_t kHashBits>
+void GrTHashTable<T, Key, kHashBits>::removeAll() {
+    fSorted.reset();
+    Gr_bzero(fHash, sizeof(fHash));
+}
+
+template <typename T, typename Key, size_t kHashBits>
+void GrTHashTable<T, Key, kHashBits>::deleteAll() {
+    fSorted.deleteAll();
+    Gr_bzero(fHash, sizeof(fHash));
+}
+
+template <typename T, typename Key, size_t kHashBits>
+void GrTHashTable<T, Key, kHashBits>::unrefAll() {
+    fSorted.unrefAll();
+    Gr_bzero(fHash, sizeof(fHash));
+}
+
+#if GR_DEBUG
+template <typename T, typename Key, size_t kHashBits>
+void GrTHashTable<T, Key, kHashBits>::validate() const {
+    for (size_t i = 0; i < GR_ARRAY_COUNT(fHash); i++) {
+        if (fHash[i]) {
+            unsigned hashIndex = hash2Index(Key::GetHash(*fHash[i]));
+            GrAssert(hashIndex == i);
+        }
+    }
+
+    int count = fSorted.count();
+    for (int i = 1; i < count; i++) {
+        GrAssert(Key::LT(*fSorted[i - 1], *fSorted[i]) ||
+                 Key::EQ(*fSorted[i - 1], *fSorted[i]));
+    }
+}
+
+template <typename T, typename Key, size_t kHashBits>
+bool GrTHashTable<T, Key, kHashBits>::contains(T* elem) const {
+    int index = fSorted.find(elem);
+    return index >= 0;
+}
+
+#endif
+
+#endif
+
diff --git a/gpu/include/GrTLList.h b/gpu/include/GrTLList.h
new file mode 100644
index 0000000..1f59635
--- /dev/null
+++ b/gpu/include/GrTLList.h
@@ -0,0 +1,61 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrTLList_DEFINED
+#define GrTLList_DEFINED
+
+#include "GrNoncopyable.h"
+
+template <typename T> class GrTLList : GrNoncopyable {
+public:
+    class Entry {
+        Entry*  fPrev;
+        Entry*  fNext;
+    };
+
+    GrTLList() : fHead(NULL), fTail(NULL) {}
+#if GR_DEBUG
+    ~GrTLList() {
+        GrAssert(NULL == fHead);
+        GrAssert(NULL == ftail);
+    }
+#endif
+
+    T*  head() const { return fHead; }
+    T*  tail() const { return fTail; }
+    
+    void addToHead(T*);
+    void addToTail(T*);
+    void removeFromList(T*);
+
+private:
+    Entry*  fHead;
+    Entry*  fTail;
+
+    friend class Entry;
+};
+
+
+class Parent {
+    GrTDLList<Child>    fList;
+};
+
+class Child : public GrTLList::Entry<Child> {
+};
+
+#endif
+
diff --git a/gpu/include/GrTextContext.h b/gpu/include/GrTextContext.h
new file mode 100644
index 0000000..6b7446b
--- /dev/null
+++ b/gpu/include/GrTextContext.h
@@ -0,0 +1,67 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrTextContext_DEFINED
+#define GrTextContext_DEFINED
+
+#include "GrGlyph.h"
+#include "GrGpuVertex.h"
+
+class GrContext;
+class GrTextStrike;
+class GrFontScaler;
+
+class GrTextContext {
+public:
+    GrTextContext(GrContext*, const GrMatrix* extMatrix = NULL);
+    ~GrTextContext();
+
+    void drawPackedGlyph(GrGlyph::PackedID, GrFixed left, GrFixed top,
+                         GrFontScaler*);
+
+    void flush();   // optional; automatically called by destructor
+
+private:
+    GrContext*      fContext;
+    GrDrawTarget*   fDrawTarget;
+
+    GrMatrix        fExtMatrix;
+    GrFontScaler*   fScaler;
+    GrTextStrike*   fStrike;
+
+    inline void flushGlyphs();
+
+    enum {
+        kMinRequestedGlyphs      = 1,
+        kDefaultRequestedGlyphs  = 64,
+        kMinRequestedVerts       = kMinRequestedGlyphs * 4,
+        kDefaultRequestedVerts   = kDefaultRequestedGlyphs * 4,
+    };
+
+    GrGpuTextVertex* fVertices;
+    
+    int32_t     fMaxVertices;
+    GrTexture*  fCurrTexture;
+    int         fCurrVertex;
+
+    GrIRect     fClipRect;
+    GrMatrix    fOrigViewMatrix;    // restore previous viewmatrix
+};
+
+#endif
+
+
diff --git a/gpu/include/GrTextStrike.h b/gpu/include/GrTextStrike.h
new file mode 100644
index 0000000..abafa57
--- /dev/null
+++ b/gpu/include/GrTextStrike.h
@@ -0,0 +1,119 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrTextStrike_DEFINED
+#define GrTextStrike_DEFINED
+
+#include "GrAllocPool.h"
+#include "GrFontScaler.h"
+#include "GrTHashCache.h"
+#include "GrPoint.h"
+#include "GrGlyph.h"
+
+class GrAtlasMgr;
+class GrFontCache;
+class GrGpu;
+class GrFontPurgeListener;
+
+/**
+ *  The textcache maps a hostfontscaler instance to a dictionary of
+ *  glyphid->strike
+ */
+class GrTextStrike {
+public:
+    GrTextStrike(GrFontCache*, const GrKey* fontScalerKey, GrAtlasMgr*);
+    ~GrTextStrike();
+
+    const GrKey* getFontScalerKey() const { return fFontScalerKey; }
+    GrFontCache* getFontCache() const { return fFontCache; }
+
+    inline GrGlyph* getGlyph(GrGlyph::PackedID, GrFontScaler*);
+    bool getGlyphAtlas(GrGlyph*, GrFontScaler*);
+
+    // testing
+    int countGlyphs() const { return fCache.getArray().count(); }
+    const GrGlyph* glyphAt(int index) const {
+        return fCache.getArray()[index];
+    }
+    GrAtlas* getAtlas() const { return fAtlas; }
+
+public:
+    // for LRU
+    GrTextStrike*   fPrev;
+    GrTextStrike*   fNext;
+
+private:
+    class Key;
+    GrTHashTable<GrGlyph, Key, 7> fCache;
+    const GrKey* fFontScalerKey;
+    GrTAllocPool<GrGlyph> fPool;
+
+    GrFontCache*    fFontCache;
+    GrAtlasMgr*     fAtlasMgr;
+    GrAtlas*        fAtlas;     // linklist
+
+    GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
+    // returns true if after the purge, the strike is empty
+    bool purgeAtlasAtY(GrAtlas* atlas, int yCoord);
+
+    friend class GrFontCache;
+};
+
+class GrFontCache {
+public:
+    GrFontCache(GrGpu*);
+    ~GrFontCache();
+
+    inline GrTextStrike* getStrike(GrFontScaler*);
+
+    void freeAll();
+    void abandonAll();
+
+    void purgeExceptFor(GrTextStrike*);
+
+    // testing
+    int countStrikes() const { return fCache.getArray().count(); }
+    const GrTextStrike* strikeAt(int index) const {
+        return fCache.getArray()[index];
+    }
+    GrTextStrike* getHeadStrike() const { return fHead; }
+
+#if GR_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+private:
+    friend class GrFontPurgeListener;
+
+    class Key;
+    GrTHashTable<GrTextStrike, Key, 8> fCache;
+    // for LRU
+    GrTextStrike* fHead;
+    GrTextStrike* fTail;
+
+    GrGpu*      fGpu;
+    GrAtlasMgr* fAtlasMgr;
+
+    
+    GrTextStrike* generateStrike(GrFontScaler*, const Key&);
+    inline void detachStrikeFromList(GrTextStrike*);
+};
+
+#endif
+
diff --git a/gpu/include/GrTexture.h b/gpu/include/GrTexture.h
new file mode 100644
index 0000000..71a58e6
--- /dev/null
+++ b/gpu/include/GrTexture.h
@@ -0,0 +1,213 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrTexture_DEFINED
+#define GrTexture_DEFINED
+
+#include "GrRefCnt.h"
+
+class GrTexture;
+
+/**
+ * GrRenderTarget represents a 2D buffer of pixels that can be rendered to.
+ * A context's render target is set by setRenderTarget(). Render targets are
+ * created by a createTexture with the kRenderTarget_TextureFlag flag. 
+ * Additionally, the rendering destination set in the underlying 3D API at the
+ * time of GrContext's creation can be retrieved by calling 
+ * currentRenderTarget() after creation before any calles to setRenderTarget().
+ */
+class GrRenderTarget : public GrRefCnt {
+public:
+    /**
+     * @return the width of the rendertarget
+     */
+    virtual uint32_t width() const = 0;
+    /**
+     * @return the height of the rendertarget
+     */
+    virtual uint32_t height() const = 0;
+    
+    /**
+     * @return the texture associated with the rendertarget, may be NULL.
+     */
+    GrTexture* asTexture() {return fTexture;}
+
+protected:
+    GrRenderTarget(GrTexture* texture) : fTexture(texture) {}
+    GrTexture* fTexture;
+};
+
+class GrTexture : public GrRefCnt {
+public:
+    enum PixelConfig {
+        kUnknown_PixelConfig,
+        kAlpha_8_PixelConfig,
+        kIndex_8_PixelConfig,
+        kRGB_565_PixelConfig,
+        kRGBA_4444_PixelConfig, //!< premultiplied
+        kRGBA_8888_PixelConfig, //!< premultiplied
+        kRGBX_8888_PixelConfig, //!< treat the alpha channel as opaque
+    };
+    static size_t BytesPerPixel(PixelConfig);
+    static bool PixelConfigIsOpaque(PixelConfig);
+
+protected:
+    GrTexture(uint32_t contentWidth,
+              uint32_t contentHeight,
+              uint32_t allocWidth,
+              uint32_t allocHeight,
+              PixelConfig config) : 
+                fAllocWidth(allocWidth), 
+                fAllocHeight(allocHeight),
+                fContentWidth(contentWidth), 
+                fContentHeight(contentHeight),
+                fConfig(config) {
+                    // only make sense if alloc size is pow2
+                    fShiftFixedX = 31 - Gr_clz(allocWidth);
+                    fShiftFixedY = 31 - Gr_clz(allocHeight);
+                }
+public:
+    virtual ~GrTexture();
+    
+    /**
+     * Retrieves the width of the content area of the texture. Reflects the
+     * width passed to GrGpu::createTexture().
+     * 
+     * @return the width in texels
+     */
+    uint32_t contentWidth() const { return fContentWidth; }
+    /**
+     * Retrieves the height of the content area of the texture. Reflects the
+     * height passed to GrGpu::createTexture().
+     * 
+     * @return the height in texels
+     */
+    uint32_t contentHeight() const { return fContentHeight; }
+
+    /**
+     * Retrieves the texture width actually allocated in texels.
+     *
+     * @return the width in texels
+     */
+    uint32_t allocWidth() const { return fAllocWidth; }
+    /**
+     * Retrieves the texture height actually allocated in texels.
+     *
+     * @return the height in texels
+     */
+    uint32_t allocHeight() const { return fAllocHeight; }
+
+    /**
+     * Convert from texels to normalized texture coords for POT textures
+     * only.
+     */
+    GrFixed normalizeFixedX(GrFixed x) const { GrAssert(GrIsPow2(fAllocWidth));
+                                               return x >> fShiftFixedX; }
+    GrFixed normalizeFixedY(GrFixed y) const { GrAssert(GrIsPow2(fAllocHeight));
+                                               return y >> fShiftFixedY; }
+
+    /** 
+     * Retrieves the pixel config specified when the texture was created.
+     */
+    PixelConfig config() const { return fConfig; }
+
+    /**
+     *  The number of bytes used by the texture
+     */
+    size_t sizeInBytes() const {
+        return fAllocWidth * fAllocHeight * BytesPerPixel(fConfig);
+    }
+
+    /**
+     * Updates a subrectangle of texels in the texture.
+     *
+     * @param x       left edge of rectangle to update
+     * @param y       top edge of rectangle to update
+     * @param width   width of rectangle to update
+     * @param height  height of rectangle to update
+     * @param srcData width*height texels of data in same format that was used
+     *                at texture creation.
+     */
+    virtual void uploadTextureData(uint32_t x,
+                                   uint32_t y,
+                                   uint32_t width,
+                                   uint32_t height,
+                                   const void* srcData) = 0;
+    /**
+     * Indicates that GPU context in which this texture was created is destroyed
+     * and that Ganesh should not attempt to free the texture with the 
+     * underlying API.
+     */
+    virtual void abandon() = 0;
+
+    /**
+     * Queries whether the texture was created as a render target.
+     *
+     * Use asRenderTarget() to use the texture as a render target if this 
+     * returns true.
+     *
+     * @return true if the texture was created as a render target.
+     */
+    virtual bool isRenderTarget() const = 0;
+
+    /**
+     * Retrieves the render target underlying this texture that can be passed to
+     * GrGpu::setRenderTarget().
+     *
+     * If isRenderTarget() is false then the returned handle is undefined.
+     *
+     * @return    handle to render target or undefined if the texture is not a
+     *            render target
+     */
+    virtual GrRenderTarget* asRenderTarget() = 0;
+
+    /**
+     * Removes the "rendertargetness" from a texture. This may or may not
+     * actually do anything with the underlying 3D API.
+     */
+    virtual void removeRenderTarget() = 0;
+
+    /**
+     *  Return the native ID or handle to the texture, depending on the
+     *  platform. e.g. on opengl, return the texture ID.
+     */
+    virtual intptr_t getTextureHandle() = 0;
+
+#if GR_DEBUG
+    void validate() const {
+        this->INHERITED::validate();
+    }
+#else
+    void validate() const {}
+#endif
+    
+private:    
+    uint32_t fAllocWidth;
+    uint32_t fAllocHeight;
+    uint32_t fContentWidth;
+    uint32_t fContentHeight;
+    // these two shift a fixed-point value into normalized coordinates
+    // for this texture if the texture is power of two sized.
+    int      fShiftFixedX;
+    int      fShiftFixedY;
+    PixelConfig fConfig;
+
+    typedef GrRefCnt INHERITED;
+};
+
+#endif
+
diff --git a/gpu/include/GrTextureCache.h b/gpu/include/GrTextureCache.h
new file mode 100644
index 0000000..e3d4f0a
--- /dev/null
+++ b/gpu/include/GrTextureCache.h
@@ -0,0 +1,289 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrTextureCache_DEFINED
+#define GrTextureCache_DEFINED
+
+#include "GrTypes.h"
+#include "GrTHashCache.h"
+
+class GrTexture;
+
+// return true if a<b, or false if b<a
+//
+#define RET_IF_LT_OR_GT(a, b)   \
+    do {                        \
+        if ((a) < (b)) {        \
+            return true;        \
+        }                       \
+        if ((b) < (a)) {        \
+            return false;       \
+        }                       \
+    } while (0)
+
+/**
+ *  Helper class for GrTextureCache, the Key is used to identify src data for
+ *  a texture. It is identified by 2 32bit data fields which can hold any
+ *  data (uninterpreted by the cache) and a width/height.
+ */
+class GrTextureKey {
+public:
+    enum {
+        kHashBits   = 7,
+        kHashCount  = 1 << kHashBits,
+        kHashMask   = kHashCount - 1
+    };
+
+    GrTextureKey(uint32_t p0, uint32_t p1, uint16_t width, uint16_t height) {
+        fP0 = p0;
+        fP1 = p1;
+        fP2 = width | (height << 16);
+        GR_DEBUGCODE(fHashIndex = -1);
+    }
+
+    GrTextureKey(const GrTextureKey& src) {
+        fP0 = src.fP0;
+        fP1 = src.fP1;
+        fP2 = src.fP2;
+        finalize(src.fPrivateBits);
+    }
+
+    //!< returns hash value [0..kHashMask] for the key
+    int hashIndex() const { return fHashIndex; }
+
+    friend bool operator==(const GrTextureKey& a, const GrTextureKey& b) {
+        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
+        return a.fP0 == b.fP0 && a.fP1 == b.fP1 && a.fP2 == b.fP2 && 
+               a.fPrivateBits == b.fPrivateBits;
+    }
+
+    friend bool operator!=(const GrTextureKey& a, const GrTextureKey& b) {
+        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
+        return !(a == b);
+    }
+
+    friend bool operator<(const GrTextureKey& a, const GrTextureKey& b) {
+        RET_IF_LT_OR_GT(a.fP0, b.fP0);
+        RET_IF_LT_OR_GT(a.fP1, b.fP1);
+        RET_IF_LT_OR_GT(a.fP2, b.fP2);
+        return a.fPrivateBits < b.fPrivateBits;
+    }
+    
+private:
+    void finalize(uint32_t privateBits) { 
+        fPrivateBits = privateBits;
+        this->computeHashIndex();
+    }
+    
+    uint16_t width() const { return fP2 & 0xffff; }
+    uint16_t height() const { return (fP2 >> 16); }
+    
+    static uint32_t rol(uint32_t x) {
+        return (x >> 24) | (x << 8);
+    }
+    static uint32_t ror(uint32_t x) {
+        return (x >> 8) | (x << 24);
+    }
+    static uint32_t rohalf(uint32_t x) {
+        return (x >> 16) | (x << 16);
+    }
+    
+    void computeHashIndex() {
+        uint32_t hash = fP0 ^ rol(fP1) ^ ror(fP2) ^ rohalf(fPrivateBits);
+        // this way to mix and reduce hash to its index may have to change
+        // depending on how many bits we allocate to the index
+        hash ^= hash >> 16;
+        hash ^= hash >> 8;
+        fHashIndex = hash & kHashMask;
+    }
+
+    uint32_t    fP0;
+    uint32_t    fP1;
+    uint32_t    fP2;
+    uint32_t    fPrivateBits;
+
+    // this is computed from the fP... fields
+    int         fHashIndex;
+    
+    friend class GrContext;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrTextureEntry {
+public:
+    GrTexture* texture() const { return fTexture; }
+    const GrTextureKey& key() const { return fKey; }
+
+#if GR_DEBUG
+    GrTextureEntry* next() const { return fNext; }
+    GrTextureEntry* prev() const { return fPrev; }
+#endif
+
+#if GR_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+private:
+    GrTextureEntry(const GrTextureKey& key, GrTexture* texture);
+    ~GrTextureEntry();
+
+    bool isLocked() const { return fLockCount != 0; }
+    void lock() { ++fLockCount; }
+    void unlock() {
+        GrAssert(fLockCount > 0);
+        --fLockCount;
+    }
+
+    GrTextureKey    fKey;
+    GrTexture*      fTexture;
+
+    // track if we're in use, used when we need to purge
+    // we only purge unlocked entries
+    int fLockCount;
+
+    // we're a dlinklist
+    GrTextureEntry* fPrev;
+    GrTextureEntry* fNext;
+
+    friend class GrTextureCache;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "GrTHashCache.h"
+
+/**
+ *  Cache of GrTexture objects.
+ *
+ *  These have a corresponding GrTextureKey, built from 96bits identifying the
+ *  texture/bitmap.
+ *
+ *  The cache stores the entries in a double-linked list, which is its LRU.
+ *  When an entry is "locked" (i.e. given to the caller), it is moved to the
+ *  head of the list. If/when we must purge some of the entries, we walk the
+ *  list backwards from the tail, since those are the least recently used.
+ *
+ *  For fast searches, we maintain a sorted array (based on the GrTextureKey)
+ *  which we can bsearch. When a new entry is added, it is inserted into this
+ *  array.
+ *
+ *  For even faster searches, a hash is computed from the Key. If there is
+ *  a collision between two keys with the same hash, we fall back on the
+ *  bsearch, and update the hash to reflect the most recent Key requested.
+ */
+class GrTextureCache {
+public:
+    GrTextureCache(int maxCount, size_t maxBytes);
+    ~GrTextureCache();  // uses kFreeTexture_DeleteMode
+
+    /**
+     *  Search for an entry with the same Key. If found, "lock" it and return it.
+     *  If not found, return null.
+     */
+    GrTextureEntry* findAndLock(const GrTextureKey&);
+
+    /**
+     *  Create a new entry, based on the specified key and texture, and return
+     *  its "locked" entry.
+     *
+     *  Ownership of the texture is transferred to the Entry, which will unref()
+     *  it when we are purged or deleted.
+     */
+    GrTextureEntry* createAndLock(const GrTextureKey&, GrTexture*);
+    
+    /**
+     * Detach removes an entry from the cache. This prevents the entry from 
+     * being found by a subsequent findAndLock() until it is reattached. The 
+     * entry still counts against the cache's budget and should be reattached
+     * when exclusive access is no longer needed.
+     */
+    void detach(GrTextureEntry*);
+    
+    /**
+     * Reattaches a texture to the cache and unlocks it. Allows it to be found 
+     * by a subsequent findAndLock or be purged (provided its lock count is 
+     * now 0.)
+     */
+    void reattachAndUnlock(GrTextureEntry*);
+
+    /**
+     *  When done with an entry, call unlock(entry) on it, which returns it to
+     *  a purgable state.
+     */
+    void unlock(GrTextureEntry*);
+
+    enum DeleteMode {
+        kFreeTexture_DeleteMode,
+        kAbandonTexture_DeleteMode
+    };
+    void deleteAll(DeleteMode);
+
+#if GR_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+private:
+    void internalDetach(GrTextureEntry*, bool);
+    void attachToHead(GrTextureEntry*, bool);
+    void purgeAsNeeded();   // uses kFreeTexture_DeleteMode
+
+    class Key;
+    GrTHashTable<GrTextureEntry, Key, 8> fCache;
+
+    // manage the dlink list
+    GrTextureEntry* fHead;
+    GrTextureEntry* fTail;
+
+    // our budget, used in purgeAsNeeded()
+    const int fMaxCount;
+    const size_t fMaxBytes;
+
+    // our current stats, related to our budget
+    int fEntryCount;
+    size_t fEntryBytes;
+    int fClientDetachedCount;
+    size_t fClientDetachedBytes;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_DEBUG
+    class GrAutoTextureCacheValidate {
+    public:
+        GrAutoTextureCacheValidate(GrTextureCache* cache) : fCache(cache) {
+            cache->validate();
+        }
+        ~GrAutoTextureCacheValidate() {
+            fCache->validate();
+        }
+    private:
+        GrTextureCache* fCache;
+    };
+#else
+    class GrAutoTextureCacheValidate {
+    public:
+        GrAutoTextureCacheValidate(GrTextureCache*) {}
+    };
+#endif
+
+#endif
+
diff --git a/gpu/include/GrTouchGesture.h b/gpu/include/GrTouchGesture.h
new file mode 100644
index 0000000..03f970b
--- /dev/null
+++ b/gpu/include/GrTouchGesture.h
@@ -0,0 +1,56 @@
+#ifndef GrTouchGesture_DEFINED
+#define GrTouchGesture_DEFINED
+
+#include "GrTypes.h"
+#include "SkTDArray.h"
+#include "SkMatrix.h"
+
+#include "FlingState.h"
+
+class GrTouchGesture {
+public:
+    GrTouchGesture();
+    ~GrTouchGesture();
+
+    void touchBegin(void* owner, float x, float y);
+    void touchMoved(void* owner, float x, float y);
+    void touchEnd(void* owner);
+    void reset();
+
+    const SkMatrix& localM();
+    const SkMatrix& globalM() const { return fGlobalM; }
+
+private:
+    enum State {
+        kEmpty_State,
+        kTranslate_State,
+        kZoom_State,
+    };
+
+    struct Rec {
+        void*   fOwner;
+        float   fStartX, fStartY;
+        float   fPrevX, fPrevY;
+        float   fLastX, fLastY;
+        SkMSec  fPrevT, fLastT;
+    };
+    SkTDArray<Rec> fTouches;
+
+    State    fState;
+    SkMatrix fLocalM, fGlobalM;
+    FlingState  fFlinger;
+    SkMSec  fLastUpT;
+    SkPoint fLastUpP;
+
+
+    void flushLocalM();
+    int findRec(void* owner) const;
+    void appendNewRec(void* owner, float x, float y);
+    float computePinch(const Rec&, const Rec&);
+    float limitTotalZoom(float scale) const;
+    bool handleDblTap(float, float);
+};
+
+#endif
+
+
diff --git a/gpu/include/GrTypes.h b/gpu/include/GrTypes.h
new file mode 100644
index 0000000..a491295
--- /dev/null
+++ b/gpu/include/GrTypes.h
@@ -0,0 +1,142 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrTypes_DEFINED
+#define GrTypes_DEFINED
+
+#include "GrConfig.h"
+
+#include <memory.h>
+#include <string.h>
+
+/**
+ *  Macro to round n up to the next multiple of 4, or return it unchanged if
+ *  n is already a multiple of 4
+ */
+#define GrALIGN4(n)     (((n) + 3) >> 2 << 2)
+#define GrIsALIGN4(n)   (((n) & 3) == 0)
+
+template <typename T> const T& GrMin(const T& a, const T& b) {
+	return (a < b) ? a : b;
+}
+
+template <typename T> const T& GrMax(const T& a, const T& b) {
+	return (b < a) ? a : b;
+}
+
+// compile time versions of min/max
+#define GR_CT_MAX(a, b) (((b) < (a)) ? (a) : (b))
+#define GR_CT_MIN(a, b) (((b) < (a)) ? (b) : (a))
+
+/**
+ *  divide, rounding up
+ */
+inline uint32_t GrUIDivRoundUp(uint32_t x, uint32_t y) {
+    return (x + (y-1)) / y;
+}
+
+/**
+ *  align up
+ */
+inline uint32_t GrUIAlignUp(uint32_t x, uint32_t alignment) {
+    return GrUIDivRoundUp(x, alignment) * alignment;
+}
+
+/**
+ * amount of pad needed to align up
+ */
+inline uint32_t GrUIAlignUpPad(uint32_t x, uint32_t alignment) {
+    return (alignment - x % alignment) % alignment;
+}
+
+/**
+ *  align down
+ */
+inline uint32_t GrUIAlignDown(uint32_t x, uint32_t alignment) {
+    return (x / alignment) * alignment;
+}
+
+/**
+ *  Count elements in an array
+ */
+#define GR_ARRAY_COUNT(array)  (sizeof(array) / sizeof(array[0]))
+
+//!< allocate a block of memory, will never return NULL
+extern void* GrMalloc(size_t bytes);
+
+//!< free block allocated by GrMalloc. ptr may be NULL
+extern void GrFree(void* ptr);
+
+static inline void Gr_bzero(void* dst, size_t size) {
+    memset(dst, 0, size);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  Return the number of leading zeros in n
+ */
+extern int Gr_clz(uint32_t n);
+
+/**
+ *  Return true if n is a power of 2
+ */
+static inline bool GrIsPow2(unsigned n) {
+    return n && 0 == (n & (n - 1));
+}
+
+/**
+ *  Return the next power of 2 >= n.
+ */
+static inline uint32_t GrNextPow2(uint32_t n) {
+    return n ? (1 << (32 - Gr_clz(n - 1))) : 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  16.16 fixed point type
+ */
+typedef int32_t GrFixed;
+
+#if GR_DEBUG
+
+static inline int16_t GrToS16(intptr_t x) {
+    GrAssert((int16_t)x == x);
+    return (int16_t)x;
+}
+
+#else
+
+#define GrToS16(x)  x
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Type used to describe format of vertices in arrays
+ * Values are defined in GrDrawTarget
+ */
+typedef uint16_t GrVertexLayout;
+
+///////////////////////////////////////////////////////////////////////////////
+
+// this is included only to make it easy to use this debugging facility
+#include "GrInstanceCounter.h"
+
+#endif
diff --git a/gpu/include/GrUserConfig.h b/gpu/include/GrUserConfig.h
new file mode 100644
index 0000000..2e6f391
--- /dev/null
+++ b/gpu/include/GrUserConfig.h
@@ -0,0 +1,57 @@
+#ifndef GrUserConfig_DEFINED
+#define GrUserConfig_DEFINED
+
+#if defined(GR_USER_CONFIG_FILE)
+    #error "default user config pulled in but GR_USER_CONFIG_FILE is defined."
+#endif
+
+#if 0
+    #undef GR_RELEASE
+    #undef GR_DEBUG
+    #define GR_RELEASE  0
+    #define GR_DEBUG    1
+#endif
+
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+//#define GR_FORCE_GLCHECKERR   1
+
+/*
+ *  The default 32bit pixel config for texture upload is GL_RGBA. If your
+ *  bitmaps map to a different GL enum, specify that with this define.
+ */
+//#define SK_GL_32BPP_COLOR_FORMAT  GL_RGBA
+
+/*
+ *  To diagnose texture cache performance, define this to 1 if you want to see
+ *  a log statement everytime we upload an image to create a texture.
+ */
+//#define GR_DUMP_TEXTURE_UPLOAD    1
+
+////////////////////////////////////////////////////////////////////////////////
+// Decide Ganesh types
+
+#define GR_SCALAR_IS_FIXED          0
+#define GR_SCALAR_IS_FLOAT          1
+
+#define GR_TEXT_SCALAR_IS_USHORT    0
+#define GR_TEXT_SCALAR_IS_FIXED     0
+#define GR_TEXT_SCALAR_IS_FLOAT     1
+
+#endif
+
+
diff --git a/gpu/include/GrVertexBuffer.h b/gpu/include/GrVertexBuffer.h
new file mode 100644
index 0000000..5e83de9
--- /dev/null
+++ b/gpu/include/GrVertexBuffer.h
@@ -0,0 +1,92 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrVertexBuffer_DEFINED
+#define GrVertexBuffer_DEFINED
+
+#include "GrRefCnt.h"
+
+class GrVertexBuffer : public GrRefCnt {
+protected:
+    GrVertexBuffer(uint32_t sizeInBytes, bool dynamic) : 
+             fSizeInBytes(sizeInBytes),
+             fDynamic(dynamic) {}
+public:
+    virtual ~GrVertexBuffer() {}
+    
+    /**
+     Retrieves the size of the vertex buffer
+
+     @return the size of the vertex buffer in bytes
+     */
+    uint32_t size() { return fSizeInBytes; }
+
+    /**
+     Retrieves whether the vertex buffer was created with the dynamic flag
+
+     @return true if the vertex buffer was created with the dynamic flag
+     */
+    bool dynamic() const { return fDynamic; }
+
+    /**
+     Indicates that GPU context in which this veretx buffer was created is 
+     destroyed and that Ganesh should not attempt to free the texture with the
+     underlying API.
+     */
+    virtual void abandon() = 0;
+
+    /**
+     Locks the vertex buffer to be written by the CPU.
+     
+     The previous content of the vertex buffer is invalidated. It is an error to
+     draw whil the buffer is locked.  It is an error to call lock on an already
+     locked vertex buffer.
+     
+     @return a pointer to the vertex data or NULL if the lock fails.
+     */
+    virtual void* lock() = 0;
+
+    /** 
+     Unlocks the vertex buffer. 
+     
+     The pointer returned by the previous lock call will no longer be valid.
+     */
+    virtual void unlock() = 0;
+
+    /** 
+     Queries whether the vertex buffer has been locked.
+     
+     @return true if the vertex buffer is locked, false otherwise.
+     */
+    virtual bool isLocked() const = 0;
+
+    /**
+     Updates the vertex buffer data. 
+     
+     The size of the vertex buffer will be preserved. However, only the updated
+     region will have defined contents.
+
+     @return returns true if the update succeeds, false otherwise.
+     */
+    virtual bool updateData(const void* src, uint32_t srcSizeInBytes) = 0;
+
+private:
+    uint32_t fSizeInBytes;
+    bool     fDynamic;
+};
+
+#endif
diff --git a/gpu/include/GrVertexBufferAllocPool.h b/gpu/include/GrVertexBufferAllocPool.h
new file mode 100644
index 0000000..6a781aa
--- /dev/null
+++ b/gpu/include/GrVertexBufferAllocPool.h
@@ -0,0 +1,141 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrVertexBufferAllocPool_DEFINED
+#define GrVertexBufferAllocPool_DEFINED
+
+#include "GrNoncopyable.h"
+#include "GrTDArray.h"
+#include "GrTArray.h"
+
+class GrVertexBuffer;
+class GrGpu;
+
+/**
+ * A pool of vertices in vertex buffers tied to a GrGpu.
+ *
+ * The pool has an alloc() function that returns a pointer into a locked
+ * vertex buffer. A client can release() if it has over-allocated.
+ *
+ * At creation time a minimum VB size can be specified. Additionally,
+ * a number of vertex buffers to preallocate can be specified. These will
+ * be allocated at the min size and kept until the pool is destroyed.
+ */
+class GrVertexBufferAllocPool : GrNoncopyable {
+public:
+    /**
+     * Constructor
+     *
+     * @param gpu               The GrGpu used to create the vertex buffers.
+     * @param bufferSize        The size of created VBs (unless an alloc request
+     *                          exceeds this size in which case a larger VB is
+     *                          created). This value is clamped to some
+     *                          reasonable minimum.
+     * @param preallocBufferCnt The pool will allocate this number of VBs at
+     *                          bufferSize and keep them until it is destroyed.
+     */
+     GrVertexBufferAllocPool(GrGpu* gpu,
+                             size_t   bufferSize = 0,
+                             int preallocBufferCnt = 0);
+    ~GrVertexBufferAllocPool();
+
+    /**
+     * Ensures all VBs are unlocked. Call before using to draw.
+     */
+    void unlock();
+
+    /**
+     *  Frees all vertex data that has been allocated with alloc().
+     */
+    void reset();
+
+    /**
+     * Returns a block of memory bytes size big. The vertex buffer
+     * containing the memory is returned in buffer.
+     *
+     * @param layout       specifies type of vertices to allocate space for
+     * @param vertexCount  number of vertices to allocate space for
+     * @param buffer       returns the vertex buffer that will hold the
+     *                     vertices.
+     * @param startVertex  returns the offset into buffer of the first vertex.
+     *                     In units of the size of a vertex using layout param.
+     * @return pointer to first vertex.
+     */
+    void* alloc(GrVertexLayout layout,
+                uint32_t vertexCount,
+                GrVertexBuffer** buffer,
+                uint32_t* startVertex);
+
+    /**
+     * Gets the number of vertices that can be allocated without changing VBs.
+     * This means either the last VB returned by alloc() if the last alloc did
+     * not exhaust it. If that VB was exhausted by the last alloc or alloc hasn't
+     * been called since reset() then it will be the number of vertices that
+     * would fit in an available preallocated VB. If no preallocated VB
+     * is available then it returns 0 since the next alloc would force a new
+     * VB to be created.
+     */
+    int currentBufferVertices(GrVertexLayout layout) const;
+
+    /**
+     * Gets the number of preallocated buffers that are yet to be used.
+     */
+    int preallocatedBuffersRemaining() const;
+
+    /**
+     * Gets the number of vertices that can fit in a  preallocated vertex buffer.
+     * Zero if no preallocated buffers.
+     */
+    int preallocatedBufferVertices(GrVertexLayout layout) const;
+
+    /**
+     * gets the number of preallocated vertex buffers
+     */
+    int preallocatedBufferCount() const;
+
+
+    /**
+     * Releases the most recently allocated bytes back to the pool.
+     */
+    void release(size_t bytes);
+
+    /**
+     * Gets the GrGpu that this pool is associated with.
+     */
+    GrGpu* getGpu() { return fGpu; }
+
+
+private:
+    struct BufferBlock {
+        size_t              fBytesFree;
+        GrVertexBuffer*     fVertexBuffer;
+    };
+
+    bool createBlock(size_t size);
+    void destroyBlock();
+
+    GrTArray<BufferBlock>       fBlocks;
+    GrTDArray<GrVertexBuffer*>  fPreallocBuffers;
+    int                         fPreallocBuffersInUse;
+    int                         fFirstPreallocBuffer;
+
+    size_t              fMinBlockSize;
+    GrGpu*              fGpu;
+    void*               fBufferPtr;
+};
+
+#endif
diff --git a/gpu/include/SkGpuCanvas.h b/gpu/include/SkGpuCanvas.h
new file mode 100644
index 0000000..e8e6e7a
--- /dev/null
+++ b/gpu/include/SkGpuCanvas.h
@@ -0,0 +1,72 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef SkGpuCanvas_DEFINED
+#define SkGpuCanvas_DEFINED
+
+#include "SkCanvas.h"
+
+class GrContext;
+
+/**
+ *  Subclass of canvas that creates devices compatible with the GrContext pass
+ *  to the canvas' constructor.
+ */
+class SkGpuCanvas : public SkCanvas {
+public:
+    /**
+     *  The GrContext object is reference counted. When passed to our 
+     *  constructor, its reference count is incremented. In our destructor, the 
+     *  GrGpu's reference count will be decremented.
+     */
+    explicit SkGpuCanvas(GrContext*);
+    virtual ~SkGpuCanvas();
+
+    /**
+     *  Return our GrContext instance
+     */
+    GrContext* context() const { return fContext; }
+
+    /**
+     *  Override from SkCanvas. Returns true, and if not-null, sets size to
+     *  be the width/height of our viewport.
+     */
+    virtual bool getViewport(SkIPoint* size) const;
+
+    /**
+     *  Override from SkCanvas. Returns a new device of the correct subclass,
+     *  as determined by the GrGpu passed to our constructor.
+     */
+    virtual SkDevice* createDevice(SkBitmap::Config, int width, int height,
+                                   bool isOpaque, bool isLayer);
+
+#if 0
+    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags flags = kARGB_ClipLayer_SaveFlag) {
+        return this->save(flags);
+    }
+#endif
+    
+private:
+    GrContext* fContext;
+
+    typedef SkCanvas INHERITED;
+};
+
+#endif
+
+
diff --git a/gpu/include/SkGr.h b/gpu/include/SkGr.h
new file mode 100644
index 0000000..4e9801f
--- /dev/null
+++ b/gpu/include/SkGr.h
@@ -0,0 +1,238 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef SkGr_DEFINED
+#define SkGr_DEFINED
+
+#include <stddef.h>
+
+// tetrark headers
+#include "GrConfig.h"
+#include "GrContext.h"
+#include "GrFontScaler.h"
+#include "GrPathIter.h"
+#include "GrClipIterator.h"
+
+// skia headers
+#include "SkBitmap.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+
+#if (GR_DEBUG && defined(SK_RELEASE)) || (GR_RELEASE && defined(SK_DEBUG))
+//    #error "inconsistent GR_DEBUG and SK_DEBUG"
+#endif
+
+#if GR_SCALAR_IS_FIXED
+    #ifdef SK_SCALAR_IS_FIXED
+        #define SK_SCALAR_IS_GR_SCALAR  1
+    #else
+        #define SK_SCALAR_IS_GR_SCALAR  0
+    #endif
+    #define SkScalarToGrScalar(x)       SkScalarToFixed(x)
+
+#elif GR_SCALAR_IS_FLOAT
+
+    #ifdef SK_SCALAR_IS_FLOAT
+        #define SK_SCALAR_IS_GR_SCALAR  1
+    #else
+        #define SK_SCALAR_IS_GR_SCALAR  0
+    #endif
+    #define SkScalarToGrScalar(x)       SkScalarToFloat(x)
+
+#else 
+    #error "Ganesh scalar type not defined"
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Sk to Gr Type conversions
+
+// Verify that SkPoint and GrPoint are compatible if using the same scalar type
+#if 0/*SK_SCALAR_IS_GR_SCALAR*/
+    GR_STATIC_ASSERT(sizeof(SkPoint) == sizeof(GrPoint));
+    GR_STATIC_ASSERT(offsetof(SkPoint,fX) == offsetof(GrPoint,fX)));
+    GR_STATIC_ASSERT(offsetof(SkPoint,fY) == offsetof(GrPoint,fY)));
+#endif
+
+GR_STATIC_ASSERT((int)GrSamplerState::kClamp_WrapMode == (int)SkShader::kClamp_TileMode);
+GR_STATIC_ASSERT((int)GrSamplerState::kRepeat_WrapMode ==(
+                 int)SkShader::kRepeat_TileMode);
+GR_STATIC_ASSERT((int)GrSamplerState::kMirror_WrapMode == 
+                 (int)SkShader::kMirror_TileMode);
+
+#define sk_tile_mode_to_grwrap(X) ((GrSamplerState::WrapMode)(X))
+
+GR_STATIC_ASSERT((int)GrGpu::kZero_BlendCoeff == (int)SkXfermode::kZero_Coeff);
+GR_STATIC_ASSERT((int)GrGpu::kOne_BlendCoeff  == (int)SkXfermode::kOne_Coeff);
+GR_STATIC_ASSERT((int)GrGpu::kSC_BlendCoeff   == (int)SkXfermode::kSC_Coeff);
+GR_STATIC_ASSERT((int)GrGpu::kISC_BlendCoeff  == (int)SkXfermode::kISC_Coeff);
+GR_STATIC_ASSERT((int)GrGpu::kDC_BlendCoeff   == (int)SkXfermode::kDC_Coeff);
+GR_STATIC_ASSERT((int)GrGpu::kIDC_BlendCoeff  == (int)SkXfermode::kIDC_Coeff);
+GR_STATIC_ASSERT((int)GrGpu::kSA_BlendCoeff   == (int)SkXfermode::kSA_Coeff);
+GR_STATIC_ASSERT((int)GrGpu::kISA_BlendCoeff  == (int)SkXfermode::kISA_Coeff);
+GR_STATIC_ASSERT((int)GrGpu::kDA_BlendCoeff   == (int)SkXfermode::kDA_Coeff);
+GR_STATIC_ASSERT((int)GrGpu::kIDA_BlendCoeff  == (int)SkXfermode::kIDA_Coeff);
+
+#define sk_blend_to_grblend(X) ((GrGpu::BlendCoeff)(X))
+
+GR_STATIC_ASSERT((int)SkPath::kMove_Verb  == (int)GrPathIter::kMove_Command);
+GR_STATIC_ASSERT((int)SkPath::kLine_Verb  == (int)GrPathIter::kLine_Command);
+GR_STATIC_ASSERT((int)SkPath::kQuad_Verb  == (int)GrPathIter::kQuadratic_Command);
+GR_STATIC_ASSERT((int)SkPath::kCubic_Verb == (int)GrPathIter::kCubic_Command);
+GR_STATIC_ASSERT((int)SkPath::kClose_Verb == (int)GrPathIter::kClose_Command);
+GR_STATIC_ASSERT((int)SkPath::kDone_Verb  == (int)GrPathIter::kEnd_Command);
+
+#define sk_path_verb_to_gr_path_command(X) ((GrPathIter::Command)(X))
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorPriv.h"
+
+static inline GrRect Sk2Gr(const SkRect& src) {
+    return GrRect(SkScalarToGrScalar(src.fLeft),
+                  SkScalarToGrScalar(src.fTop),
+                  SkScalarToGrScalar(src.fRight),
+                  SkScalarToGrScalar(src.fBottom));
+}
+
+class SkGr {
+public:
+    static inline SkIRect& SetIRect(SkIRect* dst, const GrIRect& src) {
+        GR_STATIC_ASSERT(sizeof(*dst) == sizeof(src));
+        memcpy(dst, &src, sizeof(*dst));
+        return *dst;
+    }
+    
+    static inline GrIRect& SetIRect(GrIRect* dst, const SkIRect& src) {
+        GR_STATIC_ASSERT(sizeof(*dst) == sizeof(src));
+        memcpy(dst, &src, sizeof(*dst));
+        return *dst;
+    }
+    
+    /**
+     *  Convert the SkBitmap::Config to the corresponding PixelConfig, or
+     *  kUnknown_PixelConfig if the conversion cannot be done.
+     */
+    static GrTexture::PixelConfig BitmapConfig2PixelConfig(SkBitmap::Config,
+                                                        bool isOpaque);
+
+    static GrTexture::PixelConfig Bitmap2PixelConfig(const SkBitmap& bm) {
+        return BitmapConfig2PixelConfig(bm.config(), bm.isOpaque());
+    }
+
+    static void SkMatrix2GrMatrix(const SkMatrix& m, GrMatrix* g) {
+        g->setAll(SkScalarToGrScalar(m[0]),
+                  SkScalarToGrScalar(m[1]),
+                  SkScalarToGrScalar(m[2]),
+                  SkScalarToGrScalar(m[3]),
+                  SkScalarToGrScalar(m[4]),
+                  SkScalarToGrScalar(m[5]),
+                  SkScalarToGrScalar(m[6]),
+                  SkScalarToGrScalar(m[7]),
+                  SkScalarToGrScalar(m[8]));
+    }
+    
+    static GrColor SkColor2GrColor(SkColor c) {
+        SkPMColor pm = SkPreMultiplyColor(c);
+        unsigned r = SkGetPackedR32(pm);
+        unsigned g = SkGetPackedG32(pm);
+        unsigned b = SkGetPackedB32(pm);
+        unsigned a = SkGetPackedA32(pm);
+        return GrColorPackRGBA(r, g, b, a);
+    }
+
+    /**
+     *  This abandons all texture caches (for bitmaps and text) associated with
+     *  the gpu, and frees any associated skia caches. It differs from
+     *  deleteAllTextures in that it assumes that the gpu has lots its context,
+     *  and thus the associated HW textures are no longer valid
+     */
+    static void AbandonAllTextures(GrContext*);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Classes
+
+class SkGrPathIter : public GrPathIter {
+public:
+    SkGrPathIter(const SkPath& path) : fIter(path, false), fPath(path) {}
+    virtual Command next(GrPoint pts[]);
+    virtual Command next();
+    virtual void rewind();
+    virtual ConvexHint hint() const;
+private:
+    
+#if !SK_SCALAR_IS_GR_SCALAR
+    SkPoint             fPoints[4];
+#endif
+    SkPath::Iter        fIter;
+    const SkPath&       fPath;
+};
+
+class SkGrClipIterator : public GrClipIterator {
+public:
+    void reset(const SkRegion& clip) {
+        fIter.reset(clip);
+        this->invalidateBoundsCache();
+    }
+    
+    // overrides
+    
+    virtual bool isDone() { return fIter.done(); }
+    virtual void getRect(GrIRect* rect) {
+        SkGr::SetIRect(rect, fIter.rect());
+    }
+    virtual void next() { fIter.next(); }
+    virtual void rewind() { fIter.rewind(); }
+    virtual void computeBounds(GrIRect* bounds);
+
+private:
+    SkRegion::Iterator fIter;
+};
+
+class SkGlyphCache;
+
+class SkGrFontScaler : public GrFontScaler {
+public:
+    explicit SkGrFontScaler(SkGlyphCache* strike);
+    virtual ~SkGrFontScaler();
+
+    // overrides
+    virtual const GrKey* getKey();
+    virtual bool getPackedGlyphBounds(GrGlyph::PackedID, GrIRect* bounds);
+    virtual bool getPackedGlyphImage(GrGlyph::PackedID, int width, int height,
+                                     int rowBytes, void* image);
+    virtual bool getGlyphPath(uint16_t glyphID, GrPath*);
+
+private:
+    SkGlyphCache* fStrike;
+    GrKey*  fKey;
+//    DECLARE_INSTANCE_COUNTER(SkGrFontScaler);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Helper functions
+
+GrTextureEntry* sk_gr_create_bitmap_texture(GrContext* ctx, 
+                                            GrTextureKey* key,
+                                            const GrSamplerState& sampler,
+                                            const SkBitmap& bitmap);
+
+void sk_gr_set_paint(GrContext* ctx, const SkPaint& paint, bool justAlpha = false);
+
+#endif
diff --git a/gpu/include/SkGrTexturePixelRef.h b/gpu/include/SkGrTexturePixelRef.h
new file mode 100644
index 0000000..1f5133f
--- /dev/null
+++ b/gpu/include/SkGrTexturePixelRef.h
@@ -0,0 +1,50 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef SkGrTexturePixelRef_DEFINED
+#define SkGrTexturePixelRef_DEFINED
+
+#include "SkPixelRef.h"
+#include "GrGpu.h"
+
+class SkGrTexturePixelRef : public SkPixelRef {
+public:
+            SkGrTexturePixelRef(GrTexture*);
+    virtual ~SkGrTexturePixelRef();
+
+    // override from SkPixelRef
+    virtual SkGpuTexture* getTexture() { return (SkGpuTexture*)fTexture; }
+
+protected:
+    // override from SkPixelRef
+    virtual void* onLockPixels(SkColorTable** ptr) {
+        if (ptr) {
+            *ptr = NULL;
+        }
+        return NULL;
+    }
+
+    // override from SkPixelRef
+    virtual void onUnlockPixels() {}
+
+private:
+    GrTexture*  fTexture;
+    typedef SkPixelRef INHERITED;
+};
+
+#endif
+
diff --git a/gpu/include/SkUIView.h b/gpu/include/SkUIView.h
new file mode 100644
index 0000000..5e12e00
--- /dev/null
+++ b/gpu/include/SkUIView.h
@@ -0,0 +1,64 @@
+#import <UIKit/UIKit.h>
+
+#include "SkMatrix.h"
+#include "FlingState.h"
+
+#import <OpenGLES/EAGL.h>
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+
+class SkOSWindow;
+class SkEvent;
+
+@interface SkUIView : UIView <UIAccelerometerDelegate> {
+    BOOL fRedrawRequestPending;
+    SkOSWindow* fWind;
+    SkMatrix    fMatrix, fLocalMatrix;
+    bool        fNeedGestureEnded;
+
+    SkMatrix    fRotateMatrix;
+
+    float       fFirstPinchX, fFirstPinchY;
+    bool        fNeedFirstPinch;
+
+    float       fZoomAroundX, fZoomAroundY;
+    bool        fZoomAround;
+
+    FlingState  fFlingState;
+
+    GrAnimateFloat  fWarpState;
+    bool            fUseWarp;
+
+    struct {
+        EAGLContext*    fContext;
+        GLuint          fRenderbuffer;
+        GLuint          fStencilbuffer;
+        GLuint          fFramebuffer;
+        GLint           fWidth;
+        GLint           fHeight;
+    } fGL;
+    
+    UILabel* fTitleLabel;
+
+    enum Backend {
+        kGL_Backend,
+        kRaster_Backend,
+    };
+
+    // these are visible to DetailViewController
+    Backend fBackend;
+    bool    fComplexClip;
+}
+
+@property (nonatomic, assign) SkOSWindow *fWind;
+@property (nonatomic, retain) UILabel* fTitleLabel;
+@property (nonatomic, assign) Backend fBackend;
+@property (nonatomic, assign) bool fComplexClip;
+@property (nonatomic, assign, setter=setWarpState) bool fUseWarp;
+
+- (void)setSkTitle:(const char*)title;
+- (void)postInvalWithRect:(const SkIRect*)rectOrNil;
+- (BOOL)onHandleEvent:(const SkEvent&)event;
+
+@end
+
diff --git a/gpu/src/FlingState.cpp b/gpu/src/FlingState.cpp
new file mode 100644
index 0000000..cb634cc
--- /dev/null
+++ b/gpu/src/FlingState.cpp
@@ -0,0 +1,134 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "FlingState.h"
+#include "SkMatrix.h"
+#include "SkTime.h"
+
+#define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER   true
+
+static const float MAX_FLING_SPEED = 1500;
+
+static float pin_max_fling(float speed) {
+    if (speed > MAX_FLING_SPEED) {
+        speed = MAX_FLING_SPEED;
+    }
+    return speed;
+}
+
+static double getseconds() {
+    return SkTime::GetMSecs() * 0.001;
+}
+
+// returns +1 or -1, depending on the sign of x
+// returns +1 if x is zero
+static SkScalar SkScalarSign(SkScalar x) {
+    SkScalar sign = SK_Scalar1;
+    if (x < 0) {
+        sign = -sign;
+    }
+    return sign;
+}
+
+static void unit_axis_align(SkVector* unit) {
+    const SkScalar TOLERANCE = SkDoubleToScalar(0.15);
+    if (SkScalarAbs(unit->fX) < TOLERANCE) {
+        unit->fX = 0;
+        unit->fY = SkScalarSign(unit->fY);
+    } else if (SkScalarAbs(unit->fY) < TOLERANCE) {
+        unit->fX = SkScalarSign(unit->fX);
+        unit->fY = 0;
+    }
+}
+
+void FlingState::reset(float sx, float sy) {
+    fActive = true;
+    fDirection.set(sx, sy);
+    fSpeed0 = SkPoint::Normalize(&fDirection);
+    fSpeed0 = pin_max_fling(fSpeed0);
+    fTime0 = getseconds();
+
+    unit_axis_align(&fDirection);
+//    printf("---- speed %g dir %g %g\n", fSpeed0, fDirection.fX, fDirection.fY);
+}
+
+bool FlingState::evaluateMatrix(SkMatrix* matrix) {
+    if (!fActive) {
+        return false;
+    }
+
+    const float t =  getseconds() - fTime0;
+    const float MIN_SPEED = 2;
+    const float K0 = 5.0;
+    const float K1 = 0.02;
+    const float speed = fSpeed0 * (sk_float_exp(- K0 * t) - K1);
+    if (speed <= MIN_SPEED) {
+        fActive = false;
+        return false;
+    }
+    float dist = (fSpeed0 - speed) / K0;
+
+//    printf("---- time %g speed %g dist %g\n", t, speed, dist);
+    float tx = fDirection.fX * dist;
+    float ty = fDirection.fY * dist;
+    if (DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER) {
+        tx = sk_float_round2int(tx);
+        ty = sk_float_round2int(ty);
+    }
+    matrix->setTranslate(tx, ty);
+//    printf("---- evaluate (%g %g)\n", tx, ty);
+
+    return true;
+}
+
+////////////////////////////////////////
+
+GrAnimateFloat::GrAnimateFloat() : fTime0(0) {}
+
+void GrAnimateFloat::start(float v0, float v1, float duration) {
+    fValue0 = v0;
+    fValue1 = v1;
+    fDuration = duration;
+    if (duration > 0) {
+        fTime0 = SkTime::GetMSecs();
+        if (!fTime0) {
+            fTime0 = 1;  // time0 is our sentinel
+        }
+    } else {
+        fTime0 = 0;
+    }
+}
+
+float GrAnimateFloat::evaluate() {
+    if (!fTime0) {
+        return fValue1;
+    }
+    
+    double elapsed = (SkTime::GetMSecs() - fTime0) * 0.001;
+    if (elapsed >= fDuration) {
+        fTime0 = 0;
+        return fValue1;
+    }
+    
+    double t = elapsed / fDuration;
+    if (true) {
+        t = (3 - 2 * t) * t * t;
+    }
+    return fValue0 + t * (fValue1 - fValue0);
+}
+
+
diff --git a/gpu/src/GrAllocPool.cpp b/gpu/src/GrAllocPool.cpp
new file mode 100644
index 0000000..f133f97
--- /dev/null
+++ b/gpu/src/GrAllocPool.cpp
@@ -0,0 +1,127 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrAllocPool.h"
+
+#define GrAllocPool_MIN_BLOCK_SIZE      ((size_t)128)
+
+struct GrAllocPool::Block {
+    Block*  fNext;
+    char*   fPtr;
+    size_t  fBytesFree;
+    size_t  fBytesTotal;
+
+    static Block* Create(size_t size, Block* next) {
+        GrAssert(size >= GrAllocPool_MIN_BLOCK_SIZE);
+
+        Block* block = (Block*)GrMalloc(sizeof(Block) + size);
+        block->fNext = next;
+        block->fPtr = (char*)block + sizeof(Block);
+        block->fBytesFree = size;
+        block->fBytesTotal = size;
+        return block;
+    }
+
+    bool canAlloc(size_t bytes) const {
+        return bytes <= fBytesFree;
+    }
+
+    void* alloc(size_t bytes) {
+        GrAssert(bytes <= fBytesFree);
+        fBytesFree -= bytes;
+        void* ptr = fPtr;
+        fPtr += bytes;
+        return ptr;
+    }
+    
+    size_t release(size_t bytes) {
+        GrAssert(bytes > 0);
+        size_t free = GrMin(bytes, fBytesTotal - fBytesFree);
+        fBytesFree += free;
+        fPtr -= free;
+        return bytes - free;
+    }
+    
+    bool empty() const { return fBytesTotal == fBytesFree; }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrAllocPool::GrAllocPool(size_t blockSize) {
+    fBlock = NULL;
+    fMinBlockSize = GrMax(blockSize, GrAllocPool_MIN_BLOCK_SIZE);
+    GR_DEBUGCODE(fBlocksAllocated = 0;)
+}
+
+GrAllocPool::~GrAllocPool() {
+    this->reset();
+}
+
+void GrAllocPool::reset() {
+    this->validate();
+
+    Block* block = fBlock;
+    while (block) {
+        Block* next = block->fNext;
+        GrFree(block);
+        block = next;
+    }
+    fBlock = NULL;
+    GR_DEBUGCODE(fBlocksAllocated = 0;)
+}
+
+void* GrAllocPool::alloc(size_t size) {
+    this->validate();
+    
+    if (!fBlock || !fBlock->canAlloc(size)) {
+        size_t blockSize = GrMax(fMinBlockSize, size);
+        fBlock = Block::Create(blockSize, fBlock);
+        GR_DEBUGCODE(fBlocksAllocated += 1;)
+    }
+    return fBlock->alloc(size);
+}
+
+void GrAllocPool::release(size_t bytes) {
+    this->validate();
+    
+    while (bytes && NULL != fBlock) {
+        bytes = fBlock->release(bytes);
+        if (fBlock->empty()) {
+            Block* next = fBlock->fNext;
+            GrFree(fBlock);
+            fBlock = next;
+            GR_DEBUGCODE(fBlocksAllocated -= 1;)
+        }
+    }
+}
+
+
+#if GR_DEBUG
+
+void GrAllocPool::validate() const {
+    Block* block = fBlock;
+    int count = 0;
+    while (block) {
+        count += 1;
+        block = block->fNext;
+    }
+    GrAssert(fBlocksAllocated == count);
+}
+
+#endif
+
+
diff --git a/gpu/src/GrAtlas.cpp b/gpu/src/GrAtlas.cpp
new file mode 100644
index 0000000..b55a029
--- /dev/null
+++ b/gpu/src/GrAtlas.cpp
@@ -0,0 +1,187 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrAtlas.h"
+#include "GrGpu.h"
+#include "GrMemory.h"
+#include "GrRectanizer.h"
+#include "GrPlotMgr.h"
+
+#if 0
+#define GR_PLOT_WIDTH   8
+#define GR_PLOT_HEIGHT  4
+#define GR_ATLAS_WIDTH  256
+#define GR_ATLAS_HEIGHT 256
+
+#define GR_ATLAS_TEXTURE_WIDTH  (GR_PLOT_WIDTH * GR_ATLAS_WIDTH)
+#define GR_ATLAS_TEXTURE_HEIGHT (GR_PLOT_HEIGHT * GR_ATLAS_HEIGHT)
+
+#else
+
+#define GR_ATLAS_TEXTURE_WIDTH  1024
+#define GR_ATLAS_TEXTURE_HEIGHT 2048
+
+#define GR_ATLAS_WIDTH  341
+#define GR_ATLAS_HEIGHT 341
+
+#define GR_PLOT_WIDTH   (GR_ATLAS_TEXTURE_WIDTH / GR_ATLAS_WIDTH)
+#define GR_PLOT_HEIGHT  (GR_ATLAS_TEXTURE_HEIGHT / GR_ATLAS_HEIGHT)
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define BORDER      1
+
+#if GR_DEBUG
+    static int gCounter;
+#endif
+
+GrAtlas::GrAtlas(GrAtlasMgr* mgr, int plotX, int plotY) {
+    fAtlasMgr = mgr;    // just a pointer, not an owner
+    fNext = NULL;
+    fTexture = mgr->getTexture(); // we're not an owner, just a pointer
+    fPlot.set(plotX, plotY);
+
+    fRects = GrRectanizer::Factory(GR_ATLAS_WIDTH - BORDER,
+                                   GR_ATLAS_HEIGHT - BORDER);
+
+#if GR_DEBUG
+    GrPrintf(" GrAtlas %p [%d %d] %d\n", this, plotX, plotY, gCounter);
+    gCounter += 1;
+#endif
+}
+
+GrAtlas::~GrAtlas() {
+    fAtlasMgr->freePlot(fPlot.fX, fPlot.fY);
+
+    delete fRects;
+
+#if GR_DEBUG
+    --gCounter;
+    GrPrintf("~GrAtlas %p [%d %d] %d\n", this, fPlot.fX, fPlot.fY, gCounter);
+#endif
+}
+
+static void adjustForPlot(GrIPoint16* loc, const GrIPoint16& plot) {
+    loc->fX += plot.fX * GR_ATLAS_WIDTH;
+    loc->fY += plot.fY * GR_ATLAS_HEIGHT;
+}
+
+bool GrAtlas::addSubImage(int width, int height, const void* image,
+                          GrIPoint16* loc) {
+    if (!fRects->addRect(width + BORDER, height + BORDER, loc)) {
+        return false;
+    }
+
+    GrAutoSMalloc<1024> storage;
+    int srcW = width + 2*BORDER;
+    int srcH = height + 2*BORDER;
+    if (BORDER) {
+        uint8_t* ptr = (uint8_t*)storage.realloc(srcW * srcH);
+        Gr_bzero(ptr, srcW);                // zero top row
+        ptr += srcW;
+        for (int y = 0; y < height; y++) {
+            *ptr++ = 0;                     // zero left edge
+            memcpy(ptr, image, width); ptr += width;
+            *ptr++ = 0;                     // zero right edge
+            image = (const void*)((const char*)image + width);
+        }
+        Gr_bzero(ptr, srcW);                // zero bottom row
+        image = storage.get();
+    }
+    adjustForPlot(loc, fPlot);
+    fTexture->uploadTextureData(loc->fX, loc->fY, srcW, srcH, image);
+
+    // now tell the caller to skip the top/left BORDER
+    loc->fX += BORDER;
+    loc->fY += BORDER;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrAtlasMgr::GrAtlasMgr(GrGpu* gpu) {
+    fGpu = gpu;
+    gpu->ref();
+    fTexture = NULL;
+    fPlotMgr = new GrPlotMgr(GR_PLOT_WIDTH, GR_PLOT_HEIGHT);
+}
+
+GrAtlasMgr::~GrAtlasMgr() {
+    GrSafeUnref(fTexture);
+    delete fPlotMgr;
+    fGpu->unref();
+}
+
+GrAtlas* GrAtlasMgr::addToAtlas(GrAtlas* atlas,
+                                int width, int height, const void* image,
+                                GrIPoint16* loc) {
+    if (atlas && atlas->addSubImage(width, height, image, loc)) {
+        return atlas;
+    }
+
+    // If the above fails, then either we have no starting atlas, or the current
+    // one is full. Either way we need to allocate a new atlas
+
+    GrIPoint16 plot;
+    if (!fPlotMgr->newPlot(&plot)) {
+        return NULL;
+    }
+
+    if (NULL == fTexture) {
+        GrGpu::TextureDesc desc = {
+            GrGpu::kDynamicUpdate_TextureFlag, 
+            GrGpu::kNone_AALevel,
+            GR_ATLAS_TEXTURE_WIDTH, 
+            GR_ATLAS_TEXTURE_HEIGHT,
+            GrTexture::kAlpha_8_PixelConfig
+        };
+        fTexture = fGpu->createTexture(desc, NULL, 0);
+        if (NULL == fTexture) {
+            return NULL;
+        }
+    }
+
+    GrAtlas* newAtlas = new GrAtlas(this, plot.fX, plot.fY);
+    if (!newAtlas->addSubImage(width, height, image, loc)) {
+        delete newAtlas;
+        return NULL;
+    }
+
+    newAtlas->fNext = atlas;
+    return newAtlas;
+}
+
+void GrAtlasMgr::freePlot(int x, int y) {
+    GrAssert(fPlotMgr->isBusy(x, y));
+    fPlotMgr->freePlot(x, y);
+}
+
+void GrAtlasMgr::abandonAll() {
+#if 0
+    GrAtlas** curr = fList.begin();
+    GrAtlas** stop = fList.end();
+    for (; curr < stop; curr++) {
+        (*curr)->texture()->abandon();
+        delete *curr;
+    }
+    fList.reset();
+#endif
+}
+
+
diff --git a/gpu/src/GrClip.cpp b/gpu/src/GrClip.cpp
new file mode 100644
index 0000000..b7a839b
--- /dev/null
+++ b/gpu/src/GrClip.cpp
@@ -0,0 +1,136 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrClip.h"
+
+GrClip::GrClip() {
+    fBounds.setEmpty();
+    this->validate();
+}
+
+GrClip::GrClip(const GrClip& src) {
+    *this = src;
+}
+
+GrClip::GrClip(GrClipIterator* iter) {
+    fBounds.setEmpty();
+    this->setFromIterator(iter);
+}
+
+GrClip::~GrClip() {}
+
+GrClip& GrClip::operator=(const GrClip& src) {
+    fList = src.fList;
+    fBounds = src.fBounds;
+    this->validate();
+    return *this;
+}
+
+void GrClip::setEmpty() {
+    fList.reset();
+    fBounds.setEmpty();
+    this->validate();
+}
+
+void GrClip::setRect(const GrIRect& r) {
+    fList.reset();
+
+    // we need a canonical "empty" rect, so that our operator== will behave
+    // correctly with two empty clips.
+    if (r.isEmpty()) {
+        fBounds.setEmpty();
+    } else {
+        fBounds = r;
+    }
+    this->validate();
+}
+
+void GrClip::addRect(const GrIRect& r) {
+    if (!r.isEmpty()) {
+        this->validate();
+        if (this->isEmpty()) {
+            GrAssert(fList.count() == 0);
+            fBounds = r;
+        } else {
+            if (this->isRect()) {
+                *fList.append() = fBounds;
+            }
+            *fList.append() = r;
+            fBounds.unionWith(r);
+        }
+        this->validate();
+    }
+}
+
+void GrClip::setFromIterator(GrClipIterator* iter) {
+    this->setEmpty();
+    if (iter) {
+        for (iter->rewind(); !iter->isDone(); iter->next()) {
+            GrIRect r;
+            iter->getRect(&r);
+            this->addRect(r);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrClipIter::reset(const GrClip& clip) { fClip = &clip; fIndex = 0; }
+
+bool GrClipIter::isDone() { 
+    return (NULL == fClip) || (fIndex >= fClip->countRects()); 
+}
+
+void GrClipIter::rewind() { fIndex = 0; }
+void GrClipIter::getRect(GrIRect* r) { *r = fClip->getRects()[fIndex]; }
+void GrClipIter::next() { fIndex += 1; }
+void GrClipIter::computeBounds(GrIRect* r) { 
+    if (NULL == fClip) {
+        r->setEmpty();
+    } else {
+        *r = fClip->getBounds();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_DEBUG
+
+void GrClip::validate() const {
+    if (fBounds.isEmpty()) {
+        GrAssert(0 == fBounds.fLeft);
+        GrAssert(0 == fBounds.fTop);
+        GrAssert(0 == fBounds.fRight);
+        GrAssert(0 == fBounds.fBottom);
+        GrAssert(0 == fList.count());
+    } else {
+        int count = fList.count();
+        if (count > 0) {
+            GrAssert(count > 1);
+            GrAssert(!fList[0].isEmpty());
+            GrIRect bounds = fList[0];
+            for (int i = 1; i < count; i++) {
+                GrAssert(!fList[i].isEmpty());
+                bounds.unionWith(fList[i]);
+            }
+            GrAssert(fBounds == bounds);
+        }
+    }
+}
+
+#endif
+
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
new file mode 100644
index 0000000..6e94ef7
--- /dev/null
+++ b/gpu/src/GrContext.cpp
@@ -0,0 +1,1040 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+

+#include "GrContext.h"

+#include "GrTextureCache.h"

+#include "GrTextStrike.h"

+#include "GrMemory.h"

+#include "GrPathIter.h"

+#include "GrClipIterator.h"

+#include "GrIndexBuffer.h"

+

+#define DEFER_TEXT_RENDERING 1

+

+static const size_t MAX_TEXTURE_CACHE_COUNT = 128;

+static const size_t MAX_TEXTURE_CACHE_BYTES = 8 * 1024 * 1024;

+

+#if DEFER_TEXT_RENDERING

+    static const uint32_t POOL_VB_SIZE = 2048 *

+            GrDrawTarget::VertexSize(GrDrawTarget::kTextFormat_VertexLayoutBit);

+    static const uint32_t NUM_POOL_VBS = 8;

+#else

+    static const uint32_t POOL_VB_SIZE = 0;

+    static const uint32_t NUM_POOL_VBS = 0;

+

+#endif

+

+GrContext* GrContext::Create(GrGpu::Engine engine,

+                             GrGpu::Platform3DContext context3D) {

+    GrContext* ctx = NULL;

+    GrGpu* fGpu = GrGpu::Create(engine, context3D);

+    if (NULL != fGpu) {

+        ctx = new GrContext(fGpu);

+        fGpu->unref();

+    }

+    return ctx;

+}

+

+GrContext::~GrContext() {

+    fGpu->unref();

+    delete fTextureCache;

+    delete fFontCache;

+}

+

+void GrContext::abandonAllTextures() {

+    fTextureCache->deleteAll(GrTextureCache::kAbandonTexture_DeleteMode);

+    fFontCache->abandonAll();

+}

+

+GrTextureEntry* GrContext::findAndLockTexture(GrTextureKey* key,

+                                              const GrSamplerState& sampler) {

+    finalizeTextureKey(key, sampler);

+    return fTextureCache->findAndLock(*key);

+}

+

+static void stretchImage(void* dst,

+                         int dstW,

+                         int dstH,

+                         void* src,

+                         int srcW,

+                         int srcH,

+                         int bpp) {

+    GrFixed dx = (srcW << 16) / dstW;

+    GrFixed dy = (srcH << 16) / dstH;

+

+    GrFixed y = dy >> 1;

+

+    int dstXLimit = dstW*bpp;

+    for (int j = 0; j < dstH; ++j) {

+        GrFixed x = dx >> 1;

+        void* srcRow = (uint8_t*)src + (y>>16)*srcW*bpp;

+        void* dstRow = (uint8_t*)dst + j*dstW*bpp;

+        for (int i = 0; i < dstXLimit; i += bpp) {

+            memcpy((uint8_t*) dstRow + i,

+                   (uint8_t*) srcRow + (x>>16)*bpp,

+                   bpp);

+            x += dx;

+        }

+        y += dy;

+    }

+}

+

+GrTextureEntry* GrContext::createAndLockTexture(GrTextureKey* key,

+                                                const GrSamplerState& sampler,

+                                                const GrGpu::TextureDesc& desc,

+                                                void* srcData, size_t rowBytes) {

+    GrAssert(key->width() == desc.fWidth);

+    GrAssert(key->height() == desc.fHeight);

+

+#if GR_DUMP_TEXTURE_UPLOAD

+    GrPrintf("GrContext::createAndLockTexture [%d %d]\n", desc.fWidth, desc.fHeight);

+#endif

+

+    GrTextureEntry* entry = NULL;

+    bool special = finalizeTextureKey(key, sampler);

+    if (special) {

+        GrTextureEntry* clampEntry;

+        GrTextureKey clampKey(*key);

+        clampEntry = findAndLockTexture(&clampKey, GrSamplerState::ClampNoFilter());

+

+        if (NULL == clampEntry) {

+            clampEntry = createAndLockTexture(&clampKey,

+                                              GrSamplerState::ClampNoFilter(),

+                                              desc, srcData, rowBytes);

+            GrAssert(NULL != clampEntry);

+            if (NULL == clampEntry) {

+                return NULL;

+            }

+        }

+        GrTexture* clampTexture = clampEntry->texture();

+        GrGpu::TextureDesc rtDesc = desc;

+        rtDesc.fFlags |= GrGpu::kRenderTarget_TextureFlag |

+                         GrGpu::kNoPathRendering_TextureFlag;

+        rtDesc.fWidth  = GrNextPow2(GrMax<int>(desc.fWidth,

+                                               fGpu->minRenderTargetWidth()));

+        rtDesc.fHeight = GrNextPow2(GrMax<int>(desc.fHeight,

+                                               fGpu->minRenderTargetHeight()));

+

+        GrTexture* texture = fGpu->createTexture(rtDesc, NULL, 0);

+

+        if (NULL != texture) {

+            GrGpu::AutoStateRestore asr(fGpu);

+            fGpu->setRenderTarget(texture->asRenderTarget());

+            fGpu->setTexture(clampEntry->texture());

+            fGpu->setStencilPass(GrGpu::kNone_StencilPass);

+            fGpu->setTextureMatrix(GrMatrix::I());

+            fGpu->setViewMatrix(GrMatrix::I());

+            fGpu->setAlpha(0xff);

+            fGpu->setBlendFunc(GrGpu::kOne_BlendCoeff, GrGpu::kZero_BlendCoeff);

+            fGpu->disableState(GrGpu::kDither_StateBit |

+                               GrGpu::kClip_StateBit   |

+                               GrGpu::kAntialias_StateBit);

+            GrSamplerState stretchSampler(GrSamplerState::kClamp_WrapMode,

+                                          GrSamplerState::kClamp_WrapMode,

+                                          sampler.isFilter());

+            fGpu->setSamplerState(stretchSampler);

+

+            static const GrVertexLayout layout =

+                                GrDrawTarget::kSeparateTexCoord_VertexLayoutBit;

+            GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, 4, 0);

+

+            if (arg.succeeded()) {

+                GrPoint* verts = (GrPoint*) arg.vertices();

+                verts[0].setIRectFan(0, 0,

+                                     texture->contentWidth(),

+                                     texture->contentHeight(),

+                                     2*sizeof(GrPoint));

+                GrScalar tw = GrFixedToScalar(GR_Fixed1 *

+                                              clampTexture->contentWidth() /

+                                              clampTexture->allocWidth());

+                GrScalar th = GrFixedToScalar(GR_Fixed1 *

+                                              clampTexture->contentHeight() /

+                                              clampTexture->allocHeight());

+                verts[1].setRectFan(0, 0, tw, th, 2*sizeof(GrPoint));

+                fGpu->drawNonIndexed(GrGpu::kTriangleFan_PrimitiveType,

+                                     0, 4);

+                entry = fTextureCache->createAndLock(*key, texture);

+            }

+            texture->removeRenderTarget();

+        } else {

+            // TODO: Our CPU stretch doesn't filter. But we create separate

+            // stretched textures when the sampler state is either filtered or

+            // not. Either implement filtered stretch blit on CPU or just create

+            // one when FBO case fails.

+

+            rtDesc.fFlags = 0;

+            // no longer need to clamp at min RT size.

+            rtDesc.fWidth  = GrNextPow2(desc.fWidth);

+            rtDesc.fHeight = GrNextPow2(desc.fHeight);

+            int bpp = GrTexture::BytesPerPixel(desc.fFormat);

+            GrAutoSMalloc<128*128*4> stretchedPixels(bpp *

+                                                     rtDesc.fWidth *

+                                                     rtDesc.fHeight);

+            stretchImage(stretchedPixels.get(), rtDesc.fWidth, rtDesc.fHeight,

+                         srcData, desc.fWidth, desc.fHeight, bpp);

+

+            size_t stretchedRowBytes = rtDesc.fWidth * bpp;

+

+            GrTexture* texture = fGpu->createTexture(rtDesc,

+                                                     stretchedPixels.get(),

+                                                     stretchedRowBytes);

+            GrAssert(NULL != texture);

+            entry = fTextureCache->createAndLock(*key, texture);

+        }

+        fTextureCache->unlock(clampEntry);

+

+    } else {

+        GrTexture* texture = fGpu->createTexture(desc, srcData, rowBytes);

+        if (NULL != texture) {

+            entry = fTextureCache->createAndLock(*key, texture);

+        } else {

+            entry = NULL;

+        }

+    }

+    return entry;

+}

+

+void GrContext::unlockTexture(GrTextureEntry* entry) {

+    fTextureCache->unlock(entry);

+}

+

+void GrContext::detachCachedTexture(GrTextureEntry* entry) {

+    fTextureCache->detach(entry);

+}

+

+void GrContext::reattachAndUnlockCachedTexture(GrTextureEntry* entry) {

+    fTextureCache->reattachAndUnlock(entry);

+}

+

+GrTexture* GrContext::createUncachedTexture(const GrGpu::TextureDesc& desc,

+                                            void* srcData,

+                                            size_t rowBytes) {

+    return fGpu->createTexture(desc, srcData, rowBytes);

+}

+

+GrRenderTarget* GrContext::createPlatformRenderTarget(intptr_t platformRenderTarget,

+                                                      int width, int height) {

+    return fGpu->createPlatformRenderTarget(platformRenderTarget,

+                                            width, height);

+}

+

+bool GrContext::supportsIndex8PixelConfig(const GrSamplerState& sampler,

+                                          int width, int height) {

+    if (!fGpu->supports8BitPalette()) {

+        return false;

+    }

+

+    bool needsRepeat = sampler.getWrapX() != GrSamplerState::kClamp_WrapMode ||

+                       sampler.getWrapY() != GrSamplerState::kClamp_WrapMode;

+    bool isPow2 = GrIsPow2(width) && GrIsPow2(height);

+

+    switch (fGpu->npotTextureSupport()) {

+        case GrGpu::kNone_NPOTTextureType:

+            return isPow2;

+        case GrGpu::kNoRepeat_NPOTTextureType:

+            return isPow2 || !needsRepeat;

+        case GrGpu::kNonRendertarget_NPOTTextureType:

+        case GrGpu::kFull_NPOTTextureType:

+            return true;

+    }

+    // should never get here

+    GrAssert(!"Bad enum from fGpu->npotTextureSupport");

+    return false;

+}

+

+////////////////////////////////////////////////////////////////////////////////

+

+void GrContext::eraseColor(GrColor color) {

+    fGpu->eraseColor(color);

+}

+

+void GrContext::drawFull(bool useTexture) {

+    // set rect to be big enough to fill the space, but not super-huge, so we

+    // don't overflow fixed-point implementations

+    GrRect r(fGpu->getClip().getBounds());

+    GrMatrix inverse;

+    if (fGpu->getViewInverse(&inverse)) {

+        inverse.mapRect(&r);

+    } else {

+        GrPrintf("---- fGpu->getViewInverse failed\n");

+    }

+

+    this->fillRect(r, useTexture);

+}

+

+/*  create a triangle strip that strokes the specified triangle. There are 8

+ unique vertices, but we repreat the last 2 to close up. Alternatively we

+ could use an indices array, and then only send 8 verts, but not sure that

+ would be faster.

+ */

+static void setStrokeRectStrip(GrPoint verts[10], const GrRect& rect,

+                               GrScalar width) {

+    const GrScalar rad = GrScalarHalf(width);

+

+    verts[0].set(rect.fLeft + rad, rect.fTop + rad);

+    verts[1].set(rect.fLeft - rad, rect.fTop - rad);

+    verts[2].set(rect.fRight - rad, rect.fTop + rad);

+    verts[3].set(rect.fRight + rad, rect.fTop - rad);

+    verts[4].set(rect.fRight - rad, rect.fBottom - rad);

+    verts[5].set(rect.fRight + rad, rect.fBottom + rad);

+    verts[6].set(rect.fLeft + rad, rect.fBottom - rad);

+    verts[7].set(rect.fLeft - rad, rect.fBottom + rad);

+    verts[8] = verts[0];

+    verts[9] = verts[1];

+}

+

+void GrContext::drawRect(const GrRect& rect, bool useTexture, GrScalar width) {

+    GrVertexLayout layout = useTexture ?

+                            GrDrawTarget::kPositionAsTexCoord_VertexLayoutBit :

+                            0;

+

+    static const int worstCaseVertCount = 10;

+    GrDrawTarget::AutoReleaseGeometry geo(fGpu, layout, worstCaseVertCount, 0);

+    if (!geo.succeeded()) {

+        return;

+    }

+

+    this->flushText();

+

+    int vertCount;

+    GrGpu::PrimitiveType primType;

+    GrPoint* vertex = geo.positions();

+

+    if (width >= 0) {

+        if (width > 0) {

+            vertCount = 10;

+            primType = GrGpu::kTriangleStrip_PrimitiveType;

+            setStrokeRectStrip(vertex, rect, width);

+        } else {

+            // hairline

+            vertCount = 5;

+            primType = GrGpu::kLineStrip_PrimitiveType;

+            vertex[0].set(rect.fLeft, rect.fTop);

+            vertex[1].set(rect.fRight, rect.fTop);

+            vertex[2].set(rect.fRight, rect.fBottom);

+            vertex[3].set(rect.fLeft, rect.fBottom);

+            vertex[4].set(rect.fLeft, rect.fTop);

+        }

+    } else {

+        vertCount = 4;

+        primType = GrGpu::kTriangleFan_PrimitiveType;

+        vertex->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);

+    }

+

+    fGpu->drawNonIndexed(primType, 0, vertCount);

+}

+

+////////////////////////////////////////////////////////////////////////////////

+

+#define NEW_EVAL        1   // Use adaptive path tesselation

+#define STENCIL_OFF     0   // Always disable stencil (even when needed)

+#define CPU_TRANSFORM   0   // Transform path verts on CPU

+

+#if NEW_EVAL

+

+#define EVAL_TOL GR_Scalar1

+

+static uint32_t quadratic_point_count(const GrPoint points[], GrScalar tol) {

+    GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);

+    // TODO: fixed points sqrt

+    if (d < tol) {

+        return 1;

+    } else {

+        // Each time we subdivide, d should be cut in 4. So we need to

+        // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)

+        // points.

+        // 2^(log4(x)) = sqrt(x);

+        d = ceilf(sqrtf(d/tol));

+        return GrNextPow2((uint32_t)d);

+    }

+}

+

+static uint32_t generate_quadratic_points(const GrPoint& p0,

+                                          const GrPoint& p1,

+                                          const GrPoint& p2,

+                                          GrScalar tolSqd,

+                                          GrPoint** points,

+                                          uint32_t pointsLeft) {

+    if (pointsLeft < 2 ||

+        (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {

+        (*points)[0] = p2;

+        *points += 1;

+        return 1;

+    }

+

+    GrPoint q[] = {

+        GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),

+        GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),

+    };

+    GrPoint r(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY));

+

+    pointsLeft >>= 1;

+    uint32_t a = generate_quadratic_points(p0, q[0], r, tolSqd, points, pointsLeft);

+    uint32_t b = generate_quadratic_points(r, q[1], p2, tolSqd, points, pointsLeft);

+    return a + b;

+}

+

+static uint32_t cubic_point_count(const GrPoint points[], GrScalar tol) {

+    GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),

+                       points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));

+    d = sqrtf(d);

+    if (d < tol) {

+        return 1;

+    } else {

+        d = ceilf(sqrtf(d/tol));

+        return GrNextPow2((uint32_t)d);

+    }

+}

+

+static uint32_t generate_cubic_points(const GrPoint& p0,

+                                      const GrPoint& p1,

+                                      const GrPoint& p2,

+                                      const GrPoint& p3,

+                                      GrScalar tolSqd,

+                                      GrPoint** points,

+                                      uint32_t pointsLeft) {

+    if (pointsLeft < 2 ||

+        (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&

+         p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {

+            (*points)[0] = p3;

+            *points += 1;

+            return 1;

+        }

+    GrPoint q[] = {

+        GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),

+        GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),

+        GrPoint(GrScalarAve(p2.fX, p3.fX), GrScalarAve(p2.fY, p3.fY))

+    };

+    GrPoint r[] = {

+        GrPoint(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY)),

+        GrPoint(GrScalarAve(q[1].fX, q[2].fX), GrScalarAve(q[1].fY, q[2].fY))

+    };

+    GrPoint s(GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(r[0].fY, r[1].fY));

+    pointsLeft >>= 1;

+    uint32_t a = generate_cubic_points(p0, q[0], r[0], s, tolSqd, points, pointsLeft);

+    uint32_t b = generate_cubic_points(s, r[1], q[2], p3, tolSqd, points, pointsLeft);

+    return a + b;

+}

+

+#else // !NEW_EVAL

+

+static GrScalar gr_eval_quad(const GrScalar coord[], GrScalar t) {

+    GrScalar A = coord[0] - 2 * coord[2] + coord[4];

+    GrScalar B = 2 * (coord[2] - coord[0]);

+    GrScalar C = coord[0];

+

+    return GrMul(GrMul(A, t) + B, t) + C;

+}

+

+static void gr_eval_quad_at(const GrPoint src[3], GrScalar t, GrPoint* pt) {

+    GrAssert(src);

+    GrAssert(pt);

+    GrAssert(t >= 0 && t <= GR_Scalar1);

+    pt->set(gr_eval_quad(&src[0].fX, t), gr_eval_quad(&src[0].fY, t));

+}

+

+static GrScalar gr_eval_cubic(const GrScalar coord[], GrScalar t) {

+    GrScalar A = coord[6] - coord[0] + 3 * (coord[2] - coord[4]);

+    GrScalar B = 3 * (coord[0] - 2 * coord[2] + coord[4]);

+    GrScalar C = 3 * (coord[2] - coord[0]);

+    GrScalar D = coord[0];

+

+    return GrMul(GrMul(GrMul(A, t) + B, t) + C, t) + D;

+}

+

+static void gr_eval_cubic_at(const GrPoint src[4], GrScalar t, GrPoint* pt) {

+    GrAssert(src);

+    GrAssert(pt);

+    GrAssert(t >= 0 && t <= GR_Scalar1);

+

+    pt->set(gr_eval_cubic(&src[0].fX, t), gr_eval_cubic(&src[0].fY, t));

+}

+

+#endif // !NEW_EVAL

+

+static int worst_case_point_count(GrPathIter* path,

+                                  int* subpaths,

+                                  const GrMatrix& matrix,

+                                  GrScalar tol) {

+    int pointCount = 0;

+    *subpaths = 1;

+

+    bool first = true;

+

+    GrPathIter::Command cmd;

+

+    GrPoint pts[4];

+    while ((cmd = path->next(pts)) != GrPathIter::kEnd_Command) {

+

+        switch (cmd) {

+            case GrPathIter::kLine_Command:

+                pointCount += 1;

+                break;

+            case GrPathIter::kQuadratic_Command:

+#if NEW_EVAL

+                matrix.mapPoints(pts, pts, 3);

+                pointCount += quadratic_point_count(pts, tol);

+#else

+                pointCount += 9;

+#endif

+                break;

+            case GrPathIter::kCubic_Command:

+#if NEW_EVAL

+                matrix.mapPoints(pts, pts, 4);

+                pointCount += cubic_point_count(pts, tol);

+#else

+                pointCount += 17;

+#endif

+                break;

+            case GrPathIter::kMove_Command:

+                pointCount += 1;

+                if (!first) {

+                    ++(*subpaths);

+                }

+                break;

+            default:

+                break;

+        }

+        first = false;

+    }

+    return pointCount;

+}

+

+static inline bool single_pass_path(const GrPathIter& path,

+                                    GrContext::PathFills fill,

+                                    bool useTex,

+                                    const GrGpu& gpu) {

+#if STENCIL_OFF

+    return true;

+#else

+    if (GrContext::kEvenOdd_PathFill == fill) {

+        GrPathIter::ConvexHint hint = path.hint();

+        return hint == GrPathIter::kConvex_ConvexHint ||

+               hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint;

+    } else if (GrContext::kWinding_PathFill == fill) {

+        GrPathIter::ConvexHint hint = path.hint();

+        return hint == GrPathIter::kConvex_ConvexHint ||

+               hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint ||

+               (hint == GrPathIter::kSameWindingConvexPieces_ConvexHint &&

+                gpu.canDisableBlend() && !gpu.isDitherState());

+

+    }

+    return false;

+#endif

+}

+

+void GrContext::drawPath(GrPathIter* path, PathFills fill,

+                         bool useTexture, const GrPoint* translate) {

+

+    flushText();

+

+    GrGpu::AutoStateRestore asr(fGpu);

+

+#if NEW_EVAL

+    GrMatrix viewM;

+    fGpu->getViewMatrix(&viewM);

+    // In order to tesselate the path we get a bound on how much the matrix can

+    // stretch when mapping to screen coordinates.

+    GrScalar stretch = viewM.getMaxStretch();

+    bool useStretch = stretch > 0;

+    GrScalar tol = EVAL_TOL;

+    if (!useStretch) {

+        // TODO: deal with perspective in some better way.

+        tol /= 10;

+    } else {

+        // TODO: fixed point divide

+        GrScalar sinv = 1 / stretch;

+        tol = GrMul(tol, sinv);

+        viewM = GrMatrix::I();

+    }

+    GrScalar tolSqd = GrMul(tol, tol);

+#else

+    // pass to worst_case... but won't be used.

+    static const GrScalar tol = -1;

+#endif

+

+    int subpathCnt;

+    int maxPts = worst_case_point_count(path,

+                                        &subpathCnt,

+#if CPU_TRANSFORM

+                                        cpuMatrix,

+#else

+                                        GrMatrix::I(),

+#endif

+                                        tol);

+    GrVertexLayout layout = 0;

+    if (useTexture) {

+        layout = GrDrawTarget::kPositionAsTexCoord_VertexLayoutBit;

+    }

+    // add 4 to hold the bounding rect

+    GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, maxPts + 4, 0);

+

+    GrPoint* base = (GrPoint*) arg.vertices();

+    GrPoint* vert = base;

+    GrPoint* subpathBase = base;

+

+    GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);

+

+    path->rewind();

+

+    // TODO: use primitve restart if available rather than multiple draws

+    GrGpu::PrimitiveType  type;

+    int                   passCount = 0;

+    GrGpu::StencilPass    passes[3];

+    bool                  reverse = false;

+

+    if (kHairLine_PathFill == fill) {

+        type = GrGpu::kLineStrip_PrimitiveType;

+        passCount = 1;

+        passes[0] = GrGpu::kNone_StencilPass;

+    } else {

+        type = GrGpu::kTriangleFan_PrimitiveType;

+        if (single_pass_path(*path, fill, useTexture, *fGpu)) {

+            passCount = 1;

+            passes[0] = GrGpu::kNone_StencilPass;

+        } else {

+            switch (fill) {

+                case kInverseEvenOdd_PathFill:

+                    reverse = true;

+                    // fallthrough

+                case kEvenOdd_PathFill:

+                    passCount = 2;

+                    passes[0] = GrGpu::kEvenOddStencil_StencilPass;

+                    passes[1] = GrGpu::kEvenOddColor_StencilPass;

+                    break;

+

+                case kInverseWinding_PathFill:

+                    reverse = true;

+                    // fallthrough

+                case kWinding_PathFill:

+                    passes[0] = GrGpu::kWindingStencil1_StencilPass;

+                    if (fGpu->supportsSingleStencilPassWinding()) {

+                        passes[1] = GrGpu::kWindingColor_StencilPass;

+                        passCount = 2;

+                    } else {

+                        passes[1] = GrGpu::kWindingStencil2_StencilPass;

+                        passes[2] = GrGpu::kWindingColor_StencilPass;

+                        passCount = 3;

+                    }

+                    break;

+                default:

+                    GrAssert(!"Unknown path fill!");

+                    return;

+            }

+        }

+    }

+    fGpu->setReverseFill(reverse);

+#if CPU_TRANSFORM

+    GrMatrix cpuMatrix;

+    fGpu->getViewMatrix(&cpuMatrix);

+    fGpu->setViewMatrix(GrMatrix::I());

+#endif

+

+    GrPoint pts[4];

+

+    bool first = true;

+    int subpath = 0;

+

+    for (;;) {

+        GrPathIter::Command cmd = path->next(pts);

+#if CPU_TRANSFORM

+        int numPts = GrPathIter::NumCommandPoints(cmd);

+        cpuMatrix.mapPoints(pts, pts, numPts);

+#endif

+        switch (cmd) {

+            case GrPathIter::kMove_Command:

+                if (!first) {

+                    subpathVertCount[subpath] = vert-subpathBase;

+                    subpathBase = vert;

+                    ++subpath;

+                }

+                *vert = pts[0];

+                vert++;

+                break;

+            case GrPathIter::kLine_Command:

+                *vert = pts[1];

+                vert++;

+                break;

+            case GrPathIter::kQuadratic_Command: {

+#if NEW_EVAL

+

+                generate_quadratic_points(pts[0], pts[1], pts[2],

+                                          tolSqd, &vert,

+                                          quadratic_point_count(pts, tol));

+#else

+                const int n = 8;

+                const GrScalar dt = GR_Scalar1 / n;

+                GrScalar t = dt;

+                for (int i = 1; i < n; i++) {

+                    gr_eval_quad_at(pts, t, (GrPoint*)vert);

+                    t += dt;

+                    vert++;

+                }

+                vert->set(pts[2].fX, pts[2].fY);

+                vert++;

+#endif

+                break;

+            }

+            case GrPathIter::kCubic_Command: {

+#if NEW_EVAL

+                generate_cubic_points(pts[0], pts[1], pts[2], pts[3],

+                                      tolSqd, &vert,

+                                      cubic_point_count(pts, tol));

+#else

+                const int n = 16;

+                const GrScalar dt = GR_Scalar1 / n;

+                GrScalar t = dt;

+                for (int i = 1; i < n; i++) {

+                    gr_eval_cubic_at(pts, t, (GrPoint*)vert);

+                    t += dt;

+                    vert++;

+                }

+                vert->set(pts[3].fX, pts[3].fY);

+                vert++;

+#endif

+                break;

+            }

+            case GrPathIter::kClose_Command:

+                break;

+            case GrPathIter::kEnd_Command:

+                subpathVertCount[subpath] = vert-subpathBase;

+                ++subpath; // this could be only in debug

+                goto FINISHED;

+        }

+        first = false;

+    }

+FINISHED:

+    GrAssert(subpath == subpathCnt);

+    GrAssert((vert - base) <= maxPts);

+

+    if (translate) {

+        int count = vert - base;

+        for (int i = 0; i < count; i++) {

+            base[i].offset(translate->fX, translate->fY);

+        }

+    }

+

+    // arbitrary path complexity cutoff

+    bool useBounds = fill != kHairLine_PathFill &&

+                    (reverse || (vert - base) > 8);

+    GrPoint* boundsVerts = base + maxPts;

+    if (useBounds) {

+        GrRect bounds;

+        if (reverse) {

+            GrAssert(NULL != fGpu->currentRenderTarget());

+            // draw over the whole world.

+            bounds.setLTRB(0, 0,

+                           GrIntToScalar(fGpu->currentRenderTarget()->width()),

+                           GrIntToScalar(fGpu->currentRenderTarget()->height()));

+        } else {

+            bounds.setBounds((GrPoint*)base, vert - base);

+        }

+        boundsVerts[0].setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight,

+                                  bounds.fBottom);

+    }

+

+    for (int p = 0; p < passCount; ++p) {

+        fGpu->setStencilPass(passes[p]);

+        if (useBounds && (GrGpu::kEvenOddColor_StencilPass == passes[p] ||

+                          GrGpu::kWindingColor_StencilPass == passes[p])) {

+            fGpu->drawNonIndexed(GrGpu::kTriangleFan_PrimitiveType,

+                                 maxPts, 4);

+        } else {

+            int baseVertex = 0;

+            for (int sp = 0; sp < subpathCnt; ++sp) {

+                fGpu->drawNonIndexed(type,

+                                     baseVertex,

+                                     subpathVertCount[sp]);

+                baseVertex += subpathVertCount[sp];

+            }

+        }

+    }

+}

+

+void GrContext::flush(bool flushRenderTarget) {

+    flushText();

+    if (flushRenderTarget) {

+        fGpu->forceRenderTargetFlush();

+    }

+}

+

+void GrContext::flushText() {

+    fTextDrawBuffer.playback(fGpu);

+    fTextDrawBuffer.reset();

+}

+

+bool GrContext::readPixels(int left, int top, int width, int height,

+                           GrTexture::PixelConfig config, void* buffer) {

+    this->flush(true);

+    return fGpu->readPixels(left, top, width, height, config, buffer);

+}

+

+void GrContext::writePixels(int left, int top, int width, int height,

+                            GrTexture::PixelConfig config, const void* buffer,

+                            size_t stride) {

+    const GrGpu::TextureDesc desc = {

+        0, GrGpu::kNone_AALevel, width, height, config

+    };

+    GrTexture* texture = fGpu->createTexture(desc, buffer, stride);

+    if (NULL == texture) {

+        return;

+    }

+

+    this->flush(true);

+

+    GrAutoUnref                     aur(texture);

+    GrDrawTarget::AutoStateRestore  asr(fGpu);

+

+    GrMatrix matrix;

+    matrix.setTranslate(GrIntToScalar(left), GrIntToScalar(top));

+    fGpu->setViewMatrix(matrix);

+    matrix.setScale(GR_Scalar1 / texture->allocWidth(),

+                    GR_Scalar1 / texture->allocHeight());

+    fGpu->setTextureMatrix(matrix);

+

+    fGpu->disableState(GrDrawTarget::kClip_StateBit);

+    fGpu->setAlpha(0xFF);

+    fGpu->setBlendFunc(GrDrawTarget::kOne_BlendCoeff,

+                       GrDrawTarget::kZero_BlendCoeff);

+    fGpu->setTexture(texture);

+    fGpu->setSamplerState(GrSamplerState::ClampNoFilter());

+

+    this->fillRect(GrRect(0, 0, GrIntToScalar(width), GrIntToScalar(height)),

+                   true);

+}

+

+////////////////////////////////////////////////////////////////////////////////

+

+

+/* -------------------------------------------------------

+ * Mimicking the GrGpu interface for now

+ * TODO: define appropriate higher-level API for context

+ */

+

+void GrContext::resetContext() {

+    fGpu->resetContext();

+}

+

+GrVertexBuffer* GrContext::createVertexBuffer(uint32_t size, bool dynamic) {

+    return fGpu->createVertexBuffer(size, dynamic);

+}

+

+GrIndexBuffer* GrContext::createIndexBuffer(uint32_t size, bool dynamic) {

+    return fGpu->createIndexBuffer(size, dynamic);

+}

+

+void GrContext::setTexture(GrTexture* texture) {

+    fGpu->setTexture(texture);

+}

+

+void GrContext::setRenderTarget(GrRenderTarget* target) {

+    flushText();

+    fGpu->setRenderTarget(target);

+}

+

+GrRenderTarget* GrContext::currentRenderTarget() const {

+    return fGpu->currentRenderTarget();

+}

+

+void GrContext::setDefaultRenderTargetSize(uint32_t width, uint32_t height) {

+    fGpu->setDefaultRenderTargetSize(width, height);

+}

+

+void GrContext::setSamplerState(const GrSamplerState& samplerState) {

+    fGpu->setSamplerState(samplerState);

+}

+

+void GrContext::setTextureMatrix(const GrMatrix& m) {

+    fGpu->setTextureMatrix(m);

+}

+

+void GrContext::getViewMatrix(GrMatrix* m) const {

+    fGpu->getViewMatrix(m);

+}

+

+void GrContext::setViewMatrix(const GrMatrix& m) {

+    fGpu->setViewMatrix(m);

+}

+

+bool GrContext::reserveAndLockGeometry(GrVertexLayout    vertexLayout,

+                                       uint32_t          vertexCount,

+                                       uint32_t          indexCount,

+                                       void**            vertices,

+                                       void**            indices) {

+    return fGpu->reserveAndLockGeometry(vertexLayout,

+                                        vertexCount,

+                                        indexCount,

+                                        vertices,

+                                        indices);

+}

+

+void GrContext::drawIndexed(GrGpu::PrimitiveType type,

+                            uint32_t startVertex,

+                            uint32_t startIndex,

+                            uint32_t vertexCount,

+                            uint32_t indexCount) {

+    flushText();

+    fGpu->drawIndexed(type,

+                      startVertex,

+                      startIndex,

+                      vertexCount,

+                      indexCount);

+}

+

+void GrContext::drawNonIndexed(GrGpu::PrimitiveType type,

+                               uint32_t startVertex,

+                               uint32_t vertexCount) {

+    flushText();

+    fGpu->drawNonIndexed(type,

+                         startVertex,

+                         vertexCount);

+}

+

+void GrContext::setVertexSourceToArray(const void* array,

+                                       GrVertexLayout vertexLayout) {

+    fGpu->setVertexSourceToArray(array, vertexLayout);

+}

+

+void GrContext::setIndexSourceToArray(const void* array) {

+    fGpu->setIndexSourceToArray(array);

+}

+

+void GrContext::setVertexSourceToBuffer(GrVertexBuffer* buffer,

+                                       GrVertexLayout vertexLayout) {

+    fGpu->setVertexSourceToBuffer(buffer, vertexLayout);

+}

+

+void GrContext::setIndexSourceToBuffer(GrIndexBuffer* buffer) {

+    fGpu->setIndexSourceToBuffer(buffer);

+}

+

+void GrContext::releaseReservedGeometry() {

+    fGpu->releaseReservedGeometry();

+}

+

+void GrContext::setClip(const GrClip& clip) {

+    fGpu->setClip(clip);

+    fGpu->enableState(GrDrawTarget::kClip_StateBit);

+}

+

+void GrContext::setAlpha(uint8_t alpha) {

+    fGpu->setAlpha(alpha);

+}

+

+void GrContext::setColor(GrColor color) {

+    fGpu->setColor(color);

+}

+

+static inline intptr_t setOrClear(intptr_t bits, int shift, intptr_t pred) {

+    intptr_t mask = 1 << shift;

+    if (pred) {

+        bits |= mask;

+    } else {

+        bits &= ~mask;

+    }

+    return bits;

+}

+

+void GrContext::setAntiAlias(bool aa) {

+    if (aa) {

+        fGpu->enableState(GrGpu::kAntialias_StateBit);

+    } else {

+        fGpu->disableState(GrGpu::kAntialias_StateBit);

+    }

+}

+

+void GrContext::setDither(bool dither) {

+    // hack for now, since iPad dither is hella-slow

+    dither = false;

+

+    if (dither) {

+        fGpu->enableState(GrGpu::kDither_StateBit);

+    } else {

+        fGpu->disableState(GrGpu::kDither_StateBit);

+    }

+}

+

+void GrContext::setPointSize(float size) {

+    fGpu->setPointSize(size);

+}

+

+void GrContext::setBlendFunc(GrGpu::BlendCoeff srcCoef,

+                             GrGpu::BlendCoeff dstCoef) {

+    fGpu->setBlendFunc(srcCoef, dstCoef);

+}

+

+void GrContext::resetStats() {

+    fGpu->resetStats();

+}

+

+const GrGpu::Stats& GrContext::getStats() const {

+    return fGpu->getStats();

+}

+

+void GrContext::printStats() const {

+    fGpu->printStats();

+}

+

+GrContext::GrContext(GrGpu* gpu) :

+        fVBAllocPool(gpu,

+                     gpu->supportsBufferLocking() ? POOL_VB_SIZE : 0,

+                     gpu->supportsBufferLocking() ? NUM_POOL_VBS : 0),

+        fTextDrawBuffer(gpu->supportsBufferLocking() ? &fVBAllocPool : NULL) {

+    fGpu = gpu;

+    fGpu->ref();

+    fTextureCache = new GrTextureCache(MAX_TEXTURE_CACHE_COUNT,

+                                       MAX_TEXTURE_CACHE_BYTES);

+    fFontCache = new GrFontCache(fGpu);

+}

+

+bool GrContext::finalizeTextureKey(GrTextureKey* key,

+                                   const GrSamplerState& sampler) const {

+    uint32_t bits = 0;

+    uint16_t width = key->width();

+    uint16_t height = key->height();

+    if (fGpu->npotTextureSupport() < GrGpu::kNonRendertarget_NPOTTextureType) {

+        if ((sampler.getWrapX() != GrSamplerState::kClamp_WrapMode ||

+             sampler.getWrapY() != GrSamplerState::kClamp_WrapMode) &&

+            (!GrIsPow2(width) || !GrIsPow2(height))) {

+            bits |= 1;

+            bits |= sampler.isFilter() ? 2 : 0;

+        }

+    }

+    key->finalize(bits);

+    return 0 != bits;

+}

+

+GrDrawTarget* GrContext::getTextTarget() {

+#if DEFER_TEXT_RENDERING

+    fTextDrawBuffer.initializeDrawStateAndClip(*fGpu);

+    return &fTextDrawBuffer;

+#else

+    return fGpu;

+#endif

+}

+

+const GrIndexBuffer* GrContext::quadIndexBuffer() const {

+    return fGpu->quadIndexBuffer();

+}

+

+int GrContext::maxQuadsInIndexBuffer() const {

+    return fGpu->maxQuadsInIndexBuffer();

+}

+

+

+

+
diff --git a/gpu/src/GrDrawMesh.cpp b/gpu/src/GrDrawMesh.cpp
new file mode 100644
index 0000000..bd79005
--- /dev/null
+++ b/gpu/src/GrDrawMesh.cpp
@@ -0,0 +1,140 @@
+#include "GrMesh.h"
+#include "SkCanvas.h"
+
+GrMesh::GrMesh() : fPts(NULL), fCount(0), fIndices(NULL), fIndexCount(0) {}
+
+GrMesh::~GrMesh() {
+    delete[] fPts;
+    delete[] fIndices;
+}
+
+GrMesh& GrMesh::operator=(const GrMesh& src) {
+    delete[] fPts;
+    delete[] fIndices;
+
+    fBounds = src.fBounds;
+    fRows = src.fRows;
+    fCols = src.fCols;
+
+    fCount = src.fCount;
+    fPts = new SkPoint[fCount * 2];
+    fTex = fPts + fCount;
+    memcpy(fPts, src.fPts, fCount * 2 * sizeof(SkPoint));
+    
+    delete[] fIndices;
+    fIndexCount = src.fIndexCount;
+    fIndices = new uint16_t[fIndexCount];
+    memcpy(fIndices, src.fIndices, fIndexCount * sizeof(uint16_t));
+
+    return *this;
+}
+
+void GrMesh::init(const SkRect& bounds, int rows, int cols,
+                const SkRect& texture) {
+    SkASSERT(rows > 0 && cols > 0);
+
+    fBounds = bounds;
+    fRows = rows;
+    fCols = cols;
+
+    delete[] fPts;
+    fCount = (rows + 1) * (cols + 1);
+    fPts = new SkPoint[fCount * 2];
+    fTex = fPts + fCount;
+
+    delete[] fIndices;
+    fIndexCount = rows * cols * 6;
+    fIndices = new uint16_t[fIndexCount];
+    
+    SkPoint* pts = fPts;
+    const SkScalar dx = bounds.width() / rows;
+    const SkScalar dy = bounds.height() / cols;
+    SkPoint* tex = fTex;
+    const SkScalar dtx = texture.width() / rows;
+    const SkScalar dty = texture.height() / cols;
+    uint16_t* idx = fIndices;
+    int index = 0;
+    for (int y = 0; y <= cols; y++) {
+        for (int x = 0; x <= rows; x++) {
+            pts->set(bounds.fLeft + x*dx, bounds.fTop + y*dy);
+            pts += 1;
+            tex->set(texture.fLeft + x*dtx, texture.fTop + y*dty);
+            tex += 1;
+            
+            if (y < cols && x < rows) {
+                *idx++ = index;
+                *idx++ = index + rows + 1;
+                *idx++ = index + 1;
+
+                *idx++ = index + 1;
+                *idx++ = index + rows + 1;
+                *idx++ = index + rows + 2;
+                
+                index += 1;
+            }
+        }
+        index += 1;
+    }
+}
+
+void GrMesh::draw(SkCanvas* canvas, const SkPaint& paint) {
+    canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount,
+                         fPts, fTex, NULL, NULL, fIndices, fIndexCount,
+                         paint);
+}
+
+/////////////////////////////////////////////
+
+#include "SkBoundaryPatch.h"
+#include "SkMeshUtils.h"
+
+static SkPoint SkPointInterp(const SkPoint& a, const SkPoint& b, SkScalar t) {
+    return SkPoint::Make(SkScalarInterp(a.fX, b.fX, t),
+                         SkScalarInterp(a.fY, b.fY, t));
+}
+
+static void set_cubic(SkPoint pts[4], SkScalar x0, SkScalar y0,
+                      SkScalar x3, SkScalar y3, SkScalar scale = 1) {
+    SkPoint tmp, tmp2;
+    
+    pts[0].set(x0, y0);
+    pts[3].set(x3, y3);
+    
+    tmp = SkPointInterp(pts[0], pts[3], SK_Scalar1/3);
+    tmp2 = pts[0] - tmp;
+    tmp2.rotateCW();
+    tmp2.scale(scale);
+    pts[1] = tmp + tmp2;
+    
+    tmp = SkPointInterp(pts[0], pts[3], 2*SK_Scalar1/3);
+    tmp2 = pts[3] - tmp;
+    tmp2.rotateCW();
+    tmp2.scale(scale);
+    pts[2] = tmp + tmp2;
+}
+
+void test_patch(SkCanvas* canvas, const SkBitmap& bm, SkScalar scale) {
+    const float w = bm.width();
+    const float h = bm.height();
+    SkCubicBoundary cubic;    
+    set_cubic(cubic.fPts + 0, 0, 0, w, 0, scale);
+    set_cubic(cubic.fPts + 3, w, 0, w, h, scale);
+    set_cubic(cubic.fPts + 6, w, h, 0, h, -scale);
+    set_cubic(cubic.fPts + 9, 0, h, 0, 0, scale);
+    
+    SkBoundaryPatch patch;
+    patch.setBoundary(&cubic);
+    
+    const int Rows = 16;
+    const int Cols = 16;
+    SkPoint pts[Rows * Cols];
+    patch.evalPatch(pts, Rows, Cols);
+    
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setFilterBitmap(true);
+
+    SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint);
+}
+
+
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
new file mode 100644
index 0000000..82f94a3
--- /dev/null
+++ b/gpu/src/GrDrawTarget.cpp
@@ -0,0 +1,296 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrDrawTarget.h"
+#include "GrGpuVertex.h"
+
+#define VERTEX_LAYOUT_ASSERTS \
+    GrAssert(!(vertexLayout & kTextFormat_VertexLayoutBit) ||           \
+             vertexLayout == kTextFormat_VertexLayoutBit);              \
+    GrAssert(!(vertexLayout & kSeparateTexCoord_VertexLayoutBit) ||     \
+             !(vertexLayout & kPositionAsTexCoord_VertexLayoutBit));
+
+size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
+    VERTEX_LAYOUT_ASSERTS
+    if ((vertexLayout & kTextFormat_VertexLayoutBit)) {
+        return 2 * sizeof(GrGpuTextVertex);
+    } else {
+        size_t size = sizeof(GrPoint);
+        if (vertexLayout & kSeparateTexCoord_VertexLayoutBit) {
+            size += sizeof(GrPoint);
+        }
+        if (vertexLayout & kColor_VertexLayoutBit) {
+            size += sizeof(GrColor);
+        }
+        return size;
+    }
+}
+
+int GrDrawTarget::VertexTexCoordOffset(GrVertexLayout vertexLayout) {
+    VERTEX_LAYOUT_ASSERTS
+    if ((vertexLayout & kTextFormat_VertexLayoutBit)) {
+        return sizeof(GrGpuTextVertex);
+    } else if (vertexLayout & kSeparateTexCoord_VertexLayoutBit) {
+        return sizeof(GrPoint);
+    } else if (vertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
+        return 0;
+    }
+    return -1;
+}
+
+int  GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) {
+    VERTEX_LAYOUT_ASSERTS
+    if (vertexLayout & kColor_VertexLayoutBit) {
+        if (vertexLayout & kSeparateTexCoord_VertexLayoutBit) {
+            return 2 * sizeof(GrPoint);
+        } else {
+            return sizeof(GrPoint);
+        }
+    }
+    return -1;
+}
+
+int GrDrawTarget::VertexSizeAndOffsets(GrVertexLayout vertexLayout,
+                                       int* texCoordOffset,
+                                       int* colorOffset) {
+    VERTEX_LAYOUT_ASSERTS
+
+    GrAssert(NULL != texCoordOffset);
+    GrAssert(NULL != colorOffset);
+
+    if ((vertexLayout & kTextFormat_VertexLayoutBit)) {
+        *texCoordOffset = sizeof(GrGpuTextVertex);
+        *colorOffset = 0;
+        return 2 * sizeof(GrGpuTextVertex);
+    } else {
+        size_t size = sizeof(GrPoint);
+        if (vertexLayout & kSeparateTexCoord_VertexLayoutBit) {
+            *texCoordOffset = sizeof(GrPoint);
+            size += sizeof(GrPoint);
+        } else if (vertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
+            *texCoordOffset = 0;
+        } else {
+            *texCoordOffset = -1;
+        }
+        if (vertexLayout & kColor_VertexLayoutBit) {
+            *colorOffset = size;
+            size += sizeof(GrColor);
+        } else {
+            *colorOffset = -1;
+        }
+        return size;
+    }
+}
+
+bool GrDrawTarget::VertexHasTexCoords(GrVertexLayout vertexLayout) {
+    return !!(vertexLayout & (kSeparateTexCoord_VertexLayoutBit   |
+                              kPositionAsTexCoord_VertexLayoutBit |
+                              kTextFormat_VertexLayoutBit));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrDrawTarget::GrDrawTarget() {
+    fReservedGeometry.fLocked = false;
+#if GR_DEBUG
+    fReservedGeometry.fVertexCount  = ~0;
+    fReservedGeometry.fIndexCount   = ~0;
+#endif
+    fGeometrySrc.fVertexSrc = kReserved_GeometrySrcType;
+    fGeometrySrc.fIndexSrc  = kReserved_GeometrySrcType;
+}
+
+void GrDrawTarget::setClip(const GrClip& clip) {
+    clipWillChange(clip);
+    fClip = clip;
+}
+
+const GrClip& GrDrawTarget::getClip() const {
+    return fClip;
+}
+
+void GrDrawTarget::setTexture(GrTexture* tex) {
+    fCurrDrawState.fTexture = tex;
+}
+
+GrTexture* GrDrawTarget::currentTexture() const {
+    return fCurrDrawState.fTexture;
+}
+
+void GrDrawTarget::setRenderTarget(GrRenderTarget* target) {
+    fCurrDrawState.fRenderTarget = target;
+}
+
+GrRenderTarget* GrDrawTarget::currentRenderTarget() const {
+    return fCurrDrawState.fRenderTarget;
+}
+
+void GrDrawTarget::concatViewMatrix(const GrMatrix& matrix) {
+    GrMatrix mv;
+    mv.setConcat(fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode], matrix);
+    this->loadMatrix(mv, kModelView_MatrixMode);
+}
+
+void GrDrawTarget::getViewMatrix(GrMatrix* matrix) const {
+    *matrix = fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode];
+}
+
+bool GrDrawTarget::getViewInverse(GrMatrix* matrix) const {
+    // Can we cache this somewhere?
+
+    GrMatrix inverse;
+    if (fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode].invert(&inverse)) {
+        if (matrix) {
+            *matrix = inverse;
+        }
+        return true;
+    }
+    return false;
+}
+
+void GrDrawTarget::setSamplerState(const GrSamplerState& state) {
+    fCurrDrawState.fSamplerState = state;
+}
+
+void GrDrawTarget::setStencilPass(StencilPass pass) {
+    fCurrDrawState.fStencilPass = pass;
+}
+
+void GrDrawTarget::setReverseFill(bool reverse) {
+    fCurrDrawState.fReverseFill = reverse;
+}
+
+void GrDrawTarget::enableState(uint32_t bits) {
+    fCurrDrawState.fFlagBits |= bits;
+}
+
+void GrDrawTarget::disableState(uint32_t bits) {
+    fCurrDrawState.fFlagBits &= ~(bits);
+}
+
+void GrDrawTarget::loadMatrix(const GrMatrix& matrix, MatrixMode m) {
+    fCurrDrawState.fMatrixModeCache[m] = matrix;
+}
+
+void GrDrawTarget::setPointSize(float size) {
+    fCurrDrawState.fPointSize = size;
+}
+
+void GrDrawTarget::setBlendFunc(BlendCoeff srcCoef,
+                                BlendCoeff dstCoef) {
+    fCurrDrawState.fSrcBlend = srcCoef;
+    fCurrDrawState.fDstBlend = dstCoef;
+}
+
+void GrDrawTarget::setColor(GrColor c) {
+    fCurrDrawState.fColor = c;
+}
+
+void GrDrawTarget::setAlpha(uint8_t a) {
+    this->setColor((a << 24) | (a << 16) | (a << 8) | a);
+}
+
+void GrDrawTarget::saveCurrentDrawState(SavedDrawState* state) const {
+    state->fState = fCurrDrawState;
+}
+
+void GrDrawTarget::restoreDrawState(const SavedDrawState& state) {
+    fCurrDrawState = state.fState;
+}
+
+void GrDrawTarget::copyDrawState(const GrDrawTarget& srcTarget) {
+    fCurrDrawState = srcTarget.fCurrDrawState;
+}
+
+
+bool GrDrawTarget::reserveAndLockGeometry(GrVertexLayout    vertexLayout,
+                                          uint32_t          vertexCount,
+                                          uint32_t          indexCount,
+                                          void**            vertices,
+                                          void**            indices) {
+    GrAssert(!fReservedGeometry.fLocked);
+    fReservedGeometry.fVertexCount  = vertexCount;
+    fReservedGeometry.fIndexCount   = indexCount;
+
+    fReservedGeometry.fLocked = acquireGeometryHelper(vertexLayout,
+                                                      vertices,
+                                                      indices);
+    if (fReservedGeometry.fLocked) {
+        if (vertexCount) {
+            fGeometrySrc.fVertexSrc = kReserved_GeometrySrcType;
+            fGeometrySrc.fVertexLayout = vertexLayout;
+        }
+        if (indexCount) {
+            fGeometrySrc.fIndexSrc = kReserved_GeometrySrcType;
+        }
+    }
+    return fReservedGeometry.fLocked;
+}
+
+bool GrDrawTarget::geometryHints(GrVertexLayout vertexLayout,
+                                 int32_t* vertexCount,
+                                 int32_t* indexCount) const {
+    GrAssert(!fReservedGeometry.fLocked);
+    if (NULL != vertexCount) {
+        *vertexCount = -1;
+    }
+    if (NULL != indexCount) {
+        *indexCount = -1;
+    }
+    return false;
+}
+
+void GrDrawTarget::releaseReservedGeometry() {
+    GrAssert(fReservedGeometry.fLocked);
+    releaseGeometryHelper();
+    fReservedGeometry.fLocked = false;
+}
+
+void GrDrawTarget::setVertexSourceToArray(const void* array,
+                                          GrVertexLayout vertexLayout) {
+    fGeometrySrc.fVertexSrc    = kArray_GeometrySrcType;
+    fGeometrySrc.fVertexArray  = array;
+    fGeometrySrc.fVertexLayout = vertexLayout;
+}
+
+void GrDrawTarget::setIndexSourceToArray(const void* array) {
+    fGeometrySrc.fIndexSrc   = kArray_GeometrySrcType;
+    fGeometrySrc.fIndexArray = array;
+}
+
+void GrDrawTarget::setVertexSourceToBuffer(const GrVertexBuffer* buffer,
+                                           GrVertexLayout vertexLayout) {
+    fGeometrySrc.fVertexSrc    = kBuffer_GeometrySrcType;
+    fGeometrySrc.fVertexBuffer = buffer;
+    fGeometrySrc.fVertexLayout = vertexLayout;
+}
+
+void GrDrawTarget::setIndexSourceToBuffer(const GrIndexBuffer* buffer) {
+    fGeometrySrc.fIndexSrc     = kBuffer_GeometrySrcType;
+    fGeometrySrc.fIndexBuffer  = buffer;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrDrawTarget::AutoStateRestore::AutoStateRestore(GrDrawTarget* target) {
+    fDrawTarget = target;
+    fDrawTarget->saveCurrentDrawState(&fDrawState);
+}
+
+GrDrawTarget::AutoStateRestore::~AutoStateRestore() {
+    fDrawTarget->restoreDrawState(fDrawState);
+}
diff --git a/gpu/src/GrGLIndexBuffer.cpp b/gpu/src/GrGLIndexBuffer.cpp
new file mode 100644
index 0000000..82cffaa
--- /dev/null
+++ b/gpu/src/GrGLIndexBuffer.cpp
@@ -0,0 +1,106 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrGLIndexBuffer.h"
+#include "GrGpuGL.h"
+
+GrGLIndexBuffer::GrGLIndexBuffer(GLuint id, GrGpuGL* gl, uint32_t sizeInBytes,
+                                   bool dynamic) : 
+                                INHERITED(sizeInBytes, dynamic),
+                                fGL(gl),
+                                fBufferID(id),
+                                fLockPtr(NULL) {
+}
+
+GLuint GrGLIndexBuffer::bufferID() const {
+    return fBufferID;
+}
+
+GrGLIndexBuffer::~GrGLIndexBuffer() {
+    // make sure we've not been abandoned
+    if (fBufferID) {
+        fGL->notifyIndexBufferDelete(this);
+        GR_GL(DeleteBuffers(1, &fBufferID));
+    }
+}
+
+void GrGLIndexBuffer::abandon() {
+    fBufferID = 0; 
+    fGL = NULL;
+    fLockPtr = NULL;
+}
+
+void* GrGLIndexBuffer::lock() {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    if (fGL->supportsBufferLocking()) {
+        GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, fBufferID));
+        fGL->notifyIndexBufferBind(this);
+        // call bufferData with null ptr to allow driver to perform renaming
+        // If this call is removed revisit updateData to be sure it doesn't
+        // leave buffer undersized (as it currently does).
+        GR_GL(BufferData(GL_ELEMENT_ARRAY_BUFFER, size(), NULL, 
+                         dynamic() ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW));
+        fLockPtr = GR_GLEXT(fGL->extensions(),
+                            MapBuffer(GL_ELEMENT_ARRAY_BUFFER, GR_WRITE_ONLY));
+
+        return fLockPtr;
+    }
+    return NULL;
+}
+
+void GrGLIndexBuffer::unlock() {
+    GrAssert(fBufferID);
+    GrAssert(isLocked());
+
+    if (fGL->supportsBufferLocking()) {
+        GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, fBufferID));
+        fGL->notifyIndexBufferBind(this);
+        GR_GLEXT(fGL->extensions(),
+                 UnmapBuffer(GL_ELEMENT_ARRAY_BUFFER));
+        fLockPtr = NULL;
+    }
+}
+
+bool GrGLIndexBuffer::isLocked() const {
+    GrAssert(fBufferID);
+#if GR_DEBUG
+    if (fGL->supportsBufferLocking()) {
+        GLint mapped;
+        GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, fBufferID));
+        fGL->notifyIndexBufferBind(this);
+        GR_GL(GetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, 
+                                   GR_BUFFER_MAPPED, &mapped));
+        GrAssert(!!mapped == !!fLockPtr);
+    }
+#endif
+    return NULL != fLockPtr;
+}
+
+bool GrGLIndexBuffer::updateData(const void* src, uint32_t srcSizeInBytes) {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    if (srcSizeInBytes > size()) {
+        return false;
+    }
+    GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, fBufferID));
+    fGL->notifyIndexBufferBind(this);
+    GR_GL(BufferData(GL_ELEMENT_ARRAY_BUFFER, srcSizeInBytes, src, 
+                     dynamic() ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW));
+    return true;
+}
+
diff --git a/gpu/src/GrGLTexture.cpp b/gpu/src/GrGLTexture.cpp
new file mode 100644
index 0000000..b75cad5
--- /dev/null
+++ b/gpu/src/GrGLTexture.cpp
@@ -0,0 +1,174 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrGLTexture.h"
+#include "GrGpuGL.h"
+
+GrGLRenderTarget::GrGLRenderTarget(const GLRenderTargetIDs& ids, 
+                                   const GrIRect& viewport,
+                                   GrGLTexture* texture,
+                                   GrGpuGL* gl) : INHERITED(texture) {
+    fGL                     = gl;
+    fRTFBOID                = ids.fRTFBOID;
+    fTexFBOID               = ids.fTexFBOID;
+    fStencilRenderbufferID  = ids.fStencilRenderbufferID;
+    fMSColorRenderbufferID  = ids.fMSColorRenderbufferID;
+    fNeedsResolve           = false;
+    fViewport               = viewport;
+    fOwnIDs                 = ids.fOwnIDs;
+    // viewport should be GL's viewport with top >= bottom
+    GrAssert(viewport.height() <= 0);
+}
+
+GrGLRenderTarget::~GrGLRenderTarget() {
+    fGL->notifyRenderTargetDelete(this);
+    if (fOwnIDs) {
+        if (fTexFBOID) {
+            GR_GLEXT(fGL->extensions(), DeleteFramebuffers(1, &fTexFBOID));
+        }
+        if (fRTFBOID && fRTFBOID != fTexFBOID) {
+            GR_GLEXT(fGL->extensions(), DeleteFramebuffers(1, &fRTFBOID));
+        }
+        if (fStencilRenderbufferID) {
+            GR_GLEXT(fGL->extensions(), DeleteRenderbuffers(1, &fStencilRenderbufferID));
+        }
+        if (fMSColorRenderbufferID) {
+            GR_GLEXT(fGL->extensions(), DeleteRenderbuffers(1, &fMSColorRenderbufferID));
+        }
+    }
+}
+
+void GrGLRenderTarget::abandon() {
+    fRTFBOID                = 0;
+    fTexFBOID               = 0;
+    fStencilRenderbufferID  = 0;
+    fMSColorRenderbufferID  = 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+const GLenum GrGLTexture::gWrapMode2GLWrap[] = {
+    GL_CLAMP_TO_EDGE,
+    GL_REPEAT,
+#ifdef GL_MIRRORED_REPEAT
+    GL_MIRRORED_REPEAT
+#else
+    GL_REPEAT       // GL_MIRRORED_REPEAT not supported :(
+#endif
+};
+
+
+GrGLTexture::GrGLTexture(const GLTextureDesc& textureDesc,
+                         const GLRenderTargetIDs& rtIDs,
+                         GrGpuGL* gl) :
+        INHERITED(textureDesc.fContentWidth, 
+                  textureDesc.fContentHeight, 
+                  textureDesc.fAllocWidth, 
+                  textureDesc.fAllocHeight,
+                  textureDesc.fFormat),
+        fTextureID(textureDesc.fTextureID),
+        fUploadFormat(textureDesc.fUploadFormat),
+        fUploadByteCount(textureDesc.fUploadByteCount),
+        fUploadType(textureDesc.fUploadType),
+        fOrientation(textureDesc.fOrientation),
+        fRenderTarget(NULL),
+        fGpuGL(gl) {
+
+    GrAssert(0 != textureDesc.fTextureID);
+
+    if (rtIDs.fTexFBOID) {
+        GrIRect vp;
+        vp.fLeft   = 0;
+        vp.fRight  = (int32_t) textureDesc.fContentWidth;
+        // viewport for GL is top > bottom 
+        vp.fTop    = (int32_t) textureDesc.fAllocHeight;
+        vp.fBottom = (int32_t) textureDesc.fAllocHeight - 
+                     (int32_t)textureDesc.fContentHeight;
+        fRenderTarget = new GrGLRenderTarget(rtIDs, vp, this, gl);
+    }
+
+    fSamplerState.setClampNoFilter();
+    
+    GR_GL(BindTexture(GL_TEXTURE_2D, fTextureID));
+    gl->notifyTextureBind(this);
+    GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+    GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+    GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+    GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+}
+
+GrGLTexture::~GrGLTexture() {
+    // make sure we haven't been abandoned
+    if (fTextureID) {
+        fGpuGL->notifyTextureDelete(this);
+        GR_GL(DeleteTextures(1, &fTextureID));
+    }
+    delete fRenderTarget;
+}
+
+void GrGLTexture::abandon() {
+    fTextureID = 0;
+    if (NULL != fRenderTarget) {
+    	fRenderTarget->abandon();
+    }
+}
+
+bool GrGLTexture::isRenderTarget() const {
+    return NULL != fRenderTarget;
+}
+
+GrRenderTarget* GrGLTexture::asRenderTarget() {
+    return (GrRenderTarget*)fRenderTarget;
+}
+
+void GrGLTexture::removeRenderTarget() {
+    GrAssert(NULL != fRenderTarget);
+    if (NULL != fRenderTarget) {
+        // must do this notify before the delete
+        fGpuGL->notifyTextureRemoveRenderTarget(this);
+        delete fRenderTarget;
+        fRenderTarget = NULL;        
+    }
+}
+
+void GrGLTexture::uploadTextureData(uint32_t x,
+                                    uint32_t y,
+                                    uint32_t width,
+                                    uint32_t height,
+                                    const void* srcData) {
+    // glCompressedTexSubImage2D doesn't support any formats
+    // (at least without extensions)
+    GrAssert(fUploadFormat != GR_PALETTE8_RGBA8);
+
+    // If we need to update textures that are created upside down
+    // then we have to modify this code to flip the srcData
+    GrAssert(kTopDown_Orientation == fOrientation);
+    GR_GL(BindTexture(GL_TEXTURE_2D, fTextureID));
+    fGpuGL->notifyTextureBind(this);
+    GR_GL(PixelStorei(GL_UNPACK_ALIGNMENT, fUploadByteCount));
+    GR_GL(TexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, 
+                        fUploadFormat, fUploadType, srcData));
+
+}
+
+intptr_t GrGLTexture::getTextureHandle() {
+    return fTextureID;
+}
+
+
+
diff --git a/gpu/src/GrGLVertexBuffer.cpp b/gpu/src/GrGLVertexBuffer.cpp
new file mode 100644
index 0000000..b4ddc24
--- /dev/null
+++ b/gpu/src/GrGLVertexBuffer.cpp
@@ -0,0 +1,103 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrGLVertexBuffer.h"
+#include "GrGpuGL.h"
+
+GrGLVertexBuffer::GrGLVertexBuffer(GLuint id, GrGpuGL* gl, uint32_t sizeInBytes,
+                                   bool dynamic) : 
+                                   INHERITED(sizeInBytes, dynamic),
+                                   fGL(gl),
+                                   fBufferID(id),
+                                   fLockPtr(NULL) {
+}
+
+GrGLVertexBuffer::~GrGLVertexBuffer() {
+    // make sure we've not been abandoned
+    if (fBufferID) {
+        fGL->notifyVertexBufferDelete(this);
+        GR_GL(DeleteBuffers(1, &fBufferID));
+    }
+}
+
+GLuint GrGLVertexBuffer::bufferID() const {
+    return fBufferID;
+}
+
+void GrGLVertexBuffer::abandon() { 
+    fBufferID = 0;
+    fGL = NULL;
+    fLockPtr = NULL;
+}
+
+void* GrGLVertexBuffer::lock() {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    if (fGL->supportsBufferLocking()) {
+        GR_GL(BindBuffer(GL_ARRAY_BUFFER, fBufferID));
+        fGL->notifyVertexBufferBind(this);
+        // call bufferData with null ptr to allow driver to perform renaming
+        // If this call is removed revisit updateData to be sure it doesn't
+        // leave buffer undersized (as it currently does).
+        GR_GL(BufferData(GL_ARRAY_BUFFER, size(), NULL, 
+                         dynamic() ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW));
+        fLockPtr = GR_GLEXT(fGL->extensions(),
+                            MapBuffer(GL_ARRAY_BUFFER, GR_WRITE_ONLY)); 
+        return fLockPtr;
+    }
+    return NULL;
+}
+
+void GrGLVertexBuffer::unlock() {
+    GrAssert(fBufferID);
+    GrAssert(isLocked());
+    if (fGL->supportsBufferLocking()) {
+        GR_GL(BindBuffer(GL_ARRAY_BUFFER, fBufferID));
+        fGL->notifyVertexBufferBind(this);
+        GR_GLEXT(fGL->extensions(),
+                 UnmapBuffer(GL_ARRAY_BUFFER));
+        fLockPtr = NULL;
+    }
+}
+
+bool GrGLVertexBuffer::isLocked() const {
+    GrAssert(fBufferID);
+#if GR_DEBUG
+    if (fGL->supportsBufferLocking()) {
+        GLint mapped;
+        GR_GL(BindBuffer(GL_ARRAY_BUFFER, fBufferID));
+        fGL->notifyVertexBufferBind(this);
+        GR_GL(GetBufferParameteriv(GL_ARRAY_BUFFER, GR_BUFFER_MAPPED, &mapped));
+        GrAssert(!!mapped == !!fLockPtr);
+    }
+#endif
+    return NULL != fLockPtr;
+}
+
+bool GrGLVertexBuffer::updateData(const void* src, uint32_t srcSizeInBytes) {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    if (srcSizeInBytes > size()) {
+        return false;
+    }
+    GR_GL(BindBuffer(GL_ARRAY_BUFFER, fBufferID));
+    fGL->notifyVertexBufferBind(this);
+    GR_GL(BufferData(GL_ARRAY_BUFFER, srcSizeInBytes, src, 
+                     dynamic() ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW));
+    return true;
+}
+
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
new file mode 100644
index 0000000..c6340bd
--- /dev/null
+++ b/gpu/src/GrGpu.cpp
@@ -0,0 +1,343 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrGpu.h"
+#include "GrMemory.h"
+#include "GrTextStrike.h"
+#include "GrTextureCache.h"
+#include "GrClipIterator.h"
+#include "GrIndexBuffer.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+size_t GrTexture::BytesPerPixel(PixelConfig config) {
+    switch (config) {
+        case kAlpha_8_PixelConfig:
+        case kIndex_8_PixelConfig:
+            return 1;
+        case kRGB_565_PixelConfig:
+        case kRGBA_4444_PixelConfig:
+            return 2;
+        case kRGBA_8888_PixelConfig:
+        case kRGBX_8888_PixelConfig:
+            return 4;
+        default:
+            return 0;
+    }
+}
+
+bool GrTexture::PixelConfigIsOpaque(PixelConfig config) {
+    switch (config) {
+        case GrTexture::kRGB_565_PixelConfig:
+        case GrTexture::kRGBX_8888_PixelConfig:
+            return true;
+        default:
+            return false;
+    }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern void gr_run_unittests();
+
+GrGpu::GrGpu() : f8bitPaletteSupport(false),
+                 fNPOTTextureSupport(kNone_NPOTTextureType),
+                 fQuadIndexBuffer(NULL) {
+#if GR_DEBUG
+//    gr_run_unittests();
+#endif
+    resetStats();
+}
+
+GrGpu::~GrGpu() {
+    if (NULL != fQuadIndexBuffer) {
+        fQuadIndexBuffer->unref();
+    }
+}
+
+void GrGpu::resetContext() {
+}
+
+void GrGpu::unimpl(const char msg[]) {
+//    GrPrintf("--- GrGpu unimplemented(\"%s\")\n", msg);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool GrGpu::canDisableBlend() const {
+    if ((kOne_BlendCoeff == fCurrDrawState.fSrcBlend) &&
+        (kZero_BlendCoeff == fCurrDrawState.fDstBlend)) {
+            return true;
+    }
+
+    // If we have vertex color without alpha then we can't force blend off
+    if ((fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit) ||
+         0xff != GrColorUnpackA(fCurrDrawState.fColor)) {
+        return false;
+    }
+
+    // If the src coef will always be 1...
+    bool fullSrc = kSA_BlendCoeff == fCurrDrawState.fSrcBlend ||
+                   kOne_BlendCoeff == fCurrDrawState.fSrcBlend;
+
+    // ...and the dst coef is always 0...
+    bool noDst = kISA_BlendCoeff == fCurrDrawState.fDstBlend ||
+                 kZero_BlendCoeff == fCurrDrawState.fDstBlend;
+
+    // ...and there isn't a texture with an alpha channel...
+    bool noTexAlpha = !VertexHasTexCoords(fGeometrySrc.fVertexLayout)  ||
+        fCurrDrawState.fTexture->config() == GrTexture::kRGB_565_PixelConfig ||
+        fCurrDrawState.fTexture->config() == GrTexture::kRGBX_8888_PixelConfig;
+
+    // ...then we disable blend.
+    return fullSrc && noDst && noTexAlpha;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const int MAX_QUADS = 512; // max possible: (1 << 14) - 1;
+
+GR_STATIC_ASSERT(4 * MAX_QUADS <= UINT16_MAX);
+
+static inline void fillIndices(uint16_t* indices, int quadCount) {
+    for (int i = 0; i < quadCount; ++i) {
+        indices[6 * i + 0] = 4 * i + 0;
+        indices[6 * i + 1] = 4 * i + 1;
+        indices[6 * i + 2] = 4 * i + 2;
+        indices[6 * i + 3] = 4 * i + 0;
+        indices[6 * i + 4] = 4 * i + 2;
+        indices[6 * i + 5] = 4 * i + 3;
+    }
+}
+
+const GrIndexBuffer* GrGpu::quadIndexBuffer() const {
+    if (NULL == fQuadIndexBuffer) {
+        static const int SIZE = sizeof(uint16_t) * 6 * MAX_QUADS;
+        GrGpu* me = const_cast<GrGpu*>(this);
+        fQuadIndexBuffer = me->createIndexBuffer(SIZE, false);
+        if (NULL != fQuadIndexBuffer) {
+            uint16_t* indices = (uint16_t*)fQuadIndexBuffer->lock();
+            if (NULL != indices) {
+                fillIndices(indices, MAX_QUADS);
+                fQuadIndexBuffer->unlock();
+            } else {
+                indices = (uint16_t*)GrMalloc(SIZE);
+                fillIndices(indices, MAX_QUADS);
+                if (!fQuadIndexBuffer->updateData(indices, SIZE)) {
+                    fQuadIndexBuffer->unref();
+                    fQuadIndexBuffer = NULL;
+                    GrAssert(!"Can't get indices into buffer!");
+                }
+                GrFree(indices);
+            }
+        }
+    }
+
+    return fQuadIndexBuffer;
+}
+
+int GrGpu::maxQuadsInIndexBuffer() const {
+    return (NULL == this->quadIndexBuffer()) ? 0 : MAX_QUADS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGpu::clipWillChange(const GrClip& clip) {
+    if (clip != fClip) {
+        fClipState.fClipIsDirty = true;
+    }
+}
+
+bool GrGpu::setupClipAndFlushState(PrimitiveType type) {
+    const GrIRect* r = NULL;
+
+    if (fCurrDrawState.fFlagBits & kClip_StateBit) {
+        fClipState.fClipInStencil = fClip.countRects() > 1;
+
+        if (fClipState.fClipInStencil &&
+            (fClipState.fClipIsDirty ||
+             fClipState.fStencilClipTarget != fCurrDrawState.fRenderTarget)) {
+
+            AutoStateRestore asr(this);
+            AutoGeometrySrcRestore agsr(this);
+            this->disableState(kClip_StateBit);
+            eraseStencilClip();
+
+            int rectTotal = fClip.countRects();
+            static const int PtsPerRect = 4;
+            // this may be called while geometry is already reserved by the
+            // client. So we use our own vertex array where we avoid malloc
+            // if we have 4 or fewer rects.
+            GrAutoSTMalloc<PtsPerRect * 4, GrPoint> vertices(PtsPerRect *
+                                                             rectTotal);
+            this->setVertexSourceToArray(vertices.get(), 0);
+            int currRect = 0;
+            while (currRect < rectTotal) {
+                int rectCount = GrMin(this->maxQuadsInIndexBuffer(),
+                                      rectTotal - currRect);
+
+                GrPoint* verts = (GrPoint*)vertices +
+                                 (currRect * PtsPerRect);
+
+                for (int i = 0; i < rectCount; i++) {
+                    GrRect r(fClip.getRects()[i + currRect]);
+                    verts = r.setRectFan(verts);
+                }
+                this->setIndexSourceToBuffer(quadIndexBuffer());
+
+                this->setViewMatrix(GrMatrix::I());
+                this->setStencilPass((GrDrawTarget::StencilPass)kSetClip_StencilPass);
+                this->drawIndexed(GrGpu::kTriangles_PrimitiveType,
+                                  currRect * PtsPerRect, 0,
+                                  rectCount * PtsPerRect, rectCount * 6);
+
+                currRect += rectCount;
+            }
+            fClipState.fStencilClipTarget = fCurrDrawState.fRenderTarget;
+        }
+        fClipState.fClipIsDirty = false;
+        if (!fClipState.fClipInStencil) {
+            r = &fClip.getBounds();
+        }
+    }
+    // Must flush the scissor after graphics state
+    if (!flushGraphicsState(type)) {
+        return false;
+    }
+    flushScissor(r);
+    return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGpu::drawIndexed(PrimitiveType type,
+                        uint32_t startVertex,
+                        uint32_t startIndex,
+                        uint32_t vertexCount,
+                        uint32_t indexCount) {
+    GrAssert(kReserved_GeometrySrcType != fGeometrySrc.fVertexSrc ||
+             fReservedGeometry.fLocked);
+    GrAssert(kReserved_GeometrySrcType != fGeometrySrc.fIndexSrc ||
+             fReservedGeometry.fLocked);
+
+    if (!setupClipAndFlushState(type)) {
+        return;
+    }
+
+#if GR_COLLECT_STATS
+    fStats.fVertexCnt += vertexCount;
+    fStats.fIndexCnt  += indexCount;
+    fStats.fDrawCnt   += 1;
+#endif
+
+    setupGeometry(startVertex, startIndex, vertexCount, indexCount);
+
+    drawIndexedHelper(type, startVertex, startIndex,
+                      vertexCount, indexCount);
+}
+
+void GrGpu::drawNonIndexed(PrimitiveType type,
+                           uint32_t startVertex,
+                           uint32_t vertexCount) {
+    GrAssert(kReserved_GeometrySrcType != fGeometrySrc.fVertexSrc ||
+             fReservedGeometry.fLocked);
+
+    if (!setupClipAndFlushState(type)) {
+        return;
+    }
+#if GR_COLLECT_STATS
+    fStats.fVertexCnt += vertexCount;
+    fStats.fDrawCnt   += 1;
+#endif
+
+    setupGeometry(startVertex, 0, vertexCount, 0);
+
+    drawNonIndexedHelper(type, startVertex, vertexCount);
+}
+
+bool GrGpu::acquireGeometryHelper(GrVertexLayout vertexLayout,
+                                  void**         vertices,
+                                  void**         indices) {
+    GrAssert((fReservedGeometry.fVertexCount == 0) ||
+             (NULL != vertices));
+    if (NULL != vertices) {
+        *vertices = fVertices.realloc(VertexSize(vertexLayout) *
+                                      fReservedGeometry.fVertexCount);
+        if (!*vertices && fReservedGeometry.fVertexCount) {
+            return false;
+        }
+    }
+    GrAssert((fReservedGeometry.fIndexCount == 0) ||
+             (NULL != indices));
+    if (NULL != indices) {
+        *indices =  fIndices.realloc(sizeof(uint16_t) *
+                                     fReservedGeometry.fIndexCount);
+        if (!*indices && fReservedGeometry.fIndexCount) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void GrGpu::releaseGeometryHelper() {
+    return;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const GrGpu::Stats& GrGpu::getStats() const {
+    return fStats;
+}
+
+void GrGpu::resetStats() {
+    memset(&fStats, 0, sizeof(fStats));
+}
+
+void GrGpu::printStats() const {
+    if (GR_COLLECT_STATS) {
+     GrPrintf(
+     "-v-------------------------GPU STATS----------------------------v-\n"
+     "Stats collection is: %s\n"
+     "Draws: %04d, Verts: %04d, Indices: %04d\n"
+     "ProgChanges: %04d, TexChanges: %04d, RTChanges: %04d\n"
+     "TexCreates: %04d, RTCreates:%04d\n"
+     "-^--------------------------------------------------------------^-\n",
+     (GR_COLLECT_STATS ? "ON" : "OFF"),
+    fStats.fDrawCnt, fStats.fVertexCnt, fStats.fIndexCnt,
+    fStats.fProgChngCnt, fStats.fTextureChngCnt, fStats.fRenderTargetChngCnt,
+    fStats.fTextureCreateCnt, fStats.fRenderTargetCreateCnt);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrTexture::~GrTexture() {
+    // use this to set a break-point if needed
+//    Gr_clz(3);
+}
+
+const GrSamplerState GrSamplerState::gClampNoFilter(
+    GrSamplerState::kClamp_WrapMode,
+    GrSamplerState::kClamp_WrapMode,
+    GrSamplerState::kNormal_SampleMode,
+    false);
+
+
+
+
diff --git a/gpu/src/GrGpuD3D9.cpp b/gpu/src/GrGpuD3D9.cpp
new file mode 100644
index 0000000..868097d
--- /dev/null
+++ b/gpu/src/GrGpuD3D9.cpp
@@ -0,0 +1,1484 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrGpuD3D9.h"
+#include "GrGpuVertex.h"
+
+void d3dCheckErr(HRESULT hr) {
+    GrAssert(SUCCEEDED(hr));
+}
+
+#if GR_DEBUG
+    #define GR_D3D9(OBJ, X) d3dCheckErr(OBJ-> X);
+#else
+    #define GR_D3D9(OBJ, X) OBJ-> X;
+#endif
+
+#if GR_SCALAR_IS_FIXED
+    //mobile d3d allows 3 component fixed point verts
+    #error "fixed is unsupported in D3D9"
+#elif GR_SCALAR_IS_FLOAT
+    #define FVF_POS_TYPE D3DFVF_XYZ
+    #define FVF_TEX_TYPE (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(1))
+#else
+    #error "unknown GPU type"
+#endif
+
+#define FVF_COL_TYPE D3DFVF_DIFFUSE
+
+#if GR_TEXT_SCALAR_IS_FIXED
+    //mobile d3d allows 3 component fixed point verts
+    #error "fixed is unsupported in D3D9"
+#elif GR_TEXT_SCALAR_IS_FLOAT
+    #define FVF_POS_TYPE_TEXT D3DFVF_XYZ
+    #define FVF_TEX_TYPE_TEXT (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(1))
+#elif GR_TEXT_SCALAR_IS_USHORT
+    #error "positions must be float in fixed-pipe D3D9"
+#else
+    #error "unknown GPU text type"
+#endif
+
+static const int STAGE_NUM_VERTS = 512;
+static const int STAGE_VERTEX_SIZE = sizeof(GrPoint)*STAGE_NUM_VERTS;
+static const int STAGE_INDEX_SIZE  = 2*STAGE_NUM_VERTS*2*3; 
+                                  // 2 bytes per index * 
+                                  // 2 triangles per vert (euler char) * 3
+                                  // 3 indices/triangle
+
+static const D3DTRANSFORMSTATETYPE gMatrixMode2D3D9Matrix[] = {
+    D3DTS_WORLD,     // kModelView_MatrixMode
+    D3DTS_TEXTURE0,  // kTexture_MatrixMode
+};
+
+static const D3DPRIMITIVETYPE gPrimType2D3D9PrimType[] = {
+    D3DPT_TRIANGLELIST,
+    D3DPT_TRIANGLESTRIP,
+    D3DPT_TRIANGLEFAN,
+    D3DPT_POINTLIST,
+    D3DPT_LINELIST,
+    D3DPT_LINESTRIP,
+};
+
+const GrGpuD3D9::VertDecls GrGpuD3D9::gVertFlags2VertDeclIdx[] = {
+    kPosOnly_VertDecl,              // no flags
+    kTex_VertDecl,                  // kTexCoord_VertFlag
+    kColors_VertDecl,               // kColors_VertFlag
+    kTexAndColors_VertDecl,         // kColors_VertFlag & kColors_VertFlag
+    kInvalid_VertDecl,              // kPositionAsTexCoord_VertFlag
+    kPosAsTex_VertDecl,             // kPositionAsTexCoord_VertFlag & kTexCoord_VertFlag
+    kInvalid_VertDecl,              // kPositionAsTexCoord_VertFlag & kColors_VertFlag
+    kPosAsTexAndColors_VertDecl     // kPositionAsTexCoord_VertFlag & kTexCoord_VertFlag & kColors_VertFlag
+};
+
+const DWORD GrGpuD3D9::gDeclToFVFs[] = {
+    FVF_POS_TYPE, // kPosOnly_VertDecl
+    FVF_POS_TYPE | FVF_TEX_TYPE, // kTex_VertDecl
+    FVF_POS_TYPE | FVF_COL_TYPE, // kColors_VertDecl
+    FVF_POS_TYPE | FVF_TEX_TYPE | FVF_COL_TYPE, // kTexAndColors_VertDecl
+    FVF_POS_TYPE, // kPosAsTex_VertDecl
+    FVF_POS_TYPE | FVF_COL_TYPE, // kPosAsTexAndColors_VertDecl
+};
+
+const DWORD GrGpuD3D9::gTextFVF = FVF_POS_TYPE_TEXT | FVF_TEX_TYPE_TEXT;
+
+#if   (SK_A32_SHIFT == 24) && (SK_R32_SHIFT == 16) && \
+      (SK_G32_SHIFT == 8) && (SK_B32_SHIFT == 0)
+    #define GR_D3D9_32BPP_COLOR_FORMAT D3DFMT_A8R8G8B8
+#elif (SK_A32_SHIFT == 24) && (SK_B32_SHIFT == 16) && \
+      (SK_G32_SHIFT == 8) && (SK_R32_SHIFT == 0)
+    #define GR_D3D9_32BPP_COLOR_FORMAT D3DFMT_A8B8G8R8
+#else
+    #error "Skia's 32bit color format is not understood by D3D9."
+#endif
+
+static const DWORD gXfermodeCoeff2Blend[] = {
+    D3DBLEND_ZERO,
+    D3DBLEND_ONE,
+    D3DBLEND_SRCCOLOR,
+    D3DBLEND_INVSRCCOLOR,
+    D3DBLEND_DESTCOLOR,
+    D3DBLEND_INVDESTCOLOR,
+    D3DBLEND_SRCALPHA,
+    D3DBLEND_INVSRCALPHA,
+    D3DBLEND_DESTALPHA,
+    D3DBLEND_INVDESTALPHA,
+};
+
+static const DWORD gTileMode2D3D9Wrap[] = {
+    D3DTADDRESS_CLAMP,
+    D3DTADDRESS_WRAP,
+    D3DTADDRESS_MIRROR
+};
+
+static bool can_be_texture(GrTexture::PixelConfig config, D3DFORMAT* format) {
+    switch (config) {
+        case GrTexture::kRGBA_8888_PixelConfig:
+            *format = GR_D3D9_32BPP_COLOR_FORMAT;
+            break;
+        case GrTexture::kRGB_565_PixelConfig:
+            *format = D3DFMT_R5G6B5;
+            break;
+        case GrTexture::kRGBA_4444_PixelConfig:
+            *format = D3DFMT_A4R4G4B4;
+            break;
+        case GrTexture::kIndex_8_PixelConfig:
+            // we promote index to argb32
+            *format = GR_D3D9_32BPP_COLOR_FORMAT;
+            break;
+        case GrTexture::kAlpha_8_PixelConfig:
+            *format = D3DFMT_A8;
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+
+static int format_bytes(D3DFORMAT format) {
+    switch (format) {
+        case GR_D3D9_32BPP_COLOR_FORMAT:
+            return 4;
+        case D3DFMT_R5G6B5:
+            return 2;
+        case D3DFMT_A4R4G4B4:
+            return 2;
+        case D3DFMT_A8:
+            return 1;
+        default:
+            GrAssert(!"Unexpected D3D format!");
+            return 0;
+    }
+}
+
+uint32_t vertex_to_primitive_count(GrGpu::PrimitiveTypes type, 
+                                   uint32_t vertexCount) {
+    switch (type) {
+        case GrGpu::kTriangles_PrimitiveType:
+            return vertexCount / 3;
+        case GrGpu::kTriangleStrip_PrimitiveType: // fallthru
+        case GrGpu::kTriangleFan_PrimitiveType:
+            return vertexCount > 2 ? vertexCount - 2 : 0;
+        case GrGpu::kPoints_PrimitiveType:
+            return vertexCount;
+        case GrGpu::kLines_PrimitiveType:
+            return vertexCount / 2;
+        case GrGpu::kLineStrip_PrimitiveType:
+            return vertexCount > 1 ? vertexCount - 1 : 0;
+        default:
+            GrAssert(!"Unknown primitive type!");
+            return 0;
+    }
+}
+
+void gr_matrix_to_d3d_matrix(D3DMATRIX* d3dmat, GrMatrix& grmat) {
+    d3dmat->_11 = grmat[GrMatrix::kScaleX];
+    d3dmat->_21 = grmat[GrMatrix::kSkewX];
+    d3dmat->_31 = 0;
+    d3dmat->_41 = grmat[GrMatrix::kTransX];
+
+    d3dmat->_12 = grmat[GrMatrix::kSkewY];
+    d3dmat->_22 = grmat[GrMatrix::kScaleY];
+    d3dmat->_32 = 0;
+    d3dmat->_42 = grmat[GrMatrix::kTransY];
+    
+    d3dmat->_13 = 0;
+    d3dmat->_23 = 0;
+    d3dmat->_33 = 1;
+    d3dmat->_43 = 0;
+
+    d3dmat->_14 = grmat[GrMatrix::kPersp0];
+    d3dmat->_24 = grmat[GrMatrix::kPersp1];
+    d3dmat->_34 = 0;
+    d3dmat->_44 = grmat[GrMatrix::kPersp2];
+}
+
+bool color_and_stencil_compatible(const D3DSURFACE_DESC& rtDesc, 
+                                  const D3DSURFACE_DESC& dsDesc) {
+    return (rtDesc.Width  <= dsDesc.Width)  &&
+           (rtDesc.Height <= dsDesc.Height) &&
+           (rtDesc.MultiSampleType == dsDesc.MultiSampleType) &&
+           (rtDesc.MultiSampleQuality == dsDesc.MultiSampleQuality);
+}
+
+int format_stencil_bits(D3DFORMAT format) {
+    switch (format) {
+    case D3DFMT_D24S8:
+    case D3DFMT_D24FS8:
+    case D3DFMT_S8_LOCKABLE:
+        return 8;
+    }
+    return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrGpuD3D9::GrGpuD3D9(IDirect3DDevice9* device) : GrGpu(), fDevice(device) {
+    GrPrintf("----------------------- create GrGpuD3D9 %p --------------\n", this);
+
+    fDeviceEx = NULL;
+    fDevice->QueryInterface(__uuidof(::IDirect3DDevice9Ex), (void**)&fDeviceEx);
+
+    fLastBlendOff = false;
+    GR_D3D9(fDevice, SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE));
+
+    GR_D3D9(fDevice, SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE));
+    GR_D3D9(fDevice, SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE));
+    GR_D3D9(fDevice, SetRenderState(D3DRS_LIGHTING, FALSE));
+    GR_D3D9(fDevice, SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE));
+    GR_D3D9(fDevice, SetRenderState(D3DRS_COLORVERTEX, TRUE));
+
+    fLastVertexState.fFlagBits = 0;
+    GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_SELECTARG2));
+    GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG2));
+    GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE));
+    GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE));
+    GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CONSTANT));
+    GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_CONSTANT));
+
+    fLastVertFVF = -1;
+
+    fLastColorArg1 = D3DTA_TEXTURE;
+
+    fLastDrawState.fSamplerState.fFilter = false;
+    GR_D3D9(fDevice, SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT));
+    GR_D3D9(fDevice, SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT));
+    GR_D3D9(fDevice, SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE));
+
+    fNextDrawState.fSamplerState.fWrapX = (GrGpu::WrapModes)-1; // illegal
+    fNextDrawState.fSamplerState.fWrapY = (GrGpu::WrapModes)-1; // illegal
+
+    GR_D3D9(fDevice, SetPixelShader(NULL));
+    GR_D3D9(fDevice, SetVertexShader(NULL));
+
+    fLastDrawState.fFlagBits = 0;    
+    GR_D3D9(fDevice, SetRenderState(D3DRS_DITHERENABLE, FALSE));
+    GR_D3D9(fDevice, SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, FALSE));
+
+    // illegal values
+    fLastDrawState.fSrcBlend = (BlendCoeff)-1;
+    fLastDrawState.fDstBlend = (BlendCoeff)-1;
+    fLastDrawState.fColor = GrColor_ILLEGAL;
+    fLastDrawState.fPointSize = fLastDrawState.fLineWidth = -1;
+    fLastDrawState.fTexture = NULL;
+    
+    GR_D3D9(fDevice,GetRenderTarget(0,&fDefaultRenderTarget.fColor));
+    fLastDrawState.fRenderTarget = (GrRenderTarget*) &fDefaultRenderTarget;
+    GrAssert(NULL != fDefaultRenderTarget.fColor);
+
+    // We need a stencil buffer to do path rendering.
+    D3DSURFACE_DESC rtDesc;
+    GR_D3D9(fDefaultRenderTarget.fColor, GetDesc(&rtDesc));
+    fDefaultRenderTarget.fStencil = NULL;
+    GR_D3D9(fDevice, GetDepthStencilSurface(&fDefaultRenderTarget.fStencil));
+    // make sure any existing depth stencil is compatible with the rendertarget
+    // and has at least 8 bits of stencil
+    if (NULL != fDefaultRenderTarget.fStencil) {
+        D3DSURFACE_DESC dsDesc;
+        GR_D3D9(fDefaultRenderTarget.fStencil, GetDesc(&dsDesc));
+        if (!color_and_stencil_compatible(rtDesc, dsDesc) ||
+            format_stencil_bits(dsDesc.Format) < 8) {
+            fDefaultRenderTarget.fStencil = NULL;
+        } else {
+            // add a ref so that we can safely Release in destructor
+            fDefaultRenderTarget.fStencil->AddRef();
+        }
+    }    
+    if (NULL == fDefaultRenderTarget.fStencil) {
+        fDefaultRenderTarget.fStencil = createStencil(rtDesc.Width, 
+                                                      rtDesc.Height,
+                                                      rtDesc.MultiSampleType,
+                                                      rtDesc.MultiSampleQuality);
+        GrAssert(NULL != fDefaultRenderTarget.fStencil);
+        GR_D3D9(fDevice, SetDepthStencilSurface(fDefaultRenderTarget.fStencil));
+    }
+
+    fLastDrawState.fScissorRect.setEmpty();
+    RECT rect;
+    rect.left = rect.right = rect.top = rect.bottom = 0;
+    GR_D3D9(fDevice,SetScissorRect(&rect));
+    
+    D3DMATRIX identity;
+    memset(&identity, 0, sizeof(identity));
+    identity._11 = identity._22 = identity._33 = identity._44 = 1.f;
+    for (int i = 0; i < kMatrixModeCount; i++) {
+        fLastDrawState.fMatrixModeCache[i].setIdentity();
+        GR_D3D9(fDevice, SetTransform(gMatrixMode2D3D9Matrix[i], &identity));
+    }
+    GR_D3D9(fDevice, 
+        SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2));
+    GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0));
+    fLastTexGen = false;
+
+    fLastDrawState.fViewportW = -1;
+    fLastDrawState.fViewportH = -1;
+
+    GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILENABLE, FALSE));
+    GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILMASK, 0xffffffff));
+    GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff));
+    GR_D3D9(fDevice, SetRenderState(D3DRS_COLORWRITEENABLE, 0xf));
+    fLastDrawState.fDrawMode = kOther_DrawMode;
+    fLastDrawState.fPathPass = (PathPass)-1;
+    fLastDrawState.fReverseFill = false;
+     
+    fNextDrawState = fLastDrawState;
+    fNextVertexState = fLastVertexState;
+
+    fLastIndexBuffer = NULL;
+
+    fStageVBuffer = (GrD3D9VertexBuffer*) 
+                        this->createVertexBuffer(STAGE_VERTEX_SIZE, true);
+    GrAssert(NULL != fStageVBuffer);
+
+    fStageIBuffer = (GrD3D9IndexBuffer*) 
+                        this->createIndexBuffer(STAGE_INDEX_SIZE, true);
+    GrAssert(NULL != fStageIBuffer);
+
+    TextureDesc dummyDesc = {
+        0, 
+        kNone_AALevel, 
+        1, 
+        1, 
+        false,
+        GrTexture::kAlpha_8_PixelConfig
+    };
+    fDummyTexture = (GrD3D9Texture*) this->createTexture(dummyDesc, NULL);
+    GrAssert(NULL != fDummyTexture);
+
+    fNPOTTextureSupport = kFull_NPOTTextureType;
+    D3DCAPS9 caps;
+    GR_D3D9(fDevice, GetDeviceCaps(&caps));
+    fSingleStencilPassForWinding = 
+        0 != (caps.StencilCaps & D3DSTENCILCAPS_TWOSIDED);
+    GrAssert(D3DSTENCILOP_INVERT & caps.StencilCaps);
+    GrAssert(D3DSTENCILOP_INCR & caps.StencilCaps);
+    GrAssert(D3DSTENCILOP_DECR & caps.StencilCaps);
+    GrAssert(D3DSTENCILOP_ZERO & caps.StencilCaps);
+
+    // start off with all zeros, keep this after fNextDrawState assignment
+    eraseStencil();
+    fDefaultRenderTarget.fClearStencil = false;
+}
+
+GrGpuD3D9::~GrGpuD3D9() {
+    fStageVBuffer->unref();
+    fStageIBuffer->unref();
+    fDummyTexture->unref();
+    // Currently we are assuming that the default render target
+    // existed before our constructor was called. We don't ever create
+    // it and we never add a ref to it, so don't release.
+    // We do create a stencil buffer if needed, though.
+    fDefaultRenderTarget.fStencil->Release();
+}
+
+IDirect3DSurface9* GrGpuD3D9::createStencil(uint32_t width, 
+                                            uint32_t height,
+                                            D3DMULTISAMPLE_TYPE msType,
+                                            DWORD msQual) {
+    IDirect3DSurface9* dsSurface = NULL;
+    // Direct3D9 Ex adds a stencil only format.
+    if (NULL != fDeviceEx) {
+        GR_D3D9(fDeviceEx, CreateDepthStencilSurfaceEx(width, height,
+                                                       D3DFMT_S8_LOCKABLE, 
+                                                       msType, msQual, FALSE, 
+                                                       &dsSurface, NULL, 
+                                                       D3DUSAGE_DEPTHSTENCIL));
+        fDeviceEx->Release();
+    }
+    if (NULL == dsSurface) {
+        fDevice->CreateDepthStencilSurface(width, height, D3DFMT_D24S8, msType, 
+                                           msQual, FALSE, &dsSurface, NULL);
+    }
+    return dsSurface;
+}
+
+GrTexture* GrGpuD3D9::createTexture(const TextureDesc& desc,
+                                    const void* srcData) {
+    D3DFORMAT d3dformat;
+    bool renderTarget = (desc.fFlags & kRenderTarget_TextureFlag);
+    if (desc.fAALevel != kNone_AALevel  && renderTarget) {
+        GrPrintf("Requested AA RT/Tex but not yet implemented in D3D.");
+    }
+    if (can_be_texture(desc.fFormat, &d3dformat)) {
+        DWORD usage = desc.fDynamic ? D3DUSAGE_DYNAMIC : 0;
+        usage |= renderTarget ? D3DUSAGE_RENDERTARGET : 0;
+        IDirect3DTexture9* d3dTex = NULL;
+    
+        GR_D3D9(fDevice, CreateTexture(desc.fWidth, desc.fHeight, 1, usage, 
+                                       d3dformat, D3DPOOL_DEFAULT, &d3dTex, 
+                                       NULL));
+
+        // In D3D9 the depth-stencil can be larger but not smaller than the RT
+        IDirect3DSurface9* depthStencil = NULL;
+        D3DSURFACE_DESC dsDesc;
+        fDefaultRenderTarget.fStencil->GetDesc(&dsDesc);        
+        // check if existing depth stencil is compatible
+        if ((renderTarget) && 
+                ((desc.fWidth > dsDesc.Width) || 
+                 (desc.fHeight > dsDesc.Height) ||
+                 (dsDesc.MultiSampleType != D3DMULTISAMPLE_NONE))) {
+            depthStencil = createStencil(desc.fWidth, desc.fHeight, 
+                                         D3DMULTISAMPLE_NONE, 0);
+            GrAssert(NULL != depthStencil);
+        }
+        if (d3dTex) {
+            GrD3D9Texture* texture = new GrD3D9Texture(desc.fWidth, 
+                                                       desc.fHeight, 
+                                                       desc.fFormat,
+                                                       d3dTex, 
+                                                       depthStencil,
+                                                       true,
+                                                       this);
+            if (NULL != srcData) {
+                texture->uploadTextureData(0, 0, desc.fWidth, 
+                                           desc.fHeight, srcData);
+            }
+            return texture;
+        }
+    }
+    return NULL;
+}
+
+GrVertexBuffer* GrGpuD3D9::createVertexBuffer(uint32_t size, bool dynamic) {
+    DWORD usage = (dynamic & kRenderTarget_TextureFlag) ? D3DUSAGE_DYNAMIC : 0;
+    usage |= D3DUSAGE_WRITEONLY;
+    IDirect3DVertexBuffer9* vbuffer = NULL;
+    GR_D3D9(fDevice, CreateVertexBuffer(size, usage, 0, 
+                                        D3DPOOL_DEFAULT, &vbuffer, NULL));
+    if (vbuffer) {
+        return new GrD3D9VertexBuffer(size, dynamic, vbuffer, this);
+    }
+    return NULL;
+}
+
+GrIndexBuffer* GrGpuD3D9::createIndexBuffer(uint32_t size, bool dynamic) {
+    DWORD usage = (dynamic & kRenderTarget_TextureFlag) ? D3DUSAGE_DYNAMIC : 0;
+    usage |= D3DUSAGE_WRITEONLY;
+    IDirect3DIndexBuffer9* ibuffer = NULL;
+    GR_D3D9(fDevice, CreateIndexBuffer(size, usage, D3DFMT_INDEX16, 
+                                       D3DPOOL_DEFAULT, &ibuffer, NULL));
+    if (ibuffer) {
+        return new GrD3D9IndexBuffer(size, dynamic, ibuffer, this);
+    }
+    return NULL;
+}
+
+bool GrGpuD3D9::flushGraphicsState(PrimitiveTypes type) {
+    GrAssert(fNextDrawState.fViewportW != -1);
+
+    if (fNextDrawState.fDrawMode == kRadialTexture_DrawMode ||
+        fNextDrawState.fDrawMode == kSweepTexture_DrawMode ||
+        fNextDrawState.fDrawMode == kTwoPointRadialTexture_DrawMode) {
+        unimpl("Fixed pipe doesn't support radial/sweep gradient");
+        return false;
+    }
+
+    uint32_t stateDiff = fNextDrawState.fFlagBits ^ fLastDrawState.fFlagBits;
+
+    if (fLastDrawState.fRenderTarget != fNextDrawState.fRenderTarget) {
+        setRenderTargetImm();
+        GrD3D9RenderTarget& rt = *(GrD3D9RenderTarget*)fNextDrawState.fRenderTarget;
+        if (rt.fClearStencil) {
+            eraseStencil();
+            rt.fClearStencil = false;
+        }
+        // may need to change how AA is handled.
+        stateDiff |= (1 << kAntialias_StateFlag);
+    }
+
+    if (stateDiff)
+    {
+        if (stateDiff & (1<<kDither_StateFlag)) {
+            GR_D3D9(fDevice,SetRenderState(D3DRS_DITHERENABLE, 
+                    (fNextDrawState.fFlagBits & (1<<kDither_StateFlag)) ? 
+                        TRUE : 
+                        FALSE));
+        }
+        if (stateDiff & (1<<kAntialias_StateFlag)) {
+            DWORD aa = fNextDrawState.fFlagBits & (1<<kAntialias_StateFlag) ? 
+                                TRUE : FALSE;
+            GrD3D9RenderTarget& rt = *(GrD3D9RenderTarget*)fNextDrawState.fRenderTarget;
+            D3DSURFACE_DESC desc;
+            rt.fColor->GetDesc(&desc);
+            if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) {
+                GR_D3D9(fDevice, SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, aa));
+            } else {
+                GR_D3D9(fDevice, SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, aa));
+            }
+        }
+        fLastDrawState.fFlagBits = fNextDrawState.fFlagBits;
+    }
+    bool blendOff = canDisableBlend();
+    if (fLastBlendOff != blendOff) {
+        GR_D3D9(fDevice, SetRenderState(D3DRS_ALPHABLENDENABLE, 
+                         blendOff ? FALSE : TRUE));
+        fLastBlendOff = blendOff;
+    }
+    if (!blendOff) {
+        if (fLastDrawState.fSrcBlend != fNextDrawState.fSrcBlend) {
+            GR_D3D9(fDevice, SetRenderState(D3DRS_SRCBLEND, 
+                                 gXfermodeCoeff2Blend[fNextDrawState.fSrcBlend]));
+            fLastDrawState.fSrcBlend = fNextDrawState.fSrcBlend;
+        }
+        if (fLastDrawState.fDstBlend != fNextDrawState.fDstBlend) {
+            GR_D3D9(fDevice, SetRenderState(D3DRS_DESTBLEND, 
+                                gXfermodeCoeff2Blend[fNextDrawState.fDstBlend]));
+            fLastDrawState.fDstBlend = fNextDrawState.fDstBlend;
+        }
+    }
+
+    // bind texture and set sampler state
+    if (fNextVertexState.fFlagBits & (1 << kTexCoord_VertFlag)) {
+        GrD3D9Texture* nextTexture = (GrD3D9Texture*)fNextDrawState.fTexture;
+        if (NULL != nextTexture) {
+            if (fLastDrawState.fTexture != nextTexture) {
+                GR_D3D9(fDevice, SetTexture(0, nextTexture->texture()));
+                DWORD nextColorArg1 = nextTexture->format() == D3DFMT_A8 ?
+                                    (D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE) :
+                                    D3DTA_TEXTURE;                        
+                if (fLastColorArg1 != nextColorArg1) {                    
+                    GR_D3D9(fDevice, SetTextureStageState(0, 
+                                                          D3DTSS_COLORARG1, 
+                                                          nextColorArg1));
+                    fLastColorArg1 = nextColorArg1;
+                }
+                fLastDrawState.fTexture = nextTexture;
+            }
+
+            if (fLastDrawState.fSamplerState.fFilter != 
+                    fNextDrawState.fSamplerState.fFilter) {
+                DWORD filter = fNextDrawState.fSamplerState.fFilter ?
+                                                       D3DTEXF_LINEAR :
+                                                       D3DTEXF_POINT;
+                GR_D3D9(fDevice, SetSamplerState(0, D3DSAMP_MAGFILTER, filter));
+                GR_D3D9(fDevice, SetSamplerState(0, D3DSAMP_MINFILTER, filter));
+                fLastDrawState.fSamplerState.fFilter = 
+                    fNextDrawState.fSamplerState.fFilter;
+            }
+            if (fLastDrawState.fSamplerState.fWrapX != 
+                    fNextDrawState.fSamplerState.fWrapX) {
+                GR_D3D9(fDevice, SetSamplerState(0, D3DSAMP_ADDRESSU, 
+                    gTileMode2D3D9Wrap[fNextDrawState.fSamplerState.fWrapX]));
+                fLastDrawState.fSamplerState.fWrapX = 
+                    fNextDrawState.fSamplerState.fWrapX;
+            }
+            if (fLastDrawState.fSamplerState.fWrapY != 
+                    fNextDrawState.fSamplerState.fWrapY) {
+                GR_D3D9(fDevice, SetSamplerState(0, D3DSAMP_ADDRESSV, 
+                    gTileMode2D3D9Wrap[fNextDrawState.fSamplerState.fWrapY]));
+                fLastDrawState.fSamplerState.fWrapY = 
+                    fNextDrawState.fSamplerState.fWrapY;
+            }
+        } else {
+            GrAssert(!"Rendering with texture vert flag set but no bound texture");
+            if (NULL != fLastDrawState.fTexture) {            
+                GR_D3D9(fDevice,SetTexture(0, NULL));
+    //            GrPrintf("---- bindtexture 0\n");
+                fLastDrawState.fTexture = NULL;
+            }
+        }
+    }
+
+    // check for circular rendering
+    GrAssert(!(fNextVertexState.fFlagBits & (1 << kTexCoord_VertFlag)) ||
+             NULL == fNextDrawState.fRenderTarget || 
+             NULL == fNextDrawState.fTexture ||
+             fNextDrawState.fTexture->asRenderTarget() != fNextDrawState.fRenderTarget);
+
+    if ((type == GrGpu::kLineStrip_PrimitiveType ||
+         type == GrGpu::kLines_PrimitiveType) &&
+        fLastDrawState.fLineWidth != fNextDrawState.fLineWidth) {
+        // D3D9 doesn't support wide lines!
+        //GrAssert(fNextDrawState.fLineWidth == 1);
+    }
+
+    bool stencilChange = 
+                fLastDrawState.fPathPass != fNextDrawState.fPathPass ||
+                (kNone_PathPass != fNextDrawState.fPathPass &&
+                 fLastDrawState.fReverseFill != fNextDrawState.fReverseFill);
+                        
+    if (stencilChange) {
+        switch (fNextDrawState.fPathPass) {
+        case kNone_PathPass:
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILENABLE, FALSE));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_COLORWRITEENABLE, 0xf));
+            if (!fSingleStencilPassForWinding) {
+                GR_D3D9(fDevice, SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE));
+            }
+            break;
+        case kEvenOddStencil_PathPass:
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILENABLE, TRUE));
+            if (fSingleStencilPassForWinding) {
+                GR_D3D9(fDevice, SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, FALSE));
+            } else {
+                GR_D3D9(fDevice, SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE));
+            }
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_INVERT));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INVERT));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_INVERT));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_COLORWRITEENABLE, 0x0));
+            break;
+        case kEvenOddColor_PathPass:
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILENABLE, TRUE));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILREF, 0xffffffff));
+            if (fSingleStencilPassForWinding) {
+                GR_D3D9(fDevice, SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, FALSE));
+            } else {
+                GR_D3D9(fDevice, SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE));
+            }
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILFUNC, 
+                fNextDrawState.fReverseFill ? D3DCMP_NOTEQUAL : D3DCMP_EQUAL));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_ZERO));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_ZERO));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_ZERO));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_COLORWRITEENABLE, 0xf));
+            break;
+        case kWindingStencil1_PathPass:
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILENABLE, TRUE));
+            if (fSingleStencilPassForWinding) {
+                GR_D3D9(fDevice, SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, TRUE));
+                GR_D3D9(fDevice, SetRenderState(D3DRS_CCW_STENCILFUNC, D3DCMP_ALWAYS));
+                GR_D3D9(fDevice, SetRenderState(D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_DECR));
+                GR_D3D9(fDevice, SetRenderState(D3DRS_CCW_STENCILPASS, D3DSTENCILOP_DECR));
+                GR_D3D9(fDevice, SetRenderState(D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_DECR));
+            } else {
+                GR_D3D9(fDevice, SetRenderState(D3DRS_CULLMODE, D3DCULL_CW));
+            }
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS));            
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_INCR));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INCR));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_INCR));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_COLORWRITEENABLE, 0x0));
+            break;
+        case kWindingStencil2_PathPass:
+            GrAssert(!fSingleStencilPassForWinding);
+            GR_D3D9(fDevice, SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILENABLE, TRUE));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS));            
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_DECR));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_DECR));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_DECR));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_COLORWRITEENABLE, 0x0));
+            break;
+        case kWindingColor_PathPass:
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILENABLE, TRUE));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILREF, 0x00000000));
+            if (fSingleStencilPassForWinding) {
+                GR_D3D9(fDevice, SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, FALSE));
+            } else {
+                GR_D3D9(fDevice, SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE));
+            }
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILFUNC, 
+                    fNextDrawState.fReverseFill ? D3DCMP_EQUAL : D3DCMP_NOTEQUAL));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_ZERO));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_ZERO));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_ZERO));
+            GR_D3D9(fDevice, SetRenderState(D3DRS_COLORWRITEENABLE, 0xf));
+            break;
+        default:
+            GrAssert(!"Unexpected path pass.");
+            break;
+        }
+        fLastDrawState.fPathPass = fNextDrawState.fPathPass;
+        fLastDrawState.fReverseFill = fNextDrawState.fReverseFill;
+    }
+    fLastDrawState.fDrawMode    = fNextDrawState.fDrawMode;
+    
+    //////////////////////////////////////////////////////////////////////////
+    // Fixed pipe stuff
+
+    DWORD vertFVF = (fNextDrawState.fDrawMode == kTextGlyphs_DrawMode) ? 
+                gTextFVF :
+                gDeclToFVFs[gVertFlags2VertDeclIdx[fNextVertexState.fFlagBits]];
+    GrAssert(-1 != vertFVF);
+    if (fLastVertFVF != vertFVF) {
+        GR_D3D9(fDevice, SetFVF(vertFVF));
+        fLastVertFVF = vertFVF;
+    }
+
+    // this has to stay after the set render target because
+    // setrendertarget resets the viewport
+    if (fLastDrawState.fViewportW != fNextDrawState.fViewportW ||
+        fLastDrawState.fViewportH != fNextDrawState.fViewportH) {
+        D3DVIEWPORT9 vp;
+        vp.X = vp.Y = 0;
+        vp.MinZ = 0; vp.MaxZ = 1;
+        vp.Width  = fNextDrawState.fViewportW;
+        vp.Height = fNextDrawState.fViewportH;
+        GR_D3D9(fDevice, SetViewport(&vp));
+        
+        D3DMATRIX mat;
+        sk_bzero(&mat, sizeof(mat));
+    
+        float invW = 1.f / fNextDrawState.fViewportW;
+        float invH = 1.f / fNextDrawState.fViewportH;
+
+        mat._11 = 2.f * invW;
+        mat._22 = -2.f * invH;
+        //mat._33 = -1.f;
+        mat._33 = 1.f;
+        mat._44 = 1;
+    
+        // included here is 1/2 pixel adjustment because
+        // d3d9 puts pixel *centers* at integer offset in viewport space.
+        mat._41 = -1.f - invW;
+        mat._42 = 1.f + invH;
+
+        GR_D3D9(fDevice, SetTransform(D3DTS_PROJECTION, &mat));
+
+        fLastDrawState.fViewportW = fNextDrawState.fViewportW;
+        fLastDrawState.fViewportH = fNextDrawState.fViewportH;
+    }
+
+    uint32_t vertDiff = fNextVertexState.fFlagBits ^ fLastVertexState.fFlagBits;
+
+    if (vertDiff) {
+        if (vertDiff & (1 << kTexCoord_VertFlag)) {            
+            if (fNextVertexState.fFlagBits & (1 << kTexCoord_VertFlag)) {
+                GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_COLOROP, 
+                                                      D3DTOP_MODULATE));
+                GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_ALPHAOP, 
+                                                      D3DTOP_MODULATE));
+            } else {
+                GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_COLOROP, 
+                                                      D3DTOP_SELECTARG2));
+                GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_ALPHAOP, 
+                                                      D3DTOP_SELECTARG2));
+            }
+        }
+        if (vertDiff & (1 << kColors_VertFlag)) {
+            if (fNextVertexState.fFlagBits & (1 << kColors_VertFlag)) {
+                GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_COLORARG2,
+                                                      D3DTA_CURRENT));
+                GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_ALPHAARG2, 
+                                                      D3DTA_CURRENT));
+            } else {
+                GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_COLORARG2, 
+                                                      D3DTA_CONSTANT));
+                GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_ALPHAARG2, 
+                                                      D3DTA_CONSTANT));                
+            }
+        }
+        fLastVertexState.fFlagBits = fNextVertexState.fFlagBits;
+    }
+ 
+    if (fLastDrawState.fTexture != fDummyTexture &&
+        !(fNextVertexState.fFlagBits & ((1 << kColors_VertFlag) || 
+                                     (1 << kColors_VertFlag)))) {
+        GR_D3D9(fDevice, SetTexture(0, fDummyTexture->texture()));
+        fLastDrawState.fTexture = fDummyTexture;
+    }
+
+    if (fLastDrawState.fPointSize != fNextDrawState.fPointSize) {
+        GR_D3D9(fDevice,SetRenderState(D3DRS_POINTSIZE, 
+                                       *(DWORD*)&fNextDrawState.fPointSize));
+        fLastDrawState.fPointSize = fNextDrawState.fPointSize;
+    }
+    
+    if (!(fNextVertexState.fFlagBits & (1 << kColors_VertFlag)) &&
+        fLastDrawState.fColor != fNextDrawState.fColor) {
+        GR_D3D9(fDevice, SetTextureStageState(0, D3DTSS_CONSTANT, 
+                                              fNextDrawState.fColor));
+        fLastDrawState.fColor = fNextDrawState.fColor;
+    }
+    bool mvChanged = fLastDrawState.fMatrixModeCache[kModelView_MatrixMode] != 
+                     fNextDrawState.fMatrixModeCache[kModelView_MatrixMode];
+    if (mvChanged) {
+        D3DMATRIX mat;
+        gr_matrix_to_d3d_matrix(&mat, 
+            fNextDrawState.fMatrixModeCache[kModelView_MatrixMode]);
+        GR_D3D9(fDevice, SetTransform(
+            gMatrixMode2D3D9Matrix[kModelView_MatrixMode], &mat));
+        fLastDrawState.fMatrixModeCache[kModelView_MatrixMode] = 
+            fNextDrawState.fMatrixModeCache[kModelView_MatrixMode];
+    }
+
+    // Since fixed-pipe FVF doesn't allow using positions
+    // as tex coords like vertex decls do in for shaders, we
+    // use tex gen
+    // D3D9 tex coord gen uses the camera space vertex pos as the 
+    // texture coordinates. We want to use the worldspace pos so
+    // we invert the view matrix as part of the texture matrix.
+    if ((fNextVertexState.fFlagBits & (1 << kTexCoord_VertFlag))) {
+        bool texGen = 0 != (fNextVertexState.fFlagBits & 
+                      (1 << kPositionAsTexCoord_VertFlag));
+
+        bool texGenChange = fLastTexGen != texGen;
+
+        if (fLastDrawState.fMatrixModeCache[kTexture_MatrixMode] != 
+            fNextDrawState.fMatrixModeCache[kTexture_MatrixMode] || 
+            texGenChange ||
+            (texGen && mvChanged)) {
+            GrMatrix* m;
+            GrMatrix temp;
+            D3DMATRIX d3dMat;
+            if (texGen) {
+                fNextDrawState.fMatrixModeCache[kModelView_MatrixMode].
+                    invert(&temp);
+                temp.postConcat(fNextDrawState.fMatrixModeCache[kTexture_MatrixMode]);
+                m =  &temp;
+            } else {
+                m = &fNextDrawState.fMatrixModeCache[kTexture_MatrixMode];
+            }
+
+            gr_matrix_to_d3d_matrix(&d3dMat, *m);
+            GR_D3D9(fDevice, SetTransform(
+                gMatrixMode2D3D9Matrix[kTexture_MatrixMode], &d3dMat));
+            fLastDrawState.fMatrixModeCache[kTexture_MatrixMode] = 
+                fNextDrawState.fMatrixModeCache[kTexture_MatrixMode];
+            if (texGenChange) {
+                GR_D3D9(fDevice, SetTextureStageState(0, 
+                            D3DTSS_TEXCOORDINDEX,
+                            texGen ? D3DTSS_TCI_CAMERASPACEPOSITION : 0));
+            }
+            fLastTexGen = texGen;
+        }
+    }
+    return true;
+}
+
+void GrGpuD3D9::flushScissor() {
+    if (fLastDrawState.fScissorRect != fNextDrawState.fScissorRect) {
+        RECT rect;
+        rect.left = fNextDrawState.fScissorRect.fLeft;
+        rect.right = fNextDrawState.fScissorRect.fRight;
+        rect.top = fNextDrawState.fScissorRect.fTop;
+        rect.bottom = fNextDrawState.fScissorRect.fBottom;
+    
+        GR_D3D9(fDevice, SetScissorRect(&rect));
+        fLastDrawState.fScissorRect != fNextDrawState.fScissorRect;
+    }
+
+}
+
+void GrGpuD3D9::eraseColor(GrColor color) {
+    
+    DWORD clr = D3DCLEAR_TARGET;
+
+    if (fLastDrawState.fRenderTarget != fNextDrawState.fRenderTarget) {
+        setRenderTargetImm();
+        // In D3D9 setting the render target resets the viewport
+        fLastDrawState.fViewportH = -1;
+        GrD3D9RenderTarget& rt = *(GrD3D9RenderTarget*)fNextDrawState.fRenderTarget;
+        if ((NULL != rt.fStencil) && rt.fClearStencil) {
+            clr |= D3DCLEAR_STENCIL;
+        }
+    }
+
+    D3DCOLOR d3dColor = D3DCOLOR_ARGB(GrColorUnpackA(color),
+                                      GrColorUnpackR(color),
+                                      GrColorUnpackG(color),
+                                      GrColorUnpackB(color));
+     
+    // we enable the scissor in the preamble and flush 
+    // assumes it is always enabled
+    GR_D3D9(fDevice, SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE));
+    GR_D3D9(fDevice, Clear(0, NULL, clr, d3dColor, 0.f, 0x0));    
+    GR_D3D9(fDevice, SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE));
+}
+
+void GrGpuD3D9::eraseStencil() {
+    if (fLastDrawState.fRenderTarget != fNextDrawState.fRenderTarget) {
+        setRenderTargetImm();
+        // In D3D9 setting the render target resets the viewport
+        fLastDrawState.fViewportH = -1;
+    }
+
+    // we enable the scissor in the preamble and flush 
+    // assumes it is always enabled
+    GR_D3D9(fDevice, SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE));
+    GR_D3D9(fDevice,Clear(0, NULL, D3DCLEAR_STENCIL, 0x0, 0.f, 0x0));    
+    GR_D3D9(fDevice, SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE));
+}
+
+GrD3D9VertexBuffer* GrGpuD3D9::setupVBufferStage(int vsize,
+                                                 int* baseVertex,
+                                                 int vertexCount,
+                                                 DrawModes mode) {
+    GrD3D9VertexBuffer*vbuf;
+    if (vsize*(vertexCount) > STAGE_VERTEX_SIZE) {
+        GrPrintf("Staging vertex buffer is too small!");
+        vbuf = (GrD3D9VertexBuffer*) createVertexBuffer(vsize*vertexCount, true);
+        if (NULL == vbuf) {
+            GrAssert(!"Temporary vertex buffer failed!");
+            return NULL;
+        }
+    } else {
+        vbuf = fStageVBuffer;
+        // add a reference so that caller can unref without deleting
+        vbuf->ref();
+    }
+    void* vptr = vbuf->lock();
+    if (NULL == vptr) {
+        GrAssert(!"Locking staging/temp vbuffer failed!");
+        vbuf->unref();
+        return NULL;
+    }
+    vptr = (char*) vptr;
+    
+    if (mode == kTextGlyphs_DrawMode) {
+        GrAssert((fNextVertexState.fFlagBits & (1 << kTexCoord_VertFlag)) &&
+                    !(fNextVertexState.fFlagBits & (1 << kColors_VertFlag)) &&
+                    !(fNextVertexState.fFlagBits & 
+                                      (1 << kPositionAsTexCoord_VertFlag)));
+        GrAssert(sizeof(GrGpuTextVertex) == 2*sizeof(float));
+        for (int i = 0; i < vertexCount; ++i) {
+            GrGpuTextVertex* posxy = (GrGpuTextVertex*)((char*)vptr + i*vsize);
+            float* posz  = (float*)posxy + 2;
+            GrGpuTextVertex* tex = (GrGpuTextVertex*)(posz+1);
+            *posxy = *((GrGpuTextVertex*)fNextVertexState.fArrays.positions + i + *baseVertex);
+            *posz = .5;
+            *tex = *((GrGpuTextVertex*)fNextVertexState.fArrays.texCoords + i + *baseVertex);
+        }
+    } else {
+        switch (fNextVertexState.fFlagBits) {
+            GrAssert(sizeof(GrPoint) == 2*sizeof(float));
+            case 0: // position only
+            case (1 << kTexCoord_VertFlag) | 
+                 (1 << kPositionAsTexCoord_VertFlag): 
+                for (int i = 0; i < vertexCount; ++i) {
+                    GrPoint* posxy = (GrPoint*)((char*)vptr + i*vsize);
+                    float* posz = (float*)(posxy + 1);
+                    *posxy = *((GrPoint*)fNextVertexState.fArrays.positions + i + *baseVertex);
+                    *posz = .5;
+                }
+                break;
+            case (1 << kTexCoord_VertFlag):
+                for (int i = 0; i < vertexCount; ++i) {
+                    GrPoint* posxy = (GrPoint*)((char*)vptr + i*vsize);
+                    float* posz = (float*)(posxy + 1);
+                    GrPoint* tex = (GrPoint*)(posz + 1);
+                    *posxy = *((GrPoint*)fNextVertexState.fArrays.positions + i + *baseVertex);
+                    *posz = .5;
+                    *tex = *((GrPoint*)fNextVertexState.fArrays.texCoords + i + *baseVertex);
+                }
+                break;
+            case (1 << kColors_VertFlag):
+            case (1 << kTexCoord_VertFlag) |
+                 (1 << kPositionAsTexCoord_VertFlag) |
+                 (1 << kColors_VertFlag):
+                for (int i = 0; i < vertexCount; ++i) {
+                    GrPoint* posxy = (GrPoint*)((char*)vptr + i*vsize);
+                    float* posz = (float*)(posxy + 1);
+                    uint32_t* col = (uint32_t*)(posz + 1);
+                    *posxy = *((GrPoint*)fNextVertexState.fArrays.positions + i + *baseVertex);
+                    *posz = .5;
+                    *col =  *((uint32_t*)fNextVertexState.fArrays.colors + i + *baseVertex);
+                }
+                break;
+            case (1 << kTexCoord_VertFlag) | (1 << kColors_VertFlag):
+                for (int i = 0; i < vertexCount; ++i) {
+                    GrPoint* posxy = (GrPoint*)((char*)vptr + i*vsize);
+                    float* posz = (float*)posxy + 2;
+                    uint32_t* col = (uint32_t*)(posz + 1);
+                    GrPoint* tex = (GrPoint*)(col + 1);
+                    *posxy = *((GrPoint*)fNextVertexState.fArrays.positions + i + *baseVertex);
+                    *posz = .5;
+                    *col =  *((uint32_t*)fNextVertexState.fArrays.colors + i + *baseVertex);
+                    *tex = *((GrPoint*)fNextVertexState.fArrays.texCoords + i + *baseVertex);
+                }
+                break;
+            default:
+                GrAssert(!"Unexpected vertex flags!");
+        }
+    }
+    *baseVertex = 0;
+    vbuf->unlock();
+    return vbuf;
+}
+
+void GrGpuD3D9::setRenderTargetImm() {
+    GrD3D9RenderTarget& rt = *(GrD3D9RenderTarget*)fNextDrawState.fRenderTarget;
+    GrAssert(NULL != rt.fColor);
+    GR_D3D9(fDevice, SetRenderTarget(0, 
+            (IDirect3DSurface9*) rt.fColor));
+    if (NULL != rt.fStencil) {
+        GR_D3D9(fDevice, SetDepthStencilSurface(rt.fStencil));
+    } else {
+        GrAssert(NULL != fDefaultRenderTarget.fStencil);
+        GR_DEBUGCODE(D3DSURFACE_DESC rtDesc;)
+        GR_DEBUGCODE(D3DSURFACE_DESC dsDesc;)
+        GR_DEBUGCODE(GR_D3D9(rt.fColor,       GetDesc(&rtDesc));)
+        GR_DEBUGCODE(GR_D3D9(fDefaultRenderTarget.fStencil, \
+                        GetDesc(&dsDesc));)
+        GR_DEBUGCODE(GrAssert(color_and_stencil_compatible(rtDesc, 
+                                                            dsDesc));)
+        GR_D3D9(fDevice, 
+            SetDepthStencilSurface(fDefaultRenderTarget.fStencil));        
+    }
+    fLastDrawState.fRenderTarget = fNextDrawState.fRenderTarget;
+}
+
+GrD3D9IndexBuffer* GrGpuD3D9::setupIBufferStage(int* startIndex, int indexCount,
+                                                const uint16_t* indices) {
+    GrD3D9IndexBuffer* ibuf;
+    if (indexCount*2 > STAGE_INDEX_SIZE) {
+        GrPrintf("Staging index buffer is too small!");
+        ibuf = (GrD3D9IndexBuffer*) createIndexBuffer(indexCount*2, true);
+        if (NULL == ibuf) {
+            GrAssert(!"Temporary index buffer is too small!");
+            return NULL;
+        }
+    } else {
+        ibuf = fStageIBuffer;
+        // add a reference so that caller can unref without deleting
+        ibuf->ref();
+    }
+    void* iptr = ibuf->lock();
+    if (NULL == iptr) {
+        GrAssert(!"Locking staging/temp ibuffer failed!");
+        ibuf->unref();
+        return NULL;
+    }
+    memcpy(iptr, indices + *startIndex, 2*indexCount);
+    *startIndex = 0;
+    ibuf->unlock();
+    return ibuf;
+}
+
+int GrGpuD3D9::vertexSize(int vertFlagBits, GrGpu::DrawModes mode) {
+    if (mode == kTextGlyphs_DrawMode) {
+        return 5*sizeof(float);
+    } else {
+        switch (vertFlagBits) {
+            case 0: // position only
+            case (1 << kTexCoord_VertFlag) | 
+                 (1 << kPositionAsTexCoord_VertFlag):
+                return 3*sizeof(float);
+            case (1 << kTexCoord_VertFlag):
+                return 5*sizeof(float);
+            case (1 << kColors_VertFlag):
+            case (1 << kTexCoord_VertFlag) | 
+                 (1 << kPositionAsTexCoord_VertFlag) | 
+                 (1 << kColors_VertFlag):
+                return 3*sizeof(float) + 4;
+            case (1 << kTexCoord_VertFlag) | (1 << kColors_VertFlag):
+                return 5*sizeof(float) + 4;
+            default:
+                GrAssert(!"Unexpected vertex flags!");
+                return 0;
+        }
+    }
+}
+
+void GrGpuD3D9::drawIndexArrayApi(PrimitiveTypes type,
+                                  int baseVertex,
+                                  int vertexCount,
+                                  int indexCount,
+                                  const uint16_t* indexArray,
+                                  bool redrawHint) {
+    int vsize = vertexSize(fNextVertexState.fFlagBits, fNextDrawState.fDrawMode);
+
+    GrD3D9VertexBuffer* vbuf;
+    if (fNextVertexState.fUsingBuffer) {
+        vbuf = (GrD3D9VertexBuffer*) fNextVertexState.fBuffer;
+    } else {
+        vbuf = setupVBufferStage(vsize, &baseVertex, vertexCount,
+                                 fNextDrawState.fDrawMode);
+        if (NULL == vbuf) {
+            return;
+        }
+    }
+    int startIndex = 0;
+    GrD3D9IndexBuffer* ibuf = 
+                    setupIBufferStage(&startIndex, indexCount, indexArray);    
+    if (NULL == ibuf) {
+        vbuf->unref();
+    }
+
+    GR_D3D9(fDevice,SetStreamSource(0, vbuf->buffer(), 0, vsize));
+    GR_D3D9(fDevice,SetIndices(ibuf->buffer()));
+    GR_D3D9(fDevice,DrawIndexedPrimitive(gPrimType2D3D9PrimType[type], 
+                                            baseVertex, 0, vertexCount, startIndex,
+                                            vertex_to_primitive_count(type,
+                                                            indexCount)));
+    vbuf->unref();
+    ibuf->unref();
+}
+
+void GrGpuD3D9::drawIndexBufferApi(PrimitiveTypes type,
+                                   int baseVertex,
+                                   int startIndex,
+                                   int vertexCount,
+                                   int indexCount,                                   
+                                   GrIndexBuffer* indexBuffer,
+                                   bool redrawHint) {
+    int vsize = vertexSize(fNextVertexState.fFlagBits, fNextDrawState.fDrawMode);
+    GrD3D9VertexBuffer* vbuf;
+    bool unrefVBuf = false;
+    if (fNextVertexState.fUsingBuffer) {
+        vbuf = (GrD3D9VertexBuffer*) fNextVertexState.fBuffer;
+    } else {
+        vbuf = setupVBufferStage(vsize, &baseVertex, vertexCount, 
+                                 fNextDrawState.fDrawMode);
+        if (NULL == vbuf) {
+            return;
+        }
+    }
+    GR_D3D9(fDevice,SetStreamSource(0, vbuf->buffer(), 0, vsize));
+    GR_D3D9(fDevice,SetIndices(((GrD3D9IndexBuffer*)indexBuffer)->buffer()));
+    GR_D3D9(fDevice,DrawIndexedPrimitive(gPrimType2D3D9PrimType[type], 
+                                         baseVertex, 0, vertexCount, startIndex,
+                                         vertex_to_primitive_count(type, 
+                                                                  indexCount)));
+    vbuf->unref();
+}
+
+void GrGpuD3D9::drawNonIndexedApi(PrimitiveTypes type,
+                                  int baseVertex,
+                                  int vertexCount,
+                                  bool redrawHint) {
+    
+    int vsize = vertexSize(fNextVertexState.fFlagBits, fNextDrawState.fDrawMode);
+    GrD3D9VertexBuffer* vbuf;
+
+    if (fNextVertexState.fUsingBuffer) {
+        vbuf = (GrD3D9VertexBuffer*) fNextVertexState.fBuffer;
+    } else {
+        vbuf = setupVBufferStage(vsize, &baseVertex, vertexCount, 
+                                 fNextDrawState.fDrawMode);
+    }
+    GR_D3D9(fDevice,SetStreamSource(0, vbuf->buffer(), 0, vsize));
+    GR_D3D9(fDevice,DrawPrimitive(gPrimType2D3D9PrimType[type], baseVertex, 
+                                 vertex_to_primitive_count(type, vertexCount)));
+    vbuf->unref();
+}
+
+void GrGpuD3D9::notifyVertexBufferBind(GrD3D9VertexBuffer* buffer) {
+}
+
+void GrGpuD3D9::notifyVertexBufferDelete(GrD3D9VertexBuffer* buffer) {
+    if (fNextVertexState.fUsingBuffer && fNextVertexState.fBuffer == buffer) {
+        fNextVertexState.fBuffer = NULL;
+    }
+}
+
+void GrGpuD3D9::notifyIndexBufferBind(GrD3D9IndexBuffer* buffer) {
+}
+
+void GrGpuD3D9::notifyIndexBufferDelete(GrD3D9IndexBuffer* buffer) {
+    if (fLastIndexBuffer == buffer) {
+        fLastIndexBuffer = NULL;
+    }
+}
+
+void GrGpuD3D9::notifyTextureDelete(GrD3D9Texture* texture) {
+    if (fNextDrawState.fTexture == texture ||
+        fLastDrawState.fTexture == texture) {
+        fNextDrawState.fTexture = NULL;
+        fLastDrawState.fTexture = NULL;
+        GR_D3D9(fDevice, SetTexture(0, NULL));
+    }
+    if (fNextDrawState.fRenderTarget == texture->asRenderTarget() || 
+        fLastDrawState.fRenderTarget == texture->asRenderTarget()) {        
+        fNextDrawState.fRenderTarget = (GrRenderTarget*) &fDefaultRenderTarget;
+        setRenderTargetImm();
+    }
+}
+
+void GrGpuD3D9::notifyTextureRemoveRenderTarget(GrD3D9Texture* texture) {
+    if (fNextDrawState.fRenderTarget == texture->asRenderTarget() || 
+        fLastDrawState.fRenderTarget == texture->asRenderTarget()) {        
+        fNextDrawState.fRenderTarget = (GrRenderTarget*) &fDefaultRenderTarget;
+        setRenderTargetImm();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrD3D9Texture::GrD3D9Texture(uint32_t width, 
+                             uint32_t height,
+                             GrTexture::PixelConfig config,
+                             IDirect3DTexture9* texture,
+                             IDirect3DSurface9* stencil,
+                             bool clearStencil,
+                             GrGpuD3D9* gpuD3D9) : 
+        INHERITED(width, height, width, height, config),
+        fTexture(texture),
+        fStencil(stencil),
+        fGpuD3D9(gpuD3D9) {    
+    GrAssert(NULL != fTexture);
+    fTexture->GetLevelDesc(0, &fDesc);
+    
+    if (fDesc.Usage & D3DUSAGE_RENDERTARGET) {
+        BOOL result = fTexture->GetSurfaceLevel(0, &fRenderTarget.fColor);
+        GrAssert(S_OK == result && NULL != fRenderTarget.fColor);
+        fRenderTarget.fStencil = stencil;
+        fRenderTarget.fClearStencil = clearStencil;
+    } else {
+        GrAssert(NULL == stencil);
+        fRenderTarget.fColor = NULL;
+        fRenderTarget.fStencil = NULL;
+        fRenderTarget.fClearStencil = false;
+    }
+}
+
+GrD3D9Texture::~GrD3D9Texture() {
+    fGpuD3D9->notifyTextureDelete(this);
+    if (NULL != fRenderTarget.fColor) {
+        fRenderTarget.fColor->Release();
+    }
+    if (NULL != fRenderTarget.fStencil) {
+        fRenderTarget.fStencil->Release();
+    }
+    if (NULL != fTexture) {
+        fTexture->Release();
+    }
+}
+    
+void GrD3D9Texture::abandon() {
+    GrAssert(NULL != fTexture);
+    // release on device already deleted the objects?
+    fTexture = NULL;
+    fRenderTarget.fColor = NULL;
+    fRenderTarget.fStencil = NULL;
+}
+
+bool GrD3D9Texture::isRenderTarget() {
+    GrAssert(NULL != fTexture);
+    return (fDesc.Usage & D3DUSAGE_RENDERTARGET);
+}
+
+void GrD3D9Texture::removeRenderTarget() {
+    fGpuD3D9->notifyTextureRemoveRenderTarget(this);
+    if (NULL != fRenderTarget.fColor) {
+        fRenderTarget.fColor->Release();
+        fRenderTarget.fColor = NULL;
+    }
+    if (NULL != fRenderTarget.fStencil) {
+        fRenderTarget.fStencil->Release();
+        fRenderTarget.fStencil = NULL;
+    }
+    fRenderTarget.fClearStencil = false;
+}
+
+void GrD3D9Texture::uploadTextureData(uint32_t x,
+                                      uint32_t y,
+                                      uint32_t width,
+                                      uint32_t height,
+                                      const void* srcData) {
+    GrAssert(NULL != fTexture);
+    HRESULT hr;
+#if 0 // is it ever beneficial to lock the texture directly?
+    if (fDesc.Usage & D3DUSAGE_DYNAMIC) {
+        D3DLOCKED_RECT lock;
+        RECT rect;
+        rect.left = x;
+        rect.right = x + width;
+        rect.top = y;
+        rect.bottom = y + height;
+        hr = fTexture->LockRect(0, &lock, &rect, 0);
+        if (FAILED(hr)) {
+            GrAssert(!"Failed to lock texture!");
+            return;
+        }
+        int bpp = format_bytes(fDesc.Format);
+        if (lock.Pitch == width * bpp) {
+            memcpy(lock.pBits, srcData, width*height*bpp);
+        } else {
+            for (uint32_t y = 0; y < height; ++y) {
+                memcpy((char*)lock.pBits + y * lock.Pitch,
+                       (char*)srcData + y*width*bpp, width*bpp);
+            }
+        }
+        hr = fTexture->UnlockRect(0);
+        GrAssert(SUCCEEDED(hr));
+    } else 
+#endif
+    {
+        // should the temp textures be cached 
+        // somewhere so we aren't recreating them?
+        IDirect3DDevice9* device;
+        hr = fTexture->GetDevice(&device);
+        if (FAILED(hr) || NULL == device) {
+            GrAssert("getting device from texture failed!");
+            return;
+        }
+        IDirect3DTexture9* tempTexture;
+        GR_D3D9(device, CreateTexture(width, height, 1, 0, fDesc.Format, 
+                                   D3DPOOL_SYSTEMMEM, &tempTexture, NULL));
+        GrAssert(NULL != tempTexture);
+        IDirect3DSurface9* tempSurface;
+        GR_D3D9(tempTexture, GetSurfaceLevel(0, &tempSurface));
+        GrAssert(NULL != tempTexture);
+
+        D3DLOCKED_RECT lock;
+        GR_D3D9(tempSurface, LockRect(&lock, NULL, 0));
+        GrAssert(NULL != lock.pBits);
+
+        // For 4444 D3D uses ARGB for while Skia uses RGBA
+        if (D3DFMT_A4R4G4B4 == fDesc.Format) {
+            WORD* src = (WORD*)srcData;
+            for (uint32_t y = 0; y < height; ++y) {
+                for (uint32_t x = 0; x < width; ++x, ++src) {
+                    WORD* dst = (WORD*)((char*)lock.pBits + y * lock.Pitch)+x;
+                    *dst = ((0xfff0 & *src) >> 4) | ((0x000f & *src) << 12);                    
+                }
+            }
+        } else {
+            int bpp = format_bytes(fDesc.Format);        
+            if (lock.Pitch == width * bpp) {
+                memcpy(lock.pBits, srcData, width*height*bpp);
+            } else {
+                for (uint32_t y = 0; y < height; ++y) {
+                    memcpy((char*)lock.pBits + y * lock.Pitch,
+                           (char*)srcData + y*width*bpp, width*bpp);
+                }
+            }
+        }
+        GR_D3D9(tempSurface, UnlockRect());
+
+        IDirect3DSurface9* level0;
+        GR_D3D9(fTexture, GetSurfaceLevel(0, &level0));
+
+        POINT xy = {x, y};
+        GR_D3D9(device, UpdateSurface(tempSurface, NULL, level0, &xy));
+
+        tempSurface->Release();
+        tempTexture->Release();
+        level0->Release();
+        device->Release();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrD3D9VertexBuffer::GrD3D9VertexBuffer(uint32_t size, 
+                                       bool dynamic,
+                                       IDirect3DVertexBuffer9* vbuffer,
+                                       GrGpuD3D9* gpuD3D9) :
+            INHERITED(size, dynamic),
+            fBuffer(vbuffer),
+            fLocked(false),
+            fGpuD3D9(gpuD3D9) {
+    HRESULT hr = fBuffer->GetDesc(&fDesc);
+    GrAssert(SUCCEEDED(hr));
+}
+
+GrD3D9VertexBuffer::~GrD3D9VertexBuffer() {
+    fGpuD3D9->notifyVertexBufferDelete(this);
+    if (NULL != fBuffer) {
+        fBuffer->Release();
+    }
+}
+
+void GrD3D9VertexBuffer::abandon() {
+    GrAssert(NULL != fBuffer);
+    fBuffer = NULL;
+}
+
+void* GrD3D9VertexBuffer::lock() {
+    GrAssert(NULL != fBuffer);
+    HRESULT hr;
+    void* data = NULL;
+    hr = fBuffer->Lock(0, fDesc.Size, &data, D3DLOCK_DISCARD);
+    fLocked = SUCCEEDED(hr);
+    GrAssert(fLocked && NULL != data);
+    return data;
+}
+
+void GrD3D9VertexBuffer::unlock() {
+    GrAssert(fLocked);
+    HRESULT hr = fBuffer->Unlock();
+    GrAssert(SUCCEEDED(hr));
+}
+
+bool GrD3D9VertexBuffer::isLocked() {
+    return fLocked;
+}
+
+bool GrD3D9VertexBuffer::updateData(const void* src, uint32_t srcSizeInBytes) {
+    GrAssert(srcSizeInBytes <= fDesc.Size);
+    HRESULT hr;
+    void* data;
+    hr = fBuffer->Lock(0, fDesc.Size, &data, D3DLOCK_DISCARD);
+    GrAssert(SUCCEEDED(hr));
+    if (SUCCEEDED(hr)) {
+        fLocked = true;
+        GrAssert(NULL != data);
+        memcpy(data, src, srcSizeInBytes);
+        hr = fBuffer->Unlock();
+        fLocked = SUCCEEDED(hr);
+        GrAssert(!fLocked);
+        return !fLocked;
+    }
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrD3D9IndexBuffer::GrD3D9IndexBuffer(uint32_t size, 
+                                     bool dynamic,
+                                     IDirect3DIndexBuffer9* ibuffer,
+                                     GrGpuD3D9* gpuD3D9) :
+            INHERITED(size, dynamic),
+            fBuffer(ibuffer),
+            fLocked(false),
+            fGpuD3D9(gpuD3D9) {
+    HRESULT hr = fBuffer->GetDesc(&fDesc);
+    GrAssert(SUCCEEDED(hr));
+}
+
+GrD3D9IndexBuffer::~GrD3D9IndexBuffer() {
+    fGpuD3D9->notifyIndexBufferDelete(this);
+    if (NULL != fBuffer) {
+        fBuffer->Release();
+    }
+}
+
+void GrD3D9IndexBuffer::abandon() {
+    GrAssert(NULL != fBuffer);
+    fBuffer = NULL;
+}
+
+void* GrD3D9IndexBuffer::lock() {
+    GrAssert(NULL != fBuffer);
+    HRESULT hr;
+    void* data = NULL;
+    hr = fBuffer->Lock(0, fDesc.Size, &data, D3DLOCK_DISCARD);
+    fLocked = SUCCEEDED(hr);
+    GrAssert(fLocked && NULL != data);
+    return data;
+}
+
+void GrD3D9IndexBuffer::unlock() {
+    GrAssert(fLocked);
+    HRESULT hr = fBuffer->Unlock();
+    GrAssert(SUCCEEDED(hr));
+}
+
+bool GrD3D9IndexBuffer::isLocked() {
+    return fLocked;
+}
+
+bool GrD3D9IndexBuffer::updateData(const void* src, uint32_t srcSizeInBytes) {
+    GrAssert(srcSizeInBytes <= fDesc.Size);
+    HRESULT hr;
+    void* data;
+    hr = fBuffer->Lock(0, fDesc.Size, &data, D3DLOCK_DISCARD);
+    GrAssert(SUCCEEDED(hr));
+    if (SUCCEEDED(hr)) {
+        fLocked = true;
+        GrAssert(NULL != data);
+        memcpy(data, src, srcSizeInBytes);
+        hr = fBuffer->Unlock();
+        fLocked = SUCCEEDED(hr);
+        GrAssert(!fLocked);
+        return !fLocked;
+    }
+    return false;
+}
diff --git a/gpu/src/GrGpuFactory.cpp b/gpu/src/GrGpuFactory.cpp
new file mode 100644
index 0000000..b3627c9
--- /dev/null
+++ b/gpu/src/GrGpuFactory.cpp
@@ -0,0 +1,78 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrTypes.h"
+
+// must be before GrGLConfig.h
+#if GR_WIN32_BUILD
+//    #include "GrGpuD3D9.h"
+#endif
+
+#include "GrGLConfig.h"
+
+#define GR_USE_GLSHADERS2 0
+
+#if GR_SUPPORT_GLES1 || GR_SUPPORT_GLDESKTOP
+    #include "GrGpuGLFixed.h"
+#endif
+
+#if GR_SUPPORT_GLES2 || GR_SUPPORT_GLDESKTOP
+    #if GR_USE_GLSHADERS2
+        #include "GrGpuGLShaders2.h"
+    #else
+        #include "GrGpuGLShaders.h"
+    #endif
+#endif
+
+#include "GrGpu.h"
+
+GrGpu* GrGpu::Create(Engine engine, Platform3DContext context3D) {
+    GrGpu* gpu = NULL;
+
+    switch (engine) {
+        case kOpenGL_Shaders_Engine:
+            GrAssert(NULL == context3D);
+#if GR_SUPPORT_GLES2 || GR_SUPPORT_GLDESKTOP
+    #if GR_USE_GLSHADERS2
+            gpu = new GrGpuGLShaders2;
+    #else
+            gpu = new GrGpuGLShaders;
+    #endif
+#endif
+            break;
+        case kOpenGL_Fixed_Engine:
+            GrAssert(NULL == context3D);
+#if GR_SUPPORT_GLES1 || GR_SUPPORT_GLDESKTOP
+            gpu = new GrGpuGLFixed;
+#endif
+            break;
+        case kDirect3D9_Engine:
+            GrAssert(NULL != context3D);
+#if GR_WIN32_BUILD
+//            gpu = new GrGpuD3D9((IDirect3DDevice9*)context3D);
+#endif
+            break;
+        default:
+            GrAssert(!"unknown engine");
+            break;
+    }
+
+    return gpu;
+}
+
+
+
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
new file mode 100644
index 0000000..8a1169e
--- /dev/null
+++ b/gpu/src/GrGpuGL.cpp
@@ -0,0 +1,1824 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+#include "GrGpuGL.h"
+#include "GrMemory.h"
+#include <stdio.h>
+
+static const GLuint GR_MAX_GLUINT = ~0;
+static const GLint  GR_INVAL_GLINT = ~0;
+
+#define SKIP_CACHE_CHECK    true
+
+static const GLenum gXfermodeCoeff2Blend[] = {
+    GL_ZERO,
+    GL_ONE,
+    GL_SRC_COLOR,
+    GL_ONE_MINUS_SRC_COLOR,
+    GL_DST_COLOR,
+    GL_ONE_MINUS_DST_COLOR,
+    GL_SRC_ALPHA,
+    GL_ONE_MINUS_SRC_ALPHA,
+    GL_DST_ALPHA,
+    GL_ONE_MINUS_DST_ALPHA,
+};
+
+bool has_gl_extension(const char* ext) {
+    const char* glstr = (const char*) glGetString(GL_EXTENSIONS);
+
+    int extLength = strlen(ext);
+
+    while (true) {
+        int n = strcspn(glstr, " ");
+        if (n == extLength && 0 == strncmp(ext, glstr, n)) {
+            return true;
+        }
+        if (0 == glstr[n]) {
+            return false;
+        }
+        glstr += n+1;
+    }
+}
+
+void gl_version(int* major, int* minor) {
+    const char* v = (const char*) glGetString(GL_VERSION);
+    if (NULL == v) {
+        GrAssert(0);
+        *major = 0;
+        *minor = 0;
+        return;
+    }
+#if GR_GL_DESKTOP
+    int n = sscanf(v, "%d.%d", major, minor);
+    if (n != 2) {
+        GrAssert(0);
+        *major = 0;
+        *minor = 0;
+        return;
+    }
+#else
+    char profile[2];
+    int n = sscanf(v, "OpenGL ES-%c%c %d.%d", profile, profile+1, major, minor);
+    bool ok = 4 == n;
+    if (!ok) {
+        int n = sscanf(v, "OpenGL ES %d.%d", major, minor);
+        ok = 2 == n;
+    }
+    if (!ok) {
+        GrAssert(0);
+        *major = 0;
+        *minor = 0;
+        return;
+    }
+#endif
+}
+///////////////////////////////////////////////////////////////////////////////
+
+bool fbo_test(GrGLExts exts, int w, int h) {
+    GLuint testFBO;
+    GR_GLEXT(exts, GenFramebuffers(1, &testFBO));
+    GR_GLEXT(exts, BindFramebuffer(GR_FRAMEBUFFER, testFBO));
+    GLuint testRTTex;
+    GR_GL(GenTextures(1, &testRTTex));
+    GR_GL(BindTexture(GL_TEXTURE_2D, testRTTex));
+
+    GR_GL(TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h,
+                     0, GL_RGBA, GL_UNSIGNED_BYTE, NULL));
+    GR_GL(BindTexture(GL_TEXTURE_2D, 0));
+    GR_GLEXT(exts, FramebufferTexture2D(GR_FRAMEBUFFER, GR_COLOR_ATTACHMENT0,
+                                        GL_TEXTURE_2D, testRTTex, 0));
+    GLenum status = GR_GLEXT(exts, CheckFramebufferStatus(GR_FRAMEBUFFER));
+    GR_GLEXT(exts, DeleteFramebuffers(1, &testFBO));
+    GR_GL(DeleteTextures(1, &testRTTex));
+    return status == GR_FRAMEBUFFER_COMPLETE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGpuGL::GrGpuGL() {
+    GrPrintf("------------------------- create GrGpuGL %p --------------\n",
+             this);
+    GrPrintf("------ VENDOR %s\n", glGetString(GL_VENDOR));
+    GrPrintf("------ RENDERER %s\n", glGetString(GL_RENDERER));
+    GrPrintf("------ VERSION %s\n", glGetString(GL_VERSION));
+    GrPrintf("------ EXTENSIONS\n %s \n", glGetString(GL_EXTENSIONS));
+
+    GrGLClearErr();
+
+    GrGLInitExtensions(&fExts);
+
+    resetContextHelper();
+
+    GrGLRenderTarget::GLRenderTargetIDs defaultRTIDs;
+    GR_GL(GetIntegerv(GR_FRAMEBUFFER_BINDING, (GLint*)&defaultRTIDs.fRTFBOID));
+    defaultRTIDs.fTexFBOID = defaultRTIDs.fRTFBOID;
+    defaultRTIDs.fMSColorRenderbufferID = 0;
+    defaultRTIDs.fStencilRenderbufferID = 0;
+    GLint vp[4];
+    GR_GL(GetIntegerv(GL_VIEWPORT, vp));
+    fHWBounds.fViewportRect.setLTRB(vp[0],
+                                    vp[1] + vp[3],
+                                    vp[0] + vp[2],
+                                    vp[1]);
+    defaultRTIDs.fOwnIDs = false;
+
+    fDefaultRenderTarget = new GrGLRenderTarget(defaultRTIDs,
+                                                fHWBounds.fViewportRect,
+                                                NULL,
+                                                this);
+    fHWDrawState.fRenderTarget = fDefaultRenderTarget;
+    fRenderTargetChanged = true;
+
+    fCurrDrawState = fHWDrawState;
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Check for supported features.
+
+    int major, minor;
+    gl_version(&major, &minor);
+
+    GLint numFormats;
+    GR_GL(GetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats));
+    GrAutoSTMalloc<10, GLint> formats(numFormats);
+    GR_GL(GetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, formats));
+    for (int i = 0; i < numFormats; ++i) {
+        if (formats[i] == GR_PALETTE8_RGBA8) {
+            f8bitPaletteSupport = true;
+            break;
+        }
+    }
+    GrPrintf("Palette8 support: %s\n", (f8bitPaletteSupport ? "YES" : "NO"));
+
+    GR_STATIC_ASSERT(0 == kNone_AALevel);
+    GR_STATIC_ASSERT(1 == kLow_AALevel);
+    GR_STATIC_ASSERT(2 == kMed_AALevel);
+    GR_STATIC_ASSERT(3 == kHigh_AALevel);
+
+    memset(fAASamples, 0, sizeof(fAASamples));
+    fMSFBOType = kNone_MSFBO;
+    if (has_gl_extension("GL_IMG_multisampled_render_to_texture")) {
+        fMSFBOType = kIMG_MSFBO;
+        GrPrintf("MSAA Support: IMG ES EXT.\n");
+    }
+    else if (has_gl_extension("GL_APPLE_framebuffer_multisample")) {
+        fMSFBOType = kApple_MSFBO;
+        GrPrintf("MSAA Support: APPLE ES EXT.\n");
+    }
+#if GR_GL_DESKTOP
+    else if ((major >= 3) ||
+             has_gl_extension("GL_ARB_framebuffer_object") ||
+             (has_gl_extension("GL_EXT_framebuffer_multisample") &&
+              has_gl_extension("GL_EXT_framebuffer_blit"))) {
+        fMSFBOType = kDesktop_MSFBO;
+        GrPrintf("MSAA Support: DESKTOP\n");
+    }
+#endif
+    else {
+        GrPrintf("MSAA Support: NONE\n");
+    }
+
+    if (kNone_MSFBO != fMSFBOType) {
+        GLint maxSamples;
+        GLenum maxSampleGetter = (kIMG_MSFBO == fMSFBOType) ?
+                                                            GR_MAX_SAMPLES_IMG :
+                                                            GR_MAX_SAMPLES;
+        GR_GL(GetIntegerv(maxSampleGetter, &maxSamples));
+        if (maxSamples > 1 ) {
+            fAASamples[kNone_AALevel] = 0;
+            fAASamples[kLow_AALevel] = GrMax(2,
+                                             GrFixedFloorToInt((GR_FixedHalf) *
+                                                             maxSamples));
+            fAASamples[kMed_AALevel] = GrMax(2,
+                                             GrFixedFloorToInt(((GR_Fixed1*3)/4) *
+                                                             maxSamples));
+            fAASamples[kHigh_AALevel] = maxSamples;
+        }
+        GrPrintf("\tMax Samples: %d\n", maxSamples);
+    }
+
+#if GR_GL_DESKTOP
+    fHasStencilWrap = (major >= 2 || (major == 1 && minor >= 4)) ||
+                      has_gl_extension("GL_EXT_stencil_wrap");
+#else
+    fHasStencilWrap = (major >= 2) || has_gl_extension("GL_OES_stencil_wrap");
+#endif
+    GrPrintf("Stencil Wrap: %s\n", (fHasStencilWrap ? "YES" : "NO"));
+
+#if GR_GL_DESKTOP
+    // we could also look for GL_ATI_separate_stencil extension or
+    // GL_EXT_stencil_two_side but they use different function signatures
+    // than GL2.0+ (and than each other).
+    fSingleStencilPassForWinding = (major >= 2);
+#else
+    // ES 2 has two sided stencil but 1.1 doesn't. There doesn't seem to be
+    // an ES1 extension.
+    fSingleStencilPassForWinding = (major >= 2);
+#endif
+    GrPrintf("Single Stencil Pass For Winding: %s\n", (fSingleStencilPassForWinding ? "YES" : "NO"));
+
+
+#if GR_GL_DESKTOP
+    fRGBA8Renderbuffer = true;
+#else
+    fRGBA8Renderbuffer = has_gl_extension("GL_OES_rgb8_rgba8");
+#endif
+    GrPrintf("RGBA Renderbuffer: %s\n", (fRGBA8Renderbuffer ? "YES" : "NO"));
+
+
+#if GR_GL_DESKTOP
+    fBufferLockSupport = true; // we require VBO support and the desktop VBO
+                               // extension includes glMapBuffer.
+#else
+    fBufferLockSupport = has_gl_extension("GL_OES_mapbuffer");
+#endif
+    GrPrintf("Map Buffer: %s\n", (fBufferLockSupport ? "YES" : "NO"));
+
+#if GR_GL_DESKTOP
+    fNPOTTextureSupport =
+        (major >= 2 || has_gl_extension("GL_ARB_texture_non_power_of_two")) ?
+            kFull_NPOTTextureType :
+            kNone_NPOTTextureType;
+#else
+    if (has_gl_extension("GL_OES_texture_npot")) {
+        fNPOTTextureSupport = kFull_NPOTTextureType;
+    } else if (major >= 2 ||
+               has_gl_extension("GL_APPLE_texture_2D_limited_npot")) {
+        fNPOTTextureSupport = kNoRepeat_NPOTTextureType;
+    } else {
+        fNPOTTextureSupport = kNone_NPOTTextureType;
+    }
+#endif
+    ////////////////////////////////////////////////////////////////////////////
+    // Experiments to determine limitations that can't be queried. TODO: Make
+    // these a preprocess that generate some compile time constants.
+
+    /* Experimentation has found that some GLs that support NPOT textures
+       do not support FBOs with a NPOT texture. They report "unsupported" FBO
+       status. I don't know how to explicitly query for this. Do an
+       experiment. Note they may support NPOT with a renderbuffer but not a
+       texture. Presumably, the implementation bloats the renderbuffer
+       internally to the next POT.
+     */
+    if (fNPOTTextureSupport == kFull_NPOTTextureType) {
+        bool npotFBOSuccess = fbo_test(fExts, 200, 200);
+        if (!npotFBOSuccess) {
+            fNPOTTextureSupport = kNonRendertarget_NPOTTextureType;
+            GrPrintf("NPOT Renderbuffer Test: FAILED\n");
+        } else {
+            GrPrintf("NPOT Renderbuffer Test: PASSED\n");
+        }
+    }
+    switch (fNPOTTextureSupport) {
+    case kNone_NPOTTextureType:
+        GrPrintf("NPOT Support: NONE\n");
+        break;
+    case kNoRepeat_NPOTTextureType:
+        GrPrintf("NPOT Support: NO REPEAT\n");
+        break;
+    case kNonRendertarget_NPOTTextureType:
+        GrPrintf("NPOT Support: NO FBOTEX\n");
+        break;
+    case kFull_NPOTTextureType:
+        GrPrintf("NPOT Support: FULL\n");
+        break;
+    }
+
+    // sanity check to make sure we can at least create an FBO from a POT texture
+    if (fNPOTTextureSupport < kFull_NPOTTextureType) {
+        bool npotFBOSuccess = fbo_test(fExts, 128, 128);
+        if (!npotFBOSuccess) {
+            GrPrintf("FBO Sanity Test: FAILED\n");
+        } else {
+            GrPrintf("FBO Sanity Test: PASSED\n");
+        }
+    }
+
+    /* The iPhone 4 has a restriction that for an FBO with texture color
+       attachment with height <= 8 then the width must be <= height. Here
+       we look for such a limitation.
+     */
+    fMinRenderTargetHeight = GR_INVAL_GLINT;
+    GLint maxRenderSize;
+    glGetIntegerv(GR_MAX_RENDERBUFFER_SIZE, &maxRenderSize);
+
+    GrPrintf("Small height FBO texture experiments\n");
+    for (GLuint i = 1; i <= 256;
+         (kFull_NPOTTextureType != fNPOTTextureSupport) ? i *= 2 : ++i) {
+        GLuint w = maxRenderSize;
+        GLuint h = i;
+        if (fbo_test(fExts, w, h)) {
+            GrPrintf("\t[%d, %d]: PASSED\n", w, h);
+            fMinRenderTargetHeight = i;
+            break;
+        } else {
+            GrPrintf("\t[%d, %d]: FAILED\n", w, h);
+        }
+    }
+    GrAssert(GR_INVAL_GLINT != fMinRenderTargetHeight);
+
+    GrPrintf("Small width FBO texture experiments\n");
+    fMinRenderTargetWidth = GR_MAX_GLUINT;
+    for (GLuint i = 1; i <= 256;
+         (kFull_NPOTTextureType != fNPOTTextureSupport) ? i *= 2 : ++i) {
+        GLuint w = i;
+        GLuint h = maxRenderSize;
+        if (fbo_test(fExts, w, h)) {
+            GrPrintf("\t[%d, %d]: PASSED\n", w, h);
+            fMinRenderTargetWidth = i;
+            break;
+        } else {
+            GrPrintf("\t[%d, %d]: FAILED\n", w, h);
+        }
+    }
+    GrAssert(GR_INVAL_GLINT != fMinRenderTargetWidth);
+
+#if GR_IOS_BUILD
+    /*
+        The iPad seems to fail, at least sometimes, if the height is < 16,
+        so we pin the values here for now. A better fix might be to
+        conditionalize this based on known that its an iPad (or some other
+        check).
+     */
+    fMinRenderTargetWidth = GrMax<GLuint>(fMinRenderTargetWidth, 16);
+    fMinRenderTargetHeight = GrMax<GLuint>(fMinRenderTargetHeight, 16);
+#endif
+    // bind back to original FBO
+    GR_GLEXT(fExts, BindFramebuffer(GR_FRAMEBUFFER, defaultRTIDs.fRTFBOID));
+#if GR_COLLECT_STATS
+    ++fStats.fRenderTargetChngCnt;
+#endif
+    eraseStencil(0, ~0);
+}
+
+GrGpuGL::~GrGpuGL() {
+    fDefaultRenderTarget->abandon();
+    fDefaultRenderTarget->unref();
+}
+
+void GrGpuGL::resetContextHelper() {
+// We detect cases when blending is effectively off
+    fHWBlendDisabled = false;
+    GR_GL(Enable(GL_BLEND));
+
+    // this is always disabled
+    GR_GL(Disable(GL_CULL_FACE));
+
+    GR_GL(Disable(GL_DITHER));
+#if GR_GL_DESKTOP
+    GR_GL(Disable(GL_LINE_SMOOTH));
+    GR_GL(Disable(GL_POINT_SMOOTH));
+	GR_GL(Disable(GL_MULTISAMPLE));
+#endif
+
+    // we only ever use lines in hairline mode
+    GR_GL(LineWidth(1));
+
+    GR_GL(ActiveTexture(GL_TEXTURE0));
+
+    fHWDrawState.fFlagBits = 0;
+
+    // illegal values
+    fHWDrawState.fSrcBlend = (BlendCoeff)-1;
+    fHWDrawState.fDstBlend = (BlendCoeff)-1;
+    fHWDrawState.fColor = GrColor_ILLEGAL;
+    fHWDrawState.fPointSize = -1;
+    fHWDrawState.fTexture = NULL;
+
+    GR_GL(Scissor(0,0,0,0));
+    fHWBounds.fScissorRect.setLTRB(0,0,0,0);
+    fHWBounds.fScissorEnabled = false;
+    GR_GL(Disable(GL_SCISSOR_TEST));
+
+    fHWDrawState.fSamplerState.setRadial2Params(-GR_ScalarMax,
+                                                -GR_ScalarMax,
+                                                true);
+
+    for (int i = 0; i < kMatrixModeCount; i++) {
+        fHWDrawState.fMatrixModeCache[i].setScale(GR_ScalarMax, GR_ScalarMax); // illegal
+    }
+
+    // disabling the stencil test also disables
+    // stencil buffer writes
+    GR_GL(Disable(GL_STENCIL_TEST));
+    GR_GL(StencilMask(0xffffffff));
+    GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
+    fHWDrawState.fReverseFill = false;
+    fHWDrawState.fStencilPass = kNone_StencilPass;
+    fHWStencilClip = false;
+
+    fHWGeometryState.fIndexBuffer = NULL;
+    fHWGeometryState.fVertexBuffer = NULL;
+    GR_GL(BindBuffer(GL_ARRAY_BUFFER, 0));
+    GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+
+    fHWDrawState.fRenderTarget = NULL;
+}
+
+void GrGpuGL::resetContext() {
+    INHERITED::resetContext();
+    resetContextHelper();
+}
+
+
+// defines stencil formats from more to less preferred
+#if GR_GL_ES
+    GLenum GR_GL_STENCIL_FORMAT_ARRAY[] = {
+        GR_STENCIL_INDEX8,
+    };
+#else
+    GLenum GR_GL_STENCIL_FORMAT_ARRAY[] = {
+        GR_STENCIL_INDEX8,
+        GR_STENCIL_INDEX16,
+        GR_UNSIGNED_INT_24_8,
+        GR_DEPTH_STENCIL,
+    };
+#endif
+
+// good to set a break-point here to know when createTexture fails
+static GrTexture* return_null_texture() {
+//    GrAssert(!"null texture");
+    return NULL;
+}
+
+#if GR_DEBUG
+static size_t as_size_t(int x) {
+    return x;
+}
+#endif
+
+GrRenderTarget* GrGpuGL::createPlatformRenderTarget(
+                                                intptr_t platformRenderTarget,
+                                                int width, int height) {
+    GrGLRenderTarget::GLRenderTargetIDs rtIDs;
+    rtIDs.fStencilRenderbufferID = 0;
+    rtIDs.fMSColorRenderbufferID = 0;
+    rtIDs.fTexFBOID              = 0;
+    rtIDs.fOwnIDs                = false;
+
+    GrIRect viewport;
+
+    // viewport is in GL coords (top >= bottom)
+    viewport.setLTRB(0, height, width, 0);
+
+    rtIDs.fRTFBOID  = (GLuint)platformRenderTarget;
+    rtIDs.fTexFBOID = (GLuint)platformRenderTarget;
+
+    GrGLRenderTarget* rt = new GrGLRenderTarget(rtIDs, viewport, NULL, this);
+
+    return rt;
+}
+
+GrTexture* GrGpuGL::createTexture(const TextureDesc& desc,
+                                  const void* srcData, size_t rowBytes) {
+
+#if GR_COLLECT_STATS
+    ++fStats.fTextureCreateCnt;
+#endif
+    GrGLTexture::GLTextureDesc glDesc;
+    GLenum internalFormat;
+
+    glDesc.fContentWidth  = desc.fWidth;
+    glDesc.fContentHeight = desc.fHeight;
+    glDesc.fAllocWidth    = desc.fWidth;
+    glDesc.fAllocHeight   = desc.fHeight;
+    glDesc.fFormat        = desc.fFormat;
+
+    bool renderTarget = 0 != (desc.fFlags & kRenderTarget_TextureFlag);
+    if (!canBeTexture(desc.fFormat,
+                      &internalFormat,
+                      &glDesc.fUploadFormat,
+                      &glDesc.fUploadType)) {
+        return return_null_texture();
+    }
+
+    GrAssert(as_size_t(desc.fAALevel) < GR_ARRAY_COUNT(fAASamples));
+    GLint samples = fAASamples[desc.fAALevel];
+    if (kNone_MSFBO == fMSFBOType && desc.fAALevel != kNone_AALevel) {
+        GrPrintf("AA RT requested but not supported on this platform.");
+    }
+
+    GR_GL(GenTextures(1, &glDesc.fTextureID));
+    if (!glDesc.fTextureID) {
+        return return_null_texture();
+    }
+
+    glDesc.fUploadByteCount = GrTexture::BytesPerPixel(desc.fFormat);
+
+    /*
+     *  check if our srcData has extra bytes past each row. If so, we need
+     *  to trim those off here, since GL doesn't let us pass the rowBytes as
+     *  a parameter to glTexImage2D
+     */
+#if GR_GL_DESKTOP
+    if (srcData) {
+        GR_GL(PixelStorei(GL_UNPACK_ROW_LENGTH,
+                          rowBytes / glDesc.fUploadByteCount));
+    }
+#else
+    GrAutoSMalloc<128 * 128> trimStorage;
+    size_t trimRowBytes = desc.fWidth * glDesc.fUploadByteCount;
+    if (srcData && (trimRowBytes < rowBytes)) {
+        size_t trimSize = desc.fHeight * trimRowBytes;
+        trimStorage.realloc(trimSize);
+        // now copy the data into our new storage, skipping the trailing bytes
+        const char* src = (const char*)srcData;
+        char* dst = (char*)trimStorage.get();
+        for (uint32_t y = 0; y < desc.fHeight; y++) {
+            memcpy(dst, src, trimRowBytes);
+            src += rowBytes;
+            dst += trimRowBytes;
+        }
+        // now point srcData to our trimmed version
+        srcData = trimStorage.get();
+    }
+#endif
+
+    if (fNPOTTextureSupport < kNonRendertarget_NPOTTextureType ||
+        (fNPOTTextureSupport == kNonRendertarget_NPOTTextureType &&
+         renderTarget)) {
+        glDesc.fAllocWidth  = GrNextPow2(desc.fWidth);
+        glDesc.fAllocHeight = GrNextPow2(desc.fHeight);
+    }
+
+    if (renderTarget) {
+        glDesc.fAllocWidth = GrMax<int>(fMinRenderTargetWidth,
+                                        glDesc.fAllocWidth);
+        glDesc.fAllocHeight = GrMax<int>(fMinRenderTargetHeight,
+                                         glDesc.fAllocHeight);
+    }
+
+    GR_GL(BindTexture(GL_TEXTURE_2D, glDesc.fTextureID));
+#if GR_COLLECT_STATS
+    ++fStats.fTextureChngCnt;
+#endif
+
+    GR_GL(PixelStorei(GL_UNPACK_ALIGNMENT, glDesc.fUploadByteCount));
+    if (GrTexture::kIndex_8_PixelConfig == desc.fFormat &&
+        supports8BitPalette()) {
+        // ES only supports CompressedTexImage2D, not CompressedTexSubimage2D
+        GrAssert(desc.fWidth == glDesc.fAllocWidth);
+        GrAssert(desc.fHeight == glDesc.fAllocHeight);
+        GLsizei imageSize = glDesc.fAllocWidth * glDesc.fAllocHeight +
+                            kColorTableSize;
+        GR_GL(CompressedTexImage2D(GL_TEXTURE_2D, 0, glDesc.fUploadFormat,
+                                   glDesc.fAllocWidth, glDesc.fAllocHeight,
+                                   0, imageSize, srcData));
+        GrGL_RestoreResetRowLength();
+    } else {
+        if (NULL != srcData && (glDesc.fAllocWidth != desc.fWidth ||
+                                glDesc.fAllocHeight != desc.fHeight)) {
+            GR_GL(TexImage2D(GL_TEXTURE_2D, 0, internalFormat,
+                             glDesc.fAllocWidth, glDesc.fAllocHeight,
+                             0, glDesc.fUploadFormat, glDesc.fUploadType, NULL));
+            GR_GL(TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, desc.fWidth,
+                                desc.fHeight, glDesc.fUploadFormat,
+                                glDesc.fUploadType, srcData));
+            GrGL_RestoreResetRowLength();
+
+            uint32_t extraW = glDesc.fAllocWidth  - desc.fWidth;
+            uint32_t extraH = glDesc.fAllocHeight - desc.fHeight;
+            uint32_t maxTexels = extraW * extraH;
+            maxTexels = GrMax(extraW * desc.fHeight, maxTexels);
+            maxTexels = GrMax(desc.fWidth * extraH, maxTexels);
+
+            GrAutoSMalloc<128*128> texels(glDesc.fUploadByteCount * maxTexels);
+
+            uint32_t rowSize = desc.fWidth * glDesc.fUploadByteCount;
+            if (extraH) {
+                uint8_t* lastRowStart = (uint8_t*) srcData +
+                                        (desc.fHeight - 1) * rowSize;
+                uint8_t* extraRowStart = (uint8_t*)texels.get();
+
+                for (uint32_t i = 0; i < extraH; ++i) {
+                    memcpy(extraRowStart, lastRowStart, rowSize);
+                    extraRowStart += rowSize;
+                }
+                GR_GL(TexSubImage2D(GL_TEXTURE_2D, 0, 0, desc.fHeight, desc.fWidth,
+                                    extraH, glDesc.fUploadFormat, glDesc.fUploadType,
+                                    texels.get()));
+            }
+            if (extraW) {
+                uint8_t* edgeTexel = (uint8_t*)srcData + rowSize - glDesc.fUploadByteCount;
+                uint8_t* extraTexel = (uint8_t*)texels.get();
+                for (uint32_t j = 0; j < desc.fHeight; ++j) {
+                    for (uint32_t i = 0; i < extraW; ++i) {
+                        memcpy(extraTexel, edgeTexel, glDesc.fUploadByteCount);
+                        extraTexel += glDesc.fUploadByteCount;
+                    }
+                    edgeTexel += rowSize;
+                }
+                GR_GL(TexSubImage2D(GL_TEXTURE_2D, 0, desc.fWidth, 0, extraW,
+                                    desc.fHeight, glDesc.fUploadFormat,
+                                    glDesc.fUploadType, texels.get()));
+            }
+            if (extraW && extraH) {
+                uint8_t* cornerTexel = (uint8_t*)srcData + desc.fHeight * rowSize
+                                       - glDesc.fUploadByteCount;
+                uint8_t* extraTexel = (uint8_t*)texels.get();
+                for (uint32_t i = 0; i < extraW*extraH; ++i) {
+                    memcpy(extraTexel, cornerTexel, glDesc.fUploadByteCount);
+                    extraTexel += glDesc.fUploadByteCount;
+                }
+                GR_GL(TexSubImage2D(GL_TEXTURE_2D, 0, desc.fWidth, desc.fHeight,
+                                    extraW, extraH, glDesc.fUploadFormat,
+                                    glDesc.fUploadType, texels.get()));
+            }
+
+        } else {
+            GR_GL(TexImage2D(GL_TEXTURE_2D, 0, internalFormat, glDesc.fAllocWidth,
+                             glDesc.fAllocHeight, 0, glDesc.fUploadFormat,
+                             glDesc.fUploadType, srcData));
+            GrGL_RestoreResetRowLength();
+        }
+    }
+
+    glDesc.fOrientation = GrGLTexture::kTopDown_Orientation;
+
+    GrGLRenderTarget::GLRenderTargetIDs rtIDs;
+    rtIDs.fStencilRenderbufferID = 0;
+    rtIDs.fMSColorRenderbufferID = 0;
+    rtIDs.fRTFBOID = 0;
+    rtIDs.fTexFBOID = 0;
+    rtIDs.fOwnIDs = true;
+    GLenum msColorRenderbufferFormat = -1;
+
+    if (renderTarget) {
+#if GR_COLLECT_STATS
+        ++fStats.fRenderTargetCreateCnt;
+#endif
+        bool failed = true;
+        GLenum status;
+        GLint err;
+
+        // If need have both RT flag and srcData we have
+        // to invert the data before uploading because FBO
+        // will be rendered bottom up
+        GrAssert(NULL == srcData);
+        glDesc.fOrientation =  GrGLTexture::kBottomUp_Orientation;
+
+        GR_GLEXT(fExts, GenFramebuffers(1, &rtIDs.fTexFBOID));
+        GrAssert(rtIDs.fTexFBOID);
+
+        // If we are using multisampling and any extension other than the IMG
+        // one we will create two FBOs. We render to one and then resolve to
+        // the texture bound to the other. The IMG extension does an implicit
+        // resolve.
+        if (samples > 1 && kIMG_MSFBO != fMSFBOType && kNone_MSFBO != fMSFBOType) {
+            GR_GLEXT(fExts, GenFramebuffers(1, &rtIDs.fRTFBOID));
+            GrAssert(0 != rtIDs.fRTFBOID);
+            GR_GLEXT(fExts, GenRenderbuffers(1, &rtIDs.fMSColorRenderbufferID));
+            GrAssert(0 != rtIDs.fMSColorRenderbufferID);
+            if (!fboInternalFormat(desc.fFormat, &msColorRenderbufferFormat)) {
+                GR_GLEXT(fExts,
+                         DeleteRenderbuffers(1, &rtIDs.fMSColorRenderbufferID));
+                GR_GL(DeleteTextures(1, &glDesc.fTextureID));
+                GR_GLEXT(fExts, DeleteFramebuffers(1, &rtIDs.fTexFBOID));
+                GR_GLEXT(fExts, DeleteFramebuffers(1, &rtIDs.fRTFBOID));
+                fHWDrawState.fTexture = NULL;
+                return return_null_texture();
+            }
+        } else {
+            rtIDs.fRTFBOID = rtIDs.fTexFBOID;
+        }
+        int attempts = 1;
+        if (!(kNoPathRendering_TextureFlag & desc.fFlags)) {
+            GR_GLEXT(fExts, GenRenderbuffers(1, &rtIDs.fStencilRenderbufferID));
+            GrAssert(0 != rtIDs.fStencilRenderbufferID);
+            attempts = GR_ARRAY_COUNT(GR_GL_STENCIL_FORMAT_ARRAY);
+        }
+
+        // need to unbind the texture before we call FramebufferTexture2D
+        GR_GL(BindTexture(GL_TEXTURE_2D, 0));
+#if GR_COLLECT_STATS
+        ++fStats.fTextureChngCnt;
+#endif
+
+        fHWDrawState.fTexture = NULL;
+
+        err = ~GL_NO_ERROR;
+        for (int i = 0; i < attempts; ++i) {
+            if (rtIDs.fStencilRenderbufferID) {
+                GR_GLEXT(fExts, BindRenderbuffer(GR_RENDERBUFFER,
+                                                 rtIDs.fStencilRenderbufferID));
+                if (samples > 1) {
+                    GR_GLEXT_NO_ERR(fExts, RenderbufferStorageMultisample(
+                                                GR_RENDERBUFFER,
+                                                samples,
+                                                GR_GL_STENCIL_FORMAT_ARRAY[i],
+                                                glDesc.fAllocWidth,
+                                                glDesc.fAllocHeight));
+                } else {
+                    GR_GLEXT_NO_ERR(fExts, RenderbufferStorage(
+                                                GR_RENDERBUFFER,
+                                                GR_GL_STENCIL_FORMAT_ARRAY[i],
+                                                glDesc.fAllocWidth,
+                                                glDesc.fAllocHeight));
+                }
+                err = glGetError();
+                if (err != GL_NO_ERROR) {
+                    continue;
+                }
+            }
+            if (rtIDs.fRTFBOID != rtIDs.fTexFBOID) {
+                GrAssert(samples > 1);
+                GR_GLEXT(fExts, BindRenderbuffer(GR_RENDERBUFFER,
+                                                 rtIDs.fMSColorRenderbufferID));
+                GR_GLEXT_NO_ERR(fExts, RenderbufferStorageMultisample(
+                                                   GR_RENDERBUFFER,
+                                                   samples,
+                                                   msColorRenderbufferFormat,
+                                                   glDesc.fAllocWidth,
+                                                   glDesc.fAllocHeight));
+                err = glGetError();
+                if (err != GL_NO_ERROR) {
+                    continue;
+                }
+            }
+            GR_GLEXT(fExts, BindFramebuffer(GR_FRAMEBUFFER, rtIDs.fTexFBOID));
+
+#if GR_COLLECT_STATS
+            ++fStats.fRenderTargetChngCnt;
+#endif
+            if (kIMG_MSFBO == fMSFBOType && samples > 1) {
+                GR_GLEXT(fExts, FramebufferTexture2DMultisample(
+                                                         GR_FRAMEBUFFER,
+                                                         GR_COLOR_ATTACHMENT0,
+                                                         GL_TEXTURE_2D,
+                                                         glDesc.fTextureID,
+                                                         0,
+                                                         samples));
+
+            } else {
+                GR_GLEXT(fExts, FramebufferTexture2D(GR_FRAMEBUFFER,
+                                                     GR_COLOR_ATTACHMENT0,
+                                                     GL_TEXTURE_2D,
+                                                     glDesc.fTextureID, 0));
+            }
+            if (rtIDs.fRTFBOID != rtIDs.fTexFBOID) {
+                GLenum status = GR_GLEXT(fExts,
+                                         CheckFramebufferStatus(GR_FRAMEBUFFER));
+                if (status != GR_FRAMEBUFFER_COMPLETE) {
+                    GrPrintf("-- glCheckFramebufferStatus %x %d %d\n",
+                             status, desc.fWidth, desc.fHeight);
+                    continue;
+                }
+                GR_GLEXT(fExts, BindFramebuffer(GR_FRAMEBUFFER, rtIDs.fRTFBOID));
+            #if GR_COLLECT_STATS
+                ++fStats.fRenderTargetChngCnt;
+            #endif
+                GR_GLEXT(fExts, FramebufferRenderbuffer(GR_FRAMEBUFFER,
+                                                 GR_COLOR_ATTACHMENT0,
+                                                 GR_RENDERBUFFER,
+                                                 rtIDs.fMSColorRenderbufferID));
+
+            }
+            if (rtIDs.fStencilRenderbufferID) {
+                // bind the stencil to rt fbo if present, othewise the tex fbo
+                GR_GLEXT(fExts, FramebufferRenderbuffer(GR_FRAMEBUFFER,
+                                                 GR_STENCIL_ATTACHMENT,
+                                                 GR_RENDERBUFFER,
+                                                 rtIDs.fStencilRenderbufferID));
+            }
+            status = GR_GLEXT(fExts, CheckFramebufferStatus(GR_FRAMEBUFFER));
+
+#if GR_GL_DESKTOP
+            // On some implementations you have to be bound as DEPTH_STENCIL.
+            // (Even binding to DEPTH and STENCIL separately with the same
+            // buffer doesn't work.)
+            if (rtIDs.fStencilRenderbufferID &&
+                status != GR_FRAMEBUFFER_COMPLETE) {
+                GR_GLEXT(fExts, FramebufferRenderbuffer(GR_FRAMEBUFFER,
+                                                        GR_STENCIL_ATTACHMENT,
+                                                        GR_RENDERBUFFER,
+                                                        0));
+                GR_GLEXT(fExts,
+                         FramebufferRenderbuffer(GR_FRAMEBUFFER,
+                                                 GR_DEPTH_STENCIL_ATTACHMENT,
+                                                 GR_RENDERBUFFER,
+                                                 rtIDs.fStencilRenderbufferID));
+                status = GR_GLEXT(fExts, CheckFramebufferStatus(GR_FRAMEBUFFER));
+            }
+#endif
+            if (status != GR_FRAMEBUFFER_COMPLETE) {
+                GrPrintf("-- glCheckFramebufferStatus %x %d %d\n",
+                         status, desc.fWidth, desc.fHeight);
+#if GR_GL_DESKTOP
+                if (rtIDs.fStencilRenderbufferID) {
+                    GR_GLEXT(fExts, FramebufferRenderbuffer(GR_FRAMEBUFFER,
+                                                     GR_DEPTH_STENCIL_ATTACHMENT,
+                                                     GR_RENDERBUFFER,
+                                                     0));
+                }
+#endif
+                continue;
+            }
+            // we're successful!
+            failed = false;
+            break;
+        }
+        if (failed) {
+            if (rtIDs.fStencilRenderbufferID) {
+                GR_GLEXT(fExts,
+                         DeleteRenderbuffers(1, &rtIDs.fStencilRenderbufferID));
+            }
+            if (rtIDs.fMSColorRenderbufferID) {
+                GR_GLEXT(fExts,
+                         DeleteRenderbuffers(1, &rtIDs.fMSColorRenderbufferID));
+            }
+            if (rtIDs.fRTFBOID != rtIDs.fTexFBOID) {
+                GR_GLEXT(fExts, DeleteFramebuffers(1, &rtIDs.fRTFBOID));
+            }
+            if (rtIDs.fTexFBOID) {
+                GR_GLEXT(fExts, DeleteFramebuffers(1, &rtIDs.fTexFBOID));
+            }
+            GR_GL(DeleteTextures(1, &glDesc.fTextureID));
+            return return_null_texture();
+        }
+    }
+#ifdef TRACE_TEXTURE_CREATION
+    GrPrintf("--- new texture [%d] size=(%d %d) bpp=%d\n",
+             tex->fTextureID, width, height, tex->fUploadByteCount);
+#endif
+    GrGLTexture* tex = new GrGLTexture(glDesc, rtIDs, this);
+
+    if (0 != rtIDs.fTexFBOID) {
+        GrRenderTarget* rt = tex->asRenderTarget();
+        // We've messed with FBO state but may not have set the correct viewport
+        // so just dirty the rendertarget state to force a resend.
+        fHWDrawState.fRenderTarget = NULL;
+
+        // clear the new stencil buffer if we have one
+        if (!(desc.fFlags & kNoPathRendering_TextureFlag)) {
+            GrRenderTarget* rtSave = fCurrDrawState.fRenderTarget;
+            fCurrDrawState.fRenderTarget = rt;
+            eraseStencil(0, ~0);
+            fCurrDrawState.fRenderTarget = rtSave;
+        }
+    }
+    return tex;
+}
+
+GrRenderTarget* GrGpuGL::defaultRenderTarget() {
+    return fDefaultRenderTarget;
+}
+
+GrVertexBuffer* GrGpuGL::createVertexBuffer(uint32_t size, bool dynamic) {
+    GLuint id;
+    GR_GL(GenBuffers(1, &id));
+    if (id) {
+        GR_GL(BindBuffer(GL_ARRAY_BUFFER, id));
+        GrGLClearErr();
+        // make sure driver can allocate memory for this buffer
+        GR_GL_NO_ERR(BufferData(GL_ARRAY_BUFFER, size, NULL,
+                                dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW));
+        if (glGetError() != GL_NO_ERROR) {
+            GR_GL(DeleteBuffers(1, &id));
+            // deleting bound buffer does implicit bind to 0
+            fHWGeometryState.fVertexBuffer = NULL;
+            return NULL;
+        }
+        GrGLVertexBuffer* vertexBuffer = new GrGLVertexBuffer(id, this,
+                                                              size, dynamic);
+        fHWGeometryState.fVertexBuffer = vertexBuffer;
+        return vertexBuffer;
+    }
+    return NULL;
+}
+
+GrIndexBuffer* GrGpuGL::createIndexBuffer(uint32_t size, bool dynamic) {
+    GLuint id;
+    GR_GL(GenBuffers(1, &id));
+    if (id) {
+        GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, id));
+        GrGLClearErr();
+        // make sure driver can allocate memory for this buffer
+        GR_GL_NO_ERR(BufferData(GL_ELEMENT_ARRAY_BUFFER, size, NULL,
+                                dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW));
+        if (glGetError() != GL_NO_ERROR) {
+            GR_GL(DeleteBuffers(1, &id));
+            // deleting bound buffer does implicit bind to 0
+            fHWGeometryState.fIndexBuffer = NULL;
+            return NULL;
+        }
+        GrIndexBuffer* indexBuffer = new GrGLIndexBuffer(id, this,
+                                                         size, dynamic);
+        fHWGeometryState.fIndexBuffer = indexBuffer;
+        return indexBuffer;
+    }
+    return NULL;
+}
+
+void GrGpuGL::setDefaultRenderTargetSize(uint32_t width, uint32_t height) {
+    GrIRect viewport(0, height, width, 0);
+    if (viewport != fDefaultRenderTarget->viewport()) {
+        fDefaultRenderTarget->setViewport(viewport);
+        if (fHWDrawState.fRenderTarget == fDefaultRenderTarget) {
+            fHWDrawState.fRenderTarget = NULL;
+        }
+    }
+}
+
+void GrGpuGL::flushScissor(const GrIRect* rect) {
+    GrAssert(NULL != fCurrDrawState.fRenderTarget);
+    const GrIRect& vp =
+            ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->viewport();
+
+    if (NULL != rect &&
+        rect->contains(vp)) {
+        rect = NULL;
+    }
+
+    if (NULL != rect) {
+        GrIRect scissor;
+        // viewport is already in GL coords
+        // create a scissor in GL coords (top > bottom)
+        scissor.setLTRB(vp.fLeft + rect->fLeft,
+                        vp.fTop  - rect->fTop,
+                        vp.fLeft + rect->fRight,
+                        vp.fTop  - rect->fBottom);
+
+        if (fHWBounds.fScissorRect != scissor) {
+            GR_GL(Scissor(scissor.fLeft, scissor.fBottom,
+                          scissor.width(), -scissor.height()));
+            fHWBounds.fScissorRect = scissor;
+        }
+
+        if (!fHWBounds.fScissorEnabled) {
+            GR_GL(Enable(GL_SCISSOR_TEST));
+            fHWBounds.fScissorEnabled = true;
+        }
+    } else {
+        if (fHWBounds.fScissorEnabled) {
+            GR_GL(Disable(GL_SCISSOR_TEST));
+            fHWBounds.fScissorEnabled = false;
+        }
+    }
+}
+
+void GrGpuGL::setSamplerStateImm(const GrSamplerState& state) {
+
+    GLenum filter = state.isFilter() ? GL_LINEAR : GL_NEAREST;
+    GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter));
+    GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter));
+    GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+                        GrGLTexture::gWrapMode2GLWrap[state.getWrapX()]));
+    GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+                        GrGLTexture::gWrapMode2GLWrap[state.getWrapY()]));
+
+}
+
+void GrGpuGL::eraseColor(GrColor color) {
+    flushRenderTarget();
+    if (fHWBounds.fScissorEnabled) {
+        GR_GL(Disable(GL_SCISSOR_TEST));
+    }
+    GR_GL(ColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE));
+    GR_GL(ClearColor(GrColorUnpackR(color)/255.f,
+                     GrColorUnpackG(color)/255.f,
+                     GrColorUnpackB(color)/255.f,
+                     GrColorUnpackA(color)/255.f));
+    GR_GL(Clear(GL_COLOR_BUFFER_BIT));
+    fHWBounds.fScissorEnabled = false;
+    fWriteMaskChanged = true;
+}
+
+void GrGpuGL::eraseStencil(uint32_t value, uint32_t mask) {
+    flushRenderTarget();
+    if (fHWBounds.fScissorEnabled) {
+        GR_GL(Disable(GL_SCISSOR_TEST));
+    }
+    GR_GL(StencilMask(mask));
+    GR_GL(ClearStencil(value));
+    GR_GL(Clear(GL_STENCIL_BUFFER_BIT));
+    fHWBounds.fScissorEnabled = false;
+    fWriteMaskChanged = true;
+}
+
+void GrGpuGL::eraseStencilClip() {
+    GLint stencilBitCount;
+    GR_GL(GetIntegerv(GL_STENCIL_BITS, &stencilBitCount));
+    GrAssert(stencilBitCount > 0);
+    GLint clipStencilMask  = (1 << (stencilBitCount - 1));
+    eraseStencil(0, clipStencilMask);
+}
+
+void GrGpuGL::forceRenderTargetFlush() {
+    flushRenderTarget();
+}
+
+bool GrGpuGL::readPixels(int left, int top, int width, int height,
+                         GrTexture::PixelConfig config, void* buffer) {
+    GLenum internalFormat;  // we don't use this for glReadPixels
+    GLenum format;
+    GLenum type;
+    if (!this->canBeTexture(config, &internalFormat, &format, &type)) {
+        return false;
+    }
+
+    GrAssert(NULL != fCurrDrawState.fRenderTarget);
+    const GrIRect& vp = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->viewport();
+
+    // Brian says that viewport rects are already upside down (grrrrr)
+    glReadPixels(left, -vp.height() - top - height, width, height,
+                 format, type, buffer);
+
+    // now reverse the order of the rows, since GL's are bottom-to-top, but our
+    // API presents top-to-bottom
+    {
+        size_t stride = width * GrTexture::BytesPerPixel(config);
+        GrAutoMalloc rowStorage(stride);
+        void* tmp = rowStorage.get();
+
+        const int halfY = height >> 1;
+        char* top = reinterpret_cast<char*>(buffer);
+        char* bottom = top + (height - 1) * stride;
+        for (int y = 0; y < halfY; y++) {
+            memcpy(tmp, top, stride);
+            memcpy(top, bottom, stride);
+            memcpy(bottom, tmp, stride);
+            top += stride;
+            bottom -= stride;
+        }
+    }
+    return true;
+}
+
+void GrGpuGL::flushRenderTarget() {
+    if (fHWDrawState.fRenderTarget != fCurrDrawState.fRenderTarget) {
+        GrGLRenderTarget* rt = (GrGLRenderTarget*)fCurrDrawState.fRenderTarget;
+        GR_GLEXT(fExts, BindFramebuffer(GR_FRAMEBUFFER, rt->renderFBOID()));
+    #if GR_COLLECT_STATS
+        ++fStats.fRenderTargetChngCnt;
+    #endif
+        rt->setDirty(true);
+    #if GR_DEBUG
+        GLenum status = GR_GLEXT(fExts, CheckFramebufferStatus(GR_FRAMEBUFFER));
+        if (status != GR_FRAMEBUFFER_COMPLETE) {
+            GrPrintf("-- glCheckFramebufferStatus %x\n", status);
+        }
+    #endif
+        fHWDrawState.fRenderTarget = fCurrDrawState.fRenderTarget;
+        const GrIRect& vp = rt->viewport();
+        fRenderTargetChanged = true;
+        if (fHWBounds.fViewportRect != vp) {
+            GR_GL(Viewport(vp.fLeft,
+                           vp.fBottom,
+                           vp.width(),
+                           -vp.height()));
+            fHWBounds.fViewportRect = vp;
+        }
+    }
+}
+
+GLenum gPrimitiveType2GLMode[] = {
+    GL_TRIANGLES,
+    GL_TRIANGLE_STRIP,
+    GL_TRIANGLE_FAN,
+    GL_POINTS,
+    GL_LINES,
+    GL_LINE_STRIP
+};
+
+void GrGpuGL::drawIndexedHelper(PrimitiveType type,
+                                uint32_t startVertex,
+                                uint32_t startIndex,
+                                uint32_t vertexCount,
+                                uint32_t indexCount) {
+    GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
+
+    GLvoid* indices = (GLvoid*)(sizeof(uint16_t) * startIndex);
+    if (kReserved_GeometrySrcType == fGeometrySrc.fIndexSrc) {
+        indices = (GLvoid*)((intptr_t)indices + (intptr_t)fIndices.get());
+    } else if (kArray_GeometrySrcType == fGeometrySrc.fIndexSrc) {
+        indices = (GLvoid*)((intptr_t)indices +
+                            (intptr_t)fGeometrySrc.fIndexArray);
+    }
+
+    GR_GL(DrawElements(gPrimitiveType2GLMode[type], indexCount,
+                       GL_UNSIGNED_SHORT, indices));
+}
+
+void GrGpuGL::drawNonIndexedHelper(PrimitiveType type,
+                                   uint32_t startVertex,
+                                   uint32_t vertexCount) {
+    GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
+
+    GR_GL(DrawArrays(gPrimitiveType2GLMode[type], 0, vertexCount));
+}
+
+#if !defined(SK_GL_HAS_COLOR4UB)
+static inline GrFixed byte2fixed(unsigned value) {
+    return (value + (value >> 7)) << 8;
+}
+#endif
+
+void GrGpuGL::resolveTextureRenderTarget(GrGLTexture* texture) {
+    GrGLRenderTarget* rt = (GrGLRenderTarget*) texture->asRenderTarget();
+
+    if (NULL != rt && rt->needsResolve()) {
+        GrAssert(kNone_MSFBO != fMSFBOType);
+        GrAssert(rt->textureFBOID() != rt->renderFBOID());
+        GR_GLEXT(fExts, BindFramebuffer(GR_READ_FRAMEBUFFER,
+                                        rt->renderFBOID()));
+        GR_GLEXT(fExts, BindFramebuffer(GR_DRAW_FRAMEBUFFER,
+                                        rt->textureFBOID()));
+    #if GR_COLLECT_STATS
+        ++fStats.fRenderTargetChngCnt;
+    #endif
+        // make sure we go through set render target
+        fHWDrawState.fRenderTarget = NULL;
+
+        GLint left = 0;
+        GLint right = texture->contentWidth();
+        // we will have rendered to the top of the FBO.
+        GLint top = texture->allocHeight();
+        GLint bottom = texture->allocHeight() - texture->contentHeight();
+        if (kApple_MSFBO == fMSFBOType) {
+            GR_GL(Enable(GL_SCISSOR_TEST));
+            GR_GL(Scissor(left, bottom, right-left, top-bottom));
+            GR_GLEXT(fExts, ResolveMultisampleFramebuffer());
+            fHWBounds.fScissorRect.setEmpty();
+            fHWBounds.fScissorEnabled = true;
+        } else {
+            GR_GLEXT(fExts, BlitFramebuffer(left, bottom, right, top,
+                                     left, bottom, right, top,
+                                     GL_COLOR_BUFFER_BIT, GL_NEAREST));
+        }
+        rt->setDirty(false);
+
+    }
+}
+
+void GrGpuGL::flushStencil() {
+
+    // use stencil for clipping if clipping is enabled and the clip
+    // has been written into the stencil.
+    bool stencilClip = fClipState.fClipInStencil &&
+                       (kClip_StateBit & fCurrDrawState.fFlagBits);
+    bool stencilChange =
+        fWriteMaskChanged                                         ||
+        fHWStencilClip != stencilClip                             ||
+        fHWDrawState.fStencilPass != fCurrDrawState.fStencilPass  ||
+        (kNone_StencilPass != fCurrDrawState.fStencilPass &&
+         (StencilPass)kSetClip_StencilPass != fCurrDrawState.fStencilPass &&
+         fHWDrawState.fReverseFill != fCurrDrawState.fReverseFill);
+
+    if (stencilChange) {
+        GLint stencilBitCount;
+        GLint clipStencilMask;
+        GLint pathStencilMask;
+        GR_GL(GetIntegerv(GL_STENCIL_BITS, &stencilBitCount));
+        GrAssert(stencilBitCount > 0 ||
+                 kNone_StencilPass == fCurrDrawState.fStencilPass);
+        clipStencilMask  = (1 << (stencilBitCount - 1));
+        pathStencilMask = clipStencilMask - 1;
+        switch (fCurrDrawState.fStencilPass) {
+            case kNone_StencilPass:
+                if (stencilClip) {
+                    GR_GL(Enable(GL_STENCIL_TEST));
+                    GR_GL(StencilFunc(GL_EQUAL,
+                                      clipStencilMask,
+                                      clipStencilMask));
+                    GR_GL(StencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
+                } else {
+                    GR_GL(Disable(GL_STENCIL_TEST));
+                }
+                GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
+                if (!fSingleStencilPassForWinding) {
+                    GR_GL(Disable(GL_CULL_FACE));
+                }
+                break;
+            case kEvenOddStencil_StencilPass:
+                GR_GL(Enable(GL_STENCIL_TEST));
+                if (stencilClip) {
+                    GR_GL(StencilFunc(GL_EQUAL, clipStencilMask, clipStencilMask));
+                } else {
+                    GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0));
+                }
+                GR_GL(StencilMask(pathStencilMask));
+                GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
+                GR_GL(StencilOp(GL_KEEP, GL_INVERT, GL_INVERT));
+                GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
+                if (!fSingleStencilPassForWinding) {
+                    GR_GL(Disable(GL_CULL_FACE));
+                }
+                break;
+            case kEvenOddColor_StencilPass: {
+                GR_GL(Enable(GL_STENCIL_TEST));
+                GLint  funcRef  = 0;
+                GLuint funcMask = pathStencilMask;
+                if (stencilClip) {
+                    funcRef  |= clipStencilMask;
+                    funcMask |= clipStencilMask;
+                }
+                if (!fCurrDrawState.fReverseFill) {
+                    funcRef |= pathStencilMask;
+                }
+                glStencilFunc(GL_EQUAL, funcRef, funcMask);
+                glStencilMask(pathStencilMask);
+                GR_GL(StencilOp(GL_ZERO, GL_ZERO, GL_ZERO));
+                GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
+                if (!fSingleStencilPassForWinding) {
+                    GR_GL(Disable(GL_CULL_FACE));
+                }
+                } break;
+            case kWindingStencil1_StencilPass:
+                GR_GL(Enable(GL_STENCIL_TEST));
+                if (fHasStencilWrap) {
+                    if (stencilClip) {
+                        GR_GL(StencilFunc(GL_EQUAL,
+                                          clipStencilMask,
+                                          clipStencilMask));
+                    } else {
+                        GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0));
+                    }
+                    if (fSingleStencilPassForWinding) {
+                        GR_GL(StencilOpSeparate(GL_FRONT, GL_KEEP,
+                                                GL_INCR_WRAP, GL_INCR_WRAP));
+                        GR_GL(StencilOpSeparate(GL_BACK,  GL_KEEP,
+                                                GL_DECR_WRAP, GL_DECR_WRAP));
+                    } else {
+                        GR_GL(StencilOp(GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP));
+                        GR_GL(Enable(GL_CULL_FACE));
+                        GR_GL(CullFace(GL_BACK));
+                    }
+                } else {
+                    // If we don't have wrap then we use the Func to detect
+                    // values that would wrap (0 on decr and mask on incr). We
+                    // make the func fail on these values and use the sfail op
+                    // to effectively wrap by inverting.
+                    // This applies whether we are doing a two-pass (front faces
+                    // followed by back faces) or a single pass (separate func/op)
+
+                    // Note that in the case where we are also using stencil to
+                    // clip this means we will write into the path bits in clipped
+                    // out pixels. We still apply the clip bit in the color pass
+                    // stencil func so we don't draw color outside the clip.
+                    // We also will clear the stencil bits in clipped pixels by
+                    // using zero in the sfail op with write mask set to the
+                    // path mask.
+                    GR_GL(Enable(GL_STENCIL_TEST));
+                    if (fSingleStencilPassForWinding) {
+                        GR_GL(StencilFuncSeparate(GL_FRONT,
+                                                  GL_NOTEQUAL,
+                                                  pathStencilMask,
+                                                  pathStencilMask));
+                        GR_GL(StencilFuncSeparate(GL_BACK,
+                                                  GL_NOTEQUAL,
+                                                  0x0,
+                                                  pathStencilMask));
+                        GR_GL(StencilOpSeparate(GL_FRONT, GL_INVERT,
+                                                GL_INCR, GL_INCR));
+                        GR_GL(StencilOpSeparate(GL_BACK,  GL_INVERT,
+                                                GL_DECR, GL_DECR));
+                    } else {
+                        GR_GL(StencilFunc(GL_NOTEQUAL,
+                                          pathStencilMask,
+                                          pathStencilMask));
+                        GR_GL(StencilOp(GL_INVERT, GL_INCR, GL_INCR));
+                        GR_GL(Enable(GL_CULL_FACE));
+                        GR_GL(CullFace(GL_BACK));
+                    }
+                }
+                GR_GL(StencilMask(pathStencilMask));
+                GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
+                break;
+            case kWindingStencil2_StencilPass:
+                GrAssert(!fSingleStencilPassForWinding);
+                GR_GL(Enable(GL_STENCIL_TEST));
+                if (fHasStencilWrap) {
+                    if (stencilClip) {
+                        GR_GL(StencilFunc(GL_EQUAL,
+                                          clipStencilMask,
+                                          clipStencilMask));
+                    } else {
+                        GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0));
+                    }
+                    GR_GL(StencilOp(GL_DECR_WRAP, GL_DECR_WRAP, GL_DECR_WRAP));
+                } else {
+                    GR_GL(StencilFunc(GL_NOTEQUAL, 0x0, pathStencilMask));
+                    GR_GL(StencilOp(GL_INVERT, GL_DECR, GL_DECR));
+                }
+                GR_GL(StencilMask(pathStencilMask));
+                GR_GL(Enable(GL_CULL_FACE));
+                GR_GL(CullFace(GL_FRONT));
+                GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
+                break;
+            case kWindingColor_StencilPass: {
+                GR_GL(Enable(GL_STENCIL_TEST));
+                GLint  funcRef   = 0;
+                GLuint funcMask  = pathStencilMask;
+                GLenum funcFunc;
+                if (stencilClip) {
+                    funcRef  |= clipStencilMask;
+                    funcMask |= clipStencilMask;
+                }
+                if (fCurrDrawState.fReverseFill) {
+                    funcFunc = GL_EQUAL;
+                } else {
+                    funcFunc = GL_LESS;
+                }
+                GR_GL(StencilFunc(funcFunc, funcRef, funcMask));
+                GR_GL(StencilMask(pathStencilMask));
+                // must zero in sfail because winding w/o wrap will write
+                // path stencil bits in clipped out pixels
+                GR_GL(StencilOp(GL_ZERO, GL_ZERO, GL_ZERO));
+                GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
+                if (!fSingleStencilPassForWinding) {
+                    GR_GL(Disable(GL_CULL_FACE));
+                }
+                } break;
+            case kSetClip_StencilPass:
+                GR_GL(Enable(GL_STENCIL_TEST));
+                GR_GL(StencilFunc(GL_ALWAYS, clipStencilMask, clipStencilMask));
+                GR_GL(StencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE));
+                GR_GL(StencilMask(clipStencilMask));
+                GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
+                if (!fSingleStencilPassForWinding) {
+                    GR_GL(Disable(GL_CULL_FACE));
+                }
+                break;
+            default:
+                GrAssert(!"Unexpected stencil pass.");
+                break;
+
+        }
+        fHWDrawState.fStencilPass = fCurrDrawState.fStencilPass;
+        fHWDrawState.fReverseFill = fCurrDrawState.fReverseFill;
+        fWriteMaskChanged = false;
+        fHWStencilClip = stencilClip;
+    }
+}
+
+void GrGpuGL::flushGLStateCommon(PrimitiveType type) {
+
+    bool usingTexture = VertexHasTexCoords(fGeometrySrc.fVertexLayout);
+
+    // bind texture and set sampler state
+    if (usingTexture) {
+        GrGLTexture* nextTexture = (GrGLTexture*)fCurrDrawState.fTexture;
+
+        if (NULL != nextTexture) {
+            // if we created a rt/tex and rendered to it without using a texture
+            // and now we're texuring from the rt it will still be the last bound
+            // texture, but it needs resolving. So keep this out of the last
+            // != next check.
+            resolveTextureRenderTarget(nextTexture);
+
+            if (fHWDrawState.fTexture != nextTexture) {
+
+                GR_GL(BindTexture(GL_TEXTURE_2D, nextTexture->textureID()));
+            #if GR_COLLECT_STATS
+                ++fStats.fTextureChngCnt;
+            #endif
+                //GrPrintf("---- bindtexture %d\n", nextTexture->textureID());
+                fHWDrawState.fTexture = nextTexture;
+            }
+            const GrSamplerState& lastSampler = nextTexture->samplerState();
+            if (lastSampler.isFilter() != fCurrDrawState.fSamplerState.isFilter()) {
+                GLenum filter = fCurrDrawState.fSamplerState.isFilter() ?
+                                                                    GL_LINEAR :
+                                                                    GL_NEAREST;
+                GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+                                    filter));
+                GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+                                    filter));
+            }
+            if (lastSampler.getWrapX() != fCurrDrawState.fSamplerState.getWrapX()) {
+                GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+                                    GrGLTexture::gWrapMode2GLWrap[
+                                             fCurrDrawState.fSamplerState.getWrapX()]));
+            }
+            if (lastSampler.getWrapY() != fCurrDrawState.fSamplerState.getWrapY()) {
+                GR_GL(TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+                                    GrGLTexture::gWrapMode2GLWrap[
+                                             fCurrDrawState.fSamplerState.getWrapY()]));
+            }
+            nextTexture->setSamplerState(fCurrDrawState.fSamplerState);
+        } else {
+            GrAssert(!"Rendering with texture vert flag set but no texture");
+            if (NULL != fHWDrawState.fTexture) {
+                GR_GL(BindTexture(GL_TEXTURE_2D, 0));
+                //            GrPrintf("---- bindtexture 0\n");
+            #if GR_COLLECT_STATS
+                ++fStats.fTextureChngCnt;
+            #endif
+                fHWDrawState.fTexture = NULL;
+            }
+        }
+    }
+
+    flushRenderTarget();
+
+    if ((fCurrDrawState.fFlagBits & kDither_StateBit) !=
+        (fHWDrawState.fFlagBits & kDither_StateBit)) {
+        if (fCurrDrawState.fFlagBits & kDither_StateBit) {
+            GR_GL(Enable(GL_DITHER));
+        } else {
+            GR_GL(Disable(GL_DITHER));
+        }
+    }
+
+#if GR_GL_DESKTOP
+    // ES doesn't support toggling GL_MULTISAMPLE and doesn't have
+    // smooth lines.
+    if (fRenderTargetChanged ||
+        (fCurrDrawState.fFlagBits & kAntialias_StateBit) !=
+        (fHWDrawState.fFlagBits & kAntialias_StateBit)) {
+        GLint msaa = 0;
+        // only perform query if we know MSAA is supported.
+        // calling on non-MSAA target caused a crash in one environment,
+        // though I don't think it should.
+        if (!fAASamples[kHigh_AALevel]) {
+            GR_GL(GetIntegerv(GL_SAMPLE_BUFFERS, &msaa));
+        }
+        if (fCurrDrawState.fFlagBits & kAntialias_StateBit) {
+            if (msaa) {
+                GR_GL(Enable(GL_MULTISAMPLE));
+            } else {
+                GR_GL(Enable(GL_LINE_SMOOTH));
+            }
+        } else {
+            if (msaa) {
+                GR_GL(Disable(GL_MULTISAMPLE));
+            }
+            GR_GL(Disable(GL_LINE_SMOOTH));
+        }
+    }
+#endif
+
+    bool blendOff = canDisableBlend();
+    if (fHWBlendDisabled != blendOff) {
+        if (blendOff) {
+            GR_GL(Disable(GL_BLEND));
+        } else {
+            GR_GL(Enable(GL_BLEND));
+        }
+        fHWBlendDisabled = blendOff;
+    }
+
+    if (!blendOff) {
+        if (fHWDrawState.fSrcBlend != fCurrDrawState.fSrcBlend ||
+              fHWDrawState.fDstBlend != fCurrDrawState.fDstBlend) {
+            GR_GL(BlendFunc(gXfermodeCoeff2Blend[fCurrDrawState.fSrcBlend],
+                            gXfermodeCoeff2Blend[fCurrDrawState.fDstBlend]));
+            fHWDrawState.fSrcBlend = fCurrDrawState.fSrcBlend;
+            fHWDrawState.fDstBlend = fCurrDrawState.fDstBlend;
+        }
+    }
+
+    // check for circular rendering
+    GrAssert(!usingTexture ||
+             NULL == fCurrDrawState.fRenderTarget ||
+             NULL == fCurrDrawState.fTexture ||
+             fCurrDrawState.fTexture->asRenderTarget() != fCurrDrawState.fRenderTarget);
+
+    flushStencil();
+
+    fHWDrawState.fFlagBits = fCurrDrawState.fFlagBits;
+}
+
+void GrGpuGL::notifyVertexBufferBind(const GrGLVertexBuffer* buffer) {
+    fHWGeometryState.fVertexBuffer = buffer;
+}
+
+void GrGpuGL::notifyVertexBufferDelete(const GrGLVertexBuffer* buffer) {
+    GrAssert(!(kBuffer_GeometrySrcType == fGeometrySrc.fVertexSrc &&
+               buffer == fGeometrySrc.fVertexBuffer));
+
+    if (fHWGeometryState.fVertexBuffer == buffer) {
+        // deleting bound buffer does implied bind to 0
+        fHWGeometryState.fVertexBuffer = NULL;
+    }
+}
+
+void GrGpuGL::notifyIndexBufferBind(const GrGLIndexBuffer* buffer) {
+    fGeometrySrc.fIndexBuffer = buffer;
+}
+
+void GrGpuGL::notifyIndexBufferDelete(const GrGLIndexBuffer* buffer) {
+    GrAssert(!(kBuffer_GeometrySrcType == fGeometrySrc.fIndexSrc &&
+               buffer == fGeometrySrc.fIndexBuffer));
+
+    if (fHWGeometryState.fIndexBuffer == buffer) {
+        // deleting bound buffer does implied bind to 0
+        fHWGeometryState.fIndexBuffer = NULL;
+    }
+}
+
+void GrGpuGL::notifyTextureBind(GrGLTexture* texture) {
+    fHWDrawState.fTexture = texture;
+#if GR_COLLECT_STATS
+    ++fStats.fTextureChngCnt;
+#endif
+}
+
+void GrGpuGL::notifyRenderTargetDelete(GrRenderTarget* renderTarget) {
+    GrAssert(NULL != renderTarget);
+
+    // if the bound FBO is destroyed we can't rely on the implicit bind to 0
+    // a) we want the default RT which may not be FBO 0
+    // b) we set more state than just FBO based on the RT
+    // So trash the HW state to force an RT flush next time
+    if (fCurrDrawState.fRenderTarget == renderTarget) {
+        fCurrDrawState.fRenderTarget = (GrRenderTarget*)&fDefaultRenderTarget;
+    }
+    if (fHWDrawState.fRenderTarget == renderTarget) {
+        fHWDrawState.fRenderTarget = NULL;
+    }
+    if (fClipState.fStencilClipTarget == renderTarget) {
+        fClipState.fStencilClipTarget = NULL;
+    }
+}
+
+void GrGpuGL::notifyTextureDelete(GrGLTexture* texture) {
+    if (fCurrDrawState.fTexture == texture) {
+        fCurrDrawState.fTexture = NULL;
+    }
+    if (fHWDrawState.fTexture == texture) {
+        // deleting bound texture does implied bind to 0
+        fHWDrawState.fTexture = NULL;
+   }
+}
+
+void GrGpuGL::notifyTextureRemoveRenderTarget(GrGLTexture* texture) {
+    GrAssert(NULL != texture->asRenderTarget());
+
+    // if there is a pending resolve, perform it.
+    resolveTextureRenderTarget(texture);
+}
+
+bool GrGpuGL::canBeTexture(GrTexture::PixelConfig config,
+                           GLenum* internalFormat,
+                           GLenum* format,
+                           GLenum* type) {
+    switch (config) {
+        case GrTexture::kRGBA_8888_PixelConfig:
+        case GrTexture::kRGBX_8888_PixelConfig: // todo: can we tell it our X?
+            *format = SK_GL_32BPP_COLOR_FORMAT;
+            *internalFormat = GL_RGBA;
+            *type = GL_UNSIGNED_BYTE;
+            break;
+        case GrTexture::kRGB_565_PixelConfig:
+            *format = GL_RGB;
+            *internalFormat = GL_RGB;
+            *type = GL_UNSIGNED_SHORT_5_6_5;
+            break;
+        case GrTexture::kRGBA_4444_PixelConfig:
+            *format = GL_RGBA;
+            *internalFormat = GL_RGBA;
+            *type = GL_UNSIGNED_SHORT_4_4_4_4;
+            break;
+        case GrTexture::kIndex_8_PixelConfig:
+            if (this->supports8BitPalette()) {
+                *format = GR_PALETTE8_RGBA8;
+                *internalFormat = GR_PALETTE8_RGBA8;
+                *type = GL_UNSIGNED_BYTE;   // unused I think
+            } else {
+                return false;
+            }
+            break;
+        case GrTexture::kAlpha_8_PixelConfig:
+            *format = GL_ALPHA;
+            *internalFormat = GL_ALPHA;
+            *type = GL_UNSIGNED_BYTE;
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+
+/* On ES the internalFormat and format must match for TexImage and we use
+   GL_RGB, GL_RGBA for color formats. We also generally like having the driver
+   decide the internalFormat. However, on ES internalFormat for
+   RenderBufferStorage* has to be a specific format (not a base format like
+   GL_RGBA).
+ */
+bool GrGpuGL::fboInternalFormat(GrTexture::PixelConfig config, GLenum* format) {
+    switch (config) {
+        case GrTexture::kRGBA_8888_PixelConfig:
+        case GrTexture::kRGBX_8888_PixelConfig:
+            if (fRGBA8Renderbuffer) {
+                *format = GR_RGBA8;
+                return true;
+            } else {
+                return false;
+            }
+#if GR_GL_ES // ES2 supports 565. ES1 supports it with FBO extension
+             // desktop GL has no such internal format
+        case GrTexture::kRGB_565_PixelConfig:
+            *format = GR_RGB565;
+            return true;
+#endif
+        case GrTexture::kRGBA_4444_PixelConfig:
+            *format = GL_RGBA4;
+            return true;
+        default:
+            return false;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLCheckErr(const char* location, const char* call) {
+    uint32_t err =  glGetError();
+    if (GL_NO_ERROR != err) {
+        GrPrintf("---- glGetError %x", err);
+        if (NULL != location) {
+            GrPrintf(" at\n\t%s", location);
+        }
+        if (NULL != call) {
+            GrPrintf("\n\t\t%s", call);
+        }
+        GrPrintf("\n");
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*glProc)(void);
+
+void get_gl_proc(const char procName[], glProc *address) {
+#if GR_WIN32_BUILD
+    *address = wglGetProcAddress(procName);
+    GrAssert(NULL != *address);
+#elif GR_MAC_BUILD || GR_IOS_BUILD
+    GrAssert(!"Extensions don't need to be initialized!");
+#elif GR_ANDROID_BUILD
+    *address = eglGetProcAddress(procName);
+    GrAssert(NULL != *address);
+#elif GR_LINUX_BUILD
+    GR_STATIC_ASSERT(!"Add environment-dependent implementation here");
+    //*address = glXGetProcAddressARB(procName);
+    //*address = eglGetProcAddress(procName);
+#elif GR_QNX_BUILD
+    *address = eglGetProcAddress(procName);
+    GrAssert(NULL != *address);
+#else
+    // hopefully we're on a system with EGL
+    *address = eglGetProcAddress(procName);
+    GrAssert(NULL != *address);
+#endif
+}
+
+#define GET_PROC(EXT_STRUCT, PROC_NAME, EXT_TAG) \
+    get_gl_proc("gl" #PROC_NAME #EXT_TAG, (glProc*)&EXT_STRUCT-> PROC_NAME);
+
+extern void GrGLInitExtensions(GrGLExts* exts) {
+    exts->GenFramebuffers                   = NULL;
+    exts->BindFramebuffer                   = NULL;
+    exts->FramebufferTexture2D              = NULL;
+    exts->CheckFramebufferStatus            = NULL;
+    exts->DeleteFramebuffers                = NULL;
+    exts->RenderbufferStorage               = NULL;
+    exts->GenRenderbuffers                  = NULL;
+    exts->DeleteRenderbuffers               = NULL;
+    exts->FramebufferRenderbuffer           = NULL;
+    exts->BindRenderbuffer                  = NULL;
+    exts->RenderbufferStorageMultisample    = NULL;
+    exts->BlitFramebuffer                   = NULL;
+    exts->ResolveMultisampleFramebuffer     = NULL;
+    exts->FramebufferTexture2DMultisample   = NULL;
+    exts->MapBuffer                         = NULL;
+    exts->UnmapBuffer                       = NULL;
+
+#if GR_MAC_BUILD
+    exts->GenFramebuffers                   = glGenFramebuffers;
+    exts->BindFramebuffer                   = glBindFramebuffer;
+    exts->FramebufferTexture2D              = glFramebufferTexture2D;
+    exts->CheckFramebufferStatus            = glCheckFramebufferStatus;
+    exts->DeleteFramebuffers                = glDeleteFramebuffers;
+    exts->RenderbufferStorage               = glRenderbufferStorage;
+    exts->GenRenderbuffers                  = glGenRenderbuffers;
+    exts->DeleteRenderbuffers               = glDeleteRenderbuffers;
+    exts->FramebufferRenderbuffer           = glFramebufferRenderbuffer;
+    exts->BindRenderbuffer                  = glBindRenderbuffer;
+    exts->RenderbufferStorageMultisample    = glRenderbufferStorageMultisample;
+    exts->BlitFramebuffer                   = glBlitFramebuffer;
+    exts->MapBuffer                         = glMapBuffer;
+    exts->UnmapBuffer                       = glUnmapBuffer;
+#elif GR_IOS_BUILD
+    exts->GenFramebuffers                   = glGenFramebuffers;
+    exts->BindFramebuffer                   = glBindFramebuffer;
+    exts->FramebufferTexture2D              = glFramebufferTexture2D;
+    exts->CheckFramebufferStatus            = glCheckFramebufferStatus;
+    exts->DeleteFramebuffers                = glDeleteFramebuffers;
+    exts->RenderbufferStorage               = glRenderbufferStorage;
+    exts->GenRenderbuffers                  = glGenRenderbuffers;
+    exts->DeleteRenderbuffers               = glDeleteRenderbuffers;
+    exts->FramebufferRenderbuffer           = glFramebufferRenderbuffer;
+    exts->BindRenderbuffer                  = glBindRenderbuffer;
+    exts->RenderbufferStorageMultisample    = glRenderbufferStorageMultisampleAPPLE;
+    exts->ResolveMultisampleFramebuffer     = glResolveMultisampleFramebufferAPPLE;
+    exts->MapBuffer                         = glMapBufferOES;
+    exts->UnmapBuffer                       = glUnmapBufferOES;
+#else
+    GLint major, minor;
+    gl_version(&major, &minor);
+    #if GR_GL_DESKTOP
+    if (major >= 3) {// FBO, FBOMS, and FBOBLIT part of 3.0
+        exts->GenFramebuffers                   = glGenFramebuffers;
+        exts->BindFramebuffer                   = glBindFramebuffer;
+        exts->FramebufferTexture2D              = glFramebufferTexture2D;
+        exts->CheckFramebufferStatus            = glCheckFramebufferStatus;
+        exts->DeleteFramebuffers                = glDeleteFramebuffers;
+        exts->RenderbufferStorage               = glRenderbufferStorage;
+        exts->GenRenderbuffers                  = glGenRenderbuffers;
+        exts->DeleteRenderbuffers               = glDeleteRenderbuffers;
+        exts->FramebufferRenderbuffer           = glFramebufferRenderbuffer;
+        exts->BindRenderbuffer                  = glBindRenderbuffer;
+        exts->RenderbufferStorageMultisample    = glRenderbufferStorageMultisample;
+        exts->BlitFramebuffer                   = glBlitFramebuffer;
+    } else if (has_gl_extension("GL_ARB_framebuffer_object")) {
+        GET_PROC(exts, GenFramebuffers, ARB);
+        GET_PROC(exts, BindFramebuffer, ARB);
+        GET_PROC(exts, FramebufferTexture2D, ARB);
+        GET_PROC(exts, CheckFramebufferStatus, ARB);
+        GET_PROC(exts, DeleteFramebuffers, ARB);
+        GET_PROC(exts, RenderbufferStorage, ARB);
+        GET_PROC(exts, GenRenderbuffers, ARB);
+        GET_PROC(exts, DeleteRenderbuffers, ARB);
+        GET_PROC(exts, FramebufferRenderbuffer, ARB);
+        GET_PROC(exts, BindRenderbuffer, ARB);
+        GET_PROC(exts, RenderbufferStorageMultisample, ARB);
+        GET_PROC(exts, BlitFramebuffer, ARB);
+    } else {
+        // we require some form of FBO
+        GrAssert(has_gl_extension("GL_EXT_framebuffer_object"));
+        GET_PROC(exts, GenFramebuffers, EXT);
+        GET_PROC(exts, BindFramebuffer, EXT);
+        GET_PROC(exts, FramebufferTexture2D, EXT);
+        GET_PROC(exts, CheckFramebufferStatus, EXT);
+        GET_PROC(exts, DeleteFramebuffers, EXT);
+        GET_PROC(exts, RenderbufferStorage, EXT);
+        GET_PROC(exts, GenRenderbuffers, EXT);
+        GET_PROC(exts, DeleteRenderbuffers, EXT);
+        GET_PROC(exts, FramebufferRenderbuffer, EXT);
+        GET_PROC(exts, BindRenderbuffer, EXT);
+        if (has_gl_extension("GL_EXT_framebuffer_multisample")) {
+            GET_PROC(exts, RenderbufferStorageMultisample, EXT);
+        }
+        if (has_gl_extension("GL_EXT_framebuffer_blit")) {
+            GET_PROC(exts, BlitFramebuffer, EXT);
+        }
+    }
+    // we assume we have at least GL 1.5 or higher (VBOs introduced in 1.5)
+    exts->MapBuffer     = glMapBuffer;
+    exts->UnmapBuffer   = glUnmapBuffer;
+    #else // !GR_GL_DESKTOP
+    if (major >= 2) {// ES 2.0 supports FBO
+        exts->GenFramebuffers                   = glGenFramebuffers;
+        exts->BindFramebuffer                   = glBindFramebuffer;
+        exts->FramebufferTexture2D              = glFramebufferTexture2D;
+        exts->CheckFramebufferStatus            = glCheckFramebufferStatus;
+        exts->DeleteFramebuffers                = glDeleteFramebuffers;
+        exts->RenderbufferStorage               = glRenderbufferStorage;
+        exts->GenRenderbuffers                  = glGenRenderbuffers;
+        exts->DeleteRenderbuffers               = glDeleteRenderbuffers;
+        exts->FramebufferRenderbuffer           = glFramebufferRenderbuffer;
+        exts->BindRenderbuffer                  = glBindRenderbuffer;
+    } else {
+        // we require some form of FBO
+        GrAssert(has_gl_extension("GL_OES_framebuffer_object"));
+
+        GET_PROC(exts, GenFramebuffers, OES);
+        GET_PROC(exts, BindFramebuffer, OES);
+        GET_PROC(exts, FramebufferTexture2D, OES);
+        GET_PROC(exts, CheckFramebufferStatus, OES);
+        GET_PROC(exts, DeleteFramebuffers, OES);
+        GET_PROC(exts, RenderbufferStorage, OES);
+        GET_PROC(exts, GenRenderbuffers, OES);
+        GET_PROC(exts, DeleteRenderbuffers, OES);
+        GET_PROC(exts, FramebufferRenderbuffer, OES);
+        GET_PROC(exts, BindRenderbuffer, OES);
+    }
+    if (has_gl_extension("GL_APPLE_framebuffer_multisample")) {
+        GET_PROC(exts, ResolveMultisampleFramebuffer, APPLE);
+    }
+    if (has_gl_extension("GL_IMG_multisampled_render_to_texture")) {
+        GET_PROC(exts, FramebufferTexture2DMultisample, IMG);
+    }
+    if (has_gl_extension("GL_OES_mapbuffer")) {
+        GET_PROC(exts, MapBuffer, OES);
+        GET_PROC(exts, UnmapBuffer, OES);
+    }
+    #endif // !GR_GL_DESKTOP
+#endif // BUILD
+}
+
+bool gPrintGL = true;
+
diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h
new file mode 100644
index 0000000..bedb85a
--- /dev/null
+++ b/gpu/src/GrGpuGL.h
@@ -0,0 +1,188 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGpuGL_DEFINED
+#define GrGpuGL_DEFINED
+
+#include "GrGpu.h"
+#include "GrGLConfig.h"
+#include "GrGLTexture.h"
+
+#include "GrGLVertexBuffer.h"
+#include "GrGLIndexBuffer.h"
+
+class GrGpuGL : public GrGpu {
+public:
+            GrGpuGL();
+    virtual ~GrGpuGL();
+
+    // overrides from GrGpu
+    virtual void resetContext();
+
+    virtual GrTexture* createTexture(const TextureDesc& desc,
+                                     const void* srcData, size_t rowBytes);
+    virtual GrVertexBuffer* createVertexBuffer(uint32_t size, bool dynamic);
+    virtual GrIndexBuffer* createIndexBuffer(uint32_t size, bool dynamic);
+
+    virtual GrRenderTarget* createPlatformRenderTarget(
+                                                 intptr_t platformRenderTarget,
+                                                 int width, int height);
+
+    virtual GrRenderTarget* defaultRenderTarget();
+
+    virtual void setDefaultRenderTargetSize(uint32_t width, uint32_t height);
+
+    virtual void eraseColor(GrColor color);
+
+    virtual void forceRenderTargetFlush();
+
+    virtual bool readPixels(int left, int top, int width, int height,
+                            GrTexture::PixelConfig, void* buffer);
+
+    /**
+     * Gets the struct containing the GL extensions for the context
+     * underlying the GrGpuGL
+     *
+     * @param struct containing extension function pointers
+     */
+    const GrGLExts& extensions() { return fExts; }
+
+protected:
+    struct {
+        const void*
+        fPositionPtr;
+        GrVertexLayout          fVertexLayout;
+        const GrVertexBuffer*   fVertexBuffer;
+        const GrIndexBuffer*    fIndexBuffer;
+    } fHWGeometryState;
+
+    DrawState   fHWDrawState;
+    bool        fHWStencilClip;
+
+    virtual void drawIndexedHelper(PrimitiveType type,
+                                   uint32_t startVertex,
+                                   uint32_t startIndex,
+                                   uint32_t vertexCount,
+                                   uint32_t indexCount);
+
+    virtual void drawNonIndexedHelper(PrimitiveType type,
+                                      uint32_t vertexCount,
+                                      uint32_t numVertices);
+
+    virtual void flushScissor(const GrIRect* rect);
+
+    void eraseStencil(uint32_t value, uint32_t mask);
+    virtual void eraseStencilClip();
+
+    // flushes state that is common to fixed and programmable GL
+    // dither
+    // line smoothing
+    // blend func
+    // texture binding
+    // sampler state (filtering, tiling)
+    // FBO binding
+    // line width
+    void flushGLStateCommon(PrimitiveType type);
+
+    // pushes the filtering and tiling modes to GL
+    void setSamplerStateImm(const GrSamplerState& samplerState);
+
+    // set when this class changes the rendertarget.
+    // Subclass should notice at flush time, take appropriate action,
+    // and set false.
+    bool fRenderTargetChanged;
+
+    // set by eraseColor or eraseStencil. Picked up in in flushStencil.
+    bool fWriteMaskChanged;
+
+    // last scissor / viewport scissor state seen by the GL.
+    BoundsState fHWBounds;
+
+private:
+    GrGLExts fExts;
+
+    GrGLRenderTarget* fDefaultRenderTarget;
+
+    void resetContextHelper();
+
+    // notify callbacks to update state tracking when related
+    // objects are bound to GL or deleted outside of the class
+    void notifyVertexBufferBind(const GrGLVertexBuffer* buffer);
+    void notifyVertexBufferDelete(const GrGLVertexBuffer* buffer);
+    void notifyIndexBufferBind(const GrGLIndexBuffer* buffer);
+    void notifyIndexBufferDelete(const GrGLIndexBuffer* buffer);
+    void notifyTextureBind(GrGLTexture* texture);
+    void notifyTextureDelete(GrGLTexture* texture);
+    void notifyRenderTargetDelete(GrRenderTarget* renderTarget);
+    void notifyTextureRemoveRenderTarget(GrGLTexture* texture);
+
+    void flushRenderTarget();
+    void flushStencil();
+    void resolveTextureRenderTarget(GrGLTexture* texture);
+
+    bool canBeTexture(GrTexture::PixelConfig config,
+                      GLenum* internalFormat,
+                      GLenum* format,
+                      GLenum* type);
+    bool fboInternalFormat(GrTexture::PixelConfig config, GLenum* format);
+
+    friend class GrGLVertexBuffer;
+    friend class GrGLIndexBuffer;
+    friend class GrGLTexture;
+    friend class GrGLRenderTarget;
+
+    bool fHWBlendDisabled;
+
+    GLuint fAASamples[4];
+    enum {
+        kNone_MSFBO = 0,
+        kDesktop_MSFBO,
+        kApple_MSFBO,
+        kIMG_MSFBO
+    } fMSFBOType;
+
+    // Do we have stencil wrap ops.
+    bool fHasStencilWrap;
+
+    // ES requires an extension to support RGBA8 in RenderBufferStorage
+    bool fRGBA8Renderbuffer;
+
+    typedef GrGpu INHERITED;
+};
+
+bool has_gl_extension(const char* ext);
+void gl_version(int* major, int* minor);
+
+/**
+ *  GrGL_RestoreResetRowLength() will reset GL_UNPACK_ROW_LENGTH to 0. We write
+ *  this wrapper, since GL_UNPACK_ROW_LENGTH is not available on all GL versions
+ */
+#if GR_GL_DESKTOP
+    static inline void GrGL_RestoreResetRowLength() {
+        GR_GL(PixelStorei(GL_UNPACK_ROW_LENGTH, 0));
+    }
+#else
+    #define GrGL_RestoreResetRowLength()
+#endif
+
+#if SK_TextGLType != GL_FIXED
+    #define SK_GL_HAS_COLOR4UB
+#endif
+
+#endif
+
+
diff --git a/gpu/src/GrGpuGLFixed.cpp b/gpu/src/GrGpuGLFixed.cpp
new file mode 100644
index 0000000..77bec40
--- /dev/null
+++ b/gpu/src/GrGpuGLFixed.cpp
@@ -0,0 +1,342 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrGLConfig.h"
+
+#if GR_SUPPORT_GLES1 || GR_SUPPORT_GLDESKTOP
+
+#include "GrGpuGLFixed.h"
+#include "GrGpuVertex.h"
+
+#define SKIP_CACHE_CHECK    true
+
+struct GrGpuMatrix {
+    GrScalar    fMat[16];
+    
+    void reset() {
+        Gr_bzero(fMat, sizeof(fMat));
+        fMat[0] = fMat[5] = fMat[10] = fMat[15] = GR_Scalar1;
+    }
+    
+    void set(const GrMatrix& m) {
+        Gr_bzero(fMat, sizeof(fMat));
+        fMat[0]  = m[GrMatrix::kScaleX];
+        fMat[4]  = m[GrMatrix::kSkewX];
+        fMat[12] = m[GrMatrix::kTransX];
+        
+        fMat[1]  = m[GrMatrix::kSkewY];
+        fMat[5]  = m[GrMatrix::kScaleY];
+        fMat[13] = m[GrMatrix::kTransY];
+        
+        fMat[3]  = m[GrMatrix::kPersp0];
+        fMat[7]  = m[GrMatrix::kPersp1];
+        fMat[15] = m[GrMatrix::kPersp2];
+        
+        fMat[10] = GR_Scalar1;    // z-scale
+    }
+};
+
+// these must match the order in the corresponding enum in GrGpu.h
+static const GLenum gMatrixMode2Enum[] = {
+    GL_MODELVIEW, GL_TEXTURE
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGpuGLFixed::GrGpuGLFixed() {
+    resetContextHelper();
+}
+
+GrGpuGLFixed::~GrGpuGLFixed() {
+}
+
+void GrGpuGLFixed::resetContext() {
+    INHERITED::resetContext();
+    resetContextHelper();
+}
+
+void GrGpuGLFixed::resetContextHelper() {
+    GR_GL(Disable(GL_TEXTURE_2D));
+
+    GR_GL(EnableClientState(GL_VERTEX_ARRAY));    
+    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE));
+    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB,   GL_MODULATE));
+    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB,      GL_TEXTURE0));
+    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB,      GL_PRIMARY_COLOR));
+    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB,  GL_SRC_COLOR));
+
+    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE));
+    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0));
+    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA));
+    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PRIMARY_COLOR));
+    GR_GL(TexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA));
+    
+    // this changes between GL_SRC_COLR and GL_SRC_ALPHA depending upon
+    // whether we have a (premultiplied) RGBA texture or just an ALPHA texture
+    //glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB,  GL_SRC_COLOR);
+    fHWRGBOperand0 = (TextureEnvRGBOperands) -1;    
+
+    GR_GL(ClientActiveTexture(GL_TEXTURE0));
+
+    fHWGeometryState.fVertexLayout = 0;
+    fHWGeometryState.fPositionPtr  = (void*) ~0;
+    GR_GL(EnableClientState(GL_VERTEX_ARRAY));
+    GR_GL(DisableClientState(GL_TEXTURE_COORD_ARRAY));
+    GR_GL(ShadeModel(GL_FLAT));
+    GR_GL(DisableClientState(GL_COLOR_ARRAY));
+    
+    GrGLClearErr();
+    fTextVerts = false;
+
+    fHWTextureOrientation = (GrGLTexture::Orientation)-1; // illegal    
+    fBaseVertex = 0xffffffff;
+}
+
+
+void GrGpuGLFixed::flushProjectionMatrix() {
+    float mat[16];
+    Gr_bzero(mat, sizeof(mat));
+    
+    GrAssert(NULL != fCurrDrawState.fRenderTarget);
+    
+    mat[0] = 2.f / fCurrDrawState.fRenderTarget->width();
+    mat[5] = -2.f / fCurrDrawState.fRenderTarget->height();
+    mat[10] = -1.f;
+    mat[15] = 1;
+    
+    mat[12] = -1.f;
+    mat[13] = 1.f;
+    
+    GR_GL(MatrixMode(GL_PROJECTION));
+    GR_GL(LoadMatrixf(mat));
+}
+
+bool GrGpuGLFixed::flushGraphicsState(PrimitiveType type) {
+    
+    bool usingTexture = VertexHasTexCoords(fGeometrySrc.fVertexLayout);
+
+    if (usingTexture && fCurrDrawState.fSamplerState.isGradient()) {
+        unimpl("Fixed pipe doesn't support radial/sweep gradients");
+        return false;
+    }
+    
+    flushGLStateCommon(type);
+
+    if (fRenderTargetChanged) {    
+        flushProjectionMatrix();
+        fRenderTargetChanged = false;
+    }
+
+    bool wasUsingTexture = VertexHasTexCoords(fHWGeometryState.fVertexLayout);
+    if (usingTexture != wasUsingTexture) {
+        if (usingTexture) {
+            GR_GL(Enable(GL_TEXTURE_2D));
+        } else {
+            GR_GL(Disable(GL_TEXTURE_2D));
+        }
+    }
+    
+    uint32_t vertColor = (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit);
+    uint32_t prevVertColor = (fHWGeometryState.fVertexLayout & 
+                              kColor_VertexLayoutBit);
+    
+    if (vertColor != prevVertColor) {
+        if (vertColor) {
+            GrAssert(fCurrDrawState.fSamplerState.getSampleMode() != 
+                     GrSamplerState::kAlphaMod_SampleMode);
+            GR_GL(ShadeModel(GL_SMOOTH));
+            // invalidate the immediate mode color
+            fHWDrawState.fColor = GrColor_ILLEGAL;
+        } else {
+            GR_GL(ShadeModel(GL_FLAT));
+        }
+    }
+
+    if (kPoints_PrimitiveType == type &&
+        fHWDrawState.fPointSize != fCurrDrawState.fPointSize) {
+        GR_GL(PointSize(fCurrDrawState.fPointSize));
+        fHWDrawState.fPointSize = fCurrDrawState.fPointSize;
+    }
+    
+    if (!vertColor && fHWDrawState.fColor != fCurrDrawState.fColor) {
+        GR_GL(Color4ub(GrColorUnpackR(fCurrDrawState.fColor),
+                       GrColorUnpackG(fCurrDrawState.fColor),
+                       GrColorUnpackB(fCurrDrawState.fColor),
+                       GrColorUnpackA(fCurrDrawState.fColor)));
+        fHWDrawState.fColor = fCurrDrawState.fColor;
+    }
+
+    // set texture environment, decide whether we are modulating by RGB or A.
+    if (usingTexture) {
+        GrGLTexture* texture = (GrGLTexture*)fCurrDrawState.fTexture;
+        if (NULL != texture) {
+            TextureEnvRGBOperands nextRGBOperand0 = 
+                (texture->uploadFormat() == GL_ALPHA) ? 
+                    kAlpha_TextureEnvRGBOperand : 
+                    kColor_TextureEnvRGBOperand;
+            if (fHWRGBOperand0 != nextRGBOperand0) {
+                GR_GL(TexEnvi(GL_TEXTURE_ENV, 
+                              GL_OPERAND0_RGB,
+                              (nextRGBOperand0==kAlpha_TextureEnvRGBOperand) ? 
+                                GL_SRC_ALPHA : 
+                                GL_SRC_COLOR));
+                fHWRGBOperand0 = nextRGBOperand0;
+            }
+            
+            if (fHWTextureOrientation != texture->orientation() ||
+                fHWDrawState.fMatrixModeCache[kTexture_MatrixMode] != 
+                fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode]) {
+                GrGpuMatrix glm;
+                if (GrGLTexture::kBottomUp_Orientation == texture->orientation()) {
+                    GrMatrix m(
+                        GR_Scalar1, 0, 0,
+                        0, -GR_Scalar1, GR_Scalar1,
+                        0, 0, GrMatrix::I()[8]
+                    );
+                    m.preConcat(fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode]);
+                    glm.set(m);
+                } else {
+                    glm.set(fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode]);
+                }
+                GR_GL(MatrixMode(gMatrixMode2Enum[kTexture_MatrixMode]));
+                GR_GL(LoadMatrixf(glm.fMat));
+                fHWDrawState.fMatrixModeCache[kTexture_MatrixMode] = 
+                    fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode];
+                fHWTextureOrientation = texture->orientation();
+            }
+        } else {
+            GrAssert(!"Rendering with texture vert flag set but no bound texture");
+            return false;
+        }
+    }
+
+    if (fHWDrawState.fMatrixModeCache[kModelView_MatrixMode] != 
+        fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]) {
+        GrGpuMatrix glm;
+        glm.set(fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]);
+        GR_GL(MatrixMode(gMatrixMode2Enum[kModelView_MatrixMode]));
+        GR_GL(LoadMatrixf(glm.fMat));
+        fHWDrawState.fMatrixModeCache[kModelView_MatrixMode] = 
+            fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode];
+    }
+    return true;
+}
+
+void GrGpuGLFixed::setupGeometry(uint32_t startVertex,
+                                 uint32_t startIndex,
+                                 uint32_t vertexCount,
+                                 uint32_t indexCount) {
+    
+    int newColorOffset, newTexCoordOffset;
+    
+    GLsizei newStride = VertexSizeAndOffsets(fGeometrySrc.fVertexLayout,
+                                             &newTexCoordOffset, 
+                                             &newColorOffset);
+    int oldColorOffset, oldTexCoordOffset;
+    GLsizei oldStride = VertexSizeAndOffsets(fHWGeometryState.fVertexLayout,
+                                             &oldTexCoordOffset, 
+                                             &oldColorOffset);
+    
+    const GLvoid* posPtr = (GLvoid*)(newStride * startVertex);
+    
+    if (kBuffer_GeometrySrcType == fGeometrySrc.fVertexSrc) {
+        GrAssert(NULL != fGeometrySrc.fVertexBuffer);
+        GrAssert(!fGeometrySrc.fVertexBuffer->isLocked());
+        if (fHWGeometryState.fVertexBuffer != fGeometrySrc.fVertexBuffer) {
+            GrGLVertexBuffer* buf = 
+            (GrGLVertexBuffer*)fGeometrySrc.fVertexBuffer;
+            GR_GL(BindBuffer(GL_ARRAY_BUFFER, buf->bufferID()));
+            fHWGeometryState.fVertexBuffer = fGeometrySrc.fVertexBuffer;
+        }
+    } else { 
+        if (kArray_GeometrySrcType == fGeometrySrc.fVertexSrc) {
+            posPtr = (void*)((intptr_t)fGeometrySrc.fVertexArray + 
+                             (intptr_t)posPtr);
+        } else {
+            GrAssert(kReserved_GeometrySrcType == fGeometrySrc.fVertexSrc);
+            posPtr = (void*)((intptr_t)fVertices.get() + (intptr_t)posPtr);            
+        }
+        if (NULL != fHWGeometryState.fVertexBuffer) {
+            GR_GL(BindBuffer(GL_ARRAY_BUFFER, 0));
+            fHWGeometryState.fVertexBuffer = NULL;
+        }
+    }
+    
+    if (kBuffer_GeometrySrcType == fGeometrySrc.fIndexSrc) {
+        GrAssert(NULL != fGeometrySrc.fIndexBuffer);
+        GrAssert(!fGeometrySrc.fIndexBuffer->isLocked());
+        if (fHWGeometryState.fIndexBuffer != fGeometrySrc.fIndexBuffer) {
+            GrGLIndexBuffer* buf = 
+            (GrGLIndexBuffer*)fGeometrySrc.fIndexBuffer;
+            GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf->bufferID()));
+            fHWGeometryState.fIndexBuffer = fGeometrySrc.fIndexBuffer;
+        }
+    } else if (NULL != fHWGeometryState.fIndexBuffer) {
+        GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+        fHWGeometryState.fIndexBuffer = NULL;
+    }
+    
+    GLenum scalarType;
+    if (fGeometrySrc.fVertexLayout & kTextFormat_VertexLayoutBit) {
+        scalarType = GrGLTextType;
+    } else {
+        scalarType = GrGLType;
+    }
+    
+    bool baseChange = posPtr != fHWGeometryState.fPositionPtr;
+    bool scalarChange = 
+        (GrGLTextType != GrGLType) &&
+        (kTextFormat_VertexLayoutBit &
+         (fHWGeometryState.fVertexLayout ^ fGeometrySrc.fVertexLayout));
+    bool strideChange = newStride != oldStride;
+    bool posChange = baseChange || scalarChange || strideChange;
+    
+    if (posChange) {
+        GR_GL(VertexPointer(2, scalarType, newStride, posPtr));
+        fHWGeometryState.fPositionPtr = posPtr;
+    }
+    
+    // need to enable array if tex coord offset is 0 (using positions as coords)
+    if (newTexCoordOffset >= 0) {
+        GLvoid* texCoordPtr = (int8_t*)posPtr + newTexCoordOffset;
+        if (oldTexCoordOffset < 0) {
+            GR_GL(EnableClientState(GL_TEXTURE_COORD_ARRAY));
+        }
+        if (posChange || newTexCoordOffset != oldTexCoordOffset) {
+            GR_GL(TexCoordPointer(2, scalarType, newStride, texCoordPtr));
+        }
+    } else if (oldTexCoordOffset >= 0) {
+        GR_GL(DisableClientState(GL_TEXTURE_COORD_ARRAY));
+    }
+    
+    if (newColorOffset > 0) {
+        GLvoid* colorPtr = (int8_t*)posPtr + newColorOffset;
+        if (oldColorOffset <= 0) {
+            GR_GL(EnableClientState(GL_COLOR_ARRAY));
+        }
+        if (posChange || newColorOffset != oldColorOffset) {
+            GR_GL(ColorPointer(4, GL_UNSIGNED_BYTE, newStride, colorPtr));
+        }
+    } else if (oldColorOffset > 0) {
+        GR_GL(DisableClientState(GL_COLOR_ARRAY));
+    }
+    
+    fHWGeometryState.fVertexLayout = fGeometrySrc.fVertexLayout;
+}
+
+#endif
+
diff --git a/gpu/src/GrGpuGLFixed.h b/gpu/src/GrGpuGLFixed.h
new file mode 100644
index 0000000..f3a0332
--- /dev/null
+++ b/gpu/src/GrGpuGLFixed.h
@@ -0,0 +1,68 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGpuGLFixed_DEFINED
+#define GrGpuGLFixed_DEFINED
+
+#include "GrGpuGL.h"
+
+// Fixed Pipeline OpenGL or OpenGL ES 1.x
+class GrGpuGLFixed : public GrGpuGL {
+public:
+             GrGpuGLFixed();
+    virtual ~GrGpuGLFixed();
+    
+    virtual void resetContext();
+
+protected:
+    // overrides from GrGpu
+    virtual bool flushGraphicsState(PrimitiveType type);
+    virtual void setupGeometry(uint32_t startVertex,
+                               uint32_t startIndex,
+                               uint32_t vertexCount,
+                               uint32_t indexCount);
+
+private:
+    void resetContextHelper();
+
+    // when the texture is GL_RGBA we set the GL_COMBINE texture
+    // environment rgb operand 0 to be GL_COLOR to modulate each incoming frag's
+    // RGB by the texture's RGB. When the texture is GL_ALPHA we set
+    // the operand to GL_ALPHA so that the incoming frag's RGB is modulated
+    // by the texture's alpha.
+    enum TextureEnvRGBOperands {
+        kAlpha_TextureEnvRGBOperand,
+        kColor_TextureEnvRGBOperand,
+    };
+    TextureEnvRGBOperands fHWRGBOperand0;
+
+    void flushProjectionMatrix();
+
+    // are the currently bound vertex buffers/arrays laid
+    // out for text or other drawing.
+    bool fTextVerts;
+
+    // On GL we have to build the base vertex offset into the
+    // glVertexPointer/glTexCoordPointer/etc
+    int fBaseVertex;
+
+    GrGLTexture::Orientation fHWTextureOrientation;
+
+    typedef GrGpuGL INHERITED;
+};
+
+#endif
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
new file mode 100644
index 0000000..8f4bfaf
--- /dev/null
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -0,0 +1,937 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrGLConfig.h"
+
+#if GR_SUPPORT_GLES2 || GR_SUPPORT_GLDESKTOP
+
+#include "GrGpuGLShaders.h"
+#include "GrGpuVertex.h"
+#include "GrMemory.h"
+
+#define ATTRIBUTE_MATRIX        0
+
+#define ATTRIBUTE_TEXT_COLOR    1
+
+#if ATTRIBUTE_MATRIX
+    #define DECL_MATRIX(name) "attribute mat3 " #name ";\n"
+#else
+    #define DECL_MATRIX(name) "uniform mat3 " #name ";\n"
+#endif
+
+#define SKIP_CACHE_CHECK    true
+
+#if GR_SUPPORT_GLES2
+    #define GR_PRECISION            "mediump"
+    #define GR_SHADER_PRECISION     "precision mediump float;\n"
+#else
+    #define GR_PRECISION            ""
+    #define GR_SHADER_PRECISION     ""
+#endif
+
+static const char* gvshad[] = {
+    // 0: kTextureVertCoords_Program, kTextureVertCoordsProj_Program,
+    //    kRadialTextureVertCoords_Program, kSweepTextureVertCoords_Program
+    "attribute vec2 aPosition;\n"
+    "attribute vec4 aColor;\n"
+    "varying vec3 vTexture;\n"
+    "varying vec4 vColor;\n"
+    DECL_MATRIX(viewM)
+    DECL_MATRIX(texM)
+    "void main() {\n"
+    "   vec3 pos3 = viewM*vec3(aPosition,1);\n"
+    "   gl_Position = vec4(pos3.xy,0,pos3.z);\n"
+    "   gl_PointSize = 1.0;\n"
+    "   vTexture = texM * vec3(aPosition,1);\n"
+    "   vColor = aColor;\n"
+    "}\n",
+
+    // 1: kTextureTexCoords_Program, kTextureTexCoordsProj_Program,
+    //    kRadialTextureTexCoords_Program, kSweepTextureTexCoords_Program
+    "attribute vec2 aPosition;\n"
+    "attribute vec2 aTexture;\n"
+    "attribute vec4 aColor;\n"
+    "varying vec3 vTexture;\n"
+    "varying vec4 vColor;\n"
+    DECL_MATRIX(viewM)
+    DECL_MATRIX(texM)
+    "void main() {\n"
+    "   vec3 pos3 = viewM*vec3(aPosition,1);\n"
+    "   gl_Position = vec4(pos3.xy,0,pos3.z);\n"
+    "   gl_PointSize = 1.0;\n"
+    "   vTexture = texM * vec3(aTexture,1);\n"
+    "   vColor = aColor;\n"
+    "}\n",
+
+     // 2: kText_Program
+    "attribute vec2 aPosition;\n"
+    "attribute vec2 aTexture;\n"    
+    "varying vec2 vTexture;\n"
+    DECL_MATRIX(viewM)
+#if ATTRIBUTE_TEXT_COLOR
+    "varying vec4 vColor;\n"
+    "attribute vec4 aColor;\n"
+#endif
+    "void main() {\n"
+    "   vec3 pos3 = viewM*vec3(aPosition,1);\n"
+    "   gl_Position = vec4(pos3.xy,0,pos3.z);\n"
+    "   vTexture = aTexture;\n"
+#if ATTRIBUTE_TEXT_COLOR
+    "   vColor = aColor;\n"
+#endif
+    "}\n",
+
+    // 3: kNoTexture_Program
+    "attribute vec2 aPosition;\n"
+    "attribute vec4 aColor;\n"
+    "varying vec4 vColor;\n"
+    DECL_MATRIX(viewM)
+    "void main() {\n"
+    "   vec3 pos3 = viewM*vec3(aPosition,1);\n"
+    "   gl_Position = vec4(pos3.xy,0,pos3.z);\n"
+    "   gl_PointSize = 1.0;\n"
+    "   vColor = aColor;\n"
+    "}\n",
+
+    // 4: kTextureVertCoordsNoColor_Program
+    "attribute vec2 aPosition;\n"
+    "attribute vec4 aColor;\n"
+    "varying vec3 vTexture;\n"
+    DECL_MATRIX(viewM)
+    DECL_MATRIX(texM)
+    "void main() {\n"
+    "   vec3 pos3 = viewM*vec3(aPosition,1);\n"
+    "   gl_Position = vec4(pos3.xy,0,pos3.z);\n"
+    "   vTexture = texM * vec3(aPosition,1);\n"
+    "}\n",
+
+    // 5: kTextureTexCoordsNoColor_Program
+    "attribute vec2 aPosition;\n"
+    "attribute vec2 aTexture;\n"
+    "varying vec3 vTexture;\n"
+    DECL_MATRIX(viewM)
+    DECL_MATRIX(texM)
+    "void main() {\n"
+    "   vec3 pos3 = viewM*vec3(aPosition,1);\n"
+    "   gl_Position = vec4(pos3.xy,0,pos3.z);\n"
+    "   gl_PointSize = 1.0;\n"
+    "   vTexture = texM * vec3(aTexture,1);\n"
+    "}\n",
+
+    // 6: kTwoPointRadialTextureVertCoords_Program
+    "uniform " GR_PRECISION " float uParams[6];\n" 
+        // 0 is t^2 term of quadratic 
+        // 1 is one-half the inverse of above
+        // 2 is x offset of the second circle (post tex-matrix)
+        // 3 is the radius of the first circle (post tex-matrix)
+        // 4 is the first circle radius squared
+        // 5 is 1 to use + in the quadratic eq or -1 to use -
+    DECL_MATRIX(viewM)
+    DECL_MATRIX(texM)
+    "attribute vec2 aPosition;\n"
+    "attribute vec4 aColor;\n"
+    "varying vec4 vColor;\n"
+    "varying float vB;\n"           // t coeffecient of quadratic.
+    "varying vec2 t;\n"             // coordinates in canonical space
+    "void main() {\n"
+    "    vec3 pos3 = viewM*vec3(aPosition,1);\n"
+    "    gl_Position = vec4(pos3.xy,0,pos3.z);\n"
+    "    t = vec2(texM * vec3(aPosition,1));\n"
+    "    vColor = aColor;\n"
+    "    vB = 2.0 * (uParams[2] * t.x - uParams[3]);\n"
+    "}\n",
+
+    // 6: kTwoPointRadialTextureVertCoords_Program
+    "uniform " GR_PRECISION " float uParams[6];\n" 
+    DECL_MATRIX(viewM)
+    DECL_MATRIX(texM)
+    "attribute vec2 aPosition;\n"
+    "attribute vec2 aTexture;\n"
+    "attribute vec4 aColor;\n"
+    "varying vec4 vColor;\n"
+    "varying float vB;\n"           // t coeffecient of quadratic.
+    "varying vec2 t;\n"             // coordinates in canonical space
+    "void main() {\n"
+    "    vec3 pos3 = viewM*vec3(aPosition,1);\n"
+    "    gl_Position = vec4(pos3.xy,0,pos3.z);\n"
+    "    t = vec2(texM * vec3(aTexture,1));\n"
+    "    vColor = aColor;\n"
+    "    vB = 2.0 * (uParams[2] * t.x - uParams[3]);\n"
+    "}\n",
+};
+
+static const char* gfshad[] = {
+    // 0: kTextureVertCoords_Program, kTextureTexCoords_Program
+    GR_SHADER_PRECISION
+    "varying vec3 vTexture;\n"
+    "varying vec4 vColor;\n"
+    "uniform sampler2D sTexture;\n"
+    "void main() {\n"
+    "   gl_FragColor = vColor * texture2D(sTexture, vTexture.xy);\n"
+    "}\n",
+
+    // 1: kTextureVertCoordsProj_Program, kTextureTexCoordsProj_Program
+    GR_SHADER_PRECISION
+    "varying vec3 vTexture;\n"
+    "varying vec4 vColor;\n"
+    "uniform sampler2D sTexture;\n"
+    "void main() {\n"
+    // On Brian's PC laptop with Intel Gfx texture2DProj seems to be broken
+    // but it works everywhere else tested.
+#if GR_GLSL_2DPROJ_BROKEN
+    "   gl_FragColor = vColor * texture2D(sTexture, vTexture.xy / vTexture.z);\n"
+#else
+    "   gl_FragColor = vColor * texture2DProj(sTexture, vTexture);\n"
+#endif
+    
+    "}\n",
+
+    // 2: kText_Program
+    GR_SHADER_PRECISION
+    "varying vec2 vTexture;\n"
+#if ATTRIBUTE_TEXT_COLOR
+    "varying vec4 vColor;\n"
+#else
+    "uniform vec4 uColor;\n"
+#endif
+    "uniform sampler2D sTexture;\n"
+    "void main() {\n"
+#if ATTRIBUTE_TEXT_COLOR
+    "   gl_FragColor = vColor * texture2D(sTexture, vTexture).a;\n"
+#else
+    "   gl_FragColor = uColor * texture2D(sTexture, vTexture).a;\n"
+#endif
+    "}\n",
+
+    // 3: kNoTexture_Program
+    GR_SHADER_PRECISION
+    "varying vec4 vColor;\n"
+    "void main() {\n"
+    "   gl_FragColor = vColor;\n"
+    "}\n",
+
+    // 4: kTextureVertCoordsNoColor_Program
+    GR_SHADER_PRECISION
+    "varying vec3 vTexture;\n"
+    "uniform sampler2D sTexture;\n"
+    "void main() {\n"
+    "   gl_FragColor = texture2D(sTexture, vTexture.xy);\n"
+    "}\n",
+
+    // 5: kRadialTextureVertCoords_Program, kRadialTextureTexCoords_Program
+    GR_SHADER_PRECISION
+    "varying vec3 vTexture;\n"
+    "varying vec4 vColor;\n"
+    "uniform sampler2D sTexture;\n"
+    "void main() {\n"
+    "   gl_FragColor = vColor * texture2D(sTexture, vec2(length(vTexture.xy), 0.5));\n"
+    "}\n",
+
+    // 6: kSweepTextureVertCoords_Program, kSweepTextureTexCoords_Program
+    GR_SHADER_PRECISION
+    "varying vec3 vTexture;\n"
+    "varying vec4 vColor;\n"
+    "uniform sampler2D sTexture;\n"
+    "void main() {\n"
+    "   vec2 t = vec2(atan(-vTexture.y, -vTexture.x)*0.1591549430918 + 0.5,\n"
+    "                 0.5);\n"
+    "   gl_FragColor = vColor * texture2D(sTexture, t);\n"
+    "}\n",
+    
+    // 7: kTwoPointRadialTextureVertCoords_Program, kTwoPointRadialTextureTexCoords_Program
+    GR_SHADER_PRECISION
+    "varying vec4 vColor;\n"
+    "varying float vB;\n"             // t coeffecient of quadratic.
+    "varying vec2 t;\n"               // coordinates in canonical radial gradient space
+    "uniform sampler2D sTexture;\n"
+    "uniform float uParams[6];\n" 
+    "void main() {\n"
+        "float c = t.x*t.x + t.y*t.y - uParams[4];\n"
+        "float ac4 = uParams[0] * c * 4.0;\n"
+        "float root = sqrt(abs(vB * vB - ac4));\n"
+        "float t = (-vB + uParams[5] * root) * uParams[1];\n"
+        "gl_FragColor = vColor * texture2D(sTexture, vec2(t,0.5))\n;"
+    "}\n",
+};
+
+// determines which frag/vert shaders are used for each program in Programs enum
+
+static const struct {
+    int fVShaderIdx;
+    int fFShaderIdx;
+    bool fHasTexMatrix;
+    bool fHasTexCoords;
+    bool fTwoPointRadial;
+    GrGpuGLShaders::ColorType fColorType;    
+} gProgramLoadData[] = {
+    // kTextureVertCoords_Program
+    {0, 0, true,  false, false, GrGpuGLShaders::kAttrib_ColorType }, 
+    // kTextureVertCoordsProj_Program
+    {0, 1, true,  false, false, GrGpuGLShaders::kAttrib_ColorType }, 
+    // kTextureTexCoords_Program
+    {1, 0, true,  true,  false, GrGpuGLShaders::kAttrib_ColorType }, 
+    // kTextureTexCoordsProj_Program
+    {1, 1, true,  true,  false, GrGpuGLShaders::kAttrib_ColorType }, 
+    // kTextureVertCoordsNoColor_Program
+    {4, 4, true,  false, false, GrGpuGLShaders::kNone_ColorType },
+    // kTextureTexCoordsNoColor_Program
+    {5, 4, true,  false, false, GrGpuGLShaders::kNone_ColorType },
+    // kText_Program
+#if ATTRIBUTE_TEXT_COLOR
+    {2, 2, false, true,  false, GrGpuGLShaders::kAttrib_ColorType },
+#else
+    {2, 2, false, true,  false, GrGpuGLShaders::kUniform_ColorType },
+#endif
+    // kRadialTextureVertCoords_Program
+    {0, 5, true,  false, false, GrGpuGLShaders::kAttrib_ColorType }, 
+    // kRadialTextureTexCoords_Program
+    {1, 5, true,  true,  false, GrGpuGLShaders::kAttrib_ColorType },
+    // kSweepTextureVertCoords_Program
+    {0, 6, true,  false, false, GrGpuGLShaders::kAttrib_ColorType }, 
+    // kSweepTextureTexCoords_Program
+    {1, 6, true,  true,  false, GrGpuGLShaders::kAttrib_ColorType },
+    // kTwoPointRadialTextureVertCoords_Program
+    {6, 7, true,  false, true,  GrGpuGLShaders::kAttrib_ColorType },
+    // kTwoPointRadialTextureTexCoords_Program
+    {7, 7, true,  true,  true,  GrGpuGLShaders::kAttrib_ColorType },
+    // kNoTexture_Program
+    {3, 3, false, false, false, GrGpuGLShaders::kAttrib_ColorType },
+};
+
+#define GR_GL_POS_ATTR_LOCATION 0
+#define GR_GL_TEX_ATTR_LOCATION 1
+#define GR_GL_COL_ATTR_LOCATION 2
+#if ATTRIBUTE_MATRIX
+    #define GR_GL_MAT_ATTR_LOCATION 3
+    #define GR_GL_TEXMAT_ATTR_LOCATION 6
+#endif
+
+GLuint GrGpuGLShaders::loadShader(GLenum type, const char* src) {
+    GLuint shader = GR_GL(CreateShader(type));
+    if (0 == shader) {
+        return 0;
+    }
+
+    GLint compiled;
+    GR_GL(ShaderSource(shader, 1, &src, NULL));
+    GR_GL(CompileShader(shader));
+    GR_GL(GetShaderiv(shader, GL_COMPILE_STATUS, &compiled));
+
+    if (!compiled) {
+        GLint infoLen;
+        GR_GL(GetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen));
+        GrAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
+        if (infoLen > 0) {            
+            GR_GL(GetShaderInfoLog(shader, infoLen+1, NULL, (char*)log.get()));
+            GrPrintf((char*)log.get());
+        }
+        GrAssert(!"Shader compilation failed!");
+        GR_GL(DeleteShader(shader));
+        return 0;
+    }
+    return shader;
+}
+
+bool GrGpuGLShaders::createProgram(GLuint vshader, GLuint fshader,
+                                   bool hasTexMatrix,
+                                   bool hasTexCoords,
+                                   GrGpuGLShaders::ColorType colorType,
+                                   bool twoPointRadial,
+                                   ProgramData* program) {
+    program->fProgramID = GR_GL(CreateProgram());
+    program->fVShaderID = vshader;
+    program->fFShaderID = fshader;
+
+    GrAssert(0 != program->fProgramID);
+    
+    GR_GL(AttachShader(program->fProgramID, vshader));
+    GR_GL(AttachShader(program->fProgramID, fshader));
+
+    GR_GL(BindAttribLocation(program->fProgramID, 
+                             GR_GL_POS_ATTR_LOCATION, 
+                             "aPosition"));
+    if (hasTexCoords) {
+        GR_GL(BindAttribLocation(program->fProgramID, 
+                                 GR_GL_TEX_ATTR_LOCATION, 
+                                 "aTexture"));
+    }
+#if ATTRIBUTE_MATRIX
+    if (hasTexMatrix) {
+        GR_GL(BindAttribLocation(program->fProgramID, 
+                                 GR_GL_TEXMAT_ATTR_LOCATION, 
+                                 "texM"));
+        // set to something arbitrary to signal to flush that program
+        // uses the texture matrix.
+        program->fTexMatrixLocation = 1000;
+    }
+#endif
+    if (colorType == kAttrib_ColorType) {
+        GR_GL(BindAttribLocation(program->fProgramID, 
+                                 GR_GL_COL_ATTR_LOCATION, 
+                                 "aColor"));
+    }
+#if ATTRIBUTE_MATRIX
+    GR_GL(BindAttribLocation(program->fProgramID,
+                             GR_GL_MAT_ATTR_LOCATION, 
+                             "viewM"));
+#endif
+
+    GR_GL(LinkProgram(program->fProgramID));
+
+    GLint linked;
+    GR_GL(GetProgramiv(program->fProgramID, GL_LINK_STATUS, &linked));
+    if (!linked) {
+        GLint infoLen;
+        GR_GL(GetProgramiv(program->fProgramID, GL_INFO_LOG_LENGTH, &infoLen));
+        GrAutoMalloc log(sizeof(char)*(infoLen+1));  // outside if for debugger
+        if (infoLen > 0) {            
+            GR_GL(GetProgramInfoLog(program->fProgramID,
+                                    infoLen+1, 
+                                    NULL, 
+                                    (char*)log.get()));
+            GrPrintf((char*)log.get());
+        }
+        GrAssert(!"Error linking program");
+        GR_GL(DeleteProgram(program->fProgramID));
+        program->fProgramID = 0;
+        return false;
+    }
+    program->fColorType = colorType;
+
+#if !ATTRIBUTE_MATRIX
+    program->fMatrixLocation =
+        GR_GL(GetUniformLocation(program->fProgramID, "viewM"));
+    program->fTexMatrixLocation = 
+        GR_GL(GetUniformLocation(program->fProgramID, "texM"));
+#endif
+    program->fColorLocation = 
+        GR_GL(GetUniformLocation(program->fProgramID, "uColor"));
+    program->fTwoPointParamsLocation = 
+        GR_GL(GetUniformLocation(program->fProgramID, "uParams"));
+    
+    GLint samplerLocation = 
+                GR_GL(GetUniformLocation(program->fProgramID, "sTexture"));
+
+#if !ATTRIBUTE_MATRIX   
+    if (-1 == program->fMatrixLocation)    {
+        GrAssert(!"Cannot find matrix uniform in program");
+        GR_GL(DeleteProgram(program->fProgramID));
+        program->fProgramID = 0;
+        return false;
+    }
+#endif
+
+    bool hasTexture = hasTexCoords || hasTexMatrix;
+
+    if (-1 == samplerLocation && hasTexture) {
+        GrAssert(!"Expected to find texture sampler");
+        GR_GL(DeleteProgram(program->fProgramID));
+        program->fProgramID = 0;
+        return false;
+    } else if (-1 != samplerLocation && !hasTexture) {
+        GrAssert(!"unexpectedly found texture sampler");
+    }
+#if !ATTRIBUTE_MATRIX
+    if (-1 == program->fTexMatrixLocation && hasTexMatrix) {
+        GrAssert(!"Expected to find texture matrix");
+        GR_GL(DeleteProgram(program->fProgramID));
+        program->fProgramID = 0;
+        return false;
+    } else if (-1 != program->fTexMatrixLocation && !hasTexMatrix) {
+        GrAssert(!"unexpectedly found texture matrix");
+    }
+#endif
+
+    if (-1 == program->fColorLocation && 
+        (kUniform_ColorType == colorType)) {
+        GR_GL(DeleteProgram(program->fProgramID));
+        program->fProgramID = 0;
+        return false;
+    } else if (-1 != program->fColorLocation && 
+        (kUniform_ColorType != colorType)) {
+        GrAssert(!"Unexpectedly found color uniform");
+    }
+    
+    if (twoPointRadial) {
+        if (-1 == program->fTwoPointParamsLocation) {
+            GrAssert(!"Didn't find expected uniform for 2pt radial gradient");
+            GR_GL(DeleteProgram(program->fProgramID));
+            program->fProgramID = 0;
+            return false;
+        }
+    } else {
+        GrAssert(-1 == program->fTwoPointParamsLocation);
+    }
+    
+    GR_GL(UseProgram(program->fProgramID));
+    if (-1 != samplerLocation) {
+        GR_GL(Uniform1i(samplerLocation, 0));
+    }
+    
+    return true;
+}
+
+GrGpuGLShaders::GrGpuGLShaders() {
+    
+    resetContextHelper();
+    
+    GLuint vshadIDs[GR_ARRAY_COUNT(gvshad)];
+    for (size_t s = 0; s < GR_ARRAY_COUNT(gvshad); ++s) {
+        vshadIDs[s] = loadShader(GL_VERTEX_SHADER, gvshad[s]);
+    }
+
+    GLuint fshadIDs[GR_ARRAY_COUNT(gfshad)];
+    for (size_t s = 0; s < GR_ARRAY_COUNT(gfshad); ++s) {
+        fshadIDs[s] = loadShader(GL_FRAGMENT_SHADER, gfshad[s]);
+    }
+
+    GR_STATIC_ASSERT(kProgramCount == GR_ARRAY_COUNT(gProgramLoadData));
+    for (int p = 0; p < kProgramCount; ++p) {
+        GR_DEBUGCODE(bool result = )
+        createProgram(vshadIDs[gProgramLoadData[p].fVShaderIdx], 
+                      fshadIDs[gProgramLoadData[p].fFShaderIdx],
+                      gProgramLoadData[p].fHasTexMatrix,
+                      gProgramLoadData[p].fHasTexCoords,
+                      gProgramLoadData[p].fColorType,
+                      gProgramLoadData[p].fTwoPointRadial,
+                      &fPrograms[p]);
+        GR_DEBUGASSERT(result);
+        
+        for (int m = 0; m < kMatrixModeCount; ++m) {
+            fPrograms[p].fMatrixModeCache[m].setScale(GR_ScalarMax, 
+                                                      GR_ScalarMax); // illegal
+        };
+        fPrograms[p].fColor = GrColor_ILLEGAL;
+        fPrograms[p].fTextureOrientation = (GrGLTexture::Orientation)-1; // illegal
+        
+        // these aren't strictly invalid, just really unlikely.
+        fPrograms[p].fRadial2CenterX1 = GR_ScalarMin;
+        fPrograms[p].fRadial2Radius0  = GR_ScalarMin;
+        fPrograms[p].fRadial2PosRoot  = true; // arbitrary
+    }
+}
+
+GrGpuGLShaders::~GrGpuGLShaders() {
+    // shaders get deleted once for each program that uses them, do we care? 
+    // probably not
+    for (int i = 0; i < kProgramCount; ++i) {
+        GR_GL(DeleteProgram(fPrograms[i].fProgramID));
+        GR_GL(DeleteShader(fPrograms[i].fVShaderID));
+        GR_GL(DeleteShader(fPrograms[i].fFShaderID));
+    }
+}
+
+void GrGpuGLShaders::resetContext() {
+    INHERITED::resetContext();
+    resetContextHelper();
+}
+
+void GrGpuGLShaders::resetContextHelper() {
+    fHWProgram = (Programs)-1;
+    fTextureOrientation = (GrGLTexture::Orientation)-1; // illegal
+
+    fHWGeometryState.fVertexLayout = 0;
+    fHWGeometryState.fPositionPtr  = (void*) ~0;
+    GR_GL(DisableVertexAttribArray(GR_GL_COL_ATTR_LOCATION));
+    GR_GL(DisableVertexAttribArray(GR_GL_TEX_ATTR_LOCATION));
+    GR_GL(EnableVertexAttribArray(GR_GL_POS_ATTR_LOCATION));
+}
+
+
+void GrGpuGLShaders::flushMatrix(GLint location) {
+    GrAssert(NULL != fCurrDrawState.fRenderTarget);
+    GrMatrix m (
+        GrIntToScalar(2) / fCurrDrawState.fRenderTarget->width(), 0, -GR_Scalar1,
+        0,-GrIntToScalar(2) / fCurrDrawState.fRenderTarget->height(), GR_Scalar1,
+        0, 0, GrMatrix::I()[8]);
+    m.setConcat(m, fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]);
+    
+    // ES doesn't allow you to pass true to the transpose param, 
+    // so do our own transpose
+    GrScalar mt[]  = {
+        m[GrMatrix::kScaleX],
+        m[GrMatrix::kSkewY],
+        m[GrMatrix::kPersp0],
+        m[GrMatrix::kSkewX],
+        m[GrMatrix::kScaleY],
+        m[GrMatrix::kPersp1],
+        m[GrMatrix::kTransX],
+        m[GrMatrix::kTransY],
+        m[GrMatrix::kPersp2]
+    };
+#if ATTRIBUTE_MATRIX
+    glVertexAttrib4fv(GR_GL_MAT_ATTR_LOCATION+0, mt+0);
+    glVertexAttrib4fv(GR_GL_MAT_ATTR_LOCATION+1, mt+3);
+    glVertexAttrib4fv(GR_GL_MAT_ATTR_LOCATION+2, mt+6);
+#else 
+    GR_GL(UniformMatrix3fv(location,1,false,mt));
+#endif
+}
+
+void GrGpuGLShaders::flushTexMatrix(GLint location, 
+                                    GrGLTexture::Orientation orientation) {
+    GrMatrix* m;
+    GrMatrix temp;
+    if (GrGLTexture::kBottomUp_Orientation == orientation) {
+        temp.setAll(
+            GR_Scalar1, 0, 0,
+            0, -GR_Scalar1, GR_Scalar1,
+            0, 0, GrMatrix::I()[8]
+        );
+        temp.preConcat(fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode]);
+        m = &temp;
+    } else {
+        GrAssert(GrGLTexture::kTopDown_Orientation == orientation);
+        m = &fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode];
+    }
+
+    // ES doesn't allow you to pass true to the transpose param,
+    // so do our own transpose
+    GrScalar mt[]  = {
+        (*m)[GrMatrix::kScaleX],
+        (*m)[GrMatrix::kSkewY],
+        (*m)[GrMatrix::kPersp0],
+        (*m)[GrMatrix::kSkewX],
+        (*m)[GrMatrix::kScaleY],
+        (*m)[GrMatrix::kPersp1],
+        (*m)[GrMatrix::kTransX],
+        (*m)[GrMatrix::kTransY],
+        (*m)[GrMatrix::kPersp2]
+    };
+#if ATTRIBUTE_MATRIX
+    glVertexAttrib4fv(GR_GL_TEXMAT_ATTR_LOCATION+0, mt+0);
+    glVertexAttrib4fv(GR_GL_TEXMAT_ATTR_LOCATION+1, mt+3);
+    glVertexAttrib4fv(GR_GL_TEXMAT_ATTR_LOCATION+2, mt+6);
+#else 
+    GR_GL(UniformMatrix3fv(location,1,false,mt));
+#endif 
+}
+
+void GrGpuGLShaders::flushTwoPointRadial(GLint paramsLocation,
+                                         const GrSamplerState& state) {
+    GrScalar centerX1 = state.getRadial2CenterX1();
+    GrScalar radius0 = state.getRadial2Radius0();
+
+    GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1;
+
+    float unis[6] = {
+        GrScalarToFloat(a),
+        1 / (2.f * unis[0]),
+        GrScalarToFloat(centerX1),
+        GrScalarToFloat(radius0),
+        GrScalarToFloat(GrMul(radius0, radius0)),
+        state.isRadial2PosRoot() ? 1.f : -1.f
+    };
+    GR_GL(Uniform1fv(paramsLocation, 6, unis));
+}
+
+void GrGpuGLShaders::flushProgram(PrimitiveType type) {
+
+    Programs nextProgram = kNoTexture_Program;
+
+    if (!VertexHasTexCoords(fGeometrySrc.fVertexLayout)) {
+        goto HAVE_NEXT_PROGRAM;
+    }
+
+    GrAssert(fCurrDrawState.fTexture);
+
+    switch (fCurrDrawState.fSamplerState.getSampleMode()) {
+        case GrSamplerState::kRadial_SampleMode:
+            GrAssert(!fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].hasPerspective());
+            if (fGeometrySrc.fVertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
+                nextProgram = kRadialTextureVertCoords_Program;
+            } else {
+                nextProgram = kRadialTextureTexCoords_Program;
+            }
+            break;
+        case GrSamplerState::kSweep_SampleMode:
+            GrAssert(!fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].hasPerspective());
+            if (fGeometrySrc.fVertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
+                nextProgram = kSweepTextureVertCoords_Program;
+            } else {
+                nextProgram = kSweepTextureTexCoords_Program;
+            }
+            break;
+        case GrSamplerState::kRadial2_SampleMode:
+            GrAssert(!fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].hasPerspective());
+            if (fGeometrySrc.fVertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
+                nextProgram = kTwoPointRadialTextureVertCoords_Program;
+            } else {
+                nextProgram = kTwoPointRadialTextureTexCoords_Program;
+            }
+            break;
+        case GrSamplerState::kAlphaMod_SampleMode:
+            GrAssert(((GrGLTexture*)fCurrDrawState.fTexture)->orientation() == 
+                     GrGLTexture::kTopDown_Orientation);
+            (((GrGLTexture*)fCurrDrawState.fTexture)->uploadFormat() == GL_ALPHA);
+            
+            nextProgram = kText_Program;
+            break;
+        case GrSamplerState::kNormal_SampleMode: {
+            GR_DEBUGCODE(GrGLTexture* tex = (GrGLTexture*)fCurrDrawState.fTexture;)
+            GrAssert(tex);
+
+            bool persp = fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].hasPerspective();
+
+            if (fGeometrySrc.fVertexLayout & kPositionAsTexCoord_VertexLayoutBit) {
+                nextProgram = persp ? kTextureVertCoordsProj_Program : 
+                                      kTextureVertCoords_Program;
+            } else {
+                nextProgram = persp ? kTextureTexCoordsProj_Program : 
+                                      kTextureTexCoords_Program;
+            }
+            // check for case when frag shader can skip the color modulation
+            if (!persp && !(fGeometrySrc.fVertexLayout
+                            & kColor_VertexLayoutBit) &&
+                0xffffffff == fCurrDrawState.fColor) {
+                switch (nextProgram) {
+                case kTextureVertCoords_Program:
+                    nextProgram = kTextureVertCoordsNoColor_Program;
+                    break;
+                case kTextureTexCoords_Program:
+                    nextProgram = kTextureTexCoordsNoColor_Program;
+                    break;
+                default:
+                    GrAssert("Unexpected");
+                    break;
+                }
+            }
+        } break;
+        default:
+            GrAssert(!"Unknown samplemode");
+            break;
+    }
+
+HAVE_NEXT_PROGRAM:
+    if (fHWProgram != nextProgram) {
+        GR_GL(UseProgram(fPrograms[nextProgram].fProgramID));
+        fHWProgram = nextProgram;
+#if GR_COLLECT_STATS
+        ++fStats.fProgChngCnt;
+#endif
+    }
+}
+
+bool GrGpuGLShaders::flushGraphicsState(PrimitiveType type) {
+    
+    flushGLStateCommon(type);
+
+    if (fRenderTargetChanged) {
+        // our coords are in pixel space and the GL matrices map to NDC
+        // so if the viewport changed, our matrix is now wrong.
+#if ATTRIBUTE_MATRIX
+        fHWDrawState.fMatrixModeCache[kModelView_MatrixMode].setScale(GR_ScalarMax,
+                                                                      GR_ScalarMax);
+#else
+        // we assume all shader matrices may be wrong after viewport changes
+        for (int p = 0; p < kProgramCount; ++p) {
+            // set to illegal matrix
+            fPrograms[p].fMatrixModeCache[kModelView_MatrixMode].setScale(GR_ScalarMax, 
+                                                                          GR_ScalarMax); 
+        }
+#endif
+        fRenderTargetChanged = false;
+    }
+    
+    flushProgram(type);
+
+    if (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit) {
+        // invalidate the immediate mode color
+        fHWDrawState.fColor = GrColor_ILLEGAL;    
+    } else {
+        // if we don't have per-vert colors either set the color attr
+        // or color uniform (depending on which program).
+        if (-1 != fPrograms[fHWProgram].fColorLocation) {
+            GrAssert(kUniform_ColorType == fPrograms[fHWProgram].fColorType);
+            if (fPrograms[fHWProgram].fColor != fCurrDrawState.fColor) {
+                float c[] = {
+                    GrColorUnpackR(fCurrDrawState.fColor) / 255.f,
+                    GrColorUnpackG(fCurrDrawState.fColor) / 255.f,
+                    GrColorUnpackB(fCurrDrawState.fColor) / 255.f,
+                    GrColorUnpackA(fCurrDrawState.fColor) / 255.f
+                };
+                GR_GL(Uniform4fv(fPrograms[fHWProgram].fColorLocation, 1, c));
+                fPrograms[fHWProgram].fColor = fCurrDrawState.fColor;
+            }
+        } else if (kAttrib_ColorType == fPrograms[fHWProgram].fColorType && 
+                   fHWDrawState.fColor != fCurrDrawState.fColor) {
+            // OpenGL ES only supports the float varities of glVertexAttrib
+            float c[] = {
+                GrColorUnpackR(fCurrDrawState.fColor) / 255.f,
+                GrColorUnpackG(fCurrDrawState.fColor) / 255.f,
+                GrColorUnpackB(fCurrDrawState.fColor) / 255.f,
+                GrColorUnpackA(fCurrDrawState.fColor) / 255.f
+            };
+            GR_GL(VertexAttrib4fv(GR_GL_COL_ATTR_LOCATION, c));
+            fHWDrawState.fColor = fCurrDrawState.fColor;
+        }
+    }
+
+#if ATTRIBUTE_MATRIX
+    GrMatrix* currentMats = fHWDrawState.fMatrixModeCache;
+    GrGLTexture::Orientation& orientation = fTextureOrientation; 
+#else
+    GrMatrix* currentMats = fPrograms[fHWProgram].fMatrixModeCache;
+    GrGLTexture::Orientation& orientation = 
+                                    fPrograms[fHWProgram].fTextureOrientation;
+#endif
+
+    if (currentMats[kModelView_MatrixMode] !=
+          fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]) {
+        flushMatrix(fPrograms[fHWProgram].fMatrixLocation);
+        currentMats[kModelView_MatrixMode] = 
+            fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode];
+    }
+
+    GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTexture;
+    if (NULL != texture) {
+        if (-1 != fPrograms[fHWProgram].fTexMatrixLocation &&
+            (currentMats[kTexture_MatrixMode] !=
+             fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode] ||
+             orientation != texture->orientation())) {
+            flushTexMatrix(fPrograms[fHWProgram].fTexMatrixLocation,
+                           texture->orientation());
+            currentMats[kTexture_MatrixMode] = 
+                        fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode];
+            orientation = texture->orientation();
+        }
+    }
+
+    const GrSamplerState& sampler = fCurrDrawState.fSamplerState;
+    if (-1 != fPrograms[fHWProgram].fTwoPointParamsLocation &&
+        (fPrograms[fHWProgram].fRadial2CenterX1 != sampler.getRadial2CenterX1() ||
+         fPrograms[fHWProgram].fRadial2Radius0  != sampler.getRadial2Radius0()  ||
+         fPrograms[fHWProgram].fRadial2PosRoot  != sampler.isRadial2PosRoot())) {
+            
+        flushTwoPointRadial(fPrograms[fHWProgram].fTwoPointParamsLocation,
+                            sampler);
+        fPrograms[fHWProgram].fRadial2CenterX1 = sampler.getRadial2CenterX1();
+        fPrograms[fHWProgram].fRadial2Radius0 = sampler.getRadial2Radius0();
+        fPrograms[fHWProgram].fRadial2PosRoot = sampler.isRadial2PosRoot();
+    }
+    
+    return true;
+}
+
+void GrGpuGLShaders::setupGeometry(uint32_t startVertex,
+                                   uint32_t startIndex,
+                                   uint32_t vertexCount,
+                                   uint32_t indexCount) {
+
+    int newColorOffset, newTexCoordOffset;
+    
+    GLsizei newStride = VertexSizeAndOffsets(fGeometrySrc.fVertexLayout,
+                                             &newTexCoordOffset, 
+                                             &newColorOffset);
+    int oldColorOffset, oldTexCoordOffset;
+    GLsizei oldStride = VertexSizeAndOffsets(fHWGeometryState.fVertexLayout,
+                                             &oldTexCoordOffset, 
+                                             &oldColorOffset);
+    
+    const GLvoid* posPtr = (GLvoid*)(newStride * startVertex);
+    
+    if (kBuffer_GeometrySrcType == fGeometrySrc.fVertexSrc) {
+        GrAssert(NULL != fGeometrySrc.fVertexBuffer);
+        GrAssert(!fGeometrySrc.fVertexBuffer->isLocked());
+        if (fHWGeometryState.fVertexBuffer != fGeometrySrc.fVertexBuffer) {
+            GrGLVertexBuffer* buf = 
+            (GrGLVertexBuffer*)fGeometrySrc.fVertexBuffer;
+            GR_GL(BindBuffer(GL_ARRAY_BUFFER, buf->bufferID()));
+            fHWGeometryState.fVertexBuffer = fGeometrySrc.fVertexBuffer;
+        }
+    } else { 
+        if (kArray_GeometrySrcType == fGeometrySrc.fVertexSrc) {
+            posPtr = (void*)((intptr_t)fGeometrySrc.fVertexArray + 
+                             (intptr_t)posPtr);
+        } else {
+            GrAssert(kReserved_GeometrySrcType == fGeometrySrc.fVertexSrc);
+            posPtr = (void*)((intptr_t)fVertices.get() + (intptr_t)posPtr);            
+        }
+        if (NULL != fHWGeometryState.fVertexBuffer) {
+            GR_GL(BindBuffer(GL_ARRAY_BUFFER, 0));
+            fHWGeometryState.fVertexBuffer = NULL;
+        }
+    }
+    
+    if (kBuffer_GeometrySrcType == fGeometrySrc.fIndexSrc) {
+        GrAssert(NULL != fGeometrySrc.fIndexBuffer);
+        GrAssert(!fGeometrySrc.fIndexBuffer->isLocked());
+        if (fHWGeometryState.fIndexBuffer != fGeometrySrc.fIndexBuffer) {
+            GrGLIndexBuffer* buf = 
+            (GrGLIndexBuffer*)fGeometrySrc.fIndexBuffer;
+            GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf->bufferID()));
+            fHWGeometryState.fIndexBuffer = fGeometrySrc.fIndexBuffer;
+        }
+    } else if (NULL != fHWGeometryState.fIndexBuffer) {
+        GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+        fHWGeometryState.fIndexBuffer = NULL;
+    }
+    
+    GLenum scalarType;
+    bool texCoordNorm;
+    if (fGeometrySrc.fVertexLayout & kTextFormat_VertexLayoutBit) {
+        scalarType = GrGLTextType;
+        texCoordNorm = GR_GL_TEXT_TEXTURE_NORMALIZED;
+    } else {
+        scalarType = GrGLType;
+        texCoordNorm = false;
+    }
+    
+    bool baseChange = posPtr != fHWGeometryState.fPositionPtr;
+    bool scalarChange = (GrGLTextType != GrGLType) &&
+                        (kTextFormat_VertexLayoutBit & 
+                         (fHWGeometryState.fVertexLayout ^ 
+                          fGeometrySrc.fVertexLayout));
+    bool strideChange = newStride != oldStride;
+    bool posChange = baseChange || scalarChange || strideChange;
+    
+    if (posChange) {
+        GR_GL(VertexAttribPointer(GR_GL_POS_ATTR_LOCATION, 2, scalarType,
+                                  false, newStride, posPtr));
+        fHWGeometryState.fPositionPtr = posPtr;
+    }
+    
+    if (newTexCoordOffset > 0) {
+        GLvoid* texCoordPtr = (int8_t*)posPtr + newTexCoordOffset;
+        if (oldTexCoordOffset <= 0) {
+            GR_GL(EnableVertexAttribArray(GR_GL_TEX_ATTR_LOCATION));
+        }
+        if (posChange || newTexCoordOffset != oldTexCoordOffset) {
+            GR_GL(VertexAttribPointer(GR_GL_TEX_ATTR_LOCATION, 2, scalarType,
+                                      texCoordNorm, newStride, texCoordPtr));
+        }
+    } else if (oldTexCoordOffset > 0) {
+        GR_GL(DisableVertexAttribArray(GR_GL_TEX_ATTR_LOCATION));
+    }
+    
+    if (newColorOffset > 0) {
+        GLvoid* colorPtr = (int8_t*)posPtr + newColorOffset;
+        if (oldColorOffset <= 0) {
+            GR_GL(EnableVertexAttribArray(GR_GL_COL_ATTR_LOCATION));
+        }
+        if (posChange || newColorOffset != oldColorOffset) {
+            GR_GL(VertexAttribPointer(GR_GL_COL_ATTR_LOCATION, 4, 
+                                      GL_UNSIGNED_BYTE,
+                                      true, newStride, colorPtr));
+        }
+    } else if (oldColorOffset > 0) {
+        GR_GL(DisableVertexAttribArray(GR_GL_COL_ATTR_LOCATION));
+    }
+    
+    fHWGeometryState.fVertexLayout = fGeometrySrc.fVertexLayout;
+}
+#endif
diff --git a/gpu/src/GrGpuGLShaders.h b/gpu/src/GrGpuGLShaders.h
new file mode 100644
index 0000000..cba387b
--- /dev/null
+++ b/gpu/src/GrGpuGLShaders.h
@@ -0,0 +1,153 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGpuGLShaders_DEFINED
+#define GrGpuGLShaders_DEFINED
+
+#include "GrGpuGL.h"
+
+// Programmable OpenGL or OpenGL ES 2.0
+class GrGpuGLShaders : public GrGpuGL {
+public:
+             GrGpuGLShaders();
+    virtual ~GrGpuGLShaders();
+    
+    virtual void resetContext();
+
+    // type of colors used by a program
+    enum ColorType {
+        kNone_ColorType,
+        kAttrib_ColorType,
+        kUniform_ColorType,
+    };
+protected:
+    // overrides from GrGpu
+    virtual bool flushGraphicsState(PrimitiveType type);
+    virtual void setupGeometry(uint32_t startVertex,
+                               uint32_t startIndex,
+                               uint32_t vertexCount,
+                               uint32_t indexCount);
+    
+private:
+    void resetContextHelper();
+    
+    // sets the texture matrix uniform for currently bound program
+    void flushTexMatrix(GLint location, 
+                        GrGLTexture::Orientation orientation);
+    // sets the MVP matrix uniform for currently bound program
+    void flushMatrix(GLint location);
+    
+    void flushTwoPointRadial(GLint paramsLocation, const GrSamplerState&);
+    
+    // reads shader from array and compiles it with GL, returns shader ID or 0 if failed
+    GLuint loadShader(GLenum type, const char* src);
+    
+    struct ProgramData;
+    // creates a GL program with two shaders attached. 
+    // Gets the relevant uniform locations.
+    // Sets the texture sampler if present to texture 0
+    // Binds the program
+    // returns true if succeeded.
+    bool createProgram(GLuint vshader,
+                       GLuint fshader,
+                       bool hasTexMatrix,
+                       bool hasTexCoords,
+                       ColorType colorType,
+                       bool twoPointRadial,
+                       ProgramData* program);
+
+    // called at flush time to setup the appropriate program
+    void flushProgram(PrimitiveType type);
+
+    enum Programs {
+        // use vertex coordinates         
+        kTextureVertCoords_Program = 0,
+        kTextureVertCoordsProj_Program,
+        
+        // use separate tex coords
+        kTextureTexCoords_Program,
+        kTextureTexCoordsProj_Program,
+
+        // constant color texture, no proj
+        // verts as a tex coords
+        kTextureVertCoordsNoColor_Program,        
+
+        // constant color texture, no proj
+        // separate tex coords
+        kTextureTexCoordsNoColor_Program,
+
+        // special program for text glyphs
+        kText_Program,
+
+        // programs for radial texture lookup
+        kRadialTextureVertCoords_Program,
+        kRadialTextureTexCoords_Program,
+
+        // programs for sweep texture lookup
+        kSweepTextureVertCoords_Program,
+        kSweepTextureTexCoords_Program, 
+        
+        // programs for two-point radial lookup
+        kTwoPointRadialTextureVertCoords_Program,
+        kTwoPointRadialTextureTexCoords_Program,
+        
+        // color only drawing
+        kNoTexture_Program,
+
+        kProgramCount
+    };
+
+    // Records per-program information
+    // we can specify the attribute locations so that they are constant
+    // across our shaders. But the driver determines the uniform locations 
+    // at link time. We don't need to remember the sampler uniform location
+    // because we will bind a texture slot to it and never change it
+    // Uniforms are program-local so we can't rely on fHWState to hold the 
+    // previous uniform state after a program change.
+    struct ProgramData {
+        // IDs
+        GLuint    fVShaderID;
+        GLuint    fFShaderID;
+        GLuint    fProgramID;
+        
+        // shader uniform locations (-1 if shader doesn't use them)
+        GLint     fMatrixLocation;
+        GLint     fTexMatrixLocation;
+        GLint     fColorLocation;
+        GLint     fTwoPointParamsLocation;
+        
+        ColorType fColorType;
+
+        // these reflect the current values of uniforms
+        // (GL uniform values travel with program)
+        GrMatrix                    fMatrixModeCache[kMatrixModeCount];
+        GrColor                     fColor;
+        GrGLTexture::Orientation    fTextureOrientation;
+        GrScalar                    fRadial2CenterX1;
+        GrScalar                    fRadial2Radius0;
+        bool                        fRadial2PosRoot;
+    };
+    
+    ProgramData fPrograms[kProgramCount];
+    Programs    fHWProgram;
+    
+    GrGLTexture::Orientation  fTextureOrientation;
+
+    typedef GrGpuGL INHERITED;
+};
+
+#endif
diff --git a/gpu/src/GrGpuGLShaders2.cpp b/gpu/src/GrGpuGLShaders2.cpp
new file mode 100644
index 0000000..0a15be1
--- /dev/null
+++ b/gpu/src/GrGpuGLShaders2.cpp
@@ -0,0 +1,1388 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrGLConfig.h"
+
+#if GR_SUPPORT_GLES2 || GR_SUPPORT_GLDESKTOP
+
+#include "GrGpuGLShaders2.h"
+#include "GrGpuVertex.h"
+#include "GrMemory.h"
+#include "GrStringBuilder.h"
+
+
+#define ATTRIBUTE_MATRIX        0
+
+#define SKIP_COLOR_MODULATE_OPT 0
+
+#define PRINT_SHADERS           0
+
+#define SKIP_CACHE_CHECK    true
+
+#if GR_SUPPORT_GLES2
+    #define GR_PRECISION                "mediump"
+    const char GR_SHADER_PRECISION[] =  "precision mediump float;\n";
+#else
+    #define GR_PRECISION                ""
+    const char GR_SHADER_PRECISION[] =  "";
+#endif
+
+#define POS_ATTR_LOCATION 0
+#define TEX_ATTR_LOCATION 1
+#define COL_ATTR_LOCATION 2
+#if ATTRIBUTE_MATRIX
+#define VIEWMAT_ATTR_LOCATION 3
+#define TEXMAT_ATTR_LOCATION(X) (6 + 3 * (X))
+#define BOGUS_MATRIX_UNI_LOCATION 1000
+#endif
+
+const int GrGpuGLShaders2::NUM_STAGES = 1;
+
+struct GrGpuGLShaders2::StageUniLocations {
+    GLint fTextureMatrixUni;
+    GLint fSamplerUni;
+    GLint fRadial2Uni;
+};
+
+struct GrGpuGLShaders2::UniLocations {
+    GLint fViewMatrixUni;
+    StageUniLocations fStages[NUM_STAGES];
+};
+
+// Records per-program information
+// we can specify the attribute locations so that they are constant
+// across our shaders. But the driver determines the uniform locations
+// at link time. We don't need to remember the sampler uniform location
+// because we will bind a texture slot to it and never change it
+// Uniforms are program-local so we can't rely on fHWState to hold the
+// previous uniform state after a program change.
+struct  GrGpuGLShaders2::Program {
+    // IDs
+    GLuint    fVShaderID;
+    GLuint    fFShaderID;
+    GLuint    fProgramID;
+
+    // shader uniform locations (-1 if shader doesn't use them)
+    UniLocations fUniLocations;
+
+    // these reflect the current values of uniforms
+    // (GL uniform values travel with program)
+    GrMatrix                    fViewMatrix;
+    GrMatrix                    fTextureMatrix[NUM_STAGES];
+    GrGLTexture::Orientation    fTextureOrientation[NUM_STAGES];
+    GrScalar                    fRadial2CenterX1[NUM_STAGES];
+    GrScalar                    fRadial2Radius0[NUM_STAGES];
+    bool                        fRadial2PosRoot[NUM_STAGES];
+
+};
+
+// must be tightly packed
+struct GrGpuGLShaders2::StageDesc {
+    enum OptFlagBits {
+        kNoPerspective_OptFlagBit  = 0x1,
+        kIdentityMatrix_OptFlagBit = 0x2,
+    };
+    int fOptFlags : 8;
+    bool fEnabled;
+    enum Modulation {
+        kColor_Modulation,
+        kAlpha_Modulation,
+    } fModulation : 8;
+    enum CoordMapping {
+        kIdentity_CoordMapping,
+        kRadialGradient_CoordMapping,
+        kSweepGradient_CoordMapping,
+        kRadial2Gradient_CoordMapping,
+    } fCoordMapping : 8;
+};
+
+// must be tightly packed
+struct GrGpuGLShaders2::ProgramDesc {
+    GrVertexLayout fVertexLayout;
+    enum {
+        kNotPoints_OptFlagBit = 0x1,
+        kVertexColorAllOnes_OptFlagBit = 0x2,
+    };
+    // we're assuming optflags and layout pack into 32 bits
+    GR_STATIC_ASSERT(2 == sizeof(GrVertexLayout));
+    int fOptFlags : 16;
+
+    StageDesc fStages[NUM_STAGES];
+
+    bool operator == (const ProgramDesc& desc) const {
+        // keep 4-byte aligned and tightly packed
+        GR_STATIC_ASSERT(4 == sizeof(StageDesc));
+        GR_STATIC_ASSERT(2 + 2 + 4 * NUM_STAGES == sizeof(ProgramDesc));
+        return 0 == memcmp(this, &desc, sizeof(ProgramDesc));
+    }
+};
+
+#include "GrTHashCache.h"
+
+class GrGpuGLShaders2::ProgramCache : public ::GrNoncopyable {
+private:
+    struct Entry;
+    class HashKey {
+    public:
+        HashKey();
+        HashKey(const ProgramDesc& desc);
+        static const HashKey& GetKey(const Entry&);
+        static bool EQ(const Entry&, const HashKey&);
+        static bool LT(const Entry&, const HashKey&);
+        bool operator <(const HashKey& key) const;
+        bool operator ==(const HashKey& key) const;
+        uint32_t getHash() const;
+    private:
+        ProgramDesc fDesc;
+        uint32_t fHash;
+    };
+
+    struct Entry {
+        Program     fProgram;
+        HashKey     fKey;
+        uint32_t    fLRUStamp;
+    };
+
+    // if hash bits is changed, need to change hash function
+    GrTHashTable<Entry, HashKey, 8> fHashCache;
+
+    static const int MAX_ENTRIES = 16;
+    Entry        fEntries[MAX_ENTRIES];
+    int          fCount;
+    uint32_t     fCurrLRUStamp;
+
+public:
+    ProgramCache() {
+        fCount        = 0;
+        fCurrLRUStamp = 0;
+    }
+
+    ~ProgramCache() {
+        for (int i = 0; i < fCount; ++i) {
+            GrGpuGLShaders2::DeleteProgram(&fEntries[i].fProgram);
+        }
+    }
+
+    void abandon() {
+        fCount = 0;
+    }
+
+    void invalidateViewMatrices() {
+        for (int i = 0; i < fCount; ++i) {
+            // set to illegal matrix
+            fEntries[i].fProgram.fViewMatrix.setScale(GR_ScalarMax,
+                                                      GR_ScalarMax);
+        }
+    }
+
+    Program* getProgram(const ProgramDesc& desc) {
+        HashKey key(desc);
+        Entry* entry = fHashCache.find(key);
+        if (NULL == entry) {
+            if (fCount < MAX_ENTRIES) {
+                entry = fEntries + fCount;
+                ++fCount;
+            } else {
+                GrAssert(MAX_ENTRIES == fCount);
+                entry = fEntries;
+                for (int i = 1; i < MAX_ENTRIES; ++i) {
+                    if (fEntries[i].fLRUStamp < entry->fLRUStamp) {
+                        entry = fEntries + i;
+                    }
+                }
+                fHashCache.remove(entry->fKey, entry);
+                GrGpuGLShaders2::DeleteProgram(&entry->fProgram);
+            }
+            entry->fKey = key;
+            GrGpuGLShaders2::GenProgram(desc, &entry->fProgram);
+            fHashCache.insert(entry->fKey, entry);
+        }
+
+        entry->fLRUStamp = fCurrLRUStamp;
+        if (UINT32_MAX == fCurrLRUStamp) {
+            // wrap around! just trash our LRU, one time hit.
+            for (int i = 0; i < fCount; ++i) {
+                fEntries[i].fLRUStamp = 0;
+            }
+        }
+        ++fCurrLRUStamp;
+        return &entry->fProgram;
+    }
+};
+
+GrGpuGLShaders2::ProgramCache::HashKey::HashKey() {
+}
+
+static uint32_t ror(uint32_t x) {
+    return (x >> 8) | (x << 24);
+}
+
+
+GrGpuGLShaders2::ProgramCache::HashKey::HashKey(const ProgramDesc& desc) {
+    fDesc = desc;
+    // if you change the size of the desc, need to update the hash function
+    GR_STATIC_ASSERT(8 == sizeof(ProgramDesc));
+
+    uint32_t* d = (uint32_t*) &fDesc;
+    fHash = d[0] ^ ror(d[1]);
+}
+
+bool GrGpuGLShaders2::ProgramCache::HashKey::EQ(const Entry& entry,
+                                                const HashKey& key) {
+    return entry.fKey == key;
+}
+
+bool GrGpuGLShaders2::ProgramCache::HashKey::LT(const Entry& entry,
+                                                const HashKey& key) {
+    return entry.fKey < key;
+}
+
+bool GrGpuGLShaders2::ProgramCache::HashKey::operator ==(const HashKey& key) const {
+    return fDesc == key.fDesc;
+}
+
+bool GrGpuGLShaders2::ProgramCache::HashKey::operator <(const HashKey& key) const {
+    return memcmp(&fDesc, &key.fDesc, sizeof(HashKey)) < 0;
+}
+
+uint32_t GrGpuGLShaders2::ProgramCache::HashKey::getHash() const {
+    return fHash;
+}
+
+
+struct GrGpuGLShaders2::ShaderCodeSegments {
+    GrSStringBuilder<256> fVSUnis;
+    GrSStringBuilder<256> fVSAttrs;
+    GrSStringBuilder<256> fVaryings;
+    GrSStringBuilder<256> fFSUnis;
+    GrSStringBuilder<512> fVSCode;
+    GrSStringBuilder<512> fFSCode;
+};
+// for variable names etc
+typedef GrSStringBuilder<16> GrTokenString;
+
+#if ATTRIBUTE_MATRIX
+    #define VIEW_MATRIX_NAME "aViewM"
+#else
+    #define VIEW_MATRIX_NAME "uViewM"
+#endif
+
+#define POS_ATTR_NAME "aPosition"
+#define COL_ATTR_NAME "aColor"
+#define TEX_ATTR_NAME "aTexture"
+
+static inline const char* float_vector_type(int count) {
+    static const char* FLOAT_VECS[] = {"ERROR", "float", "vec2", "vec3", "vec4"};
+    GrAssert(count >= 1 && count < GR_ARRAY_COUNT(FLOAT_VECS));
+    return FLOAT_VECS[count];
+}
+
+static inline const char* vector_homog_coord(int count) {
+    static const char* HOMOGS[] = {"ERROR", "", ".y", ".z", ".w"};
+    GrAssert(count >= 1 && count < GR_ARRAY_COUNT(HOMOGS));
+    return HOMOGS[count];
+}
+
+static inline const char* vector_nonhomog_coords(int count) {
+    static const char* NONHOMOGS[] = {"ERROR", "", ".x", ".xy", ".xyz"};
+    GrAssert(count >= 1 && count < GR_ARRAY_COUNT(NONHOMOGS));
+    return NONHOMOGS[count];
+}
+
+static inline const char* vector_all_coords(int count) {
+    static const char* ALL[] = {"ERROR", "", ".xy", ".xyz", ".xyzw"};
+    GrAssert(count >= 1 && count < GR_ARRAY_COUNT(ALL));
+    return ALL[count];
+}
+
+static void tex_matrix_name(int stage, GrStringBuilder* s) {
+#if ATTRIBUTE_MATRIX
+    *s = "aTexM";
+#else
+    *s = "uTexM";
+#endif
+    s->appendInt(stage);
+}
+
+static void sampler_name(int stage, GrStringBuilder* s) {
+    *s = "uSampler";
+    s->appendInt(stage);
+}
+
+static void stage_varying_name(int stage, GrStringBuilder* s) {
+    *s = "vStage";
+    s->appendInt(stage);
+}
+
+static void radial2_param_name(int stage, GrStringBuilder* s) {
+    *s = "uRadial2Params";
+    s->appendInt(stage);
+}
+
+static void radial2_varying_name(int stage, GrStringBuilder* s) {
+    *s = "vB";
+    s->appendInt(stage);
+}
+
+#include "GrRandom.h"
+
+void GrGpuGLShaders2::ProgramUnitTest() {
+    static const uint16_t VFORMATS[] = {
+        0,
+        kSeparateTexCoord_VertexLayoutBit,
+        kPositionAsTexCoord_VertexLayoutBit,
+        kSeparateTexCoord_VertexLayoutBit | kColor_VertexLayoutBit,
+        kPositionAsTexCoord_VertexLayoutBit | kColor_VertexLayoutBit,
+        kTextFormat_VertexLayoutBit
+    };
+    static const int PROG_OPTS[] = {
+        0,
+        ProgramDesc::kNotPoints_OptFlagBit,
+        ProgramDesc::kVertexColorAllOnes_OptFlagBit,
+        ProgramDesc::kNotPoints_OptFlagBit | ProgramDesc::kVertexColorAllOnes_OptFlagBit
+    };
+    static const int STAGE_OPTS[] = {
+        0,
+        StageDesc::kNoPerspective_OptFlagBit,
+        StageDesc::kIdentity_CoordMapping
+    };
+    static const int STAGE_MODULATES[] = {
+        StageDesc::kColor_Modulation,
+        StageDesc::kAlpha_Modulation
+    };
+    static const int STAGE_COORD_MAPPINGS[] = {
+        StageDesc::kIdentity_CoordMapping,
+        StageDesc::kRadialGradient_CoordMapping,
+        StageDesc::kSweepGradient_CoordMapping,
+        StageDesc::kRadial2Gradient_CoordMapping
+    };
+    ProgramDesc pdesc;
+    memset(&pdesc, 0, sizeof(pdesc));
+
+    static const int NUM_TESTS = 1024;
+
+    // GrRandoms nextU() values have patterns in the low bits
+    // So using nextU() % array_count might never take some values.
+    GrRandom random;
+    for (int t = 0; t < NUM_TESTS; ++t) {
+        int x = (int)(random.nextF() * GR_ARRAY_COUNT(VFORMATS));
+        pdesc.fVertexLayout = VFORMATS[x];
+        x = (int)(random.nextF() * GR_ARRAY_COUNT(PROG_OPTS));
+        pdesc.fOptFlags = PROG_OPTS[x];
+        for (int s = 0; s < NUM_STAGES; ++s) {
+            x = (int)(random.nextF() * 2.f);
+            pdesc.fStages[s].fEnabled = x;
+            x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS));
+            pdesc.fStages[s].fOptFlags = STAGE_OPTS[x];
+            x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_MODULATES));
+            pdesc.fStages[s].fModulation = STAGE_MODULATES[x];
+            x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_COORD_MAPPINGS));
+            pdesc.fStages[s].fCoordMapping = STAGE_COORD_MAPPINGS[x];
+        }
+        Program program;
+        GenProgram(pdesc, &program);
+        DeleteProgram(&program);
+    }
+}
+
+void GrGpuGLShaders2::GenStageCode(int stageNum,
+                                   const StageDesc& desc,
+                                   const char* fsInColor, // NULL means no incoming color
+                                   const char* fsOutColor,
+                                   const char* vsInCoord,
+                                   ShaderCodeSegments* segments,
+                                   StageUniLocations* locations) {
+
+    GrAssert(stageNum >= 0 && stageNum <= 9);
+
+    GrTokenString varyingName;
+    stage_varying_name(stageNum, &varyingName);
+
+    // First decide how many coords are needed to access the texture
+    // Right now it's always 2 but we could start using 1D textures for
+    // gradients.
+    static const int coordDims = 2;
+    int varyingDims;
+    /// Vertex Shader Stuff
+
+    // decide whether we need a matrix to transform texture coords
+    // and whether the varying needs a perspective coord.
+    GrTokenString texMName;
+    tex_matrix_name(stageNum, &texMName);
+    if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
+        varyingDims = coordDims;
+    } else {
+    #if ATTRIBUTE_MATRIX
+        segments->fVSAttrs += "attribute mat3 ";
+        segments->fVSAttrs += texMName;
+        segments->fVSAttrs += ";\n";
+    #else
+        segments->fVSUnis += "uniform mat3 ";
+        segments->fVSUnis += texMName;
+        segments->fVSUnis += ";\n";
+        locations->fTextureMatrixUni = 1;
+    #endif
+        if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
+            varyingDims = coordDims;
+        } else {
+            varyingDims = coordDims + 1;
+        }
+    }
+
+    GrTokenString samplerName;
+    sampler_name(stageNum, &samplerName);
+    segments->fFSUnis += "uniform sampler2D ";
+    segments->fFSUnis += samplerName;
+    segments->fFSUnis += ";\n";
+    locations->fSamplerUni = 1;
+
+    segments->fVaryings += "varying ";
+    segments->fVaryings += float_vector_type(varyingDims);
+    segments->fVaryings += " ";
+    segments->fVaryings += varyingName;
+    segments->fVaryings += ";\n";
+
+    if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
+        GrAssert(varyingDims == coordDims);
+        segments->fVSCode += "\t";
+        segments->fVSCode += varyingName;
+        segments->fVSCode += " = ";
+        segments->fVSCode += vsInCoord;
+        segments->fVSCode += ";\n";
+    } else {
+        segments->fVSCode += "\t";
+        segments->fVSCode += varyingName;
+        segments->fVSCode += " = (";
+        segments->fVSCode += texMName;
+        segments->fVSCode += " * vec3(";
+        segments->fVSCode += vsInCoord;
+        segments->fVSCode += ", 1))";
+        segments->fVSCode += vector_all_coords(varyingDims);
+        segments->fVSCode += ";\n";
+    }
+
+    GrTokenString radial2ParamsName;
+    radial2_param_name(stageNum, &radial2ParamsName);
+    // for radial grads without perspective we can pass the linear
+    // part of the quadratic as a varying.
+    GrTokenString radial2VaryingName;
+    radial2_varying_name(stageNum, &radial2VaryingName);
+
+    if (StageDesc::kRadial2Gradient_CoordMapping == desc.fCoordMapping) {
+
+        segments->fVSUnis += "uniform " GR_PRECISION " float ";
+        segments->fVSUnis += radial2ParamsName;
+        segments->fVSUnis += "[6];\n";
+
+        segments->fFSUnis += "uniform " GR_PRECISION " float ";
+        segments->fFSUnis += radial2ParamsName;
+        segments->fFSUnis += "[6];\n";
+        locations->fRadial2Uni = 1;
+
+        // if there is perspective we don't interpolate this
+        if (varyingDims == coordDims) {
+            GrAssert(2 == coordDims);
+            segments->fVaryings += "varying float ";
+            segments->fVaryings += radial2VaryingName;
+            segments->fVaryings += ";\n";
+
+            segments->fVSCode += "\t";
+            segments->fVSCode += radial2VaryingName;
+            segments->fVSCode += " = 2.0 * (";
+            segments->fVSCode += radial2ParamsName;
+            segments->fVSCode += "[2] * ";
+            segments->fVSCode += varyingName;
+            segments->fVSCode += ".x ";
+            segments->fVSCode += " - ";
+            segments->fVSCode += radial2ParamsName;
+            segments->fVSCode += "[3]);\n";
+        }
+    }
+
+    /// Fragment Shader Stuff
+    GrTokenString fsCoordName;
+    // function used to access the shader, may be made projective
+    GrTokenString texFunc("texture2D");
+    if (desc.fOptFlags & (StageDesc::kIdentityMatrix_OptFlagBit |
+                          StageDesc::kNoPerspective_OptFlagBit)) {
+        GrAssert(varyingDims == coordDims);
+        fsCoordName = varyingName;
+    } else {
+        // if we have to do some non-matrix op on the varyings to get
+        // our final tex coords then when in perspective we have to
+        // do an explicit divide
+        if  (StageDesc::kIdentity_CoordMapping == desc.fCoordMapping) {
+            texFunc += "Proj";
+            fsCoordName = varyingName;
+        } else {
+            fsCoordName = "tCoord";
+            fsCoordName.appendInt(stageNum);
+
+            segments->fFSCode += "\t";
+            segments->fFSCode += float_vector_type(coordDims);
+            segments->fFSCode += " ";
+            segments->fFSCode += fsCoordName;
+            segments->fFSCode += " = ";
+            segments->fFSCode += varyingName;
+            segments->fFSCode += vector_nonhomog_coords(varyingDims);
+            segments->fFSCode += " / ";
+            segments->fFSCode += varyingName;
+            segments->fFSCode += vector_homog_coord(varyingDims);
+            segments->fFSCode += ";\n";
+        }
+    }
+
+    GrSStringBuilder<96> sampleCoords;
+    switch (desc.fCoordMapping) {
+    case StageDesc::kIdentity_CoordMapping:
+        sampleCoords = fsCoordName;
+        break;
+    case StageDesc::kSweepGradient_CoordMapping:
+        sampleCoords = "vec2(atan(-";
+        sampleCoords += fsCoordName;
+        sampleCoords += ".y, -";
+        sampleCoords += fsCoordName;
+        sampleCoords += ".x)*0.1591549430918 + 0.5, 0.5)";
+        break;
+    case StageDesc::kRadialGradient_CoordMapping:
+        sampleCoords = "vec2(length(";
+        sampleCoords += fsCoordName;
+        sampleCoords += ".xy), 0.5)";
+        break;
+    case StageDesc::kRadial2Gradient_CoordMapping: {
+        GrTokenString cName    = "c";
+        GrTokenString ac4Name  = "ac4";
+        GrTokenString rootName = "root";
+
+        cName.appendInt(stageNum);
+        ac4Name.appendInt(stageNum);
+        rootName.appendInt(stageNum);
+
+        GrTokenString bVar;
+        if (coordDims == varyingDims) {
+            bVar = radial2VaryingName;
+            GrAssert(2 == varyingDims);
+        } else {
+            GrAssert(3 == varyingDims);
+            bVar = "b";
+            bVar.appendInt(stageNum);
+            segments->fFSCode += "\tfloat ";
+            segments->fFSCode += bVar;
+            segments->fFSCode += " = 2.0 * (";
+            segments->fFSCode += radial2ParamsName;
+            segments->fFSCode += "[2] * ";
+            segments->fFSCode += fsCoordName;
+            segments->fFSCode += ".x ";
+            segments->fFSCode += " - ";
+            segments->fFSCode += radial2ParamsName;
+            segments->fFSCode += "[3]);\n";
+        }
+
+        segments->fFSCode += "\tfloat ";
+        segments->fFSCode += cName;
+        segments->fFSCode += " = dot(";
+        segments->fFSCode += fsCoordName;
+        segments->fFSCode += ", ";
+        segments->fFSCode += fsCoordName;
+        segments->fFSCode += ") + ";
+        segments->fFSCode += " - ";
+        segments->fFSCode += radial2ParamsName;
+        segments->fFSCode += "[4];\n";
+
+        segments->fFSCode += "\tfloat ";
+        segments->fFSCode += ac4Name;
+        segments->fFSCode += " = ";
+        segments->fFSCode += radial2ParamsName;
+        segments->fFSCode += "[0] * 4.0 * ";
+        segments->fFSCode += cName;
+        segments->fFSCode += ";\n";
+
+        segments->fFSCode += "\tfloat ";
+        segments->fFSCode += rootName;
+        segments->fFSCode += " = sqrt(abs(";
+        segments->fFSCode += bVar;
+        segments->fFSCode += " * ";
+        segments->fFSCode += bVar;
+        segments->fFSCode += " - ";
+        segments->fFSCode += ac4Name;
+        segments->fFSCode += "));\n";
+
+        sampleCoords = "vec2((-";
+        sampleCoords += bVar;
+        sampleCoords += " + ";
+        sampleCoords += radial2ParamsName;
+        sampleCoords += "[5] * ";
+        sampleCoords += rootName;
+        sampleCoords += ") * ";
+        sampleCoords += radial2ParamsName;
+        sampleCoords += "[1], 0.5)\n";
+        break;}
+    };
+
+    segments->fFSCode += "\t";
+    segments->fFSCode += fsOutColor;
+    segments->fFSCode += " = ";
+    if (NULL != fsInColor) {
+        segments->fFSCode += fsInColor;
+        segments->fFSCode += " * ";
+    }
+    segments->fFSCode += texFunc;
+    segments->fFSCode += "(";
+    segments->fFSCode += samplerName;
+    segments->fFSCode += ", ";
+    segments->fFSCode += sampleCoords;
+    segments->fFSCode += ")";
+    if (desc.fModulation == StageDesc::kAlpha_Modulation) {
+        segments->fFSCode += ".aaaa";
+    }
+    segments->fFSCode += ";\n";
+
+}
+
+void GrGpuGLShaders2::GenProgram(const ProgramDesc& desc,
+                                 Program* program) {
+
+    ShaderCodeSegments segments;
+    const uint32_t& layout = desc.fVertexLayout;
+
+    memset(&program->fUniLocations, 0, sizeof(UniLocations));
+
+    bool haveColor = !(ProgramDesc::kVertexColorAllOnes_OptFlagBit &
+                       desc.fOptFlags);
+
+#if ATTRIBUTE_MATRIX
+    segments.fVSAttrs = "attribute mat3 " VIEW_MATRIX_NAME ";\n"
+#else
+    segments.fVSUnis  = "uniform mat3 " VIEW_MATRIX_NAME ";\n";
+    segments.fVSAttrs = "";
+#endif
+    segments.fVSAttrs += "attribute vec2 " POS_ATTR_NAME ";\n";
+    if (haveColor) {
+        segments.fVSAttrs += "attribute vec4 " COL_ATTR_NAME ";\n";
+        segments.fVaryings = "varying vec4 vColor;\n";
+    } else {
+        segments.fVaryings = "";
+    }
+
+    segments.fVSCode   = "void main() {\n"
+                         "\tvec3 pos3 = " VIEW_MATRIX_NAME " * vec3(" POS_ATTR_NAME ", 1);\n"
+                         "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n";
+    if (haveColor) {
+        segments.fVSCode += "\tvColor = " COL_ATTR_NAME ";\n";
+    }
+
+    if (!(desc.fOptFlags & ProgramDesc::kNotPoints_OptFlagBit)){
+        segments.fVSCode += "\tgl_PointSize = 1.0;\n";
+    }
+    segments.fFSCode   = "void main() {\n";
+
+    bool textureCoordAttr = false;
+    static const char* IN_COORDS[] = {POS_ATTR_NAME, TEX_ATTR_NAME};
+    const char* inCoords = NULL;
+    if ((kSeparateTexCoord_VertexLayoutBit | kTextFormat_VertexLayoutBit) &
+        layout) {
+        segments.fVSAttrs += "attribute vec2 " TEX_ATTR_NAME ";\n";
+        inCoords = IN_COORDS[1];
+        textureCoordAttr = true;
+    } else if (kPositionAsTexCoord_VertexLayoutBit & layout) {
+        inCoords = IN_COORDS[0];
+    }
+
+    GrTokenString inColor = "vColor";
+    GR_STATIC_ASSERT(NUM_STAGES <= 9);
+    int numActiveStages = 0;
+    for (int i = 0; i < NUM_STAGES; ++i) {
+        if (desc.fStages[i].fEnabled) {
+            ++numActiveStages;
+        }
+    }
+    if (NULL != inCoords && numActiveStages) {
+        int currActiveStage = 0;
+        for (int i = 0; i < NUM_STAGES; ++i) {
+            if (desc.fStages[i].fEnabled) {
+                GrTokenString outColor;
+                if (currActiveStage < (numActiveStages - 1)) {
+                    outColor = "color";
+                    outColor.appendInt(currActiveStage);
+                    segments.fFSCode += "\tvec4 ";
+                    segments.fFSCode += outColor;
+                    segments.fFSCode += ";\n";
+                } else {
+                    outColor = "gl_FragColor";
+                }
+                GenStageCode(i,
+                             desc.fStages[i],
+                             haveColor ? inColor.cstr() : NULL,
+                             outColor.cstr(),
+                             inCoords,
+                             &segments,
+                             &program->fUniLocations.fStages[i]);
+                ++currActiveStage;
+                inColor = outColor;
+                haveColor = true;
+            }
+        }
+    } else {
+        segments.fFSCode += "\tgl_FragColor = ";
+        if (haveColor) {
+            segments.fFSCode += inColor;
+        } else {
+            segments.fFSCode += "vec4(1,1,1,1)";
+        }
+        segments.fFSCode += ";\n";
+    }
+    segments.fFSCode += "}\n";
+    segments.fVSCode += "}\n";
+
+
+    const char* strings[4];
+    int lengths[4];
+    int stringCnt = 0;
+
+    if (segments.fVSUnis.length()) {
+        strings[stringCnt] = segments.fVSUnis.cstr();
+        lengths[stringCnt] = segments.fVSUnis.length();
+        ++stringCnt;
+    }
+    if (segments.fVSAttrs.length()) {
+        strings[stringCnt] = segments.fVSAttrs.cstr();
+        lengths[stringCnt] = segments.fVSAttrs.length();
+        ++stringCnt;
+    }
+    if (segments.fVaryings.length()) {
+        strings[stringCnt] = segments.fVaryings.cstr();
+        lengths[stringCnt] = segments.fVaryings.length();
+        ++stringCnt;
+    }
+
+    GrAssert(segments.fVSCode.length());
+    strings[stringCnt] = segments.fVSCode.cstr();
+    lengths[stringCnt] = segments.fVSCode.length();
+    ++stringCnt;
+
+#if PRINT_SHADERS
+    GrPrintf("%s%s%s%s\n",
+             segments.fVSUnis.cstr(),
+             segments.fVSAttrs.cstr(),
+             segments.fVaryings.cstr(),
+             segments.fVSCode.cstr());
+#endif
+    program->fVShaderID = CompileShader(GL_VERTEX_SHADER,
+                                        stringCnt,
+                                        strings,
+                                        lengths);
+
+    stringCnt = 0;
+
+    if (GR_ARRAY_COUNT(GR_SHADER_PRECISION) > 1) {
+        strings[stringCnt] = GR_SHADER_PRECISION;
+        lengths[stringCnt] = GR_ARRAY_COUNT(GR_SHADER_PRECISION) - 1;
+        ++stringCnt;
+    }
+    if (segments.fFSUnis.length()) {
+        strings[stringCnt] = segments.fFSUnis.cstr();
+        lengths[stringCnt] = segments.fFSUnis.length();
+        ++stringCnt;
+    }
+    if (segments.fVaryings.length()) {
+        strings[stringCnt] = segments.fVaryings.cstr();
+        lengths[stringCnt] = segments.fVaryings.length();
+        ++stringCnt;
+    }
+
+    GrAssert(segments.fFSCode.length());
+    strings[stringCnt] = segments.fFSCode.cstr();
+    lengths[stringCnt] = segments.fFSCode.length();
+    ++stringCnt;
+
+#if PRINT_SHADERS
+    GrPrintf("%s%s%s%s\n",
+             GR_SHADER_PRECISION,
+             segments.fFSUnis.cstr(),
+             segments.fVaryings.cstr(),
+             segments.fFSCode.cstr());
+#endif
+    program->fFShaderID = CompileShader(GL_FRAGMENT_SHADER,
+                                        stringCnt,
+                                        strings,
+                                        lengths);
+
+    program->fProgramID = GR_GL(CreateProgram());
+    const GLint& progID = program->fProgramID;
+
+    GR_GL(AttachShader(progID, program->fVShaderID));
+    GR_GL(AttachShader(progID, program->fFShaderID));
+
+    // Bind the attrib locations to same values for all shaders
+    GR_GL(BindAttribLocation(progID, POS_ATTR_LOCATION, POS_ATTR_NAME));
+    if (textureCoordAttr) {
+        GR_GL(BindAttribLocation(progID, TEX_ATTR_LOCATION, TEX_ATTR_NAME));
+    }
+
+#if ATTRIBUTE_MATRIX
+    // set unis to a bogus value so that checks against -1 before
+    // flushing will pass.
+    GR_GL(BindAttribLocation(progID,
+                             VIEWMAT_ATTR_LOCATION,
+                             VIEW_MATRIX_NAME));
+
+    program->fUniLocations.fViewMatrixUni = BOGUS_MATRIX_UNI_LOCATION;
+
+    for (int i = 0; i < NUM_STAGES; ++i) {
+        if (desc.fStages[i].fEnabled) {
+            GR_GL(BindAttribLocation(progID,
+                                     TEXMAT_ATTR_LOCATION(i),
+                                     tex_matrix_name(i).cstr()));
+            program->fUniLocations.fStages[i].fTextureMatrixUni =
+                                                    BOGUS_MATRIX_UNI_LOCATION;
+        }
+    }
+#endif
+
+    GR_GL(BindAttribLocation(progID, COL_ATTR_LOCATION, COL_ATTR_NAME));
+
+    GR_GL(LinkProgram(progID));
+
+    GLint linked;
+    GR_GL(GetProgramiv(progID, GL_LINK_STATUS, &linked));
+    if (!linked) {
+        GLint infoLen;
+        GR_GL(GetProgramiv(progID, GL_INFO_LOG_LENGTH, &infoLen));
+        GrAutoMalloc log(sizeof(char)*(infoLen+1));  // outside if for debugger
+        if (infoLen > 0) {
+            GR_GL(GetProgramInfoLog(progID,
+                                    infoLen+1,
+                                    NULL,
+                                    (char*)log.get()));
+            GrPrintf((char*)log.get());
+        }
+        GrAssert(!"Error linking program");
+        GR_GL(DeleteProgram(progID));
+        program->fProgramID = 0;
+        return;
+    }
+
+    // Get uniform locations
+#if !ATTRIBUTE_MATRIX
+    program->fUniLocations.fViewMatrixUni =
+                    GR_GL(GetUniformLocation(progID, VIEW_MATRIX_NAME));
+    GrAssert(-1 != program->fUniLocations.fViewMatrixUni);
+#endif
+    for (int i = 0; i < NUM_STAGES; ++i) {
+        StageUniLocations& locations = program->fUniLocations.fStages[i];
+        if (desc.fStages[i].fEnabled) {
+#if !ATTRIBUTE_MATRIX
+            if (locations.fTextureMatrixUni) {
+                GrTokenString texMName;
+                tex_matrix_name(i, &texMName);
+                locations.fTextureMatrixUni = GR_GL(GetUniformLocation(
+                                                progID,
+                                                texMName.cstr()));
+                GrAssert(-1 != locations.fTextureMatrixUni);
+            } else {
+                locations.fTextureMatrixUni = -1;
+
+            }
+#endif
+
+            if (locations.fSamplerUni) {
+                GrTokenString samplerName;
+                sampler_name(i, &samplerName);
+                locations.fSamplerUni = GR_GL(GetUniformLocation(
+                                                     progID,
+                                                     samplerName.cstr()));
+                GrAssert(-1 != locations.fSamplerUni);
+            } else {
+                locations.fSamplerUni = -1;
+            }
+
+            if (locations.fRadial2Uni) {
+                GrTokenString radial2ParamName;
+                radial2_param_name(i, &radial2ParamName);
+                locations.fRadial2Uni = GR_GL(GetUniformLocation(
+                                             progID,
+                                             radial2ParamName.cstr()));
+                GrAssert(-1 != locations.fRadial2Uni);
+            } else {
+                locations.fRadial2Uni = -1;
+            }
+        } else {
+            locations.fSamplerUni = -1;
+            locations.fRadial2Uni = -1;
+            locations.fTextureMatrixUni = -1;
+        }
+    }
+    GR_GL(UseProgram(progID));
+
+    // init sampler unis and set bogus values for state tracking
+    for (int i = 0; i < NUM_STAGES; ++i) {
+        if (-1 != program->fUniLocations.fStages[i].fSamplerUni) {
+            GR_GL(Uniform1i(program->fUniLocations.fStages[i].fSamplerUni, i));
+        }
+        program->fTextureMatrix[i].setScale(GR_ScalarMax, GR_ScalarMax);
+        program->fRadial2CenterX1[i] = GR_ScalarMax;
+        program->fRadial2Radius0[i] = -GR_ScalarMax;
+    }
+    program->fViewMatrix.setScale(GR_ScalarMax, GR_ScalarMax);
+}
+
+void GrGpuGLShaders2::getProgramDesc(PrimitiveType primType, ProgramDesc* desc) {
+
+    // Must initialize all fields or cache will have false negatives!
+    desc->fVertexLayout = fGeometrySrc.fVertexLayout;
+    desc->fStages[0].fEnabled = VertexHasTexCoords(fGeometrySrc.fVertexLayout);
+    for (int i = 1; i < NUM_STAGES; ++i) {
+        desc->fStages[i].fEnabled       = false;
+        desc->fStages[i].fOptFlags      = 0;
+        desc->fStages[i].fCoordMapping  = 0;
+        desc->fStages[i].fModulation    = 0;
+    }
+
+    if (primType != kPoints_PrimitiveType) {
+        desc->fOptFlags = ProgramDesc::kNotPoints_OptFlagBit;
+    } else {
+        desc->fOptFlags = 0;
+    }
+#if SKIP_COLOR_MODULATE_OPT
+    if (!(desc->fVertexLayout & kColor_VertexLayoutBit) &&
+        (0xffffffff == fCurrDrawState.fColor)) {
+        desc->fOptFlags |= ProgramDesc::kVertexColorAllOnes_OptFlagBit;
+    }
+#endif
+
+    StageDesc& stage = desc->fStages[0];
+
+    if (stage.fEnabled) {
+        GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTexture;
+        GrAssert(NULL != texture);
+        // we matrix to invert when orientation is TopDown, so make sure
+        // we aren't in that case before flagging as identity.
+        if (fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].isIdentity() &&
+            GrGLTexture::kTopDown_Orientation == texture->orientation()) {
+            stage.fOptFlags = StageDesc::kIdentityMatrix_OptFlagBit;
+        } else if (!fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode].hasPerspective()) {
+            stage.fOptFlags = StageDesc::kNoPerspective_OptFlagBit;
+        } else {
+            stage.fOptFlags = 0;
+        }
+        switch (fCurrDrawState.fSamplerState.getSampleMode()) {
+        case GrSamplerState::kNormal_SampleMode:
+            stage.fCoordMapping = StageDesc::kIdentity_CoordMapping;
+            stage.fModulation = StageDesc::kColor_Modulation;
+            break;
+        case GrSamplerState::kAlphaMod_SampleMode:
+            stage.fCoordMapping = StageDesc::kIdentity_CoordMapping;
+            stage.fModulation = StageDesc::kAlpha_Modulation;
+            break;
+        case GrSamplerState::kRadial_SampleMode:
+            stage.fCoordMapping = StageDesc::kRadialGradient_CoordMapping;
+            stage.fModulation = StageDesc::kColor_Modulation;
+            break;
+        case GrSamplerState::kRadial2_SampleMode:
+            stage.fCoordMapping = StageDesc::kRadial2Gradient_CoordMapping;
+            stage.fModulation = StageDesc::kColor_Modulation;
+            break;
+        case GrSamplerState::kSweep_SampleMode:
+            stage.fCoordMapping = StageDesc::StageDesc::kSweepGradient_CoordMapping;
+            stage.fModulation = StageDesc::kColor_Modulation;
+            break;
+        default:
+            GrAssert(!"Unexpected sample mode!");
+            break;
+        }
+    } else {
+        stage.fOptFlags     = 0;
+        stage.fCoordMapping = 0;
+        stage.fModulation   = 0;
+    }
+}
+
+GLuint GrGpuGLShaders2::CompileShader(GLenum type,
+                                      int stringCnt,
+                                      const char** strings,
+                                      int* stringLengths) {
+    GLuint shader = GR_GL(CreateShader(type));
+    if (0 == shader) {
+        return 0;
+    }
+
+    GLint compiled;
+    GR_GL(ShaderSource(shader, stringCnt, strings, stringLengths));
+    GR_GL(CompileShader(shader));
+    GR_GL(GetShaderiv(shader, GL_COMPILE_STATUS, &compiled));
+
+    if (!compiled) {
+        GLint infoLen;
+        GR_GL(GetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen));
+        GrAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
+        if (infoLen > 0) {
+            GR_GL(GetShaderInfoLog(shader, infoLen+1, NULL, (char*)log.get()));
+            for (int i = 0; i < stringCnt; ++i) {
+                if (NULL == stringLengths || stringLengths[i] < 0) {
+                    GrPrintf(strings[i]);
+                } else {
+                    GrPrintf("%.*s", stringLengths[i], strings[i]);
+                }
+            }
+            GrPrintf("\n%s", log.get());
+        }
+        GrAssert(!"Shader compilation failed!");
+        GR_GL(DeleteShader(shader));
+        return 0;
+    }
+    return shader;
+}
+
+void GrGpuGLShaders2::DeleteProgram(Program* program) {
+    GR_GL(DeleteShader(program->fVShaderID));
+    GR_GL(DeleteShader(program->fFShaderID));
+    GR_GL(DeleteProgram(program->fProgramID));
+    GR_DEBUGCODE(memset(program, 0, sizeof(Program)));
+}
+
+
+GrGpuGLShaders2::GrGpuGLShaders2() {
+
+    resetContextHelper();
+
+    fProgram = NULL;
+    fProgramCache = new ProgramCache();
+
+#if GR_DEBUG
+    ProgramUnitTest();
+#endif
+}
+
+GrGpuGLShaders2::~GrGpuGLShaders2() {
+    delete fProgramCache;
+}
+
+void GrGpuGLShaders2::resetContext() {
+    INHERITED::resetContext();
+    resetContextHelper();
+}
+
+void GrGpuGLShaders2::resetContextHelper() {
+    fTextureOrientation = (GrGLTexture::Orientation)-1; // illegal
+
+    fHWGeometryState.fVertexLayout = 0;
+    fHWGeometryState.fPositionPtr  = (void*) ~0;
+    GR_GL(DisableVertexAttribArray(COL_ATTR_LOCATION));
+    GR_GL(DisableVertexAttribArray(TEX_ATTR_LOCATION));
+    GR_GL(EnableVertexAttribArray(POS_ATTR_LOCATION));
+
+    fHWProgramID = 0;
+}
+
+void GrGpuGLShaders2::flushViewMatrix() {
+    GrAssert(NULL != fCurrDrawState.fRenderTarget);
+    GrMatrix m (
+        GrIntToScalar(2) / fCurrDrawState.fRenderTarget->width(), 0, -GR_Scalar1,
+        0,-GrIntToScalar(2) / fCurrDrawState.fRenderTarget->height(), GR_Scalar1,
+        0, 0, GrMatrix::I()[8]);
+    m.setConcat(m, fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]);
+
+    // ES doesn't allow you to pass true to the transpose param,
+    // so do our own transpose
+    GrScalar mt[]  = {
+        m[GrMatrix::kScaleX],
+        m[GrMatrix::kSkewY],
+        m[GrMatrix::kPersp0],
+        m[GrMatrix::kSkewX],
+        m[GrMatrix::kScaleY],
+        m[GrMatrix::kPersp1],
+        m[GrMatrix::kTransX],
+        m[GrMatrix::kTransY],
+        m[GrMatrix::kPersp2]
+    };
+#if ATTRIBUTE_MATRIX
+    glVertexAttrib4fv(VIEWMAT_ATTR_LOCATION+0, mt+0);
+    glVertexAttrib4fv(VIEWMAT_ATTR_LOCATION+1, mt+3);
+    glVertexAttrib4fv(VIEWMAT_ATTR_LOCATION+2, mt+6);
+#else
+    GR_GL(UniformMatrix3fv(fProgram->fUniLocations.fViewMatrixUni,1,false,mt));
+#endif
+}
+
+void GrGpuGLShaders2::flushTextureMatrix() {
+
+    GrAssert(NULL != fCurrDrawState.fTexture);
+    GrGLTexture::Orientation orientation =
+         ((GrGLTexture*)fCurrDrawState.fTexture)->orientation();
+
+    GrMatrix* m;
+    GrMatrix temp;
+    if (GrGLTexture::kBottomUp_Orientation == orientation) {
+        temp.setAll(
+            GR_Scalar1, 0, 0,
+            0, -GR_Scalar1, GR_Scalar1,
+            0, 0, GrMatrix::I()[8]
+        );
+        temp.preConcat(fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode]);
+        m = &temp;
+    } else {
+        GrAssert(GrGLTexture::kTopDown_Orientation == orientation);
+        m = &fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode];
+    }
+
+    // ES doesn't allow you to pass true to the transpose param,
+    // so do our own transpose
+    GrScalar mt[]  = {
+        (*m)[GrMatrix::kScaleX],
+        (*m)[GrMatrix::kSkewY],
+        (*m)[GrMatrix::kPersp0],
+        (*m)[GrMatrix::kSkewX],
+        (*m)[GrMatrix::kScaleY],
+        (*m)[GrMatrix::kPersp1],
+        (*m)[GrMatrix::kTransX],
+        (*m)[GrMatrix::kTransY],
+        (*m)[GrMatrix::kPersp2]
+    };
+#if ATTRIBUTE_MATRIX
+    glVertexAttrib4fv(TEXMAT_ATTR_LOCATION(0)+0, mt+0);
+    glVertexAttrib4fv(TEXMAT_ATTR_LOCATION(0)+1, mt+3);
+    glVertexAttrib4fv(TEXMAT_ATTR_LOCATION(0)+2, mt+6);
+#else
+    GR_GL(UniformMatrix3fv(fProgram->fUniLocations.fStages[0].fTextureMatrixUni,
+                           1,
+                           false,
+                           mt));
+#endif
+}
+
+void GrGpuGLShaders2::flushRadial2() {
+
+    const GrSamplerState& sampler = fCurrDrawState.fSamplerState;
+
+    GrScalar centerX1 = sampler.getRadial2CenterX1();
+    GrScalar radius0 = sampler.getRadial2Radius0();
+
+    GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1;
+
+    float unis[6] = {
+        GrScalarToFloat(a),
+        1 / (2.f * unis[0]),
+        GrScalarToFloat(centerX1),
+        GrScalarToFloat(radius0),
+        GrScalarToFloat(GrMul(radius0, radius0)),
+        sampler.isRadial2PosRoot() ? 1.f : -1.f
+    };
+    GR_GL(Uniform1fv(fProgram->fUniLocations.fStages[0].fRadial2Uni, 6, unis));
+}
+
+void GrGpuGLShaders2::flushProgram(PrimitiveType type) {
+    ProgramDesc desc;
+    getProgramDesc(type, &desc);
+    fProgram = fProgramCache->getProgram(desc);
+
+    if (fHWProgramID != fProgram->fProgramID) {
+        GR_GL(UseProgram(fProgram->fProgramID));
+        fHWProgramID = fProgram->fProgramID;
+#if GR_COLLECT_STATS
+        ++fStats.fProgChngCnt;
+#endif
+    }
+}
+
+bool GrGpuGLShaders2::flushGraphicsState(PrimitiveType type) {
+
+    flushGLStateCommon(type);
+
+    if (fRenderTargetChanged) {
+        // our coords are in pixel space and the GL matrices map to NDC
+        // so if the viewport changed, our matrix is now wrong.
+#if ATTRIBUTE_MATRIX
+        fHWDrawState.fMatrixModeCache[kModelView_MatrixMode].setScale(GR_ScalarMax,
+                                                                      GR_ScalarMax);
+#else
+        // we assume all shader matrices may be wrong after viewport changes
+        fProgramCache->invalidateViewMatrices();
+#endif
+        fRenderTargetChanged = false;
+    }
+
+    flushProgram(type);
+
+    if (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit) {
+        // invalidate the immediate mode color
+        fHWDrawState.fColor = GrColor_ILLEGAL;
+    } else {
+        if (fHWDrawState.fColor != fCurrDrawState.fColor) {
+            // OpenGL ES only supports the float varities of glVertexAttrib
+            float c[] = {
+                GrColorUnpackR(fCurrDrawState.fColor) / 255.f,
+                GrColorUnpackG(fCurrDrawState.fColor) / 255.f,
+                GrColorUnpackB(fCurrDrawState.fColor) / 255.f,
+                GrColorUnpackA(fCurrDrawState.fColor) / 255.f
+            };
+            GR_GL(VertexAttrib4fv(COL_ATTR_LOCATION, c));
+            fHWDrawState.fColor = fCurrDrawState.fColor;
+        }
+    }
+
+#if ATTRIBUTE_MATRIX
+    GrMatrix& currViewMatrix = fHWDrawState.fMatrixModeCache[kModelView_MatrixMode];
+    GrMatrix& currTextureMatrix = fHWDrawState.fMatrixModeCache[kTexture_MatrixMode];
+    GrGLTexture::Orientation& orientation = fTextureOrientation;
+#else
+    GrMatrix& currViewMatrix = fProgram->fViewMatrix;
+    GrMatrix& currTextureMatrix = fProgram->fTextureMatrix[0];
+    GrGLTexture::Orientation& orientation =  fProgram->fTextureOrientation[0];
+#endif
+
+    if (currViewMatrix != fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode]) {
+        flushViewMatrix();
+        currViewMatrix = fCurrDrawState.fMatrixModeCache[kModelView_MatrixMode];
+    }
+
+    GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTexture;
+    if (NULL != texture) {
+        if (-1 != fProgram->fUniLocations.fStages[0].fTextureMatrixUni &&
+            (currTextureMatrix !=
+                        fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode] ||
+             orientation != texture->orientation())) {
+            flushTextureMatrix();
+            currTextureMatrix = fCurrDrawState.fMatrixModeCache[kTexture_MatrixMode];
+            orientation = texture->orientation();
+        }
+    }
+
+    const GrSamplerState& sampler = fCurrDrawState.fSamplerState;
+    if (-1 != fProgram->fUniLocations.fStages[0].fRadial2Uni &&
+        (fProgram->fRadial2CenterX1[0] != sampler.getRadial2CenterX1() ||
+         fProgram->fRadial2Radius0[0]  != sampler.getRadial2Radius0()  ||
+         fProgram->fRadial2PosRoot[0]  != sampler.isRadial2PosRoot())) {
+
+        flushRadial2();
+
+        fProgram->fRadial2CenterX1[0] = sampler.getRadial2CenterX1();
+        fProgram->fRadial2Radius0[0]  = sampler.getRadial2Radius0();
+        fProgram->fRadial2PosRoot[0]  = sampler.isRadial2PosRoot();
+    }
+
+    return true;
+}
+
+void GrGpuGLShaders2::setupGeometry(uint32_t startVertex,
+                                   uint32_t startIndex,
+                                   uint32_t vertexCount,
+                                   uint32_t indexCount) {
+
+    int newColorOffset, newTexCoordOffset;
+
+    GLsizei newStride = VertexSizeAndOffsets(fGeometrySrc.fVertexLayout,
+                                             &newTexCoordOffset,
+                                             &newColorOffset);
+    int oldColorOffset, oldTexCoordOffset;
+    GLsizei oldStride = VertexSizeAndOffsets(fHWGeometryState.fVertexLayout,
+                                             &oldTexCoordOffset,
+                                             &oldColorOffset);
+
+    const GLvoid* posPtr = (GLvoid*)(newStride * startVertex);
+
+    if (kBuffer_GeometrySrcType == fGeometrySrc.fVertexSrc) {
+        GrAssert(NULL != fGeometrySrc.fVertexBuffer);
+        GrAssert(!fGeometrySrc.fVertexBuffer->isLocked());
+        if (fHWGeometryState.fVertexBuffer != fGeometrySrc.fVertexBuffer) {
+            GrGLVertexBuffer* buf =
+            (GrGLVertexBuffer*)fGeometrySrc.fVertexBuffer;
+            GR_GL(BindBuffer(GL_ARRAY_BUFFER, buf->bufferID()));
+            fHWGeometryState.fVertexBuffer = fGeometrySrc.fVertexBuffer;
+        }
+    } else {
+        if (kArray_GeometrySrcType == fGeometrySrc.fVertexSrc) {
+            posPtr = (void*)((intptr_t)fGeometrySrc.fVertexArray +
+                             (intptr_t)posPtr);
+        } else {
+            GrAssert(kReserved_GeometrySrcType == fGeometrySrc.fVertexSrc);
+            posPtr = (void*)((intptr_t)fVertices.get() + (intptr_t)posPtr);
+        }
+        if (NULL != fHWGeometryState.fVertexBuffer) {
+            GR_GL(BindBuffer(GL_ARRAY_BUFFER, 0));
+            fHWGeometryState.fVertexBuffer = NULL;
+        }
+    }
+
+    if (kBuffer_GeometrySrcType == fGeometrySrc.fIndexSrc) {
+        GrAssert(NULL != fGeometrySrc.fIndexBuffer);
+        GrAssert(!fGeometrySrc.fIndexBuffer->isLocked());
+        if (fHWGeometryState.fIndexBuffer != fGeometrySrc.fIndexBuffer) {
+            GrGLIndexBuffer* buf =
+            (GrGLIndexBuffer*)fGeometrySrc.fIndexBuffer;
+            GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf->bufferID()));
+            fHWGeometryState.fIndexBuffer = fGeometrySrc.fIndexBuffer;
+        }
+    } else if (NULL != fHWGeometryState.fIndexBuffer) {
+        GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+        fHWGeometryState.fIndexBuffer = NULL;
+    }
+
+    GLenum scalarType;
+    bool texCoordNorm;
+    if (fGeometrySrc.fVertexLayout & kTextFormat_VertexLayoutBit) {
+        scalarType = GrGLTextType;
+        texCoordNorm = GR_GL_TEXT_TEXTURE_NORMALIZED;
+    } else {
+        scalarType = GrGLType;
+        texCoordNorm = false;
+    }
+
+    bool baseChange = posPtr != fHWGeometryState.fPositionPtr;
+    bool scalarChange = (GrGLTextType != GrGLType) &&
+                        (kTextFormat_VertexLayoutBit &
+                         (fHWGeometryState.fVertexLayout ^
+                          fGeometrySrc.fVertexLayout));
+    bool strideChange = newStride != oldStride;
+    bool posChange = baseChange || scalarChange || strideChange;
+
+    if (posChange) {
+        GR_GL(VertexAttribPointer(POS_ATTR_LOCATION, 2, scalarType,
+                                  false, newStride, posPtr));
+        fHWGeometryState.fPositionPtr = posPtr;
+    }
+
+    if (newTexCoordOffset > 0) {
+        GLvoid* texCoordPtr = (int8_t*)posPtr + newTexCoordOffset;
+        if (oldTexCoordOffset <= 0) {
+            GR_GL(EnableVertexAttribArray(TEX_ATTR_LOCATION));
+        }
+        if (posChange || newTexCoordOffset != oldTexCoordOffset) {
+            GR_GL(VertexAttribPointer(TEX_ATTR_LOCATION, 2, scalarType,
+                                      texCoordNorm, newStride, texCoordPtr));
+        }
+    } else if (oldTexCoordOffset > 0) {
+        GR_GL(DisableVertexAttribArray(TEX_ATTR_LOCATION));
+    }
+
+    if (newColorOffset > 0) {
+        GLvoid* colorPtr = (int8_t*)posPtr + newColorOffset;
+        if (oldColorOffset <= 0) {
+            GR_GL(EnableVertexAttribArray(COL_ATTR_LOCATION));
+        }
+        if (posChange || newColorOffset != oldColorOffset) {
+            GR_GL(VertexAttribPointer(COL_ATTR_LOCATION, 4,
+                                      GL_UNSIGNED_BYTE,
+                                      true, newStride, colorPtr));
+        }
+    } else if (oldColorOffset > 0) {
+        GR_GL(DisableVertexAttribArray(COL_ATTR_LOCATION));
+    }
+
+    fHWGeometryState.fVertexLayout = fGeometrySrc.fVertexLayout;
+}
+#endif
+
diff --git a/gpu/src/GrGpuGLShaders2.h b/gpu/src/GrGpuGLShaders2.h
new file mode 100644
index 0000000..c484544
--- /dev/null
+++ b/gpu/src/GrGpuGLShaders2.h
@@ -0,0 +1,102 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrGpuGLShaders2_DEFINED
+#define GrGpuGLShaders2_DEFINED
+
+#include "GrGpuGL.h"
+
+// Programmable OpenGL or OpenGL ES 2.0
+class GrGpuGLShaders2 : public GrGpuGL {
+public:
+             GrGpuGLShaders2();
+    virtual ~GrGpuGLShaders2();
+
+    virtual void resetContext();
+
+protected:
+    // overrides from GrGpu
+    virtual bool flushGraphicsState(PrimitiveType type);
+    virtual void setupGeometry(uint32_t startVertex,
+                               uint32_t startIndex,
+                               uint32_t vertexCount,
+                               uint32_t indexCount);
+
+private:
+    static const int NUM_STAGES;
+
+    void resetContextHelper();
+
+    // sets the texture matrix uniform for currently bound program
+    void flushTextureMatrix();
+    // sets the MVP matrix uniform for currently bound program
+    void flushViewMatrix();
+
+    // flushes the parameters to two point radial gradient
+    void flushRadial2();
+
+    // called at flush time to setup the appropriate program
+    void flushProgram(PrimitiveType type);
+
+    struct Program;
+
+    struct StageDesc;
+    struct ProgramDesc;
+
+    struct UniLocations;
+    struct StageUniLocations;
+
+    struct ShaderCodeSegments;
+
+    class ProgramCache;
+
+    // gets a description of needed shader
+    void getProgramDesc(PrimitiveType primType, ProgramDesc* desc);
+
+    // generates and compiles a program from a description and vertex layout
+    // will change GL's bound program
+    static void GenProgram(const ProgramDesc& desc, Program* program);
+
+    // generates code for a stage of the shader
+    static void GenStageCode(int stageNum,
+                             const StageDesc& desc,
+                             const char* psInColor,
+                             const char* psOutColor,
+                             const char* vsInCoord,
+                             ShaderCodeSegments* segments,
+                             StageUniLocations* locations);
+
+    // Compiles a GL shader, returns shader ID or 0 if failed
+    // params have same meaning as glShaderSource
+    static GLuint CompileShader(GLenum type, int stringCnt,
+                                             const char** strings,
+                                             int* stringLengths);
+    static void DeleteProgram(Program* program);
+
+    void ProgramUnitTest();
+
+    GrGLTexture::Orientation  fTextureOrientation;
+
+    ProgramCache*   fProgramCache;
+    Program*        fProgram;
+    GLuint          fHWProgramID;
+
+    typedef GrGpuGL INHERITED;
+};
+
+#endif
+
diff --git a/gpu/src/GrInOrderDrawBuffer.cpp b/gpu/src/GrInOrderDrawBuffer.cpp
new file mode 100644
index 0000000..3f25f2f
--- /dev/null
+++ b/gpu/src/GrInOrderDrawBuffer.cpp
@@ -0,0 +1,345 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrInOrderDrawBuffer.h"
+#include "GrTexture.h"
+#include "GrVertexBufferAllocPool.h"
+#include "GrGpu.h"
+
+GrInOrderDrawBuffer::GrInOrderDrawBuffer(GrVertexBufferAllocPool* pool) :
+        fDraws(DRAWS_BLOCK_SIZE, fDrawsStorage),
+        fStates(STATES_BLOCK_SIZE, fStatesStorage),
+        fClips(CLIPS_BLOCK_SIZE, fClipsStorage),
+        fClipChanged(true),
+        fCPUVertices((NULL == pool) ? 0 : VERTEX_BLOCK_SIZE),
+        fBufferVertices(pool),
+        fIndices(INDEX_BLOCK_SIZE),
+        fCurrReservedVertices(NULL),
+        fCurrReservedIndices(NULL),
+        fCurrVertexBuffer(NULL),
+        fReservedVertexBytes(0),
+        fReservedIndexBytes(0),
+        fUsedReservedVertexBytes(0),
+        fUsedReservedIndexBytes(0) {
+    GrAssert(NULL == pool || pool->getGpu()->supportsBufferLocking());
+}
+
+GrInOrderDrawBuffer::~GrInOrderDrawBuffer() {
+    reset();
+}
+
+void GrInOrderDrawBuffer::initializeDrawStateAndClip(const GrDrawTarget& target) {
+    this->copyDrawState(target);
+    this->setClip(target.getClip());
+}
+
+void GrInOrderDrawBuffer::drawIndexed(PrimitiveType type,
+                                      uint32_t startVertex,
+                                      uint32_t startIndex,
+                                      uint32_t vertexCount,
+                                      uint32_t indexCount) {
+
+    if (!vertexCount || !indexCount) {
+        return;
+    }
+
+    Draw& draw = fDraws.push_back();
+    draw.fType          = type;
+    draw.fStartVertex   = startVertex;
+    draw.fStartIndex    = startIndex;
+    draw.fVertexCount   = vertexCount;
+    draw.fIndexCount    = indexCount;
+    draw.fClipChanged   = grabClip();
+    draw.fStateChange   = grabState();
+
+    draw.fVertexLayout = fGeometrySrc.fVertexLayout;
+    switch (fGeometrySrc.fVertexSrc) {
+    case kArray_GeometrySrcType:
+        draw.fUseVertexBuffer = false;
+        draw.fVertexArray = fGeometrySrc.fVertexArray;
+        break;
+    case kReserved_GeometrySrcType: {
+        draw.fUseVertexBuffer = NULL != fBufferVertices;
+        if (draw.fUseVertexBuffer) {
+            draw.fVertexBuffer = fCurrVertexBuffer;
+            draw.fStartVertex += fCurrStartVertex;
+        } else {
+            draw.fVertexArray = fCurrReservedVertices;
+        }
+        size_t vertexBytes = (vertexCount + startVertex) *
+                             VertexSize(fGeometrySrc.fVertexLayout);
+        fUsedReservedVertexBytes = GrMax(fUsedReservedVertexBytes,
+                                         vertexBytes);
+        } break;
+    case kBuffer_GeometrySrcType:
+        draw.fUseVertexBuffer = true;
+        draw.fVertexBuffer = fGeometrySrc.fVertexBuffer;
+        break;
+    }
+
+    switch (fGeometrySrc.fIndexSrc) {
+    case kArray_GeometrySrcType:
+        draw.fUseIndexBuffer = false;
+        draw.fIndexArray = fGeometrySrc.fIndexArray;
+        break;
+    case kReserved_GeometrySrcType: {
+        draw.fUseIndexBuffer = false;
+        draw.fIndexArray = fCurrReservedIndices;
+        size_t indexBytes = (indexCount + startIndex) * sizeof(uint16_t);
+        fUsedReservedIndexBytes = GrMax(fUsedReservedIndexBytes, indexBytes);
+        } break;
+    case kBuffer_GeometrySrcType:
+        draw.fUseIndexBuffer = true;
+        draw.fIndexBuffer = fGeometrySrc.fIndexBuffer;
+        break;
+    }
+}
+
+void GrInOrderDrawBuffer::drawNonIndexed(PrimitiveType type,
+                                         uint32_t startVertex,
+                                         uint32_t vertexCount) {
+    if (!vertexCount) {
+        return;
+    }
+
+    Draw& draw = fDraws.push_back();
+    draw.fType          = type;
+    draw.fStartVertex   = startVertex;
+    draw.fStartIndex    = 0;
+    draw.fVertexCount   = vertexCount;
+    draw.fIndexCount    = 0;
+
+    draw.fClipChanged   = grabClip();
+    draw.fStateChange   = grabState();
+
+    draw.fVertexLayout = fGeometrySrc.fVertexLayout;
+    switch (fGeometrySrc.fVertexSrc) {
+    case kArray_GeometrySrcType:
+        draw.fUseVertexBuffer = false;
+        draw.fVertexArray = fGeometrySrc.fVertexArray;
+        break;
+    case kReserved_GeometrySrcType: {
+        draw.fUseVertexBuffer = NULL != fBufferVertices;
+        if (draw.fUseVertexBuffer) {
+            draw.fVertexBuffer = fCurrVertexBuffer;
+            draw.fStartVertex += fCurrStartVertex;
+        } else {
+            draw.fVertexArray = fCurrReservedVertices;
+        }
+        size_t vertexBytes = (vertexCount + startVertex) *
+                             VertexSize(fGeometrySrc.fVertexLayout);
+        fUsedReservedVertexBytes = GrMax(fUsedReservedVertexBytes,
+                                         vertexBytes);
+        } break;
+    case kBuffer_GeometrySrcType:
+        draw.fUseVertexBuffer = true;
+        draw.fVertexBuffer = fGeometrySrc.fVertexBuffer;
+        break;
+    }
+}
+
+void GrInOrderDrawBuffer::reset() {
+    GrAssert(!fReservedGeometry.fLocked);
+    uint32_t numStates = fStates.count();
+    for (uint32_t i = 0; i < numStates; ++i) {
+        GrTexture* tex = accessSavedDrawState(fStates[i]).fTexture;
+        if (NULL != tex) {
+            tex->unref();
+        }
+    }
+    fDraws.reset();
+    fStates.reset();
+    if (NULL == fBufferVertices) {
+        fCPUVertices.reset();
+    } else {
+        fBufferVertices->reset();
+    }
+    fIndices.reset();
+    fClips.reset();
+}
+
+void GrInOrderDrawBuffer::playback(GrDrawTarget* target) {
+    GrAssert(NULL != target);
+    GrAssert(target != this); // not considered and why?
+
+    uint32_t numDraws = fDraws.count();
+    if (!numDraws) {
+        return;
+    }
+
+    if (NULL != fBufferVertices) {
+        fBufferVertices->unlock();
+    }
+
+    GrDrawTarget::AutoStateRestore asr(target);
+    GrDrawTarget::AutoClipRestore acr(target);
+    // important to not mess with reserve/lock geometry in the target with this
+    // on the stack.
+    GrDrawTarget::AutoGeometrySrcRestore agsr(target);
+
+    uint32_t currState = ~0;
+    uint32_t currClip  = ~0;
+
+    for (uint32_t i = 0; i < numDraws; ++i) {
+        const Draw& draw = fDraws[i];
+        if (draw.fStateChange) {
+            ++currState;
+            target->restoreDrawState(fStates[currState]);
+        }
+        if (draw.fClipChanged) {
+            ++currClip;
+            target->setClip(fClips[currClip]);
+        }
+        if (draw.fUseVertexBuffer) {
+            target->setVertexSourceToBuffer(draw.fVertexBuffer, draw.fVertexLayout);
+        } else {
+            target->setVertexSourceToArray(draw.fVertexArray, draw.fVertexLayout);
+        }
+        if (draw.fIndexCount) {
+            if (draw.fUseIndexBuffer) {
+                target->setIndexSourceToBuffer(draw.fIndexBuffer);
+            } else {
+                target->setIndexSourceToArray(draw.fIndexArray);
+            }
+            target->drawIndexed(draw.fType,
+                                draw.fStartVertex,
+                                draw.fStartIndex,
+                                draw.fVertexCount,
+                                draw.fIndexCount);
+        } else {
+            target->drawNonIndexed(draw.fType,
+                                   draw.fStartVertex,
+                                   draw.fVertexCount);
+        }
+    }
+}
+
+bool GrInOrderDrawBuffer::geometryHints(GrVertexLayout vertexLayout,
+                                        int32_t* vertexCount,
+                                        int32_t* indexCount) const {
+    bool flush = false;
+    if (NULL != indexCount) {
+        *indexCount  = -1;
+    }
+    if (NULL != vertexCount) {
+        if (NULL != fBufferVertices) {
+            // we will recommend a flush if the verts could fit in a single
+            // preallocated vertex buffer but none are left and it can't fit
+            // in the current VB (which may not be prealloced).
+            if (*vertexCount > fBufferVertices->currentBufferVertices(vertexLayout) &&
+                (!fBufferVertices->preallocatedBuffersRemaining() &&
+                 *vertexCount <= fBufferVertices->preallocatedBufferVertices(vertexLayout))) {
+
+                flush = true;
+            }
+            *vertexCount = fBufferVertices->currentBufferVertices(vertexLayout);
+        } else {
+            *vertexCount = -1;
+        }
+    }
+    return flush;
+}
+
+bool GrInOrderDrawBuffer::acquireGeometryHelper(GrVertexLayout vertexLayout,
+                                                void**         vertices,
+                                                void**         indices) {
+    if (fReservedGeometry.fVertexCount) {
+        fReservedVertexBytes = VertexSize(vertexLayout) *
+                               fReservedGeometry.fVertexCount;
+        if (NULL == fBufferVertices) {
+            fCurrReservedVertices = fCPUVertices.alloc(fReservedVertexBytes);
+        } else {
+            fCurrReservedVertices = fBufferVertices->alloc(vertexLayout,
+                                                           fReservedGeometry.fVertexCount,
+                                                           &fCurrVertexBuffer,
+                                                           &fCurrStartVertex);
+        }
+        if (NULL != vertices) {
+            *vertices = fCurrReservedVertices;
+        }
+        if (NULL == fCurrReservedVertices) {
+            return false;
+        }
+    }
+    if (fReservedGeometry.fIndexCount) {
+        fReservedIndexBytes = sizeof(uint16_t) * fReservedGeometry.fIndexCount;
+        fCurrReservedIndices = fIndices.alloc(fReservedIndexBytes);
+        if (NULL != indices) {
+            *indices = fCurrReservedIndices;
+        }
+        if (NULL == fCurrReservedIndices) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void GrInOrderDrawBuffer::releaseGeometryHelper() {
+    GrAssert(fUsedReservedVertexBytes <= fReservedVertexBytes);
+    GrAssert(fUsedReservedIndexBytes <= fReservedIndexBytes);
+
+    size_t vertexSlack = fReservedVertexBytes - fUsedReservedVertexBytes;
+    if (NULL == fBufferVertices) {
+        fCPUVertices.release(vertexSlack);
+    } else {
+        fBufferVertices->release(vertexSlack);
+        GR_DEBUGCODE(fCurrVertexBuffer = NULL);
+        GR_DEBUGCODE(fCurrStartVertex  = 0);
+    }
+
+    fIndices.release(fReservedIndexBytes - fUsedReservedIndexBytes);
+
+    fCurrReservedVertices = NULL;
+    fCurrReservedIndices  = NULL;
+    fReservedVertexBytes = 0;
+    fReservedIndexBytes  = 0;
+    fUsedReservedVertexBytes = 0;
+    fUsedReservedIndexBytes  = 0;
+}
+
+bool GrInOrderDrawBuffer::grabState() {
+    bool newState;
+    if (fStates.empty()) {
+        newState = true;
+    } else {
+        const DrawState& old = accessSavedDrawState(fStates.back());
+        newState = old != fCurrDrawState;
+    }
+    if (newState) {
+        if (NULL != fCurrDrawState.fTexture) {
+            fCurrDrawState.fTexture->ref();
+        }
+        saveCurrentDrawState(&fStates.push_back());
+    }
+    return newState;
+}
+
+bool GrInOrderDrawBuffer::grabClip() {
+    if ((fCurrDrawState.fFlagBits & kClip_StateBit) &&
+        (fClipChanged || fClips.empty())) {
+
+        fClips.push_back() = fClip;
+        fClipChanged = false;
+        return true;
+    }
+    return false;
+}
+
+void GrInOrderDrawBuffer::clipWillChange(const GrClip& clip)  {
+    fClipChanged = true;
+}
+
diff --git a/gpu/src/GrMatrix.cpp b/gpu/src/GrMatrix.cpp
new file mode 100644
index 0000000..e4360cd
--- /dev/null
+++ b/gpu/src/GrMatrix.cpp
@@ -0,0 +1,767 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+

+#include "GrMatrix.h"

+#include "GrRect.h"

+#include <stddef.h>

+

+#if GR_SCALAR_IS_FLOAT

+    const GrScalar GrMatrix::gRESCALE(GR_Scalar1);

+#else

+    GR_STATIC_ASSERT(GR_SCALAR_IS_FIXED);

+    // fixed point isn't supported right now

+    GR_STATIC_ASSERT(false);

+const GrScalar GrMatrix::gRESCALE(1 << 30);

+#endif

+

+const GrMatrix::MapProc GrMatrix::gMapProcs[] = {

+// Scales are not both zero

+    &GrMatrix::mapIdentity,

+    &GrMatrix::mapScale,

+    &GrMatrix::mapTranslate,

+    &GrMatrix::mapScaleAndTranslate,

+    &GrMatrix::mapSkew,

+    &GrMatrix::mapScaleAndSkew,

+    &GrMatrix::mapSkewAndTranslate,

+    &GrMatrix::mapNonPerspective,

+    // no optimizations for perspective matrices

+    &GrMatrix::mapPerspective,

+    &GrMatrix::mapPerspective,

+    &GrMatrix::mapPerspective,

+    &GrMatrix::mapPerspective,

+    &GrMatrix::mapPerspective,

+    &GrMatrix::mapPerspective,

+    &GrMatrix::mapPerspective,

+    &GrMatrix::mapPerspective,

+    

+// Scales are zero (every other is invalid because kScale_TypeBit must be set if

+// kZeroScale_TypeBit is set)

+    &GrMatrix::mapInvalid,

+    &GrMatrix::mapZero,

+    &GrMatrix::mapInvalid,

+    &GrMatrix::mapSetToTranslate,

+    &GrMatrix::mapInvalid,

+    &GrMatrix::mapSwappedScale,

+    &GrMatrix::mapInvalid,

+    &GrMatrix::mapSwappedScaleAndTranslate,

+    

+    // no optimizations for perspective matrices

+    &GrMatrix::mapInvalid,

+    &GrMatrix::mapZero,

+    &GrMatrix::mapInvalid,

+    &GrMatrix::mapPerspective,

+    &GrMatrix::mapInvalid,

+    &GrMatrix::mapPerspective,

+    &GrMatrix::mapInvalid,

+    &GrMatrix::mapPerspective,

+};

+

+const GrMatrix& GrMatrix::I() {

+    struct FakeMatrix {

+        int fTypeMask;

+        GrScalar fM[9];

+    };

+

+#if 0

+    GR_STATIC_ASSERT(offsetof(FakeMatrix, fTypeMask) == offsetof(GrMatrix, fTypeMask));

+    GR_STATIC_ASSERT(offsetof(FakeMatrix, fM) == offsetof(GrMatrix, fM));

+#endif

+

+    GR_STATIC_ASSERT(sizeof(FakeMatrix) == sizeof(GrMatrix));

+    static const FakeMatrix I = {0,

+                                 {GR_Scalar1, 0,          0,

+                                  0,          GR_Scalar1, 0,

+                                  0,          0,          gRESCALE}};

+    return *(const GrMatrix*)&I;

+}

+

+void GrMatrix::setIdentity() {

+    fM[0] = GR_Scalar1; fM[1] = 0;          fM[2] = 0;

+    fM[3] = 0;          fM[4] = GR_Scalar1; fM[5] = 0;

+    fM[6] = 0;          fM[7] = 0;          fM[8] = gRESCALE;

+    fTypeMask = 0; 

+}

+

+void GrMatrix::setTranslate(GrScalar dx, GrScalar dy) {

+    fM[0] = GR_Scalar1; fM[1] = 0;          fM[2] = dx;

+    fM[3] = 0;          fM[4] = GR_Scalar1; fM[5] = dy;

+    fM[6] = 0;          fM[7] = 0;          fM[8] = gRESCALE;

+    fTypeMask = kTranslate_TypeBit;

+}

+

+void GrMatrix::setScale(GrScalar sx, GrScalar sy) {

+    fM[0] = sx; fM[1] = 0;  fM[2] = 0;

+    fM[3] = 0;  fM[4] = sy; fM[5] = 0;

+    fM[6] = 0;  fM[7] = 0;  fM[8] = gRESCALE;

+    fTypeMask = kScale_TypeBit;

+}

+

+void GrMatrix::setSkew(GrScalar skx, GrScalar sky) {

+    fM[0] = GR_Scalar1; fM[1] = skx;        fM[2] = 0;

+    fM[3] = sky;        fM[4] = GR_Scalar1; fM[5] = 0;

+    fM[6] = 0;          fM[7] = 0;          fM[8] = gRESCALE;

+    fTypeMask = kSkew_TypeBit;

+}

+

+void GrMatrix::setConcat(const GrMatrix& a, const GrMatrix& b) {

+    if (a.isIdentity()) {

+        if (this != &b) {

+            for (int i = 0; i < 9; ++i) {

+                fM[i] = b.fM[i];

+            }

+            fTypeMask = b.fTypeMask;

+        }

+        return;

+    }

+    

+    if (b.isIdentity()) {

+        GrAssert(!a.isIdentity());

+        if (this != &a) {

+            for (int i = 0; i < 9; ++i) {

+                    fM[i] = a.fM[i];

+            }

+            fTypeMask = a.fTypeMask;

+        }

+        return;

+    }

+    

+    // a and/or b could be this

+    GrMatrix tmp;

+    

+    // could do more optimizations based on type bits. Hopefully this call is

+    // low frequency.

+    // TODO: make this work for fixed point

+    if (!((b.fTypeMask | a.fTypeMask) & kPerspective_TypeBit)) {

+        tmp.fM[0] = a.fM[0] * b.fM[0] + a.fM[1] * b.fM[3];

+        tmp.fM[1] = a.fM[0] * b.fM[1] + a.fM[1] * b.fM[4];

+        tmp.fM[2] = a.fM[0] * b.fM[2] + a.fM[1] * b.fM[5] + a.fM[2] * gRESCALE;

+        

+        tmp.fM[3] = a.fM[3] * b.fM[0] + a.fM[4] * b.fM[3];

+        tmp.fM[4] = a.fM[3] * b.fM[1] + a.fM[4] * b.fM[4];

+        tmp.fM[5] = a.fM[3] * b.fM[2] + a.fM[4] * b.fM[5] + a.fM[5] * gRESCALE;

+   

+        tmp.fM[6] = 0;

+        tmp.fM[7] = 0;

+        tmp.fM[8] = gRESCALE * gRESCALE; 

+    } else {

+        tmp.fM[0] = a.fM[0] * b.fM[0] + a.fM[1] * b.fM[3] + a.fM[2] * b.fM[6];

+        tmp.fM[1] = a.fM[0] * b.fM[1] + a.fM[1] * b.fM[4] + a.fM[2] * b.fM[7];

+        tmp.fM[2] = a.fM[0] * b.fM[2] + a.fM[1] * b.fM[5] + a.fM[2] * b.fM[8];

+

+        tmp.fM[3] = a.fM[3] * b.fM[0] + a.fM[4] * b.fM[3] + a.fM[5] * b.fM[6];

+        tmp.fM[4] = a.fM[3] * b.fM[1] + a.fM[4] * b.fM[4] + a.fM[5] * b.fM[7];

+        tmp.fM[5] = a.fM[3] * b.fM[2] + a.fM[4] * b.fM[5] + a.fM[5] * b.fM[8];

+

+        tmp.fM[6] = a.fM[6] * b.fM[0] + a.fM[7] * b.fM[3] + a.fM[8] * b.fM[6];

+        tmp.fM[7] = a.fM[6] * b.fM[1] + a.fM[7] * b.fM[4] + a.fM[8] * b.fM[7];

+        tmp.fM[8] = a.fM[6] * b.fM[2] + a.fM[7] * b.fM[5] + a.fM[8] * b.fM[8];

+    }

+    *this = tmp;

+    setTypeMask();

+}

+

+void GrMatrix::preConcat(const GrMatrix& m) {

+    setConcat(*this, m);

+}

+

+void GrMatrix::postConcat(const GrMatrix& m) {

+    setConcat(m, *this);

+}

+

+double GrMatrix::determinant() const {

+    if (fTypeMask & kPerspective_TypeBit) {

+        return  fM[0]*((double)fM[4]*fM[8] - (double)fM[5]*fM[7]) + 

+                fM[1]*((double)fM[5]*fM[6] - (double)fM[3]*fM[8]) + 

+                fM[2]*((double)fM[3]*fM[7] - (double)fM[4]*fM[6]);

+    } else {

+        return (double)fM[0]*fM[4]*gRESCALE - 

+               (double)fM[1]*fM[3]*gRESCALE;

+    }

+}

+

+bool GrMatrix::invert(GrMatrix* inverted) const {

+    

+    if (isIdentity()) {

+        if (inverted != this) {

+            inverted->setIdentity();

+        }

+        return true;

+    }

+    static const double MIN_DETERMINANT_SQUARED = 1.e-16;

+    

+    // could do more optimizations based on type bits. Hopefully this call is

+    // low frequency.

+    

+    double det = determinant();

+

+    // check if we can't be inverted

+    if (det*det <= MIN_DETERMINANT_SQUARED) {

+        return false;

+    } else if (NULL == inverted) {

+        return true;

+    }

+

+    double t[9];

+    

+    if (fTypeMask & kPerspective_TypeBit) {

+        t[0] = ((double)fM[4]*fM[8] - (double)fM[5]*fM[7]); 

+        t[1] = ((double)fM[2]*fM[7] - (double)fM[1]*fM[8]);

+        t[2] = ((double)fM[1]*fM[5] - (double)fM[2]*fM[4]);

+        t[3] = ((double)fM[5]*fM[6] - (double)fM[3]*fM[8]);

+        t[4] = ((double)fM[0]*fM[8] - (double)fM[2]*fM[6]);

+        t[5] = ((double)fM[2]*fM[3] - (double)fM[0]*fM[5]);

+        t[6] = ((double)fM[3]*fM[7] - (double)fM[4]*fM[6]);

+        t[7] = ((double)fM[1]*fM[6] - (double)fM[0]*fM[7]);

+        t[8] = ((double)fM[0]*fM[4] - (double)fM[1]*fM[3]);

+        det = 1.0 / det;

+        for (int i = 0; i < 9; ++i) {

+            inverted->fM[i] = (GrScalar)(t[i] * det);

+        }

+    } else {

+        t[0] =  (double)fM[4]*gRESCALE; 

+        t[1] = -(double)fM[1]*gRESCALE;

+        t[2] =  (double)fM[1]*fM[5] - (double)fM[2]*fM[4];

+        t[3] = -(double)fM[3]*gRESCALE;

+        t[4] =  (double)fM[0]*gRESCALE;

+        t[5] =  (double)fM[2]*fM[3] - (double)fM[0]*fM[5];

+        //t[6] = 0.0;

+        //t[7] = 0.0;

+        t[8] = (double)fM[0]*fM[4] - (double)fM[1]*fM[3];

+        det = 1.0 / det;

+        for (int i = 0; i < 6; ++i) {

+            inverted->fM[i] = (GrScalar)(t[i] * det);

+        }

+        inverted->fM[6] = 0;

+        inverted->fM[7] = 0;

+        inverted->fM[8] = (GrScalar)(t[8] * det);

+    }

+    inverted->setTypeMask();

+    return true;

+}

+

+void GrMatrix::mapRect(GrRect* dst, const GrRect& src) const {

+    GrPoint srcPts[4], dstPts[4];

+    srcPts[0].set(src.fLeft, src.fTop);

+    srcPts[1].set(src.fRight, src.fTop);

+    srcPts[2].set(src.fRight, src.fBottom);

+    srcPts[3].set(src.fLeft, src.fBottom);

+    this->mapPoints(dstPts, srcPts, 4);

+    dst->setBounds(dstPts, 4);

+}

+

+bool GrMatrix::hasPerspective() const {

+    GrAssert(!!(kPerspective_TypeBit & fTypeMask) ==

+             (fM[kPersp0] != 0 || fM[kPersp1] != 0 || fM[kPersp2] != gRESCALE));

+    return 0 != (kPerspective_TypeBit & fTypeMask);

+}

+

+bool GrMatrix::isIdentity() const {

+    GrAssert((0 == fTypeMask) == 

+             (GR_Scalar1 == fM[kScaleX] && 0          == fM[kSkewX]  && 0          == fM[kTransX] &&

+              0          == fM[kSkewY]  && GR_Scalar1 == fM[kScaleY] && 0          == fM[kTransY] &&

+              0          == fM[kPersp0] && 0          == fM[kPersp1] && gRESCALE == fM[kPersp2]));

+    return (0 == fTypeMask);

+}

+

+

+GrScalar GrMatrix::getMaxStretch() const {

+

+    if (fTypeMask & kPerspective_TypeBit) {

+        return -GR_Scalar1;

+    }

+

+    GrScalar stretch;

+    

+    if (isIdentity()) {

+        stretch = GR_Scalar1;

+    } else if (!(fTypeMask & kSkew_TypeBit)) {

+        stretch = GrMax(GrScalarAbs(fM[kScaleX]), GrScalarAbs(fM[kScaleY]));

+    } else if (fTypeMask & kZeroScale_TypeBit) {        

+        stretch = GrMax(GrScalarAbs(fM[kSkewX]), GrScalarAbs(fM[kSkewY]));

+    } else {            

+        // ignore the translation part of the matrix, just look at 2x2 portion.

+        // compute singular values, take largest abs value.

+        // [a b; b c] = A^T*A 

+        GrScalar a = GrMul(fM[kScaleX], fM[kScaleX]) + GrMul(fM[kSkewY],  fM[kSkewY]);

+        GrScalar b = GrMul(fM[kScaleX], fM[kSkewX]) +  GrMul(fM[kScaleY], fM[kSkewY]);

+        GrScalar c = GrMul(fM[kSkewX],  fM[kSkewX]) +  GrMul(fM[kScaleY], fM[kScaleY]);

+        // eigenvalues of A^T*A are the squared singular values of A.

+        // characteristic equation is det((A^T*A) - l*I) = 0

+        // l^2 - (a + c)l + (ac-b^2)

+        // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff

+        // and roots are guaraunteed to be pos and real).

+        GrScalar largerRoot;

+        GrScalar bSqd = GrMul(b,b);

+        // TODO: fixed point tolerance value.

+        if (bSqd < 1e-10) { // will be true if upper left 2x2 is orthogonal, which is common, so save some math

+            largerRoot = GrMax(a, c);

+        } else {

+            GrScalar aminusc = a - c;

+            GrScalar apluscdiv2 = (a + c) / 2;

+            GrScalar x = sqrtf(GrMul(aminusc,aminusc) + GrMul(4,(bSqd))) / 2;

+            largerRoot = apluscdiv2 + x;

+        }

+        

+        stretch = sqrtf(largerRoot);

+    }

+#if GR_DEBUG && 0

+    // test a bunch of vectors. None should be scaled by more than stretch 

+    // (modulo some error) and we should find a vector that is scaled by almost

+    // stretch.

+    GrPoint pt;

+    GrScalar max = 0;

+    for (int i = 0; i < 1000; ++i) {

+        GrScalar x = (float)rand() / RAND_MAX;

+        GrScalar y = sqrtf(1 - (x*x));

+        pt.fX = fM[kScaleX]*x + fM[kSkewX]*y;

+        pt.fY = fM[kSkewY]*x + fM[kScaleY]*y;

+        GrScalar d = pt.distanceToOrigin(); 

+        GrAssert(d <= (1.0001 * stretch));

+        max = GrMax(max, pt.distanceToOrigin());

+    }

+    GrAssert((stretch - max) < .05*stretch);

+#endif

+    return stretch;

+}

+

+bool GrMatrix::operator == (const GrMatrix& m) const {

+    if (fTypeMask != m.fTypeMask) {

+        return false;

+    }

+    if (!fTypeMask) {

+        return true;

+    }

+    for (int i = 0; i < 9; ++i) {

+        if (m.fM[i] != fM[i]) {

+            return false;

+        }

+    }

+    return true;

+}

+

+bool GrMatrix::operator != (const GrMatrix& m) const {

+    return !(*this == m);

+}

+

+void GrMatrix::setTypeMask()

+{

+    fTypeMask = 0;

+    if (0 != fM[kPersp0] || 0 != fM[kPersp1] || gRESCALE != fM[kPersp2]) {

+        fTypeMask |= kPerspective_TypeBit;

+    }

+    if (GR_Scalar1 != fM[kScaleX] || GR_Scalar1 != fM[kScaleY]) {

+        fTypeMask |= kScale_TypeBit;

+        if (0 == fM[kScaleX] && 0 == fM[kScaleY]) {

+            fTypeMask |= kZeroScale_TypeBit;

+        }

+    }

+    if (0 != fM[kSkewX] || 0 != fM[kSkewY]) {

+        fTypeMask |= kSkew_TypeBit;

+    }

+    if (0 != fM[kTransX] || 0 != fM[kTransY]) {

+        fTypeMask |= kTranslate_TypeBit;

+    }    

+}

+

+////////////////////////////////////////////////////////////////////////////////

+// Matrix transformation procs

+//////

+

+void GrMatrix::mapIdentity(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    if (src != dst) {

+        for (uint32_t i = 0; i < count; ++i) {

+            dst[i] = src[i];

+        }

+    }

+}

+

+void GrMatrix::mapScale(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    for (uint32_t i = 0; i < count; ++i) {

+        dst[i].fX = GrMul(src[i].fX, fM[kScaleX]);

+        dst[i].fY = GrMul(src[i].fY, fM[kScaleY]);

+    }

+}

+

+

+void GrMatrix::mapTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    for (uint32_t i = 0; i < count; ++i) {

+        dst[i].fX = src[i].fX + fM[kTransX];

+        dst[i].fY = src[i].fY + fM[kTransY];

+    }

+}

+

+void GrMatrix::mapScaleAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    for (uint32_t i = 0; i < count; ++i) {

+        dst[i].fX = GrMul(src[i].fX, fM[kScaleX]) + fM[kTransX];

+        dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + fM[kTransY];

+    }

+}

+

+void GrMatrix::mapSkew(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    if (src != dst) {

+        for (uint32_t i = 0; i < count; ++i) {

+            dst[i].fX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]);

+            dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]);

+        }

+    } else {

+        for (uint32_t i = 0; i < count; ++i) {

+            GrScalar newX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]);

+            dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]);

+            dst[i].fX = newX;

+        }

+    }

+}

+

+void GrMatrix::mapScaleAndSkew(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    if (src != dst) {

+        for (uint32_t i = 0; i < count; ++i) {

+            dst[i].fX = GrMul(src[i].fX, fM[kScaleX]) + GrMul(src[i].fY, fM[kSkewX]);

+            dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + GrMul(src[i].fX, fM[kSkewY]);

+        }

+    } else {

+        for (uint32_t i = 0; i < count; ++i) {

+            GrScalar newX = GrMul(src[i].fX, fM[kScaleX]) + GrMul(src[i].fY, fM[kSkewX]);

+            dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + GrMul(src[i].fX, fM[kSkewY]);

+            dst[i].fX = newX;

+        }

+    }

+}

+

+void GrMatrix::mapSkewAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    if (src != dst) {

+        for (uint32_t i = 0; i < count; ++i) {

+            dst[i].fX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];

+            dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];

+        }

+    } else {

+        for (uint32_t i = 0; i < count; ++i) {

+            GrScalar newX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];

+            dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];

+            dst[i].fX = newX;

+        }

+    }

+}

+

+void GrMatrix::mapNonPerspective(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    if (src != dst) {

+        for (uint32_t i = 0; i < count; ++i) {

+            dst[i].fX = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX];

+            dst[i].fY = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY];           

+        }

+    } else {

+        for (uint32_t i = 0; i < count; ++i) {

+            GrScalar newX = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX];

+            dst[i].fY = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY];

+            dst[i].fX = newX;

+        }

+    }

+}

+

+void GrMatrix::mapPerspective(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    for (uint32_t i = 0; i < count; ++i) {

+        GrScalar x, y, w;

+        x = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX];

+        y = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY];

+        w = GrMul(fM[kPersp0], src[i].fX) + GrMul(fM[kPersp1], src[i].fY) + fM[kPersp2];

+        // TODO need fixed point invert

+        if (w) {

+            w = 1 / w;

+        }

+        dst[i].fX = GrMul(x, w);

+        dst[i].fY = GrMul(y, w);            

+    }

+}

+

+void GrMatrix::mapInvalid(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    GrAssert(0);

+}

+

+void GrMatrix::mapZero(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    memset(dst, 0, sizeof(GrPoint)*count);

+}

+

+void GrMatrix::mapSetToTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    for (uint32_t i = 0; i < count; ++i) {

+        dst[i].fX = fM[kTransX];

+        dst[i].fY = fM[kTransY];           

+    }

+}

+

+void GrMatrix::mapSwappedScale(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    if (src != dst) {

+        for (uint32_t i = 0; i < count; ++i) {

+            dst[i].fX = GrMul(src[i].fY, fM[kSkewX]);

+            dst[i].fY = GrMul(src[i].fX, fM[kSkewY]);

+        }

+    } else {

+        for (uint32_t i = 0; i < count; ++i) {

+            GrScalar newX = GrMul(src[i].fY, fM[kSkewX]);

+            dst[i].fY = GrMul(src[i].fX, fM[kSkewY]);

+            dst[i].fX = newX;

+        }

+    }

+}

+

+void GrMatrix::mapSwappedScaleAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {

+    if (src != dst) {

+        for (uint32_t i = 0; i < count; ++i) {

+            dst[i].fX = GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];

+            dst[i].fY = GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];

+        }

+    } else {

+        for (uint32_t i = 0; i < count; ++i) {

+            GrScalar newX = GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];

+            dst[i].fY = GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];

+            dst[i].fX = newX;

+        }

+    }

+}

+

+///////////////////////////////////////////////////////////////////////////////

+// Unit test

+//////

+

+#include "GrRandom.h"

+

+#if GR_DEBUG

+enum MatrixType {

+    kRotate_MatrixType,

+    kScaleX_MatrixType,

+    kScaleY_MatrixType,

+    kSkewX_MatrixType,

+    kSkewY_MatrixType,

+    kTranslateX_MatrixType,

+    kTranslateY_MatrixType,

+    kSwapScaleXY_MatrixType,

+    kPersp_MatrixType,

+    

+    kMatrixTypeCount

+};

+

+static void create_matrix(GrMatrix* matrix, GrRandom& rand) {

+    MatrixType type = (MatrixType)(rand.nextU() % kMatrixTypeCount);

+    switch (type) {

+        case kRotate_MatrixType: {

+            float angle = rand.nextF() * 2 *3.14159265358979323846f;

+            GrScalar cosa = GrFloatToScalar(cosf(angle));

+            GrScalar sina = GrFloatToScalar(sinf(angle));

+            matrix->setAll(cosa,      -sina,           0, 

+                           sina,       cosa,           0, 

+                           0,          0,              GrMatrix::I()[8]);

+        } break;

+        case kScaleX_MatrixType: {

+            GrScalar scale = GrFloatToScalar(rand.nextF(-2, 2));

+            matrix->setAll(scale,      0,              0, 

+                           0,          GR_Scalar1,     0, 

+                           0,          0,              GrMatrix::I()[8]);

+        } break;

+        case kScaleY_MatrixType: {

+            GrScalar scale = GrFloatToScalar(rand.nextF(-2, 2));

+            matrix->setAll(GR_Scalar1, 0,              0, 

+                           0,          scale,          0, 

+                           0,          0,              GrMatrix::I()[8]);

+        } break;

+        case kSkewX_MatrixType: {

+            GrScalar skew = GrFloatToScalar(rand.nextF(-2, 2));

+            matrix->setAll(GR_Scalar1, skew,           0, 

+                           0,          GR_Scalar1,     0, 

+                           0,          0,              GrMatrix::I()[8]);

+        } break;            

+        case kSkewY_MatrixType: {

+            GrScalar skew = GrFloatToScalar(rand.nextF(-2, 2));

+            matrix->setAll(GR_Scalar1, 0,              0, 

+                           skew,       GR_Scalar1,     0, 

+                           0,          0,              GrMatrix::I()[8]);

+        } break;

+        case kTranslateX_MatrixType: {

+            GrScalar trans = GrFloatToScalar(rand.nextF(-10, 10));

+            matrix->setAll(GR_Scalar1, 0,              trans, 

+                           0,          GR_Scalar1,     0, 

+                           0,          0,              GrMatrix::I()[8]);

+        } break;            

+        case kTranslateY_MatrixType: {

+            GrScalar trans = GrFloatToScalar(rand.nextF(-10, 10));

+            matrix->setAll(GR_Scalar1, 0,              0, 

+                           0,          GR_Scalar1,     trans, 

+                           0,          0,              GrMatrix::I()[8]);

+        } break; 

+        case kSwapScaleXY_MatrixType: {

+            GrScalar xy = GrFloatToScalar(rand.nextF(-2, 2));

+            GrScalar yx = GrFloatToScalar(rand.nextF(-2, 2));

+            matrix->setAll(0,          xy,             0, 

+                           yx,         0,              0, 

+                           0,          0,              GrMatrix::I()[8]);

+        } break;

+        case kPersp_MatrixType: {

+            GrScalar p0 = GrFloatToScalar(rand.nextF(-2, 2));

+            GrScalar p1 = GrFloatToScalar(rand.nextF(-2, 2));

+            GrScalar p2 = GrFloatToScalar(rand.nextF(-0.5f, 0.75f));

+            matrix->setAll(GR_Scalar1, 0,              0, 

+                           0,          GR_Scalar1,     0, 

+                           p0,         p1,             GrMul(p2,GrMatrix::I()[8]));

+        } break;

+        default:

+            GrAssert(0);

+            break;

+    }

+}

+#endif

+

+void GrMatrix::UnitTest() {

+    GrRandom rand;

+

+    // Create a bunch of matrices and test point mapping, max stretch calc, 

+    // inversion and multiply-by-inverse.

+#if GR_DEBUG

+    for (int i = 0; i < 10000; ++i) {

+        GrMatrix a, b;

+        a.setIdentity();

+        int num = rand.nextU() % 6;

+        // force testing of I and swapXY

+        if (0 == i) {

+            num = 0;

+            GrAssert(a.isIdentity());

+        } else if (1 == i) {

+            num = 0;

+            a.setAll(0, GR_Scalar1, 0,

+                     GR_Scalar1, 0, 0, 

+                     0, 0, I()[8]);

+        }

+        for (int j = 0; j < num; ++j) {

+            create_matrix(&b, rand);

+            a.preConcat(b);

+        }

+        

+        GrScalar maxStretch = a.getMaxStretch();

+        if (maxStretch > 0) {

+            maxStretch = GrMul(GR_Scalar1 + GR_Scalar1 / 100, maxStretch);

+        }

+        GrPoint origin = a.mapPoint(GrPoint(0,0));

+

+        for (int j = 0; j < 9; ++j) {

+            int mask, origMask = a.fTypeMask;

+            GrScalar old = a[j];

+            

+            a.set(j, GR_Scalar1);

+            mask = a.fTypeMask;

+            a.setTypeMask();

+            GrAssert(mask == a.fTypeMask);

+

+            a.set(j, 0);

+            mask = a.fTypeMask;

+            a.setTypeMask();

+            GrAssert(mask == a.fTypeMask);

+

+            a.set(j, 10 * GR_Scalar1);

+            mask = a.fTypeMask;

+            a.setTypeMask();

+            GrAssert(mask == a.fTypeMask);

+            

+            a.set(j, old);            

+            GrAssert(a.fTypeMask == origMask);

+        }

+                     

+        for (int j = 0; j < 100; ++j) {

+            GrPoint pt;

+            pt.fX = GrFloatToScalar(rand.nextF(-10, 10));

+            pt.fY = GrFloatToScalar(rand.nextF(-10, 10));            

+            

+            GrPoint t0, t1, t2;

+            t0 = a.mapPoint(pt);             // map to a new point

+            t1 = pt;

+            a.mapPoints(&t1, &t1, 1);        // in place

+            a.mapPerspective(&t2, &pt, 1);   // full mult

+            GrAssert(t0 == t1 && t1 == t2);

+            if (maxStretch >= 0.f) {

+                GrVec vec;

+                vec.setBetween(t0, origin);

+                GrScalar stretch = vec.length() / pt.distanceToOrigin();

+                GrAssert(stretch <= maxStretch);

+            }

+        }

+        double det = a.determinant();

+        if (fabs(det) > 1e-3 && a.invert(&b)) {

+            GrMatrix c;

+            c.setConcat(a,b);

+            for (int i = 0; i < 9; ++i) {

+                GrScalar diff = GrScalarAbs(c[i] - I()[i]);

+                GrAssert(diff < (5*GR_Scalar1 / 100));

+            }

+        }

+    }

+#endif

+}

+

+///////////////////////////////////////////////////////////////////////////////

+

+int Gr_clz(uint32_t n) {

+    if (0 == n) {

+        return 32;

+    }

+

+    int count = 0;

+    if (0 == (n & 0xFFFF0000)) {

+        count += 16;

+        n <<= 16;

+    }

+    if (0 == (n & 0xFF000000)) {

+        count += 8;

+        n <<= 8;

+    }

+    if (0 == (n & 0xF0000000)) {

+        count += 4;

+        n <<= 4;

+    }

+    if (0 == (n & 0xC0000000)) {

+        count += 2;

+        n <<= 2;

+    }

+    if (0 == (n & 0x80000000)) {

+        count += 1;

+    }

+    return count;

+}

+

+///////////////////////////////////////////////////////////////////////////////

+#include "GrRect.h"

+

+void GrRect::setBounds(const GrPoint pts[], int count) {

+    if (count <= 0) {

+        this->setEmpty();

+    } else {

+        GrScalar L, R, T, B;

+        L = R = pts[0].fX;

+        T = B = pts[0].fY;

+        for (int i = 1; i < count; i++) {

+            GrScalar x = pts[i].fX;

+            GrScalar 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->setLTRB(L, T, R, B);

+    }

+}

+

+
diff --git a/gpu/src/GrMemory.cpp b/gpu/src/GrMemory.cpp
new file mode 100644
index 0000000..3da924a
--- /dev/null
+++ b/gpu/src/GrMemory.cpp
@@ -0,0 +1,36 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrMemory.h"
+
+#include <stdlib.h>
+
+void* GrMalloc(size_t bytes) {
+    void* ptr = ::malloc(bytes);
+    if (NULL == ptr) {
+        ::exit(-1);
+    }
+    return ptr;
+}
+
+void GrFree(void* ptr) {
+    if (ptr) {
+        ::free(ptr);
+    }
+}
+
+
diff --git a/gpu/src/GrPath.cpp b/gpu/src/GrPath.cpp
new file mode 100644
index 0000000..7b117eb
--- /dev/null
+++ b/gpu/src/GrPath.cpp
@@ -0,0 +1,173 @@
+#include "GrPath.h"
+
+GrPath::GrPath() {}
+
+GrPath::GrPath(const GrPath& src) {
+}
+
+GrPath::GrPath(GrPathIter& iter) {
+    this->resetFromIter(&iter);
+}
+
+GrPath::~GrPath() {
+}
+
+void GrPath::ensureMoveTo() {
+    if (fVerbs.isEmpty() || this->wasLastVerb(kClose)) {
+        *fVerbs.append() = kMove;
+        fPts.append()->set(0, 0);
+    }
+}
+
+void GrPath::moveTo(GrScalar x, GrScalar y) {
+    if (this->wasLastVerb(kMove)) {
+        // overwrite prev kMove value
+        fPts[fPts.count() - 1].set(x, y);
+    } else {
+        *fVerbs.append() = kMove;
+        fPts.append()->set(x, y);
+    }
+}
+
+void GrPath::lineTo(GrScalar x, GrScalar y) {
+    this->ensureMoveTo();
+    *fVerbs.append() = kLine;
+    fPts.append()->set(x, y);
+}
+
+void GrPath::quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1) {
+    this->ensureMoveTo();
+    *fVerbs.append() = kQuad;
+    fPts.append()->set(x0, y0);
+    fPts.append()->set(x1, y1);
+}
+
+void GrPath::cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1,
+                     GrScalar x2, GrScalar y2) {
+    this->ensureMoveTo();
+    *fVerbs.append() = kCubic;
+    fPts.append()->set(x0, y0);
+    fPts.append()->set(x1, y1);
+    fPts.append()->set(x2, y2);
+}
+
+void GrPath::close() {
+    if (!fVerbs.isEmpty() && !this->wasLastVerb(kClose)) {
+        // should we allow kMove followed by kClose?
+        *fVerbs.append() = kClose;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrPath::resetFromIter(GrPathIter* iter) {
+    fPts.reset();
+    fVerbs.reset();
+
+    GrPoint pts[4];
+    GrPathIter::Command cmd;
+
+    while ((cmd = iter->next(pts)) != GrPathIter::kEnd_Command) {
+        switch (cmd) {
+            case GrPathIter::kMove_Command:
+                this->moveTo(pts[0].fX, pts[0].fY);
+                break;
+            case GrPathIter::kLine_Command:
+                this->lineTo(pts[1].fX, pts[1].fY);
+                break;
+            case GrPathIter::kQuadratic_Command:
+                this->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
+                break;
+            case GrPathIter::kCubic_Command:
+                this->cubicTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
+                              pts[3].fX, pts[3].fY);
+                break;
+            case GrPathIter::kClose_Command:
+                this->close();
+                break;
+            case GrPathIter::kEnd_Command:
+                // never get here, but include it to avoid the warning
+                break;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrPath::Iter::Iter(const GrPath& path) : fPath(path) {
+    this->rewind();
+}
+
+GrPathIter::Command GrPath::Iter::next(GrPoint points[]) {
+    if (fVerbIndex == fPath.fVerbs.count()) {
+        GrAssert(fPtIndex == fPath.fPts.count());
+        return GrPathIter::kEnd_Command;
+    } else {
+        GrAssert(fVerbIndex < fPath.fVerbs.count());
+    }
+
+    uint8_t cmd = fPath.fVerbs[fVerbIndex++];
+    const GrPoint* srcPts = fPath.fPts.begin() + fPtIndex;
+
+    switch (cmd) {
+        case kMove:
+            if (points) {
+                points[0] = srcPts[0];
+            }
+            fLastPt = srcPts[0];
+            GrAssert(fPtIndex <= fPath.fPts.count() + 1);
+            fPtIndex += 1;
+            break;
+        case kLine:
+            if (points) {
+                points[0] = fLastPt;
+                points[1] = srcPts[0];
+            }
+            fLastPt = srcPts[0];
+            GrAssert(fPtIndex <= fPath.fPts.count() + 1);
+            fPtIndex += 1;
+            break;
+        case kQuad:
+            if (points) {
+                points[0] = fLastPt;
+                points[1] = srcPts[0];
+                points[2] = srcPts[1];
+            }
+            fLastPt = srcPts[2];
+            GrAssert(fPtIndex <= fPath.fPts.count() + 2);
+            fPtIndex += 2;
+            break;
+        case kCubic:
+            if (points) {
+                points[0] = fLastPt;
+                points[1] = srcPts[0];
+                points[2] = srcPts[1];
+                points[3] = srcPts[2];
+            }
+            fLastPt = srcPts[2];
+            GrAssert(fPtIndex <= fPath.fPts.count() + 3);
+            fPtIndex += 3;
+            break;
+        case kClose:
+            break;
+        default:
+            GrAssert(!"unknown grpath verb");
+            break;
+    }
+    return (GrPathIter::Command)cmd;
+}
+
+GrPathIter::ConvexHint GrPath::Iter::hint() const {
+    return fPath.getConvexHint();
+}
+
+GrPathIter::Command GrPath::Iter::next() {
+    return this->next(NULL);
+}
+
+void GrPath::Iter::rewind() {
+    fVerbIndex = fPtIndex = 0;
+}
+
+
+
diff --git a/gpu/src/GrPrintf_printf.cpp b/gpu/src/GrPrintf_printf.cpp
new file mode 100644
index 0000000..ad239ec
--- /dev/null
+++ b/gpu/src/GrPrintf_printf.cpp
@@ -0,0 +1,36 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrTypes.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+void GrPrintf(const char format[], ...) {
+    const size_t MAX_BUFFER_SIZE = 2048;
+
+    char buffer[MAX_BUFFER_SIZE + 1];
+    va_list args;
+
+    va_start(args, format);
+    vsnprintf(buffer, MAX_BUFFER_SIZE, format, args);
+    va_end(args);
+
+    printf("%s", buffer);
+}
+
+
diff --git a/gpu/src/GrPrintf_skia.cpp b/gpu/src/GrPrintf_skia.cpp
new file mode 100644
index 0000000..fa8b6a7
--- /dev/null
+++ b/gpu/src/GrPrintf_skia.cpp
@@ -0,0 +1,39 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrTypes.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "SkTypes.h"
+
+void GrPrintf(const char format[], ...) {
+    const size_t MAX_BUFFER_SIZE = 512;
+
+    char buffer[MAX_BUFFER_SIZE + 1];
+    va_list args;
+
+    va_start(args, format);
+    vsnprintf(buffer, MAX_BUFFER_SIZE, format, args);
+    va_end(args);
+
+    // skia has already mapped this to do the "right thing"
+    SkDebugf("%s", buffer);
+}
+
+
diff --git a/gpu/src/GrQuadIndexTable.h b/gpu/src/GrQuadIndexTable.h
new file mode 100644
index 0000000..7389466
--- /dev/null
+++ b/gpu/src/GrQuadIndexTable.h
@@ -0,0 +1,98 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+/*
+    These are our vertex-indices for a series of "quads", which we implement
+    as a series of pairs of triangles. See setRectFan() for the
+    vertex order.
+
+    The largest value is 255, so we could store these in any size
+    (byte, short, int). Since the table is small, we just choose the size
+    that we think will be fastest (e.g. some drivers don't have native support
+    for byte-indices, so byte may not be the fastest)
+*/
+static const uint16_t gQuadIndexTable[] = {
+      0,   1,   2,   0,   2,   3,
+      4,   5,   6,   4,   6,   7,
+      8,   9,  10,   8,  10,  11,
+     12,  13,  14,  12,  14,  15,
+     16,  17,  18,  16,  18,  19,
+     20,  21,  22,  20,  22,  23,
+     24,  25,  26,  24,  26,  27,
+     28,  29,  30,  28,  30,  31,
+     32,  33,  34,  32,  34,  35,
+     36,  37,  38,  36,  38,  39,
+     40,  41,  42,  40,  42,  43,
+     44,  45,  46,  44,  46,  47,
+     48,  49,  50,  48,  50,  51,
+     52,  53,  54,  52,  54,  55,
+     56,  57,  58,  56,  58,  59,
+     60,  61,  62,  60,  62,  63,
+     64,  65,  66,  64,  66,  67,
+     68,  69,  70,  68,  70,  71,
+     72,  73,  74,  72,  74,  75,
+     76,  77,  78,  76,  78,  79,
+     80,  81,  82,  80,  82,  83,
+     84,  85,  86,  84,  86,  87,
+     88,  89,  90,  88,  90,  91,
+     92,  93,  94,  92,  94,  95,
+     96,  97,  98,  96,  98,  99,
+    100, 101, 102, 100, 102, 103,
+    104, 105, 106, 104, 106, 107,
+    108, 109, 110, 108, 110, 111,
+    112, 113, 114, 112, 114, 115,
+    116, 117, 118, 116, 118, 119,
+    120, 121, 122, 120, 122, 123,
+    124, 125, 126, 124, 126, 127,
+    128, 129, 130, 128, 130, 131,
+    132, 133, 134, 132, 134, 135,
+    136, 137, 138, 136, 138, 139,
+    140, 141, 142, 140, 142, 143,
+    144, 145, 146, 144, 146, 147,
+    148, 149, 150, 148, 150, 151,
+    152, 153, 154, 152, 154, 155,
+    156, 157, 158, 156, 158, 159,
+    160, 161, 162, 160, 162, 163,
+    164, 165, 166, 164, 166, 167,
+    168, 169, 170, 168, 170, 171,
+    172, 173, 174, 172, 174, 175,
+    176, 177, 178, 176, 178, 179,
+    180, 181, 182, 180, 182, 183,
+    184, 185, 186, 184, 186, 187,
+    188, 189, 190, 188, 190, 191,
+    192, 193, 194, 192, 194, 195,
+    196, 197, 198, 196, 198, 199,
+    200, 201, 202, 200, 202, 203,
+    204, 205, 206, 204, 206, 207,
+    208, 209, 210, 208, 210, 211,
+    212, 213, 214, 212, 214, 215,
+    216, 217, 218, 216, 218, 219,
+    220, 221, 222, 220, 222, 223,
+    224, 225, 226, 224, 226, 227,
+    228, 229, 230, 228, 230, 231,
+    232, 233, 234, 232, 234, 235,
+    236, 237, 238, 236, 238, 239,
+    240, 241, 242, 240, 242, 243,
+    244, 245, 246, 244, 246, 247,
+    248, 249, 250, 248, 250, 251,
+    252, 253, 254, 252, 254, 255,
+};
+
+#define GR_COUNT_QUADINDEXTABLE    GR_ARRAY_COUNT(gQuadIndexTable)
+
+
+
diff --git a/gpu/src/GrRectanizer.cpp b/gpu/src/GrRectanizer.cpp
new file mode 100644
index 0000000..cb6576a
--- /dev/null
+++ b/gpu/src/GrRectanizer.cpp
@@ -0,0 +1,130 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrRectanizer.h"
+#include "GrTBSearch.h"
+
+#define MIN_HEIGHT_POW2     2
+
+class GrRectanizerPow2 : public GrRectanizer {
+public:
+    GrRectanizerPow2(int w, int h) : GrRectanizer(w, h) {
+        fNextStripY = 0;
+        fAreaSoFar = 0;
+        Gr_bzero(fRows, sizeof(fRows));
+    }
+
+    virtual ~GrRectanizerPow2() {
+    }
+
+    virtual bool addRect(int w, int h, GrIPoint16* loc);
+
+    virtual float percentFull() const {
+        return fAreaSoFar / ((float)this->width() * this->height());
+    }
+
+    virtual int stripToPurge(int height) const { return -1; }
+    virtual void purgeStripAtY(int yCoord) { }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    struct Row {
+        GrIPoint16  fLoc;
+        int         fRowHeight;
+        
+        bool canAddWidth(int width, int containerWidth) const {
+            return fLoc.fX + width <= containerWidth;
+        }
+    };
+
+    Row fRows[16];
+
+    static int HeightToRowIndex(int height) {
+        GrAssert(height >= MIN_HEIGHT_POW2);
+        return 32 - Gr_clz(height - 1);
+    }
+
+    int fNextStripY;
+    int32_t fAreaSoFar;
+
+    bool canAddStrip(int height) const {
+        return fNextStripY + height <= this->height();
+    }
+
+    void initRow(Row* row, int rowHeight) {
+        row->fLoc.set(0, fNextStripY);
+        row->fRowHeight = rowHeight;
+        fNextStripY += rowHeight;
+    }
+};
+
+bool GrRectanizerPow2::addRect(int width, int height, GrIPoint16* loc) {
+    if ((unsigned)width > (unsigned)this->width() ||
+        (unsigned)height > (unsigned)this->height()) {
+        return false;
+    }
+
+    int32_t area = width * height;
+
+    /*
+        We use bsearch, but there may be more than one row with the same height,
+        so we actually search for height-1, which can only be a pow2 itself if
+        height == 2. Thus we set a minimum height.
+     */
+    height = GrNextPow2(height);
+    if (height < MIN_HEIGHT_POW2) {
+        height = MIN_HEIGHT_POW2;
+    }
+
+    Row* row = &fRows[HeightToRowIndex(height)];
+    GrAssert(row->fRowHeight == 0 || row->fRowHeight == height);
+
+    if (0 == row->fRowHeight) {
+        if (!this->canAddStrip(height)) {
+            return false;
+        }
+        this->initRow(row, height);
+    } else {
+        if (!row->canAddWidth(width, this->width())) {
+            if (!this->canAddStrip(height)) {
+                return false;
+            }
+            // that row is now "full", so retarget our Row record for
+            // another one
+            this->initRow(row, height);
+        }
+    }
+
+    GrAssert(row->fRowHeight == height);
+    GrAssert(row->canAddWidth(width, this->width()));
+    *loc = row->fLoc;
+    row->fLoc.fX += width;
+
+    GrAssert(row->fLoc.fX <= this->width());
+    GrAssert(row->fLoc.fY <= this->height());
+    GrAssert(fNextStripY <= this->height());
+    fAreaSoFar += area;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrRectanizer* GrRectanizer::Factory(int width, int height) {
+    return new GrRectanizerPow2(width, height);
+}
+
+
diff --git a/gpu/src/GrRectanizer_fifo.cpp b/gpu/src/GrRectanizer_fifo.cpp
new file mode 100644
index 0000000..6b1cad2
--- /dev/null
+++ b/gpu/src/GrRectanizer_fifo.cpp
@@ -0,0 +1,130 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrRectanizer.h"
+#include "GrTBSearch.h"
+
+#define MIN_HEIGHT_POW2     2
+
+class GrRectanizerFIFO : public GrRectanizer {
+public:
+    GrRectanizerFIFO(int w, int h) : GrRectanizer(w, h) {
+        fNextStripY = 0;
+        fAreaSoFar = 0;
+        Gr_bzero(fRows, sizeof(fRows));
+    }
+    
+    virtual ~GrRectanizerFIFO() {
+    }
+    
+    virtual bool addRect(int w, int h, GrIPoint16* loc);
+    
+    virtual float percentFull() const {
+        return fAreaSoFar / ((float)this->width() * this->height());
+    }
+    
+    virtual int stripToPurge(int height) const { return -1; }
+    virtual void purgeStripAtY(int yCoord) { }
+    
+    ///////////////////////////////////////////////////////////////////////////
+    
+    struct Row {
+        GrIPoint16  fLoc;
+        int         fRowHeight;
+        
+        bool canAddWidth(int width, int containerWidth) const {
+            return fLoc.fX + width <= containerWidth;
+        }
+    };
+    
+    Row fRows[16];
+    
+    static int HeightToRowIndex(int height) {
+        GrAssert(height >= MIN_HEIGHT_POW2);
+        return 32 - Gr_clz(height - 1);
+    }
+    
+    int fNextStripY;
+    int32_t fAreaSoFar;
+    
+    bool canAddStrip(int height) const {
+        return fNextStripY + height <= this->height();
+    }
+    
+    void initRow(Row* row, int rowHeight) {
+        row->fLoc.set(0, fNextStripY);
+        row->fRowHeight = rowHeight;
+        fNextStripY += rowHeight;
+    }
+};
+
+bool GrRectanizerFIFO::addRect(int width, int height, GrIPoint16* loc) {
+    if ((unsigned)width > (unsigned)this->width() ||
+        (unsigned)height > (unsigned)this->height()) {
+        return false;
+    }
+    
+    int32_t area = width * height;
+    
+    /*
+     We use bsearch, but there may be more than one row with the same height,
+     so we actually search for height-1, which can only be a pow2 itself if
+     height == 2. Thus we set a minimum height.
+     */
+    height = GrNextPow2(height);
+    if (height < MIN_HEIGHT_POW2) {
+        height = MIN_HEIGHT_POW2;
+    }
+    
+    Row* row = &fRows[HeightToRowIndex(height)];
+    GrAssert(row->fRowHeight == 0 || row->fRowHeight == height);
+    
+    if (0 == row->fRowHeight) {
+        if (!this->canAddStrip(height)) {
+            return false;
+        }
+        this->initRow(row, height);
+    } else {
+        if (!row->canAddWidth(width, this->width())) {
+            if (!this->canAddStrip(height)) {
+                return false;
+            }
+            // that row is now "full", so retarget our Row record for
+            // another one
+            this->initRow(row, height);
+        }
+    }
+    
+    GrAssert(row->fRowHeight == height);
+    GrAssert(row->canAddWidth(width, this->width()));
+    *loc = row->fLoc;
+    row->fLoc.fX += width;
+    
+    GrAssert(row->fLoc.fX <= this->width());
+    GrAssert(row->fLoc.fY <= this->height());
+    GrAssert(fNextStripY <= this->height());
+    fAreaSoFar += area;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrRectanizer* GrRectanizer::Factory(int width, int height) {
+    return new GrRectanizerFIFO(width, height);
+}
+
+
diff --git a/gpu/src/GrTextContext.cpp b/gpu/src/GrTextContext.cpp
new file mode 100644
index 0000000..d5fa1cc
--- /dev/null
+++ b/gpu/src/GrTextContext.cpp
@@ -0,0 +1,244 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrAtlas.h"
+#include "GrClipIterator.h"
+#include "GrContext.h"
+#include "GrTextContext.h"
+#include "GrTextStrike.h"
+#include "GrTextStrike_impl.h"
+#include "GrFontScaler.h"
+
+static const GrVertexLayout VLAYOUT = GrDrawTarget::kTextFormat_VertexLayoutBit;
+
+void GrTextContext::flushGlyphs() {
+    if (fCurrVertex > 0) {
+        GrDrawTarget::AutoStateRestore asr(fDrawTarget);
+
+        // setup our sampler state for our text texture/atlas
+
+        GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
+                               GrSamplerState::kRepeat_WrapMode,
+                               GrSamplerState::kAlphaMod_SampleMode,
+                               !fExtMatrix.isIdentity());
+        fDrawTarget->setSamplerState(sampler);
+
+        GrAssert(GrIsALIGN4(fCurrVertex));
+        int nIndices = fCurrVertex + (fCurrVertex >> 1);
+        GrAssert(fCurrTexture);
+        fDrawTarget->setTexture(fCurrTexture);
+        fDrawTarget->setTextureMatrix(GrMatrix::I());
+        fDrawTarget->setIndexSourceToBuffer(fContext->quadIndexBuffer());
+
+        fDrawTarget->drawIndexed(GrDrawTarget::kTriangles_PrimitiveType,
+                                 0, 0, fCurrVertex, nIndices);
+
+        fDrawTarget->releaseReservedGeometry();
+        fVertices = NULL;
+        fMaxVertices = 0;
+        fCurrVertex = 0;
+        fCurrTexture->unref();
+        fCurrTexture = NULL;
+    }
+}
+
+GrTextContext::GrTextContext(GrContext* context, const GrMatrix* extMatrix) {
+    fContext = context;
+    fStrike = NULL;
+
+    fCurrTexture = NULL;
+    fCurrVertex = 0;
+    fClipRect = context->getClip().getBounds();
+
+    if (NULL != extMatrix) {
+        fExtMatrix = *extMatrix;
+    } else {
+        fExtMatrix = GrMatrix::I();
+    }
+    if (!fExtMatrix.isIdentity()) {
+        GrMatrix inverse;
+        GrRect r;
+        r.set(fClipRect);
+        if (fExtMatrix.invert(&inverse)) {
+            inverse.mapRect(&r);
+            r.roundOut(&fClipRect);
+        }
+    }
+
+    fContext->getViewMatrix(&fOrigViewMatrix);
+    fContext->setViewMatrix(fExtMatrix);
+
+    fVertices = NULL;
+    fMaxVertices = 0;
+    fDrawTarget = fContext->getTextTarget();
+}
+
+GrTextContext::~GrTextContext() {
+    this->flushGlyphs();
+    fContext->setViewMatrix(fOrigViewMatrix);
+}
+
+void GrTextContext::flush() {
+    this->flushGlyphs();
+}
+
+static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
+                              int stride) {
+    v[0 * stride].setI(l, t);
+    v[1 * stride].setI(l, b);
+    v[2 * stride].setI(r, b);
+    v[3 * stride].setI(r, t);
+}
+
+void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
+                                    GrFixed vx, GrFixed vy,
+                                    GrFontScaler* scaler) {
+    if (NULL == fStrike) {
+        fStrike = fContext->getFontCache()->getStrike(scaler);
+    }
+
+    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
+    if (NULL == glyph || glyph->fBounds.isEmpty()) {
+        return;
+    }
+
+    vx += GrIntToFixed(glyph->fBounds.fLeft);
+    vy += GrIntToFixed(glyph->fBounds.fTop);
+
+    // keep them as ints until we've done the clip-test
+    GrFixed width = glyph->fBounds.width();
+    GrFixed height = glyph->fBounds.height();
+
+    // check if we clipped out
+    if (true || NULL == glyph->fAtlas) {
+        int x = vx >> 16;
+        int y = vy >> 16;
+        if (fClipRect.quickReject(x, y, x + width, y + height)) {
+//            Gr_clz(3);    // so we can set a break-point in the debugger
+            return;
+        }
+    }
+
+    if (NULL == glyph->fAtlas) {
+        if (fStrike->getGlyphAtlas(glyph, scaler)) {
+            goto HAS_ATLAS;
+        }
+        // try to purge
+        fContext->getFontCache()->purgeExceptFor(fStrike);
+        if (fStrike->getGlyphAtlas(glyph, scaler)) {
+            goto HAS_ATLAS;
+        }
+
+        // Draw as a path, so we flush any accumulated glyphs first
+        this->flushGlyphs();
+
+        if (NULL == glyph->fPath) {
+
+            GrPath* path = new GrPath;
+            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
+                // flag the glyph as being dead?
+                delete path;
+                return;
+            }
+            glyph->fPath = path;
+        }
+        GrPath::Iter iter(*glyph->fPath);
+        bool useTexture = false;
+        GrPoint translate;
+        translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
+                      GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
+        fContext->drawPath(&iter, GrContext::kWinding_PathFill,
+                              useTexture, &translate);
+        return;
+    }
+
+HAS_ATLAS:
+    GrAssert(glyph->fAtlas);
+
+    // now promote them to fixed
+    width = GrIntToFixed(width);
+    height = GrIntToFixed(height);
+
+    GrTexture* texture = glyph->fAtlas->texture();
+    GrAssert(texture);
+
+    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
+        this->flushGlyphs();
+        fCurrTexture = texture;
+        fCurrTexture->ref();
+    }
+
+    if (NULL == fVertices) {
+        // If we need to reserve vertices allow the draw target to suggest
+        // a number of verts to reserve and whether to perform a flush.
+        fMaxVertices = kMinRequestedVerts;
+        bool flush = fDrawTarget->geometryHints(VLAYOUT,
+                                               &fMaxVertices,
+                                               NULL);
+        if (flush) {
+            this->flushGlyphs();
+            fContext->flushText();
+            fDrawTarget = fContext->getTextTarget();
+            fMaxVertices = kDefaultRequestedVerts;
+            // ignore return, no point in flushing again.
+            fDrawTarget->geometryHints(VLAYOUT,
+                                       &fMaxVertices,
+                                       NULL);
+        }
+
+        if (fMaxVertices < kMinRequestedVerts) {
+            fMaxVertices = kDefaultRequestedVerts;
+        } else if (fMaxVertices > (fContext->maxQuadsInIndexBuffer() * 4)) {
+            // don't exceed the limit of the index buffer
+            fMaxVertices = (fContext->maxQuadsInIndexBuffer() * 4);
+        }
+        bool success = fDrawTarget->reserveAndLockGeometry(VLAYOUT,
+                                                           fMaxVertices, 0,
+                                                           (void**)&fVertices,
+                                                           NULL);
+        GrAlwaysAssert(success);
+    }
+
+    GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
+    GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
+
+#if GR_GL_TEXT_TEXTURE_NORMALIZED
+    int x = vx >> 16;
+    int y = vy >> 16;
+    int w = width >> 16;
+    int h = height >> 16;
+
+    setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
+    setRectFan(&fVertices[2*fCurrVertex+1],
+               texture->normalizeFixedX(tx),
+               texture->normalizeFixedY(ty),
+               texture->normalizeFixedX(tx + width),
+               texture->normalizeFixedY(ty + height),
+               2);
+#else
+    fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
+                                        2 * sizeof(GrGpuTextVertex));
+    fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
+                                          texture->normalizeFixedY(ty),
+                                          texture->normalizeFixedX(tx + width),
+                                          texture->normalizeFixedY(ty + height),
+                                          2 * sizeof(GrGpuTextVertex));
+#endif
+    fCurrVertex += 4;
+}
+
+
diff --git a/gpu/src/GrTextStrike.cpp b/gpu/src/GrTextStrike.cpp
new file mode 100644
index 0000000..c2d81d5
--- /dev/null
+++ b/gpu/src/GrTextStrike.cpp
@@ -0,0 +1,204 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrAtlas.h"
+#include "GrGpu.h"
+#include "GrMemory.h"
+#include "GrRectanizer.h"
+#include "GrTextStrike.h"
+#include "GrTextStrike_impl.h"
+#include "GrRect.h"
+
+GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
+    gpu->ref();
+    fAtlasMgr = NULL;
+
+    fHead = fTail = NULL;
+}
+
+GrFontCache::~GrFontCache() {
+    fCache.deleteAll();
+    delete fAtlasMgr;
+    fGpu->unref();
+}
+
+GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
+                                          const Key& key) {
+    if (NULL == fAtlasMgr) {
+        fAtlasMgr = new GrAtlasMgr(fGpu);
+    }
+    GrTextStrike* strike = new GrTextStrike(this, scaler->getKey(), fAtlasMgr);
+    fCache.insert(key, strike);
+
+    if (fHead) {
+        fHead->fPrev = strike;
+    } else {
+        GrAssert(NULL == fTail);
+        fTail = strike;
+    }
+    strike->fPrev = NULL;
+    strike->fNext = fHead;
+    fHead = strike;
+
+    return strike;
+}
+
+void GrFontCache::abandonAll() {
+    fCache.deleteAll();
+    if (fAtlasMgr) {
+        fAtlasMgr->abandonAll();
+        delete fAtlasMgr;
+        fAtlasMgr = NULL;
+    }
+}
+
+void GrFontCache::freeAll() {
+    fCache.deleteAll();
+    delete fAtlasMgr;
+    fAtlasMgr = NULL;
+}
+
+void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
+    GrTextStrike* strike = fTail;
+    if (strike == preserveStrike) {
+        strike = strike->fPrev;
+    }
+    if (strike) {
+        int index = fCache.slowFindIndex(strike);
+        GrAssert(index >= 0);
+        fCache.removeAt(index, strike->fFontScalerKey->getHash());
+        this->detachStrikeFromList(strike);
+        delete strike;
+    }
+}
+
+#if GR_DEBUG
+void GrFontCache::validate() const {
+    int count = fCache.count();
+    if (0 == count) {
+        GrAssert(!fHead);
+        GrAssert(!fTail);
+    } else if (1 == count) {
+        GrAssert(fHead == fTail);
+    } else {
+        GrAssert(fHead != fTail);
+    }
+
+    int count2 = 0;
+    const GrTextStrike* strike = fHead;
+    while (strike) {
+        count2 += 1;
+        strike = strike->fNext;
+    }
+    GrAssert(count == count2);
+
+    count2 = 0;
+    strike = fTail;
+    while (strike) {
+        count2 += 1;
+        strike = strike->fPrev;
+    }
+    GrAssert(count == count2);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_DEBUG
+    static int gCounter;
+#endif
+
+/*
+    The text strike is specific to a given font/style/matrix setup, which is
+    represented by the GrHostFontScaler object we are given in getGlyph().
+
+    We map a 32bit glyphID to a GrGlyph record, which in turn points to a
+    atlas and a position within that texture.
+ */
+
+GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
+                           GrAtlasMgr* atlasMgr) : fPool(64) {
+    fFontScalerKey = key;
+    fFontScalerKey->ref();
+
+    fFontCache = cache;     // no need to ref, it won't go away before we do
+    fAtlasMgr = atlasMgr;   // no need to ref, it won't go away before we do
+    fAtlas = NULL;
+
+#if GR_DEBUG
+    GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
+    gCounter += 1;
+#endif
+}
+
+static void FreeGlyph(GrGlyph*& glyph) { glyph->free(); }
+
+GrTextStrike::~GrTextStrike() {
+    GrAtlas::FreeLList(fAtlas);
+    fFontScalerKey->unref();
+    fCache.getArray().visit(FreeGlyph);
+
+#if GR_DEBUG
+    gCounter -= 1;
+    GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
+#endif
+}
+
+GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
+                                     GrFontScaler* scaler) {
+    GrIRect bounds;
+    if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
+        return NULL;
+    }
+
+    GrGlyph* glyph = fPool.alloc();
+    glyph->init(packed, bounds);
+    fCache.insert(packed, glyph);
+    return glyph;
+}
+
+bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
+    GrAssert(glyph);
+    GrAssert(scaler);
+    GrAssert(fCache.contains(glyph));
+    if (glyph->fAtlas) {
+        return true;
+    }
+
+    GrAutoRef ar(scaler);
+    
+    size_t size = glyph->fBounds.area();
+    GrAutoSMalloc<1024> storage(size);
+    if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
+                                     glyph->height(), glyph->width(),
+                                     storage.get())) {
+        return false;
+    }
+
+    GrAtlas* atlas = fAtlasMgr->addToAtlas(fAtlas, glyph->width(),
+                                           glyph->height(), storage.get(),
+                                           &glyph->fAtlasLocation);
+    if (NULL == atlas) {
+        return false;
+    }
+
+    // update fAtlas as well, since they may be chained in a linklist
+    glyph->fAtlas = fAtlas = atlas;
+    return true;
+}
+
+
diff --git a/gpu/src/GrTextStrike_impl.h b/gpu/src/GrTextStrike_impl.h
new file mode 100644
index 0000000..7e03e2a
--- /dev/null
+++ b/gpu/src/GrTextStrike_impl.h
@@ -0,0 +1,113 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef GrTextStrike_impl_DEFINED
+#define GrTextStrike_impl_DEFINED
+
+class GrFontCache::Key {
+public:
+    Key(GrFontScaler* scaler) {
+        fFontScalerKey = scaler->getKey();
+    }
+    
+    uint32_t getHash() const { return fFontScalerKey->getHash(); }
+    
+    static bool LT(const GrTextStrike& strike, const Key& key) {
+        return *strike.getFontScalerKey() < *key.fFontScalerKey;
+    }
+    static bool EQ(const GrTextStrike& strike, const Key& key) {
+        return *strike.getFontScalerKey() == *key.fFontScalerKey;
+    }
+    
+private:
+    const GrKey* fFontScalerKey;
+};
+
+void GrFontCache::detachStrikeFromList(GrTextStrike* strike) {
+    if (strike->fPrev) {
+        GrAssert(fHead != strike);
+        strike->fPrev->fNext = strike->fNext;
+    } else {
+        GrAssert(fHead == strike);
+        fHead = strike->fNext;
+    }
+
+    if (strike->fNext) {
+        GrAssert(fTail != strike);
+        strike->fNext->fPrev = strike->fPrev;
+    } else {
+        GrAssert(fTail == strike);
+        fTail = strike->fPrev;
+    }
+}
+
+GrTextStrike* GrFontCache::getStrike(GrFontScaler* scaler) {
+    this->validate();
+    
+    Key key(scaler);
+    GrTextStrike* strike = fCache.find(key);
+    if (NULL == strike) {
+        strike = this->generateStrike(scaler, key);
+    } else if (strike->fPrev) {
+        // Need to put the strike at the head of its dllist, since that is how
+        // we age the strikes for purging (we purge from the back of the list
+        this->detachStrikeFromList(strike);
+        // attach at the head
+        fHead->fPrev = strike;
+        strike->fNext = fHead;
+        strike->fPrev = NULL;
+        fHead = strike;
+    }
+
+    this->validate();
+    return strike;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  This Key just wraps a glyphID, and matches the protocol need for
+ *  GrTHashTable
+ */
+class GrTextStrike::Key {
+public:
+    Key(GrGlyph::PackedID id) : fPackedID(id) {}
+    
+    uint32_t getHash() const { return fPackedID; }
+    
+    static bool LT(const GrGlyph& glyph, const Key& key) {
+        return glyph.fPackedID < key.fPackedID;
+    }
+    static bool EQ(const GrGlyph& glyph, const Key& key) {
+        return glyph.fPackedID == key.fPackedID;
+    }
+    
+private:
+    GrGlyph::PackedID fPackedID;
+};
+
+GrGlyph* GrTextStrike::getGlyph(GrGlyph::PackedID packed,
+                                GrFontScaler* scaler) {
+    GrGlyph* glyph = fCache.find(packed);
+    if (NULL == glyph) {
+        glyph = this->generateGlyph(packed, scaler);
+    }
+    return glyph;
+}
+
+#endif
+
diff --git a/gpu/src/GrTextureCache.cpp b/gpu/src/GrTextureCache.cpp
new file mode 100644
index 0000000..3ba3339
--- /dev/null
+++ b/gpu/src/GrTextureCache.cpp
@@ -0,0 +1,297 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrTextureCache.h"
+#include "GrTexture.h"
+
+GrTextureEntry::GrTextureEntry(const GrTextureKey& key, GrTexture* texture)
+        : fKey(key), fTexture(texture) {
+    fLockCount = 0;
+    fPrev = fNext = NULL;
+
+    // we assume ownership of the texture, and will unref it when we die
+    GrAssert(texture);
+}
+
+GrTextureEntry::~GrTextureEntry() {
+    fTexture->unref();
+}
+
+#if GR_DEBUG
+void GrTextureEntry::validate() const {
+    GrAssert(fLockCount >= 0);
+    GrAssert(fTexture);
+    fTexture->validate();
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrTextureCache::GrTextureCache(int maxCount, size_t maxBytes) : 
+        fMaxCount(maxCount),
+        fMaxBytes(maxBytes) {
+    fEntryCount          = 0;
+    fEntryBytes          = 0;
+    fClientDetachedCount = 0;
+    fClientDetachedBytes = 0;
+
+    fHead = fTail = NULL;
+}
+
+GrTextureCache::~GrTextureCache() {
+    GrAutoTextureCacheValidate atcv(this);
+    
+    this->deleteAll(kFreeTexture_DeleteMode);
+}
+
+void GrTextureCache::internalDetach(GrTextureEntry* entry, 
+                                    bool clientDetach) {
+    GrTextureEntry* prev = entry->fPrev;
+    GrTextureEntry* next = entry->fNext;
+
+    if (prev) {
+        prev->fNext = next;
+    } else {
+        fHead = next;
+    }
+    if (next) {
+        next->fPrev = prev;
+    } else {
+        fTail = prev;
+    }
+
+    // update our stats
+    if (clientDetach) {
+        fClientDetachedCount += 1;
+        fClientDetachedBytes += entry->texture()->sizeInBytes();
+    } else {
+        fEntryCount -= 1;
+        fEntryBytes -= entry->texture()->sizeInBytes();
+    }
+}
+
+void GrTextureCache::attachToHead(GrTextureEntry* entry, 
+                                  bool clientReattach) {
+    entry->fPrev = NULL;
+    entry->fNext = fHead;
+    if (fHead) {
+        fHead->fPrev = entry;
+    }
+    fHead = entry;
+    if (NULL == fTail) {
+        fTail = entry;
+    }
+
+    // update our stats
+    if (clientReattach) {
+        fClientDetachedCount -= 1;
+        fClientDetachedBytes -= entry->texture()->sizeInBytes();
+    } else {
+        fEntryCount += 1;
+        fEntryBytes += entry->texture()->sizeInBytes();
+    }
+}
+
+class GrTextureCache::Key {
+    typedef GrTextureEntry T;
+
+    const GrTextureKey& fKey;
+public:
+    Key(const GrTextureKey& key) : fKey(key) {}
+
+    uint32_t getHash() const { return fKey.hashIndex(); }
+    
+    static bool LT(const T& entry, const Key& key) {
+        return entry.key() < key.fKey;
+    }
+    static bool EQ(const T& entry, const Key& key) {
+        return entry.key() == key.fKey;
+    }
+#if GR_DEBUG
+    static uint32_t GetHash(const T& entry) {
+        return entry.key().hashIndex();
+    }
+    static bool LT(const T& a, const T& b) {
+        return a.key() < b.key();
+    }
+    static bool EQ(const T& a, const T& b) {
+        return a.key() == b.key();
+    }
+#endif
+};
+
+GrTextureEntry* GrTextureCache::findAndLock(const GrTextureKey& key) {
+    GrAutoTextureCacheValidate atcv(this);
+
+    GrTextureEntry* entry = fCache.find(key);
+    if (entry) {
+        this->internalDetach(entry, false);
+        this->attachToHead(entry, false);
+        // mark the entry as "busy" so it doesn't get purged
+        entry->lock();
+    }
+    return entry;
+}
+
+GrTextureEntry* GrTextureCache::createAndLock(const GrTextureKey& key,
+                                              GrTexture* texture) {
+    GrAutoTextureCacheValidate atcv(this);
+
+    GrTextureEntry* entry = new GrTextureEntry(key, texture);
+
+    this->attachToHead(entry, false);
+    fCache.insert(key, entry);
+
+#if GR_DUMP_TEXTURE_UPLOAD
+    GrPrintf("--- add texture to cache %p, count=%d bytes= %d %d\n",
+             entry, fEntryCount, texture->sizeInBytes(), fEntryBytes);
+#endif
+
+    // mark the entry as "busy" so it doesn't get purged
+    entry->lock();
+    this->purgeAsNeeded();
+    return entry;
+}
+
+void GrTextureCache::detach(GrTextureEntry* entry) {
+    internalDetach(entry, true);
+    fCache.remove(entry->fKey, entry);
+}
+
+void GrTextureCache::reattachAndUnlock(GrTextureEntry* entry) {
+    attachToHead(entry, true);
+    fCache.insert(entry->key(), entry);
+    unlock(entry);
+}
+
+void GrTextureCache::unlock(GrTextureEntry* entry) {
+    GrAutoTextureCacheValidate atcv(this);
+    
+    GrAssert(entry);
+    GrAssert(entry->isLocked());
+    GrAssert(fCache.find(entry->key()));
+
+    entry->unlock();
+    this->purgeAsNeeded();
+}
+
+void GrTextureCache::purgeAsNeeded() {
+    GrAutoTextureCacheValidate atcv(this);
+    
+    GrTextureEntry* entry = fTail;
+    while (entry) {
+        if (fEntryCount <= fMaxCount && fEntryBytes <= fMaxBytes) {
+            break;
+        }
+
+        GrTextureEntry* prev = entry->fPrev;
+        if (!entry->isLocked()) {
+            // remove from our cache
+            fCache.remove(entry->fKey, entry);
+            
+            // remove from our llist
+            this->internalDetach(entry, false);
+
+#if GR_DUMP_TEXTURE_UPLOAD
+            GrPrintf("--- ~texture from cache %p [%d %d]\n", entry->texture(),
+                     entry->texture()->contentWidth(),
+                     entry->texture()->contentHeight());
+#endif
+            delete entry;
+        }
+        entry = prev;
+    }
+}
+
+void GrTextureCache::deleteAll(DeleteMode mode) {
+    GrAssert(!fClientDetachedCount);
+    GrAssert(!fClientDetachedBytes);
+    
+    GrTextureEntry* entry = fHead;
+    while (entry) {
+        GrAssert(!entry->isLocked());
+
+        GrTextureEntry* next = entry->fNext;
+        if (kAbandonTexture_DeleteMode == mode) {
+            entry->texture()->abandon();
+        }
+        delete entry;
+        entry = next;
+    }
+
+    fCache.removeAll();
+    fHead = fTail = NULL;
+    fEntryCount = 0;
+    fEntryBytes = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_DEBUG
+static int countMatches(const GrTextureEntry* head, const GrTextureEntry* target) {
+    const GrTextureEntry* entry = head;
+    int count = 0;
+    while (entry) {
+        if (target == entry) {
+            count += 1;
+        }
+        entry = entry->next();
+    }
+    return count;
+}
+
+void GrTextureCache::validate() const {
+    GrAssert(!fHead == !fTail);
+    GrAssert(!fEntryCount == !fEntryBytes);
+    GrAssert(!fClientDetachedBytes == !fClientDetachedBytes); 
+    GrAssert(fClientDetachedBytes <= fEntryBytes);
+    GrAssert(fClientDetachedCount <= fEntryCount);
+    GrAssert((fEntryCount - fClientDetachedCount) == fCache.count());
+    GrAssert(fEntryBytes >= 0);
+    GrAssert(fEntryCount >= 0);
+    GrAssert(fClientDetachedCount >= 0);
+    GrAssert(fClientDetachedBytes >= 0);
+    
+    fCache.validate();
+
+    GrTextureEntry* entry = fHead;
+    int count = 0;
+    size_t bytes = 0;
+    while (entry) {
+        entry->validate();
+        GrAssert(fCache.find(entry->key()));
+        count += 1;
+        bytes += entry->texture()->sizeInBytes();
+        entry = entry->fNext;
+    }
+    GrAssert(count == fEntryCount - fClientDetachedCount);
+    GrAssert(bytes == fEntryBytes  - fClientDetachedBytes);
+
+    count = 0;
+    for (entry = fTail; entry; entry = entry->fPrev) {
+        count += 1;
+    }
+    GrAssert(count == fEntryCount - fClientDetachedCount);
+
+    for (int i = 0; i < count; i++) {
+        int matches = countMatches(fHead, fCache.getArray()[i]);
+        GrAssert(1 == matches);
+    }
+}
+#endif
+
+
diff --git a/gpu/src/GrTouchGesture.cpp b/gpu/src/GrTouchGesture.cpp
new file mode 100644
index 0000000..0eaedc7
--- /dev/null
+++ b/gpu/src/GrTouchGesture.cpp
@@ -0,0 +1,243 @@
+#include "GrTouchGesture.h"
+#include "SkMatrix.h"
+#include "SkTime.h"
+
+#include <math.h>
+
+static const SkMSec MAX_DBL_TAP_INTERVAL = 300;
+static const float MAX_DBL_TAP_DISTANCE = 100;
+static const float MAX_JITTER_RADIUS = 2;
+
+// if true, then ignore the touch-move, 'cause its probably just jitter
+static bool close_enough_for_jitter(float x0, float y0, float x1, float y1) {
+    return  sk_float_abs(x0 - x1) <= MAX_JITTER_RADIUS &&
+            sk_float_abs(y0 - y1) <= MAX_JITTER_RADIUS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrTouchGesture::GrTouchGesture() {
+    this->reset();
+}
+
+GrTouchGesture::~GrTouchGesture() {
+}
+
+void GrTouchGesture::reset() {
+    fTouches.reset();
+    fState = kEmpty_State;
+    fLocalM.reset();
+    fGlobalM.reset();
+
+    fLastUpT = SkTime::GetMSecs() - 2*MAX_DBL_TAP_INTERVAL;
+    fLastUpP.set(0, 0);
+}
+
+void GrTouchGesture::flushLocalM() {
+    fGlobalM.postConcat(fLocalM);
+    fLocalM.reset();
+}
+
+const SkMatrix& GrTouchGesture::localM() {
+    if (fFlinger.isActive()) {
+        if (!fFlinger.evaluateMatrix(&fLocalM)) {
+            this->flushLocalM();
+        }
+    }
+    return fLocalM;
+}
+
+void GrTouchGesture::appendNewRec(void* owner, float x, float y) {
+    Rec* rec = fTouches.append();
+    rec->fOwner = owner;
+    rec->fStartX = rec->fPrevX = rec->fLastX = x;
+    rec->fStartY = rec->fPrevY = rec->fLastY = y;
+    rec->fLastT = rec->fPrevT = SkTime::GetMSecs();
+}
+
+void GrTouchGesture::touchBegin(void* owner, float x, float y) {
+//    GrPrintf("--- %d touchBegin %p %g %g\n", fTouches.count(), owner, x, y);
+
+    int index = this->findRec(owner);
+    if (index >= 0) {
+        this->flushLocalM();
+        fTouches.removeShuffle(index);
+        GrPrintf("---- already exists, removing\n");
+    }
+
+    if (fTouches.count() == 2) {
+        return;
+    }
+
+    this->flushLocalM();
+    fFlinger.stop();
+
+    this->appendNewRec(owner, x, y);
+
+    switch (fTouches.count()) {
+        case 1:
+            fState = kTranslate_State;
+            break;
+        case 2:
+            fState = kZoom_State;
+            break;
+        default:
+            break;
+    }
+}
+
+int GrTouchGesture::findRec(void* owner) const {
+    for (int i = 0; i < fTouches.count(); i++) {
+        if (owner == fTouches[i].fOwner) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static float center(float pos0, float pos1) {
+    return (pos0 + pos1) * 0.5f;
+}
+
+static const float MAX_ZOOM_SCALE = 4;
+static const float MIN_ZOOM_SCALE = 0.25f;
+
+float GrTouchGesture::limitTotalZoom(float scale) const {
+    // this query works 'cause we know that we're square-scale w/ no skew/rotation
+    const float curr = fGlobalM[0];
+    
+    if (scale > 1 && curr * scale > MAX_ZOOM_SCALE) {
+        scale = MAX_ZOOM_SCALE / curr;
+    } else if (scale < 1 && curr * scale < MIN_ZOOM_SCALE) {
+        scale = MIN_ZOOM_SCALE / curr;
+    }
+    return scale;
+}
+
+void GrTouchGesture::touchMoved(void* owner, float x, float y) {
+//    GrPrintf("--- %d touchMoved %p %g %g\n", fTouches.count(), owner, x, y);
+
+    GrAssert(kEmpty_State != fState);
+
+    int index = this->findRec(owner);
+    if (index < 0) {
+        // not found, so I guess we should add it...
+        GrPrintf("---- add missing begin\n");
+        this->appendNewRec(owner, x, y);
+        index = fTouches.count() - 1;
+    }
+
+    Rec& rec = fTouches[index];
+
+    // not sure how valuable this is
+    if (fTouches.count() == 2) {
+        if (close_enough_for_jitter(rec.fLastX, rec.fLastY, x, y)) {
+//            GrPrintf("--- drop touchMove, withing jitter tolerance %g %g\n", rec.fLastX - x, rec.fLastY - y);
+            return;
+        }
+    }
+
+    rec.fPrevX = rec.fLastX; rec.fLastX = x;
+    rec.fPrevY = rec.fLastY; rec.fLastY = y;
+    rec.fPrevT = rec.fLastT; rec.fLastT = SkTime::GetMSecs();
+
+    switch (fTouches.count()) {
+        case 1: {
+            float dx = rec.fLastX - rec.fStartX;
+            float dy = rec.fLastY - rec.fStartY;
+            dx = (float)sk_float_round2int(dx);
+            dy = (float)sk_float_round2int(dy);
+            fLocalM.setTranslate(dx, dy);
+        } break;
+        case 2: {
+            GrAssert(kZoom_State == fState);
+            const Rec& rec0 = fTouches[0];
+            const Rec& rec1 = fTouches[1];
+            
+            float scale = this->computePinch(rec0, rec1);
+            scale = this->limitTotalZoom(scale);
+
+            fLocalM.setTranslate(-center(rec0.fStartX, rec1.fStartX),
+                                 -center(rec0.fStartY, rec1.fStartY));
+            fLocalM.postScale(scale, scale);
+            fLocalM.postTranslate(center(rec0.fLastX, rec1.fLastX),
+                                  center(rec0.fLastY, rec1.fLastY));
+        } break;
+        default:
+            break;
+    }
+}
+
+void GrTouchGesture::touchEnd(void* owner) {
+//    GrPrintf("--- %d touchEnd   %p\n", fTouches.count(), owner);
+
+    int index = this->findRec(owner);
+    if (index < 0) {
+        GrPrintf("--- not found\n");
+        return;
+    }
+
+    const Rec& rec = fTouches[index];
+    if (this->handleDblTap(rec.fLastX, rec.fLastY)) {
+        return;
+    }
+
+    // count() reflects the number before we removed the owner
+    switch (fTouches.count()) {
+        case 1: {
+            this->flushLocalM();
+            float dx = rec.fLastX - rec.fPrevX;
+            float dy = rec.fLastY - rec.fPrevY;
+            float dur = (rec.fLastT - rec.fPrevT) * 0.001f;
+            if (dur > 0) {
+                fFlinger.reset(dx / dur, dy / dur);
+            }
+            fState = kEmpty_State;
+        } break;
+        case 2:
+            this->flushLocalM();
+            GrAssert(kZoom_State == fState);
+            fState = kEmpty_State;
+            break;
+        default:
+            GrAssert(kZoom_State == fState);
+            break;
+    }
+
+    fTouches.removeShuffle(index);
+}
+
+float GrTouchGesture::computePinch(const Rec& rec0, const Rec& rec1) {
+    double dx = rec0.fStartX - rec1.fStartX;
+    double dy = rec0.fStartY - rec1.fStartY;
+    double dist0 = sqrt(dx*dx + dy*dy);
+
+    dx = rec0.fLastX - rec1.fLastX;
+    dy = rec0.fLastY - rec1.fLastY;
+    double dist1 = sqrt(dx*dx + dy*dy);
+
+    double scale = dist1 / dist0;
+    return (float)scale;
+}
+
+bool GrTouchGesture::handleDblTap(float x, float y) {
+    bool found = false;
+    SkMSec now = SkTime::GetMSecs();
+    if (now - fLastUpT <= MAX_DBL_TAP_INTERVAL) {
+        if (SkPoint::Length(fLastUpP.fX - x,
+                            fLastUpP.fY - y) <= MAX_DBL_TAP_DISTANCE) {
+            fFlinger.stop();
+            fLocalM.reset();
+            fGlobalM.reset();
+            fTouches.reset();
+            fState = kEmpty_State;
+            found = true;
+        }
+    }
+
+    fLastUpT = now;
+    fLastUpP.set(x, y);
+    return found;
+}
+
+
diff --git a/gpu/src/GrVertexBufferAllocPool.cpp b/gpu/src/GrVertexBufferAllocPool.cpp
new file mode 100644
index 0000000..b6f08c9
--- /dev/null
+++ b/gpu/src/GrVertexBufferAllocPool.cpp
@@ -0,0 +1,220 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrVertexBufferAllocPool.h"
+#include "GrVertexBuffer.h"
+#include "GrGpu.h"
+
+#define GrVertexBufferAllocPool_MIN_BLOCK_SIZE      ((size_t)1 << 10)
+
+GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu,
+                                                 size_t blockSize,
+                                                 int preallocBufferCnt) :
+        fBlocks(GrMax(8, 2*preallocBufferCnt)) {
+    GrAssert(NULL != gpu);
+    fGpu = gpu;
+    fGpu->ref();
+    fBufferPtr = NULL;
+    fMinBlockSize = GrMax(GrVertexBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
+
+    fPreallocBuffersInUse = 0;
+    fFirstPreallocBuffer = 0;
+    for (int i = 0; i < preallocBufferCnt; ++i) {
+        GrVertexBuffer* buffer = gpu->createVertexBuffer(fMinBlockSize, true);
+        if (NULL != buffer) {
+            *fPreallocBuffers.append() = buffer;
+            buffer->ref();
+        }
+    }
+}
+
+GrVertexBufferAllocPool::~GrVertexBufferAllocPool() {
+    fPreallocBuffers.unrefAll();
+    while (!fBlocks.empty()) {
+        destroyBlock();
+    }
+    fGpu->unref();
+}
+
+void GrVertexBufferAllocPool::reset() {
+    while (!fBlocks.empty()) {
+        destroyBlock();
+    }
+    if (fPreallocBuffers.count()) {
+        // must set this after above loop.
+        fFirstPreallocBuffer = (fFirstPreallocBuffer + fPreallocBuffersInUse) %
+                               fPreallocBuffers.count();
+    }
+    GrAssert(0 == fPreallocBuffersInUse);
+}
+
+void GrVertexBufferAllocPool::unlock() {
+    GrAssert((NULL == fBufferPtr) ? (!fBlocks.empty() ||
+                                     !fBlocks.back().fVertexBuffer->isLocked()) :
+                                    (!fBlocks.empty() &&
+                                     fBlocks.back().fVertexBuffer->isLocked()));
+    if (NULL != fBufferPtr) {
+        GrAssert(!fBlocks.empty());
+        GrAssert(fBlocks.back().fVertexBuffer->isLocked());
+        fBufferPtr = NULL;
+        fBlocks.back().fVertexBuffer->unlock();
+    }
+#if GR_DEBUG
+    for (uint32_t i = 0; i < fBlocks.count(); ++i) {
+        GrAssert(!fBlocks[i].fVertexBuffer->isLocked());
+    }
+#endif
+}
+
+void* GrVertexBufferAllocPool::alloc(GrVertexLayout layout,
+                                     uint32_t vertexCount,
+                                     GrVertexBuffer** buffer,
+                                     uint32_t* startVertex) {
+    GrAssert(NULL != buffer);
+    size_t vSize = GrDrawTarget::VertexSize(layout);
+    size_t bytes = vSize * vertexCount;
+
+    if (NULL != fBufferPtr) {
+        GrAssert(!fBlocks.empty());
+        GrAssert(fBlocks.back().fVertexBuffer->isLocked());
+        BufferBlock& back = fBlocks.back();
+        uint32_t usedBytes = back.fVertexBuffer->size() - back.fBytesFree;
+        uint32_t pad = GrUIAlignUpPad(usedBytes, layout);
+        if ((bytes + pad) <= back.fBytesFree) {
+            usedBytes += pad;
+            *startVertex = usedBytes / vSize;
+            *buffer = back.fVertexBuffer;
+            back.fBytesFree -= bytes + pad;
+            return (void*)((intptr_t)fBufferPtr + usedBytes);
+        }
+    }
+
+    if (!createBlock(GrMax(bytes, fMinBlockSize))) {
+        return NULL;
+    }
+    *startVertex = 0;
+    GrAssert(NULL != fBufferPtr);
+    BufferBlock& back = fBlocks.back();
+    *buffer = back.fVertexBuffer;
+    back.fBytesFree -= bytes;
+    return fBufferPtr;
+}
+
+int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const {
+    if (NULL != fBufferPtr) {
+        GrAssert(!fBlocks.empty());
+        const BufferBlock& back = fBlocks.back();
+        GrAssert(back.fVertexBuffer->isLocked());
+        return back.fBytesFree / GrDrawTarget::VertexSize(layout);
+    } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
+        return fMinBlockSize / GrDrawTarget::VertexSize(layout);
+    }
+    return 0;
+}
+
+int GrVertexBufferAllocPool::preallocatedBuffersRemaining() const {
+    return fPreallocBuffers.count() - fPreallocBuffersInUse;
+}
+
+int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const {
+    return fPreallocBuffers.count() ?
+                        (fMinBlockSize / GrDrawTarget::VertexSize(layout)) :
+                        0;
+}
+
+int GrVertexBufferAllocPool::preallocatedBufferCount() const {
+    return fPreallocBuffers.count();
+}
+
+void GrVertexBufferAllocPool::release(size_t bytes) {
+    if (NULL != fBufferPtr) {
+        GrAssert(!fBlocks.empty());
+        BufferBlock& back = fBlocks.back();
+        GrAssert(back.fVertexBuffer->isLocked());
+        size_t bytesUsed = back.fVertexBuffer->size() - back.fBytesFree;
+        if (bytes >= bytesUsed) {
+            destroyBlock();
+            bytes -= bytesUsed;
+        } else {
+            back.fBytesFree += bytes;
+            return;
+        }
+    }
+    GrAssert(NULL == fBufferPtr);
+    GrAssert(fBlocks.empty() ||
+             !fBlocks.back().fVertexBuffer->isLocked());
+    // we don't honor release if it is within an already unlocked VB
+    // Our VB semantics say locking a VB discards its previous content
+    while (!fBlocks.empty() &&
+           bytes >= fBlocks.back().fVertexBuffer->size()) {
+        bytes -= fBlocks.back().fVertexBuffer->size();
+        destroyBlock();
+    }
+}
+
+bool GrVertexBufferAllocPool::createBlock(size_t size) {
+    GrAssert(size >= GrVertexBufferAllocPool_MIN_BLOCK_SIZE);
+
+    BufferBlock& block = fBlocks.push_back();
+
+    if (size == fMinBlockSize &&
+        fPreallocBuffersInUse < fPreallocBuffers.count()) {
+
+        uint32_t nextBuffer = (fPreallocBuffersInUse + fFirstPreallocBuffer) %
+                              fPreallocBuffers.count();
+        block.fVertexBuffer = fPreallocBuffers[nextBuffer];
+        block.fVertexBuffer->ref();
+        ++fPreallocBuffersInUse;
+    } else {
+        block.fVertexBuffer = fGpu->createVertexBuffer(size, true);
+        if (NULL == block.fVertexBuffer) {
+            fBlocks.pop_back();
+            return false;
+        }
+    }
+
+    block.fBytesFree = size;
+    if (NULL != fBufferPtr) {
+        GrAssert(fBlocks.count() > 1);
+        BufferBlock& prev = fBlocks.fromBack(1);
+        GrAssert(prev.fVertexBuffer->isLocked());
+        fBufferPtr = NULL;
+        prev.fVertexBuffer->unlock();
+    }
+    fBufferPtr = block.fVertexBuffer->lock();
+    return true;
+}
+
+void GrVertexBufferAllocPool::destroyBlock() {
+    GrAssert(!fBlocks.empty());
+
+    BufferBlock& block = fBlocks.back();
+    if (fPreallocBuffersInUse > 0) {
+        uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
+                                       fFirstPreallocBuffer +
+                                       (fPreallocBuffers.count() - 1)) %
+                                      fPreallocBuffers.count();
+        if (block.fVertexBuffer == fPreallocBuffers[prevPreallocBuffer]) {
+            --fPreallocBuffersInUse;
+        }
+    }
+    block.fVertexBuffer->unref();
+    fBlocks.pop_back();
+    fBufferPtr = NULL;
+}
+
+
diff --git a/gpu/src/app-android.cpp b/gpu/src/app-android.cpp
new file mode 100644
index 0000000..eea9a4d
--- /dev/null
+++ b/gpu/src/app-android.cpp
@@ -0,0 +1,387 @@
+#include <jni.h>
+#include <sys/time.h>
+#include <time.h>
+#include <android/log.h>
+#include <stdint.h>
+
+#include "GrContext.h"
+#include "SkGpuCanvas.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkTime.h"
+
+#include "GrGLConfig.h"
+
+static GrContext* make_context() {
+    SkDebugf("---- before create\n");
+    GrContext* ctx = GrContext::Create(GrGpu::kOpenGL_Shaders_Engine, NULL);
+//    GrContext* ctx = GrContext::Create(GrGpu::kOpenGL_Fixed_Engine, NULL);
+    SkDebugf("---- after create %p\n", ctx);
+    return ctx;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void gr_run_unittests() {}
+
+#include "FlingState.h"
+#include "GrTouchGesture.h"
+#include "SkView.h"
+
+typedef SkView* (*SkViewFactory)();
+
+// these values must match those in Ganesh.java
+enum TouchState {
+    kUnknown_TouchState,
+    kDown_TouchState,
+    kMoved_TouchState,
+    kUp_TouchState
+};
+
+struct State {
+    State();
+    ~State();
+
+    int countSlides() const { return fFactory.count(); }
+    const char* getSlideTitle(int index) const;
+    void chooseSlide(int index) {
+        SkDebugf("----- index %d\n", index);
+        if (index < fFactory.count()) {
+            this->setView(fFactory[index]());
+        }
+    }
+
+    void setViewport(int w, int h);
+    int getWidth() const { return fViewport.fX; }
+    int getHeight() const { return fViewport.fY; }
+    
+    void handleTouch(void*, TouchState, float x, float y);
+    void applyMatrix(SkCanvas*);
+
+    SkView* getView() const { return fView; }
+
+private:
+    SkView*     fView;
+    SkIPoint    fViewport;
+    
+    GrTouchGesture fGesture;
+
+    SkTDArray<SkViewFactory> fFactory;
+
+    void setView(SkView* view) {
+        SkSafeUnref(fView);
+        fView = view;
+
+        view->setVisibleP(true);
+        view->setClipToBounds(false);
+        view->setSize(SkIntToScalar(fViewport.fX),
+                      SkIntToScalar(fViewport.fY));
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SampleCode.h"
+
+SkViewRegister* SkViewRegister::gHead;
+SkViewRegister::SkViewRegister(SkViewFactory fact) : fFact(fact) {
+    static bool gOnce;
+    if (!gOnce) {
+        gHead = NULL;
+        gOnce = true;
+    }
+    
+    fChain = gHead;
+    gHead = this;
+}
+
+static const char gCharEvtName[] = "SampleCode_Char_Event";
+static const char gKeyEvtName[] = "SampleCode_Key_Event";
+static const char gTitleEvtName[] = "SampleCode_Title_Event";
+static const char gPrefSizeEvtName[] = "SampleCode_PrefSize_Event";
+static const char gFastTextEvtName[] = "SampleCode_FastText_Event";
+
+bool SampleCode::CharQ(const SkEvent& evt, SkUnichar* outUni) {
+    if (evt.isType(gCharEvtName, sizeof(gCharEvtName) - 1)) {
+        if (outUni) {
+            *outUni = evt.getFast32();
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SampleCode::KeyQ(const SkEvent& evt, SkKey* outKey) {
+    if (evt.isType(gKeyEvtName, sizeof(gKeyEvtName) - 1)) {
+        if (outKey) {
+            *outKey = (SkKey)evt.getFast32();
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SampleCode::TitleQ(const SkEvent& evt) {
+    return evt.isType(gTitleEvtName, sizeof(gTitleEvtName) - 1);
+}
+
+void SampleCode::TitleR(SkEvent* evt, const char title[]) {
+    GrAssert(evt && TitleQ(*evt));
+    evt->setString(gTitleEvtName, title);
+}
+
+bool SampleCode::PrefSizeQ(const SkEvent& evt) {
+    return evt.isType(gPrefSizeEvtName, sizeof(gPrefSizeEvtName) - 1);
+}
+
+void SampleCode::PrefSizeR(SkEvent* evt, SkScalar width, SkScalar height) {
+    GrAssert(evt && PrefSizeQ(*evt));
+    SkScalar size[2];
+    size[0] = width;
+    size[1] = height;
+    evt->setScalars(gPrefSizeEvtName, 2, size);
+}
+
+bool SampleCode::FastTextQ(const SkEvent& evt) {
+    return evt.isType(gFastTextEvtName, sizeof(gFastTextEvtName) - 1);
+}
+
+static SkMSec gAnimTime;
+static SkMSec gAnimTimePrev;
+
+SkMSec SampleCode::GetAnimTime() { return gAnimTime; }
+SkMSec SampleCode::GetAnimTimeDelta() { return gAnimTime - gAnimTimePrev; }
+SkScalar SampleCode::GetAnimSecondsDelta() {
+    return SkDoubleToScalar(GetAnimTimeDelta() / 1000.0);
+}
+
+SkScalar SampleCode::GetAnimScalar(SkScalar speed, SkScalar period) {
+    // since gAnimTime can be up to 32 bits, we can't convert it to a float
+    // or we'll lose the low bits. Hence we use doubles for the intermediate
+    // calculations
+    double seconds = (double)gAnimTime / 1000.0;
+    double value = SkScalarToDouble(speed) * seconds;
+    if (period) {
+        value = ::fmod(value, SkScalarToDouble(period));
+    }
+    return SkDoubleToScalar(value);
+}
+
+static void drawIntoCanvas(State* state, SkCanvas* canvas) {
+    gAnimTime = SkTime::GetMSecs();
+    SkView* view = state->getView();
+    view->draw(canvas);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void resetGpuState();
+
+State::State() {
+    fViewport.set(0, 0);
+
+    const SkViewRegister* reg = SkViewRegister::Head();
+    while (reg) {
+        *fFactory.append() = reg->factory();
+        reg = reg->next();
+    }
+
+    SkDebugf("----- %d slides\n", fFactory.count());
+    fView = NULL;
+    this->chooseSlide(0);
+}
+
+State::~State() {
+    SkSafeUnref(fView);
+}
+
+void State::setViewport(int w, int h) {
+    fViewport.set(w, h);
+    if (fView) {
+        fView->setSize(SkIntToScalar(w), SkIntToScalar(h));
+    }
+    resetGpuState();
+}
+
+const char* State::getSlideTitle(int index) const {
+    SkEvent evt(gTitleEvtName);
+    evt.setFast32(index);
+    {
+        SkView* view = fFactory[index]();
+        view->doQuery(&evt);
+        view->unref();
+    }
+    return evt.findString(gTitleEvtName);
+}
+
+void State::handleTouch(void* owner, TouchState state, float x, float y) {
+    switch (state) {
+        case kDown_TouchState:
+            fGesture.touchBegin(owner, x, y);
+            break;
+        case kMoved_TouchState:
+            fGesture.touchMoved(owner, x, y);
+            break;
+        case kUp_TouchState:
+            fGesture.touchEnd(owner);
+            break;
+    }
+}
+
+void State::applyMatrix(SkCanvas* canvas) {
+    const SkMatrix& localM = fGesture.localM();
+    if (localM.getType() & SkMatrix::kScale_Mask) {
+        canvas->setExternalMatrix(&localM);
+    }
+    canvas->concat(localM);
+    canvas->concat(fGesture.globalM());
+}
+
+static State* get_state() {
+    static State* gState;
+    if (NULL == gState) {
+        gState = new State;
+    }
+    return gState;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GrContext* gContext;
+static int gWidth;
+static int gHeight;
+static float gX, gY;
+
+static void resetGpuState() {
+    if (NULL == gContext) {
+        SkDebugf("creating context for first time\n");
+        gContext = make_context();
+    } else {
+        SkDebugf("------ gContext refcnt=%d\n", gContext->refcnt());
+        gContext->abandonAllTextures();
+        gContext->unref();
+        gContext = make_context();
+    }
+}
+
+static void doDraw() {
+    if (NULL == gContext) {
+        gContext = make_context();
+    }
+
+    State* state = get_state();
+    SkBitmap viewport;
+    viewport.setConfig(SkBitmap::kARGB_8888_Config,
+                       state->getWidth(), state->getHeight());
+
+    SkGpuCanvas canvas(gContext);
+
+    canvas.setBitmapDevice(viewport);
+    state->applyMatrix(&canvas);
+
+    drawIntoCanvas(state, &canvas);
+
+    GrGLCheckErr();
+    GrGLClearErr();
+//    gContext->checkError();
+//    gContext->clearError();
+
+    if (true) {
+        static const int FRAME_COUNT = 32;
+        static SkMSec gDuration;
+
+        static SkMSec gNow;
+        static int gFrameCounter;
+        if (++gFrameCounter == FRAME_COUNT) {
+            gFrameCounter = 0;
+            SkMSec now = SkTime::GetMSecs();
+
+            gDuration = now - gNow;
+            gNow = now;
+        }
+
+        int fps = (FRAME_COUNT * 1000) / gDuration;
+        SkString str;
+        str.printf("FPS=%3d MS=%3d", fps, gDuration / FRAME_COUNT);
+        
+        SkGpuCanvas c(gContext);
+        c.setBitmapDevice(viewport);
+
+        SkPaint p;
+        p.setAntiAlias(true);
+        SkRect r = { 0, 0, 110, 16 };
+        p.setColor(SK_ColorWHITE);
+        c.drawRect(r, p);
+        p.setColor(SK_ColorBLACK);
+        c.drawText(str.c_str(), str.size(), 4, 12, p);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern "C" {
+    JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeSurfaceCreated(
+                                                           JNIEnv*, jobject);
+    JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeViewport(JNIEnv*, jobject,
+                                                                             jint w, jint h);
+    JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeDrawFrame(JNIEnv*, jobject);
+    JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeTouch(JNIEnv*, jobject,
+                                        jint id, jint type, jfloat x, jfloat y);
+
+    JNIEXPORT int JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeCountSlides(JNIEnv*, jobject);
+    JNIEXPORT jobject JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeGetSlideTitle(JNIEnv*, jobject, jint index);
+    JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeChooseSlide(JNIEnv*, jobject, jint index);
+}
+
+JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeSurfaceCreated(
+                                                            JNIEnv*, jobject) {
+    SkDebugf("------ nativeSurfaceCreated\n");
+    resetGpuState();
+    SkDebugf("------ end nativeSurfaceCreated\n");
+}
+
+JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeViewport(JNIEnv*, jobject,
+                                                       jint w, jint h) {
+    State* state = get_state();
+    SkDebugf("---- state.setviewport %p %d %d\n", state, w, h);
+    state->setViewport(w, h);
+    SkDebugf("---- end setviewport\n");
+}
+
+JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeDrawFrame(JNIEnv*, jobject) {
+    doDraw();
+}
+
+union IntPtr {
+    jint    fInt;
+    void*   fPtr;
+};
+static void* int2ptr(jint n) {
+    IntPtr data;
+    data.fInt = n;
+    return data.fPtr;
+}
+
+JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeTouch(JNIEnv*, jobject,
+                                      jint id, jint type, jfloat x, jfloat y) {
+    get_state()->handleTouch(int2ptr(id), (TouchState)type, x, y);
+}
+
+////////////
+
+JNIEXPORT int JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeCountSlides(JNIEnv*, jobject) {
+    return get_state()->countSlides();
+}
+
+JNIEXPORT jobject JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeGetSlideTitle(JNIEnv* env, jobject, jint index) {
+    return env->NewStringUTF(get_state()->getSlideTitle(index));
+}
+
+JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeChooseSlide(JNIEnv*, jobject, jint index) {
+    get_state()->chooseSlide(index);
+}
+
+
+
+
+
diff --git a/gpu/src/gr_files.mk b/gpu/src/gr_files.mk
new file mode 100644
index 0000000..f9ca49e
--- /dev/null
+++ b/gpu/src/gr_files.mk
@@ -0,0 +1,23 @@
+SOURCE := \
+    GrAllocPool.cpp \
+    GrAtlas.cpp \
+    GrClip.cpp \
+    GrContext.cpp \
+    GrDrawTarget.cpp \
+    GrGLIndexBuffer.cpp	\
+    GrGLTexture.cpp \
+    GrGLVertexBuffer.cpp \
+    GrGpu.cpp \
+    GrGpuGLShaders.cpp \
+    GrGpuGLFixed.cpp \
+    GrGpuFactory.cpp \
+    GrGpuGL.cpp \
+    GrInOrderDrawBuffer.cpp \
+    GrMatrix.cpp \
+    GrMemory.cpp \
+    GrPath.cpp \
+    GrRectanizer_fifo.cpp \
+    GrTextureCache.cpp \
+    GrTextContext.cpp \
+    GrTextStrike.cpp \
+    GrVertexBufferAllocPool.cpp
diff --git a/gpu/src/gr_hello_world.cpp b/gpu/src/gr_hello_world.cpp
new file mode 100644
index 0000000..5638e19
--- /dev/null
+++ b/gpu/src/gr_hello_world.cpp
@@ -0,0 +1,30 @@
+#include "SkGLCanvas.h"
+#include "SkBitmap.h"
+#include "SkPaint.h"
+#include "SkGpuGLShaders.h"
+
+extern "C" {
+    void gr_hello_world();
+}
+
+void gr_hello_world() {
+    static GrGpu* gGpu;
+    if (NULL == gGpu) {
+        gGpu = new SkGpuGLShaders;
+    }
+
+    SkGLCanvas canvas(gGpu);
+    SkBitmap bm;
+
+    bm.setConfig(SkBitmap::kARGB_8888_Config, WIDTH, HEIGHT);
+    canvas.setBitmapDevice(bm);
+
+    canvas.drawColor(SK_ColorWHITE);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setTextSize(30);
+    canvas.drawText("Hello Kno", 9, 40, 40, paint);
+}
+
+
diff --git a/gpu/src/gr_unittests.cpp b/gpu/src/gr_unittests.cpp
new file mode 100644
index 0000000..a9fd40d
--- /dev/null
+++ b/gpu/src/gr_unittests.cpp
@@ -0,0 +1,143 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrClip.h"
+#include "GrTDArray.h"
+#include "GrTBSearch.h"
+#include "GrMatrix.h"
+
+static void dump(const GrTDArray<int>& array) {
+#if 0
+    for (int i = 0; i < array.count(); i++) {
+        printf(" %d", array[i]);
+    }
+    printf("\n");
+#endif
+}
+
+static void test_tdarray() {
+    GrTDArray<int> array;
+    
+    *array.append() = 0; dump(array);
+    *array.append() = 2; dump(array);
+    *array.append() = 4; dump(array);
+    *array.append() = 6; dump(array);
+    GrAssert(array.count() == 4);
+
+    *array.insert(0) = -1; dump(array);
+    *array.insert(2) = 1; dump(array);
+    *array.insert(4) = 3; dump(array);
+    *array.insert(7) = 7; dump(array);
+    GrAssert(array.count() == 8);
+    array.remove(3); dump(array);
+    array.remove(0); dump(array);
+    array.removeShuffle(4); dump(array);
+    array.removeShuffle(1); dump(array);
+    GrAssert(array.count() == 4);
+}
+
+static bool LT(const int& elem, int value) {
+    return elem < value;
+}
+static bool EQ(const int& elem, int value) {
+    return elem == value;
+}
+
+static void test_bsearch() {
+    const int array[] = {
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99
+    };
+
+    for (size_t n = 0; n < GR_ARRAY_COUNT(array); n++) {
+        for (size_t i = 0; i < n; i++) {
+            int index = GrTBSearch<int, int>(array, n, array[i]);
+            GrAssert(index == i);
+            index = GrTBSearch<int, int>(array, n, -array[i]);
+            GrAssert(index < 0);
+        }
+    }
+}
+
+static void dump(const GrClip& clip, const char message[]) {
+    GrPrintf("--- dump clip %s\n", message);
+    GrClipIter iter(clip);
+    while (!iter.isDone()) {
+        GrIRect r;
+        iter.getRect(&r);
+        GrPrintf("--- [%d %d %d %d]\n", r.fLeft, r.fTop, r.fRight, r.fBottom);
+        iter.next();
+    }
+}
+
+static void test_clip() {
+    GrClip  clip;
+    GrAssert(clip.isEmpty());
+    GrAssert(!clip.isRect());
+    GrAssert(!clip.isComplex());
+    GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0));
+    GrAssert(0 == clip.countRects());
+
+    clip.setRect(GrIRect(10, 10, 10, 10));
+    GrAssert(clip.isEmpty());
+    GrAssert(!clip.isRect());
+    GrAssert(!clip.isComplex());
+    GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0));
+    GrAssert(0 == clip.countRects());
+    dump(clip, "empty");
+
+    clip.setRect(GrIRect(10, 10, 20, 20));
+    GrAssert(!clip.isEmpty());
+    GrAssert(clip.isRect());
+    GrAssert(!clip.isComplex());
+    GrAssert(clip.getBounds().equalsLTRB(10, 10, 20, 20));
+    GrAssert(1 == clip.countRects());
+    GrAssert(clip.getRects()[0] == clip.getBounds());
+    dump(clip, "rect");
+
+    clip.addRect(GrIRect(20, 20, 25, 25));
+    GrAssert(!clip.isEmpty());
+    GrAssert(!clip.isRect());
+    GrAssert(clip.isComplex());
+    GrAssert(clip.getBounds().equalsLTRB(10, 10, 25, 25));
+    GrAssert(2 == clip.countRects());
+    dump(clip, "complex");
+
+    GrClip c1(clip);
+    GrAssert(c1 == clip);
+    GrClip c2;
+    GrAssert(c2 != c1);
+    c2 = clip;
+    GrAssert(c2 == clip);
+
+    clip.setEmpty();
+    GrAssert(clip.isEmpty());
+    GrAssert(!clip.isRect());
+    GrAssert(!clip.isComplex());
+    GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0));
+
+    GrAssert(c1 != clip);
+    GrAssert(c2 != clip);
+}
+
+void gr_run_unittests() {
+    test_tdarray();
+    test_bsearch();
+    test_clip();
+    GrMatrix::UnitTest();
+}
+
+
diff --git a/gpu/src/skia/SkGpuCanvas.cpp b/gpu/src/skia/SkGpuCanvas.cpp
new file mode 100644
index 0000000..19bda4d
--- /dev/null
+++ b/gpu/src/skia/SkGpuCanvas.cpp
@@ -0,0 +1,60 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrContext.h"
+
+#include "SkGpuCanvas.h"
+#include "SkGpuDevice.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGpuCanvas::SkGpuCanvas(GrContext* context) {
+    SkASSERT(context);
+    fContext = context;
+    fContext->ref();
+}
+
+SkGpuCanvas::~SkGpuCanvas() {
+    // call this now, while our override of restore() is in effect
+    this->restoreToCount(1);
+    fContext->flush(false);
+    fContext->unref();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkGpuCanvas::getViewport(SkIPoint* size) const {
+    if (size) {
+        SkDevice* device = this->getDevice();
+        if (device) {
+            size->set(device->width(), device->height());
+        } else {
+            size->set(0, 0);
+        }
+    }
+    return true;
+}
+
+SkDevice* SkGpuCanvas::createDevice(SkBitmap::Config config, int width, int height,
+                                    bool isOpaque, bool isLayer) {
+    SkBitmap bm;
+    bm.setConfig(config, width, height);
+    bm.setIsOpaque(isOpaque);
+    return new SkGpuDevice(this, bm, isLayer);
+}
+
+
diff --git a/gpu/src/skia/SkGpuDevice.cpp b/gpu/src/skia/SkGpuDevice.cpp
new file mode 100644
index 0000000..832fc6e
--- /dev/null
+++ b/gpu/src/skia/SkGpuDevice.cpp
@@ -0,0 +1,1048 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "GrContext.h"
+#include "GrTextContext.h"
+
+#include "SkGpuCanvas.h"
+#include "SkGpuDevice.h"
+#include "SkGrTexturePixelRef.h"
+
+#include "SkDrawProcs.h"
+#include "SkGlyphCache.h"
+
+#define CACHE_LAYER_TEXTURES 1
+
+#if 0
+    extern bool (*gShouldDrawProc)();
+    #define CHECK_SHOULD_DRAW(draw)                             \
+        do {                                                    \
+            if (gShouldDrawProc && !gShouldDrawProc()) return;  \
+            this->prepareRenderTarget(draw);                    \
+        } while (0)
+#else
+    #define CHECK_SHOULD_DRAW(draw) this->prepareRenderTarget(draw)
+#endif
+
+class SkAutoExtMatrix {
+public:
+    SkAutoExtMatrix(const SkMatrix* extMatrix) {
+        if (extMatrix) {
+            SkGr::SkMatrix2GrMatrix(*extMatrix, &fMatrix);
+            fExtMatrix = &fMatrix;
+        } else {
+            fExtMatrix = NULL;
+        }
+    }
+    const GrMatrix* extMatrix() const { return fExtMatrix; }
+
+private:
+    GrMatrix    fMatrix;
+    GrMatrix*   fExtMatrix; // NULL or &fMatrix
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGpuDevice::SkAutoCachedTexture::
+             SkAutoCachedTexture(SkGpuDevice* device,
+                                 const SkBitmap& bitmap,
+                                 const GrSamplerState& sampler,
+                                 GrTexture** texture) {
+    GrAssert(texture);
+    fTex = NULL;
+    *texture = this->set(device, bitmap, sampler);
+}
+
+SkGpuDevice::SkAutoCachedTexture::SkAutoCachedTexture() {
+    fTex = NULL;
+}
+
+GrTexture* SkGpuDevice::SkAutoCachedTexture::set(SkGpuDevice* device,
+                                                 const SkBitmap& bitmap,
+                                                 const GrSamplerState& sampler) {
+    if (fTex) {
+        fDevice->unlockCachedTexture(fTex);
+    }
+    fDevice = device;
+    GrTexture* texture = (GrTexture*)bitmap.getTexture();
+    if (texture) {
+        // return the native texture
+        fTex = NULL;
+        device->context()->setTexture(texture);
+    } else {
+        // look it up in our cache
+        fTex = device->lockCachedTexture(bitmap, sampler, &texture, false);
+    }
+    return texture;
+}
+
+SkGpuDevice::SkAutoCachedTexture::~SkAutoCachedTexture() {
+    if (fTex) {
+        fDevice->unlockCachedTexture(fTex);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool gDoTraceDraw;
+
+struct GrSkDrawProcs : public SkDrawProcs {
+public:
+    GrContext* fContext;
+    GrTextContext* fTextContext;
+    GrFontScaler* fFontScaler;  // cached in the skia glyphcache
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGpuDevice::SkGpuDevice(SkGpuCanvas* canvas, const SkBitmap& bitmap, bool isLayer)
+        : SkDevice(canvas, bitmap, false) {
+
+    fNeedPrepareRenderTarget = false;
+    fDrawProcs = NULL;
+
+    fContext = canvas->context();
+
+    fCache = NULL;
+    fTexture = NULL;
+    fRenderTarget = NULL;
+    fNeedClear = false;
+
+    if (isLayer) {
+        SkBitmap::Config c = bitmap.config();
+        if (c != SkBitmap::kRGB_565_Config) {
+            c = SkBitmap::kARGB_8888_Config;
+        }
+        SkBitmap bm;
+        bm.setConfig(c, this->width(), this->height());
+
+#if CACHE_LAYER_TEXTURES
+
+        fCache = this->lockCachedTexture(bm, GrSamplerState::ClampNoFilter(),
+                       &fTexture, true);
+        if (fCache) {
+            SkASSERT(NULL != fTexture);
+            SkASSERT(fTexture->isRenderTarget());
+        }
+#else
+        const GrGpu::TextureDesc desc = {
+            GrGpu::kRenderTarget_TextureFlag,
+            GrGpu::kNone_AALevel,
+            this->width(),
+            this->height(),
+            SkGr::Bitmap2PixelConfig(bm)
+        };
+
+        fTexture = fContext->createUncachedTexture(desc, NULL, 0);
+#endif
+        if (NULL != fTexture) {
+            fRenderTarget = fTexture->asRenderTarget();
+
+            GrAssert(NULL != fRenderTarget);
+
+            // we defer the actual clear until our gainFocus()
+            fNeedClear = true;
+
+            // wrap the bitmap with a pixelref to expose our texture
+            SkGrTexturePixelRef* pr = new SkGrTexturePixelRef(fTexture);
+            this->setPixelRef(pr, 0)->unref();
+        } else {
+            GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",
+                     this->width(), this->height());
+        }
+    }
+
+    if (NULL == fRenderTarget) {
+        GrAssert(NULL == fCache);
+        GrAssert(NULL == fTexture);
+
+        fRenderTarget = fContext->currentRenderTarget();
+        fRenderTarget->ref();
+        fContext->setDefaultRenderTargetSize(this->width(), this->height());
+    }
+}
+
+SkGpuDevice::~SkGpuDevice() {
+    if (fDrawProcs) {
+        delete fDrawProcs;
+    }
+
+    if (fCache) {
+        GrAssert(NULL != fTexture);
+        GrAssert(fRenderTarget == fTexture->asRenderTarget());
+        // IMPORTANT: reattach the rendertarget/tex back to the cache.
+        fContext->reattachAndUnlockCachedTexture((GrTextureEntry*)fCache);
+    } else if (NULL != fTexture) {
+        GrAssert(!CACHE_LAYER_TEXTURES);
+        GrAssert(fRenderTarget == fTexture->asRenderTarget());
+        fTexture->unref();
+    } else if (NULL != fRenderTarget) {
+        fRenderTarget->unref();
+    }
+}
+
+void SkGpuDevice::bindDeviceToTargetHandle(intptr_t handle) {
+    if (fCache) {
+        GrAssert(NULL != fTexture);
+        GrAssert(fRenderTarget == fTexture->asRenderTarget());
+        // IMPORTANT: reattach the rendertarget/tex back to the cache.
+        fContext->reattachAndUnlockCachedTexture((GrTextureEntry*)fCache);
+    } else if (NULL != fTexture) {
+        GrAssert(!CACHE_LAYER_TEXTURES);
+        fTexture->unref();
+    } else if (NULL != fRenderTarget) {
+        fRenderTarget->unref();
+    }
+
+    fCache  = NULL;
+    fTexture = NULL;
+    fRenderTarget = fContext->createPlatformRenderTarget(handle,
+                                                         this->width(),
+                                                         this->height());
+}
+
+intptr_t SkGpuDevice::getLayerTextureHandle() const {
+    if (fTexture) {
+        return fTexture->getTextureHandle();
+    } else {
+        return 0;
+    }
+}
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::makeRenderTargetCurrent() {
+    fContext->setRenderTarget(fRenderTarget);
+    fContext->flush(true);
+    fNeedPrepareRenderTarget = true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkGpuDevice::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
+    SkIRect bounds;
+    bounds.set(0, 0, this->width(), this->height());
+    if (!bounds.intersect(srcRect)) {
+        return false;
+    }
+
+    const int w = bounds.width();
+    const int h = bounds.height();
+    SkBitmap tmp;
+    // note we explicitly specify our rowBytes to be snug (no gap between rows)
+    tmp.setConfig(SkBitmap::kARGB_8888_Config, w, h, w * 4);
+    if (!tmp.allocPixels()) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(tmp);
+    fContext->setRenderTarget(fRenderTarget);
+    // we aren't setting the clip or matrix, so mark as dirty
+    // we don't need to set them for this call and don't have them anyway
+    fNeedPrepareRenderTarget = true;
+
+    if (!fContext->readPixels(bounds.fLeft, bounds.fTop,
+                              bounds.width(), bounds.height(),
+                              GrTexture::kRGBA_8888_PixelConfig,
+                              tmp.getPixels())) {
+        return false;
+    }
+
+    tmp.swap(*bitmap);
+    return true;
+}
+
+void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y) {
+    SkAutoLockPixels alp(bitmap);
+    if (!bitmap.readyToDraw()) {
+        return;
+    }
+    GrTexture::PixelConfig config = SkGr::BitmapConfig2PixelConfig(bitmap.config(),
+                                                                   bitmap.isOpaque());
+    fContext->setRenderTarget(fRenderTarget);
+    // we aren't setting the clip or matrix, so mark as dirty
+    // we don't need to set them for this call and don't have them anyway
+    fNeedPrepareRenderTarget = true;
+
+    fContext->writePixels(x, y, bitmap.width(), bitmap.height(),
+                          config, bitmap.getPixels(), bitmap.rowBytes());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void convert_matrixclip(GrContext* context, const SkMatrix& matrix,
+                               const SkRegion& clip) {
+    GrMatrix grmat;
+    SkGr::SkMatrix2GrMatrix(matrix, &grmat);
+    context->setViewMatrix(grmat);
+
+    SkGrClipIterator iter;
+    iter.reset(clip);
+    GrClip grc(&iter);
+    if (context->getClip() == grc) {
+    } else {
+        context->setClip(grc);
+    }
+}
+
+// call this ever each draw call, to ensure that the context reflects our state,
+// and not the state from some other canvas/device
+void SkGpuDevice::prepareRenderTarget(const SkDraw& draw) {
+    if (fNeedPrepareRenderTarget ||
+        fContext->currentRenderTarget() != fRenderTarget) {
+
+        fContext->setRenderTarget(fRenderTarget);
+        convert_matrixclip(fContext, *draw.fMatrix, *draw.fClip);
+        fNeedPrepareRenderTarget = false;
+    }
+}
+
+void SkGpuDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip) {
+    this->INHERITED::setMatrixClip(matrix, clip);
+
+    convert_matrixclip(fContext, matrix, clip);
+}
+
+void SkGpuDevice::gainFocus(SkCanvas* canvas, const SkMatrix& matrix,
+                            const SkRegion& clip) {
+    fContext->setRenderTarget(fRenderTarget);
+
+    this->INHERITED::gainFocus(canvas, matrix, clip);
+
+    convert_matrixclip(fContext, matrix, clip);
+
+    if (fNeedClear) {
+        fContext->eraseColor(0x0);
+        fNeedClear = false;
+    }
+}
+
+bool SkGpuDevice::bindDeviceAsTexture(SkPoint* max) {
+    if (NULL != fTexture) {
+        fContext->setTexture(fTexture);
+        if (NULL != max) {
+            max->set(SkFixedToScalar((width() << 16) /
+                                     fTexture->allocWidth()),
+                     SkFixedToScalar((height() << 16) /
+                                     fTexture->allocHeight()));
+        }
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// must be in the same order as SkXfermode::Coeff in SkXfermode.h
+
+SkGpuDevice::AutoPaintShader::AutoPaintShader() {
+    fSuccess = false;
+    fTexture = NULL;
+}
+
+SkGpuDevice::AutoPaintShader::AutoPaintShader(SkGpuDevice* device,
+                                              const SkPaint& paint,
+                                              const SkMatrix& matrix) {
+    fSuccess = false;
+    fTexture = NULL;
+    this->init(device, paint, matrix);
+}
+
+void SkGpuDevice::AutoPaintShader::init(SkGpuDevice* device,
+                                        const SkPaint& paint,
+                                        const SkMatrix& ctm) {
+    fSuccess = true;
+    GrContext* ctx = device->context();
+    sk_gr_set_paint(ctx, paint); // should we pass true for justAlpha if we have a shader/texture?
+
+    SkShader* shader = paint.getShader();
+    if (NULL == shader) {
+        return;
+    }
+
+    if (!shader->setContext(device->accessBitmap(false), paint, ctm)) {
+        fSuccess = false;
+        return;
+    }
+
+    GrSamplerState::SampleMode sampleMode;
+    SkBitmap bitmap;
+    SkMatrix matrix;
+    SkShader::TileMode tileModes[2];
+    SkScalar twoPointParams[3];
+    SkShader::BitmapType bmptype = shader->asABitmap(&bitmap, &matrix,
+                                                     tileModes, twoPointParams);
+
+    switch (bmptype) {
+    case SkShader::kNone_BitmapType:
+        SkDebugf("shader->asABitmap() == kNone_BitmapType");
+        return;
+    case SkShader::kDefault_BitmapType:
+        sampleMode = GrSamplerState::kNormal_SampleMode;
+        break;
+    case SkShader::kRadial_BitmapType:
+        sampleMode = GrSamplerState::kRadial_SampleMode;
+        break;
+    case SkShader::kSweep_BitmapType:
+        sampleMode = GrSamplerState::kSweep_SampleMode;
+        break;
+    case SkShader::kTwoPointRadial_BitmapType:
+        sampleMode = GrSamplerState::kRadial2_SampleMode;
+        break;
+    default:
+        SkASSERT("Unexpected return from asABitmap");
+        return;
+    }
+
+    bitmap.lockPixels();
+    if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
+        return;
+    }
+
+    // see if we've already cached the bitmap from the shader
+    GrSamplerState samplerState(sk_tile_mode_to_grwrap(tileModes[0]),
+                                sk_tile_mode_to_grwrap(tileModes[1]),
+                                sampleMode,
+                                paint.isFilterBitmap());
+
+    if (GrSamplerState::kRadial2_SampleMode == sampleMode) {
+        samplerState.setRadial2Params(twoPointParams[0],
+                                      twoPointParams[1],
+                                      twoPointParams[2] < 0);
+    }
+
+    GrTexture* texture = fCachedTexture.set(device, bitmap, samplerState);
+    if (NULL == texture) {
+        return;
+    }
+
+    // the lock has already called setTexture for us
+    ctx->setSamplerState(samplerState);
+
+    // since our texture coords will be in local space, we wack the texture
+    // matrix to map them back into 0...1 before we load it
+    SkMatrix localM;
+    if (shader->getLocalMatrix(&localM)) {
+        SkMatrix inverse;
+        if (localM.invert(&inverse)) {
+            matrix.preConcat(inverse);
+        }
+    }
+    if (SkShader::kDefault_BitmapType == bmptype) {
+        GrScalar sx = (GR_Scalar1 * texture->contentWidth()) /
+                      (bitmap.width() * texture->allocWidth());
+        GrScalar sy = (GR_Scalar1 * texture->contentHeight()) /
+                      (bitmap.height() * texture->allocHeight());
+        matrix.postScale(sx, sy);
+
+    } else if (SkShader::kRadial_BitmapType == bmptype) {
+        GrScalar s = (GR_Scalar1 * texture->contentWidth()) /
+                     (bitmap.width() * texture->allocWidth());
+        matrix.postScale(s, s);
+    }
+    GrMatrix grmat;
+    SkGr::SkMatrix2GrMatrix(matrix, &grmat);
+    ctx->setTextureMatrix(grmat);
+
+    // since we're going to use a shader/texture, we don't want the color,
+    // just its alpha
+    ctx->setAlpha(paint.getAlpha());
+    // report that we have setup the texture
+    fSuccess = true;
+    fTexture = texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw);
+
+    AutoPaintShader   shader(this, paint, *draw.fMatrix);
+    if (shader.failed()) {
+        return;
+    }
+    fContext->drawFull(shader.useTex());
+}
+
+// must be in SkCanvas::PointMode order
+static const GrGpu::PrimitiveType gPointMode2PrimtiveType[] = {
+    GrGpu::kPoints_PrimitiveType,
+    GrGpu::kLines_PrimitiveType,
+    GrGpu::kLineStrip_PrimitiveType
+};
+
+void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
+                            size_t count, const SkPoint pts[], const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw);
+
+    SkScalar width = paint.getStrokeWidth();
+    if (width < 0) {
+        return;
+    }
+
+    // we only handle hairlines here, else we let the SkDraw call our drawPath()
+    if (width > 0) {
+        draw.drawPoints(mode, count, pts, paint, true);
+        return;
+    }
+
+    AutoPaintShader shader(this, paint, *draw.fMatrix);
+    if (shader.failed()) {
+        return;
+    }
+
+    GrVertexLayout layout = shader.useTex() ?
+                            GrDrawTarget::kPositionAsTexCoord_VertexLayoutBit :
+                            0;
+#if SK_SCALAR_IS_GR_SCALAR
+    fContext->setVertexSourceToArray(pts, layout);
+    fContext->drawNonIndexed(gPointMode2PrimtiveType[mode], 0, count);
+#else
+    GrPoint* v;
+    fContext->reserveAndLockGeometry(layout, count, 0, (void**)&v, NULL);
+    for (int i = 0; i < count; ++i) {
+        v[i].set(SkScalarToGrScalar(pts[i].fX), SkScalarToGrScalar(pts[i].fY));
+    }
+    fContext->drawNonIndexed(gPointMode2PrimtiveType[mode], layout, 0, count);
+    fContext->releaseReservedGeometry();
+#endif
+
+}
+
+void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
+                          const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw);
+
+    bool doStroke = paint.getStyle() == SkPaint::kStroke_Style;
+    SkScalar width = paint.getStrokeWidth();
+
+    /*
+        We have special code for hairline strokes, miter-strokes, and fills.
+        Anything else we just call our path code. (i.e. non-miter thick stroke)
+     */
+    if (doStroke && width > 0 && paint.getStrokeJoin() != SkPaint::kMiter_Join) {
+        SkPath path;
+        path.addRect(rect);
+        this->drawPath(draw, path, paint, NULL, true);
+        return;
+    }
+
+    AutoPaintShader shader(this, paint, *draw.fMatrix);
+    if (shader.failed()) {
+        return;
+    }
+
+    fContext->drawRect(Sk2Gr(rect), shader.useTex(), doStroke ? width : -1);
+}
+
+void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& path,
+                           const SkPaint& paint, const SkMatrix* prePathMatrix,
+                           bool pathIsMutable) {
+    CHECK_SHOULD_DRAW(draw);
+
+    AutoPaintShader shader(this, paint, *draw.fMatrix);
+    if (shader.failed()) {
+        return;
+    }
+
+    const SkPath* pathPtr = &path;
+    SkPath  tmpPath;
+
+    if (prePathMatrix) {
+        if (pathIsMutable) {
+            const_cast<SkPath*>(pathPtr)->transform(*prePathMatrix);
+        } else {
+            path.transform(*prePathMatrix, &tmpPath);
+            pathPtr = &tmpPath;
+        }
+    }
+
+    SkPath               fillPath;
+    GrContext::PathFills fill = GrContext::kHairLine_PathFill;
+
+    if (paint.getFillPath(*pathPtr, &fillPath)) {
+        switch (fillPath.getFillType()) {
+            case SkPath::kWinding_FillType:
+                fill = GrContext::kWinding_PathFill;
+                break;
+            case SkPath::kEvenOdd_FillType:
+                fill = GrContext::kEvenOdd_PathFill;
+                break;
+            case SkPath::kInverseWinding_FillType:
+                fill = GrContext::kInverseWinding_PathFill;
+                break;
+            case SkPath::kInverseEvenOdd_FillType:
+                fill = GrContext::kInverseEvenOdd_PathFill;
+                break;
+            default:
+                SkDebugf("Unsupported path fill type");
+                return;
+        }
+    }
+
+    SkGrPathIter iter(fillPath);
+    fContext->drawPath(&iter, fill, shader.useTex());
+}
+
+/*
+ *  This value must not exceed the GPU's texture dimension limit, but it can
+ *  be smaller, if that helps avoid very large single textures hurting the
+ *  cache.
+ */
+#define MAX_TEXTURE_DIM     512
+
+void SkGpuDevice::drawBitmap(const SkDraw& draw,
+                             const SkBitmap& bitmap,
+                             const SkIRect* srcRectPtr,
+                             const SkMatrix& m,
+                             const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw);
+
+    SkIRect srcRect;
+    if (NULL == srcRectPtr) {
+        srcRect.set(0, 0, bitmap.width(), bitmap.height());
+    } else {
+        srcRect = *srcRectPtr;
+    }
+
+    if (bitmap.getTexture() || (bitmap.width() <= MAX_TEXTURE_DIM &&
+                                bitmap.height() <= MAX_TEXTURE_DIM)) {
+        // take the fast case
+        this->internalDrawBitmap(draw, bitmap, srcRect, m, paint);
+        return;
+    }
+
+    // undo the translate done by SkCanvas
+    int DX = SkMax32(0, srcRect.fLeft);
+    int DY = SkMax32(0, srcRect.fTop);
+    // compute clip bounds in local coordinates
+    SkIRect clipRect;
+    {
+        SkRect r;
+        r.set(draw.fClip->getBounds());
+        SkMatrix matrix, inverse;
+        matrix.setConcat(*draw.fMatrix, m);
+        if (!matrix.invert(&inverse)) {
+            return;
+        }
+        inverse.mapRect(&r);
+        r.roundOut(&clipRect);
+        // apply the canvas' translate to our local clip
+        clipRect.offset(DX, DY);
+    }
+
+    int nx = bitmap.width() / MAX_TEXTURE_DIM;
+    int ny = bitmap.height() / MAX_TEXTURE_DIM;
+    for (int x = 0; x <= nx; x++) {
+        for (int y = 0; y <= ny; y++) {
+            SkIRect tileR;
+            tileR.set(x * MAX_TEXTURE_DIM, y * MAX_TEXTURE_DIM,
+                      (x + 1) * MAX_TEXTURE_DIM, (y + 1) * MAX_TEXTURE_DIM);
+            if (!SkIRect::Intersects(tileR, clipRect)) {
+                continue;
+            }
+
+            SkIRect srcR = tileR;
+            if (!srcR.intersect(srcRect)) {
+                continue;
+            }
+
+            SkBitmap tmpB;
+            if (bitmap.extractSubset(&tmpB, tileR)) {
+                // now offset it to make it "local" to our tmp bitmap
+                srcR.offset(-tileR.fLeft, -tileR.fTop);
+
+                SkMatrix tmpM(m);
+                {
+                    int dx = tileR.fLeft - DX + SkMax32(0, srcR.fLeft);
+                    int dy = tileR.fTop -  DY + SkMax32(0, srcR.fTop);
+                    tmpM.preTranslate(SkIntToScalar(dx), SkIntToScalar(dy));
+                }
+                this->internalDrawBitmap(draw, tmpB, srcR, tmpM, paint);
+            }
+        }
+    }
+}
+
+/*
+ *  This is called by drawBitmap(), which has to handle images that may be too
+ *  large to be represented by a single texture.
+ *
+ *  internalDrawBitmap assumes that the specified bitmap will fit in a texture.
+ */
+void SkGpuDevice::internalDrawBitmap(const SkDraw& draw,
+                                     const SkBitmap& bitmap,
+                                     const SkIRect& srcRect,
+                                     const SkMatrix& m,
+                                     const SkPaint& paint) {
+    SkASSERT(bitmap.width() <= MAX_TEXTURE_DIM &&
+             bitmap.height() <= MAX_TEXTURE_DIM);
+
+    SkAutoLockPixels alp(bitmap);
+    if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
+        return;
+    }
+
+    GrSamplerState sampler(paint.isFilterBitmap()); // defaults to clamp
+    // the lock has already called setTexture for us
+    fContext->setSamplerState(sampler);
+
+    GrTexture* texture;
+    SkAutoCachedTexture act(this, bitmap, sampler, &texture);
+    if (NULL == texture) {
+        return;
+    }
+
+    GrVertexLayout layout = GrDrawTarget::kSeparateTexCoord_VertexLayoutBit;
+
+    GrPoint* vertex;
+    if (!fContext->reserveAndLockGeometry(layout, 4,
+                                          0, (void**)&vertex, NULL)) {
+        return;
+    }
+
+    {
+        GrMatrix grmat;
+        SkGr::SkMatrix2GrMatrix(m, &grmat);
+        vertex[0].setIRectFan(0, 0, srcRect.width(), srcRect.height(),
+                              2*sizeof(GrPoint));
+        grmat.mapPointsWithStride(vertex, 2*sizeof(GrPoint), 4);
+    }
+
+    SkScalar left   = SkFixedToScalar((srcRect.fLeft << 16) /
+                                      texture->allocWidth());
+    SkScalar right  = SkFixedToScalar((srcRect.fRight << 16) /
+                                      texture->allocWidth());
+    SkScalar top    = SkFixedToScalar((srcRect.fTop << 16) /
+                                      texture->allocHeight());
+    SkScalar bottom = SkFixedToScalar((srcRect.fBottom << 16) /
+                                      texture->allocHeight());
+    vertex[1].setRectFan(left, top, right, bottom, 2*sizeof(GrPoint));
+
+    fContext->setTextureMatrix(GrMatrix::I());
+    // now draw the mesh
+    sk_gr_set_paint(fContext, paint, true);
+    fContext->drawNonIndexed(GrGpu::kTriangleFan_PrimitiveType, 0, 4);
+    fContext->releaseReservedGeometry();
+}
+
+static void gl_drawSprite(GrContext* ctx,
+                          int x, int y, int w, int h, const SkPoint& max,
+                          const SkPaint& paint) {
+    GrAutoViewMatrix avm(ctx, GrMatrix::I());
+
+    ctx->setSamplerState(GrSamplerState::ClampNoFilter());
+    ctx->setTextureMatrix(GrMatrix::I());
+
+    GrPoint* vertex;
+    GrVertexLayout layout = GrGpu::kSeparateTexCoord_VertexLayoutBit;
+    if (!ctx->reserveAndLockGeometry(layout, 4, 0, (void**)&vertex, NULL)) {
+        return;
+    }
+
+    vertex[1].setRectFan(0, 0, max.fX, max.fY, 2*sizeof(GrPoint));
+
+    vertex[0].setIRectFan(x, y, x + w, y + h, 2*sizeof(GrPoint));
+
+    sk_gr_set_paint(ctx, paint, true);
+    // should look to use glDrawTexi() has we do for text...
+    ctx->drawNonIndexed(GrGpu::kTriangleFan_PrimitiveType, 0, 4);
+    ctx->releaseReservedGeometry();
+}
+
+void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+                            int left, int top, const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw);
+
+    SkAutoLockPixels alp(bitmap);
+    if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
+        return;
+    }
+
+    SkPoint max;
+    GrTexture* texture;
+    SkAutoCachedTexture act(this, bitmap, GrSamplerState::ClampNoFilter(),
+                            &texture);
+
+    max.set(SkFixedToScalar((texture->contentWidth() << 16) /
+                             texture->allocWidth()),
+            SkFixedToScalar((texture->contentHeight() << 16) /
+                            texture->allocHeight()));
+    gl_drawSprite(fContext, left, top, bitmap.width(), bitmap.height(), max, paint);
+}
+
+void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
+                            int x, int y, const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw);
+
+    SkPoint max;
+    if (((SkGpuDevice*)dev)->bindDeviceAsTexture(&max)) {
+        const SkBitmap& bm = dev->accessBitmap(false);
+        int w = bm.width();
+        int h = bm.height();
+        gl_drawSprite(fContext, x, y, w, h, max, paint);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// must be in SkCanvas::VertexMode order
+static const GrGpu::PrimitiveType gVertexMode2PrimitiveType[] = {
+    GrGpu::kTriangles_PrimitiveType,
+    GrGpu::kTriangleStrip_PrimitiveType,
+    GrGpu::kTriangleFan_PrimitiveType,
+};
+
+void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+                              int vertexCount, const SkPoint vertices[],
+                              const SkPoint texs[], const SkColor colors[],
+                              SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw);
+
+    sk_gr_set_paint(fContext, paint);
+
+    TexCache* cache = NULL;
+
+    bool useTexture = false;
+
+    AutoPaintShader autoShader;
+
+    if (texs) {
+        autoShader.init(this, paint, *draw.fMatrix);
+
+        if (autoShader.failed()) {
+            return;
+        }
+        useTexture = autoShader.useTex();
+    }
+
+    bool releaseVerts = false;
+    GrVertexLayout layout = 0;
+    if (useTexture) {
+        layout |= GrDrawTarget::kSeparateTexCoord_VertexLayoutBit;
+    }
+    if (NULL != colors) {
+        layout |= GrDrawTarget::kColor_VertexLayoutBit;
+    }
+
+    #if SK_SCALAR_IS_GR_SCALAR
+    if (!layout) {
+        fContext->setVertexSourceToArray(vertices, layout);
+    } else
+    #endif
+    {
+        void* verts;
+        releaseVerts = true;
+        if (!fContext->reserveAndLockGeometry(layout, vertexCount, 0,
+                                              &verts, NULL)) {
+            return;
+        }
+        int texOffset, colorOffset;
+        uint32_t stride = GrDrawTarget::VertexSizeAndOffsets(layout,
+                                                             &texOffset,
+                                                             &colorOffset);
+        for (int i = 0; i < vertexCount; ++i) {
+            GrPoint* p = (GrPoint*)((intptr_t)verts + i * stride);
+            p->set(SkScalarToGrScalar(vertices[i].fX),
+                   SkScalarToGrScalar(vertices[i].fY));
+            if (texOffset > 0) {
+                GrPoint* t = (GrPoint*)((intptr_t)p + texOffset);
+                t->set(SkScalarToGrScalar(texs[i].fX),
+                       SkScalarToGrScalar(texs[i].fY));
+            }
+            if (colorOffset > 0) {
+                uint32_t* color = (uint32_t*) ((intptr_t)p + colorOffset);
+                *color = SkGr::SkColor2GrColor(colors[i]);
+            }
+        }
+    }
+    if (indices) {
+        fContext->setIndexSourceToArray(indices);
+        fContext->drawIndexed(gVertexMode2PrimitiveType[vmode], 0, 0,
+                                 vertexCount, indexCount);
+    } else {
+        fContext->drawNonIndexed(gVertexMode2PrimitiveType[vmode],
+                                 0, vertexCount);
+    }
+    if (cache) {
+        this->unlockCachedTexture(cache);
+    }
+    if (releaseVerts) {
+        fContext->releaseReservedGeometry();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void GlyphCacheAuxProc(void* data) {
+    delete (GrFontScaler*)data;
+}
+
+static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {
+    void* auxData;
+    GrFontScaler* scaler = NULL;
+    if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
+        scaler = (GrFontScaler*)auxData;
+    }
+    if (NULL == scaler) {
+        scaler = new SkGrFontScaler(cache);
+        cache->setAuxProc(GlyphCacheAuxProc, scaler);
+    }
+    return scaler;
+}
+
+static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state,
+                             SkFixed fx, SkFixed fy,
+                             const SkGlyph& glyph) {
+    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+
+    GrSkDrawProcs* procs = (GrSkDrawProcs*)state.fDraw->fProcs;
+
+    if (NULL == procs->fFontScaler) {
+        procs->fFontScaler = get_gr_font_scaler(state.fCache);
+    }
+    procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), fx, 0),
+                                         SkIntToFixed(SkFixedFloor(fx)), fy,
+                                         procs->fFontScaler);
+}
+
+SkDrawProcs* SkGpuDevice::initDrawForText(const SkPaint& paint,
+                                          GrTextContext* context) {
+
+    // deferred allocation
+    if (NULL == fDrawProcs) {
+        fDrawProcs = new GrSkDrawProcs;
+        fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;
+        fDrawProcs->fContext = fContext;
+    }
+
+    // init our (and GL's) state
+    fDrawProcs->fTextContext = context;
+    fDrawProcs->fFontScaler = NULL;
+    return fDrawProcs;
+}
+
+void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
+                          size_t byteLength, SkScalar x, SkScalar y,
+                          const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw);
+
+    if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
+        // this guy will just call our drawPath()
+        draw.drawText((const char*)text, byteLength, x, y, paint);
+    } else {
+        SkAutoExtMatrix aem(draw.fExtMatrix);
+        SkDraw myDraw(draw);
+        sk_gr_set_paint(fContext, paint);
+        GrTextContext context(fContext, aem.extMatrix());
+        myDraw.fProcs = this->initDrawForText(paint, &context);
+        this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
+    }
+}
+
+void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
+                             size_t byteLength, const SkScalar pos[],
+                             SkScalar constY, int scalarsPerPos,
+                             const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw);
+
+    if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
+        // this guy will just call our drawPath()
+        draw.drawPosText((const char*)text, byteLength, pos, constY,
+                         scalarsPerPos, paint);
+    } else {
+        SkAutoExtMatrix aem(draw.fExtMatrix);
+        SkDraw myDraw(draw);
+        sk_gr_set_paint(fContext, paint);
+        GrTextContext context(fContext, aem.extMatrix());
+        myDraw.fProcs = this->initDrawForText(paint, &context);
+        this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
+                                     scalarsPerPos, paint);
+    }
+}
+
+void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,
+                                size_t len, const SkPath& path,
+                                const SkMatrix* m, const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw);
+
+    SkASSERT(draw.fDevice == this);
+    draw.drawTextOnPath((const char*)text, len, path, m, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGpuDevice::TexCache* SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
+                                                  const GrSamplerState& sampler,
+                                                  GrTexture** texture,
+                                                  bool forDeviceRenderTarget) {
+    GrContext* ctx = this->context();
+    uint32_t p0, p1;
+    if (forDeviceRenderTarget) {
+        p0 = p1 = -1;
+    } else {
+        p0 = bitmap.getGenerationID();
+        p1 = bitmap.pixelRefOffset();
+    }
+
+    GrTexture* newTexture = NULL;
+    GrTextureKey key(p0, p1, bitmap.width(), bitmap.height());
+    GrTextureEntry* entry = ctx->findAndLockTexture(&key, sampler);
+
+    if (NULL == entry) {
+
+        if (forDeviceRenderTarget) {
+            const GrGpu::TextureDesc desc = {
+                GrGpu::kRenderTarget_TextureFlag,
+                GrGpu::kNone_AALevel,
+                bitmap.width(),
+                bitmap.height(),
+                SkGr::Bitmap2PixelConfig(bitmap)
+            };
+            entry = ctx->createAndLockTexture(&key, sampler, desc, NULL, 0);
+
+        } else {
+            entry = sk_gr_create_bitmap_texture(ctx, &key, sampler, bitmap);
+        }
+        if (NULL == entry) {
+            GrPrintf("---- failed to create texture for cache [%d %d]\n",
+                     bitmap.width(), bitmap.height());
+        }
+    }
+
+    if (NULL != entry) {
+        newTexture = entry->texture();
+        ctx->setTexture(newTexture);
+        if (texture) {
+            *texture = newTexture;
+        }
+        // IMPORTANT: We can't allow another SkGpuDevice to get this
+        // cache entry until this one is destroyed!
+        if (forDeviceRenderTarget) {
+            ctx->detachCachedTexture(entry);
+        }
+    }
+    return (TexCache*)entry;
+}
+
+void SkGpuDevice::unlockCachedTexture(TexCache* cache) {
+    this->context()->unlockTexture((GrTextureEntry*)cache);
+}
+
+
diff --git a/gpu/src/skia/SkGpuDevice.h b/gpu/src/skia/SkGpuDevice.h
new file mode 100644
index 0000000..e42e997
--- /dev/null
+++ b/gpu/src/skia/SkGpuDevice.h
@@ -0,0 +1,176 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef SkGpuDevice_DEFINED
+#define SkGpuDevice_DEFINED
+
+#include "SkGr.h"
+#include "SkDevice.h"
+#include "SkRegion.h"
+
+struct SkDrawProcs;
+class SkGpuCanvas;
+struct GrSkDrawProcs;
+class GrTextContext;
+
+/**
+ *  Subclass of SkDevice, which directs all drawing to the GrGpu owned by the
+ *  canvas.
+ */
+class SkGpuDevice : public SkDevice {
+public:
+    SkGpuDevice(SkGpuCanvas*, const SkBitmap& bitmap, bool isLayer);
+    virtual ~SkGpuDevice();
+ 
+    GrContext* context() const { return fContext; }
+
+    /**
+     *  If this device was built for rendering as a layer (i.e. offscreen),
+     *  then this will return the platform-specific handle to that GPU resource.
+     *  For example, in OpenGL, this will return the FBO's texture ID.
+     *  If this device was not built for rendering as a layer, then 0
+     *  is returned.
+     */
+    intptr_t getLayerTextureHandle() const;
+    
+    /**
+     * Attaches the device to a rendering surface. This device will then render
+     * to the surface.
+     * For example, in OpenGL, the device will interpret handle as an FBO ID
+     * and drawing to the device would direct GL to the FBO.
+     */
+    void bindDeviceToTargetHandle(intptr_t handle);
+
+    // call to set the clip to the specified rect
+    void scissor(const SkIRect&);
+
+    /**
+     *  Override from SkGpuDevice, so we can set our FBO to be the render target
+     *  The canvas parameter must be a SkGpuCanvas
+     */
+    virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&);
+
+    virtual SkGpuTexture* accessTexture() { return (SkGpuTexture*)fTexture; }
+
+    // overrides from SkDevice
+
+    virtual bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap);
+    virtual void writePixels(const SkBitmap& bitmap, int x, int y);
+    
+    virtual void setMatrixClip(const SkMatrix& matrix, const SkRegion& clip);
+
+    virtual void drawPaint(const SkDraw&, const SkPaint& paint);
+    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
+                            const SkPoint[], const SkPaint& paint);
+    virtual void drawRect(const SkDraw&, const SkRect& r,
+                          const SkPaint& paint);
+    virtual void drawPath(const SkDraw&, const SkPath& path,
+                          const SkPaint& paint, const SkMatrix* prePathMatrix,
+                          bool pathIsMutable);
+    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+                            const SkIRect* srcRectOrNull,
+                            const SkMatrix& matrix, const SkPaint& paint);
+    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+                            int x, int y, const SkPaint& paint);
+    virtual void drawText(const SkDraw&, const void* text, size_t len,
+                          SkScalar x, SkScalar y, const SkPaint& paint);
+    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+                             const SkScalar pos[], SkScalar constY,
+                             int scalarsPerPos, const SkPaint& paint);
+    virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint);
+    virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount,
+                              const SkPoint verts[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint);
+    virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+                            const SkPaint&);
+    
+    virtual void flush() { fContext->flush(false); }
+    
+    /** 
+     * Make's this device's rendertarget current in the underlying 3D API.
+     * Also implicitly flushes.
+     */
+    virtual void makeRenderTargetCurrent();
+
+protected:
+    class TexCache;
+    TexCache* lockCachedTexture(const SkBitmap& bitmap,
+                                const GrSamplerState& sampler,
+                                GrTexture** texture,
+                                bool forDeviceRenderTarget = false);
+    void unlockCachedTexture(TexCache*);
+
+    class SkAutoCachedTexture {
+    public:
+        SkAutoCachedTexture();
+        SkAutoCachedTexture(SkGpuDevice* device,
+                            const SkBitmap& bitmap,
+                            const GrSamplerState& sampler,
+                            GrTexture** texture);
+        ~SkAutoCachedTexture();
+
+        GrTexture* set(SkGpuDevice*, const SkBitmap&, const GrSamplerState&);
+
+    private:
+        SkGpuDevice*    fDevice;
+        TexCache*       fTex;
+    };
+    friend class SkAutoTexCache;
+
+private:
+    GrContext*      fContext;
+    GrSkDrawProcs*  fDrawProcs;
+
+    // state for our offscreen render-target
+    TexCache*       fCache;
+    GrTexture*      fTexture;
+    GrRenderTarget* fRenderTarget;
+    bool            fNeedClear;
+    bool            fNeedPrepareRenderTarget;
+    
+    SkDrawProcs* initDrawForText(const SkPaint&, GrTextContext*);
+    bool bindDeviceAsTexture(SkPoint* max);
+
+    void prepareRenderTarget(const SkDraw&);
+    void internalDrawBitmap(const SkDraw&, const SkBitmap&,
+                            const SkIRect&, const SkMatrix&, const SkPaint&);
+        
+    class AutoPaintShader {
+    public:
+        AutoPaintShader();
+        AutoPaintShader(SkGpuDevice*, const SkPaint& paint, const SkMatrix&);
+
+        void init(SkGpuDevice*, const SkPaint& paint, const SkMatrix&);
+
+        bool failed() const { return !fSuccess; }
+        bool useTex() const { return fTexture != 0; }
+    private:
+        GrTexture*              fTexture;
+        SkAutoCachedTexture     fCachedTexture;
+        bool                    fSuccess;
+    };
+    friend class AutoPaintShader;
+
+    typedef SkDevice INHERITED;
+};
+
+#endif
+
diff --git a/gpu/src/skia/SkGr.cpp b/gpu/src/skia/SkGr.cpp
new file mode 100644
index 0000000..8849db7
--- /dev/null
+++ b/gpu/src/skia/SkGr.cpp
@@ -0,0 +1,206 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "SkGr.h"
+
+/*  Fill out buffer with the compressed format Ganesh expects from a colortable
+ based bitmap. [palette (colortable) + indices].
+ 
+ At the moment Ganesh only supports 8bit version. If Ganesh allowed we others 
+ we could detect that the colortable.count is <= 16, and then repack the
+ indices as nibbles to save RAM, but it would take more time (i.e. a lot
+ slower than memcpy), so skipping that for now.
+ 
+ Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big
+ as the colortable.count says it is.
+ */
+static void build_compressed_data(void* buffer, const SkBitmap& bitmap) {
+    SkASSERT(SkBitmap::kIndex8_Config == bitmap.config());
+  
+    SkAutoLockPixels apl(bitmap);
+    if (!bitmap.readyToDraw()) {
+        SkASSERT(!"bitmap not ready to draw!");
+        return;
+    }
+
+    SkColorTable* ctable = bitmap.getColorTable();
+    char* dst = (char*)buffer;
+    
+    memcpy(dst, ctable->lockColors(), ctable->count() * sizeof(SkPMColor));
+    ctable->unlockColors(false);
+    
+    // always skip a full 256 number of entries, even if we memcpy'd fewer
+    dst += GrGpu::kColorTableSize;
+
+    if (bitmap.width() == bitmap.rowBytes()) {
+        memcpy(dst, bitmap.getPixels(), bitmap.getSize());
+    } else {
+        // need to trim off the extra bytes per row
+        size_t width = bitmap.width();
+        size_t rowBytes = bitmap.rowBytes();
+        const char* src = (const char*)bitmap.getPixels();
+        for (int y = 0; y < bitmap.height(); y++) {
+            memcpy(dst, src, width);
+            src += rowBytes;
+            dst += width;
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrTextureEntry* sk_gr_create_bitmap_texture(GrContext* ctx, 
+                                            GrTextureKey* key,
+                                            const GrSamplerState& sampler,
+                                            const SkBitmap& origBitmap) {
+    SkAutoLockPixels alp(origBitmap);
+    if (!origBitmap.readyToDraw()) {
+        return NULL;
+    }
+
+    SkBitmap tmpBitmap;
+
+    const SkBitmap* bitmap = &origBitmap;
+    
+    GrGpu::TextureDesc desc = {
+        0,
+        GrGpu::kNone_AALevel,
+        bitmap->width(),
+        bitmap->height(),
+        SkGr::Bitmap2PixelConfig(*bitmap)
+    };
+    
+    if (SkBitmap::kIndex8_Config == bitmap->config()) {
+        // build_compressed_data doesn't do npot->pot expansion
+        // and paletted textures can't be sub-updated
+        if (ctx->supportsIndex8PixelConfig(sampler,
+                                           bitmap->width(), bitmap->height())) {
+            size_t imagesize = bitmap->width() * bitmap->height() + 
+                                GrGpu::kColorTableSize;
+            SkAutoMalloc storage(imagesize);
+        
+            build_compressed_data(storage.get(), origBitmap);
+
+            // our compressed data will be trimmed, so pass width() for its
+            // "rowBytes", since they are the same now.
+            return ctx->createAndLockTexture(key, sampler, desc, storage.get(),
+                                             bitmap->width());
+
+        } else {
+            origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config);
+            // now bitmap points to our temp, which has been promoted to 32bits
+            bitmap = &tmpBitmap;
+        }
+    } 
+
+    desc.fFormat = SkGr::Bitmap2PixelConfig(*bitmap);
+    return ctx->createAndLockTexture(key, sampler, desc, bitmap->getPixels(),
+                                     bitmap->rowBytes());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+  
+void sk_gr_set_paint(GrContext* ctx, const SkPaint& paint, bool justAlpha) {
+    ctx->setDither(paint.isDither());
+    ctx->setAntiAlias(paint.isAntiAlias());
+
+    if (justAlpha) {
+        ctx->setAlpha(paint.getAlpha());
+    } else {
+        ctx->setColor(SkGr::SkColor2GrColor(paint.getColor()));
+    }
+    
+    SkXfermode::Coeff sm = SkXfermode::kOne_Coeff;
+    SkXfermode::Coeff dm = SkXfermode::kISA_Coeff;
+    
+    SkXfermode* mode = paint.getXfermode();
+    if (mode) {
+        mode->asCoeff(&sm, &dm);
+    }   
+    ctx->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SkGrPathIter::Command SkGrPathIter::next(GrPoint pts[]) {
+    GrAssert(NULL != pts);
+#if SK_SCALAR_IS_GR_SCALAR
+    return sk_path_verb_to_gr_path_command(fIter.next((SkPoint*)pts));
+#else
+    Command cmd = sk_path_verb_to_gr_path_command(fIter.next(fPoints));
+    int n = NumCommandPoints(cmd);
+    for (int i = 0; i < n; ++i) {
+        pts[i].fX = SkScalarToGrScalar(fPoints[i].fX);
+        pts[i].fY = SkScalarToGrScalar(fPoints[i].fY);
+    }
+    return cmd;
+#endif
+}
+
+SkGrPathIter::Command SkGrPathIter::next() {
+    return sk_path_verb_to_gr_path_command(fIter.next(NULL));
+}
+
+void SkGrPathIter::rewind() {
+    fIter.setPath(fPath, false);
+}
+
+GrPathIter::ConvexHint SkGrPathIter::hint() const {
+    return fPath.isConvex() ? GrPathIter::kConvex_ConvexHint : 
+                              GrPathIter::kNone_ConvexHint;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGrClipIterator::computeBounds(GrIRect* bounds) {
+    const SkRegion* rgn = fIter.rgn();
+    if (rgn) {
+        SkGr::SetIRect(bounds, rgn->getBounds());
+    } else {
+        bounds->setEmpty();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrTexture::PixelConfig SkGr::BitmapConfig2PixelConfig(SkBitmap::Config config,
+                                                    bool isOpaque) {
+    switch (config) {
+        case SkBitmap::kA8_Config:
+            return GrTexture::kAlpha_8_PixelConfig;
+        case SkBitmap::kIndex8_Config:
+            return GrTexture::kIndex_8_PixelConfig;
+        case SkBitmap::kRGB_565_Config:
+            return GrTexture::kRGB_565_PixelConfig;
+        case SkBitmap::kARGB_4444_Config:
+            return GrTexture::kRGBA_4444_PixelConfig;
+        case SkBitmap::kARGB_8888_Config:
+            if (isOpaque) {
+                return GrTexture::kRGBX_8888_PixelConfig;
+            } else {
+                return GrTexture::kRGBA_8888_PixelConfig;
+            }
+        default:
+            return GrTexture::kUnknown_PixelConfig;
+    }
+}
+
+void SkGr::AbandonAllTextures(GrContext* ctx) {
+    ctx->abandonAllTextures();
+}
+
+
diff --git a/gpu/src/skia/SkGrFontScaler.cpp b/gpu/src/skia/SkGrFontScaler.cpp
new file mode 100644
index 0000000..5c88717
--- /dev/null
+++ b/gpu/src/skia/SkGrFontScaler.cpp
@@ -0,0 +1,142 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "SkGr.h"
+#include "SkDescriptor.h"
+#include "SkGlyphCache.h"
+
+class SkGrDescKey : public GrKey {
+public:
+    explicit SkGrDescKey(const SkDescriptor& desc);
+    virtual ~SkGrDescKey();
+    
+protected:
+    // overrides
+    virtual bool lt(const GrKey& rh) const;
+    virtual bool eq(const GrKey& rh) const;
+    
+private:
+    SkDescriptor* fDesc;
+    enum {
+        kMaxStorageInts = 16
+    };
+    uint32_t fStorage[kMaxStorageInts];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGrDescKey::SkGrDescKey(const SkDescriptor& desc) : GrKey(desc.getChecksum()) {
+    size_t size = desc.getLength();
+    if (size <= sizeof(fStorage)) {
+        fDesc = (SkDescriptor*)fStorage;
+    } else {
+        fDesc = SkDescriptor::Alloc(size);
+    }
+    memcpy(fDesc, &desc, size);
+}
+
+SkGrDescKey::~SkGrDescKey() {
+    if (fDesc != (SkDescriptor*)fStorage) {
+        SkDescriptor::Free(fDesc);
+    }
+}
+
+bool SkGrDescKey::lt(const GrKey& rh) const {
+    const SkDescriptor* srcDesc = ((const SkGrDescKey*)&rh)->fDesc;
+    size_t lenLH = fDesc->getLength();
+    size_t lenRH = srcDesc->getLength();
+    int cmp = memcmp(fDesc, srcDesc, SkMin32(lenLH, lenRH));
+    if (0 == cmp) {
+        return lenLH < lenRH;
+    } else {
+        return cmp < 0;
+    }
+}
+
+bool SkGrDescKey::eq(const GrKey& rh) const {
+    const SkDescriptor* srcDesc = ((const SkGrDescKey*)&rh)->fDesc;
+    return fDesc->equals(*srcDesc);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGrFontScaler::SkGrFontScaler(SkGlyphCache* strike) {
+    fStrike = strike;
+    fKey = NULL;
+}
+
+SkGrFontScaler::~SkGrFontScaler() {
+    GrSafeUnref(fKey);
+}
+
+const GrKey* SkGrFontScaler::getKey() {
+    if (NULL == fKey) {
+        fKey = new SkGrDescKey(fStrike->getDescriptor());
+    }
+    return fKey;
+}
+
+bool SkGrFontScaler::getPackedGlyphBounds(GrGlyph::PackedID packed,
+                                          GrIRect* bounds) {
+    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed),
+                                              GrGlyph::UnpackFixedX(packed),
+                                              GrGlyph::UnpackFixedY(packed));
+    bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight);
+    return true;
+    
+}
+
+bool SkGrFontScaler::getPackedGlyphImage(GrGlyph::PackedID packed,
+                                         int width, int height,
+                                         int dstRB, void* dst) {
+    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed),
+                                              GrGlyph::UnpackFixedX(packed),
+                                              GrGlyph::UnpackFixedY(packed));
+    GrAssert(glyph.fWidth == width);
+    GrAssert(glyph.fHeight == height);
+    const void* src = fStrike->findImage(glyph);
+    if (NULL == src) {
+        return false;
+    }
+
+    int srcRB = glyph.rowBytes();
+    if (srcRB == dstRB) {
+        memcpy(dst, src, dstRB * height);
+    } else {
+        for (int y = 0; y < height; y++) {
+            memcpy(dst, src, width);
+            src = (const char*)src + srcRB;
+            dst = (char*)dst + dstRB;
+        }
+    }
+    return true;
+}
+
+bool SkGrFontScaler::getGlyphPath(uint16_t glyphID, GrPath* path) {
+    
+    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(glyphID);
+    const SkPath* skPath = fStrike->findPath(glyph);
+    if (skPath) {
+        SkGrPathIter iter(*skPath);
+        path->resetFromIter(&iter);
+        return true;
+    }
+    return false;
+}
+
+
+
diff --git a/gpu/src/skia/SkGrTexturePixelRef.cpp b/gpu/src/skia/SkGrTexturePixelRef.cpp
new file mode 100644
index 0000000..da9ac1a
--- /dev/null
+++ b/gpu/src/skia/SkGrTexturePixelRef.cpp
@@ -0,0 +1,30 @@
+/*
+    Copyright 2010 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "SkGrTexturePixelRef.h"
+#include "GrTexture.h"
+
+SkGrTexturePixelRef::SkGrTexturePixelRef(GrTexture* tex) {
+    fTexture = tex;
+    GrSafeRef(tex);
+}
+
+SkGrTexturePixelRef::~SkGrTexturePixelRef() {
+    GrSafeUnref(fTexture);
+}
+
+
diff --git a/gpu/src/skia/SkUIView.mm b/gpu/src/skia/SkUIView.mm
new file mode 100644
index 0000000..667a474
--- /dev/null
+++ b/gpu/src/skia/SkUIView.mm
@@ -0,0 +1,858 @@
+#import "SkUIView.h"
+#include <QuartzCore/QuartzCore.h>
+
+#include "SkGpuCanvas.h"
+#include "SkCGUtils.h"
+#include "GrContext.h"
+
+#define SKWIND_CONFIG       SkBitmap::kRGB_565_Config
+//#define SKWIND_CONFIG       SkBitmap::kARGB_8888_Config
+#define SKGL_CONFIG         kEAGLColorFormatRGB565
+//#define SKGL_CONFIG         kEAGLColorFormatRGBA8
+
+#define SHOW_FPS
+#define FORCE_REDRAW
+//#define DUMP_FPS_TO_PRINTF
+
+//#define USE_ACCEL_TO_ROTATE
+
+//#define SHOULD_COUNTER_INIT 334
+static int gShouldCounter;
+static bool should_draw() {
+    if (--gShouldCounter == 0) {
+   //     printf("\n");
+    }
+    return true;
+    return gShouldCounter >= 0;
+}
+#ifdef SHOULD_COUNTER_INIT
+    bool (*gShouldDrawProc)() = should_draw;
+#else
+    bool (*gShouldDrawProc)() = NULL;
+#endif
+
+//#define USE_GL_1
+#define USE_GL_2
+
+#if defined(USE_GL_1) || defined(USE_GL_2)
+    #define USE_GL
+#endif
+
+@implementation SkUIView
+
+
+@synthesize fWind;
+@synthesize fTitleLabel;
+@synthesize fBackend;
+@synthesize fComplexClip;
+@synthesize fUseWarp;
+
+#include "SkWindow.h"
+#include "SkEvent.h"
+
+static float gScreenScale = 1;
+
+extern SkOSWindow* create_sk_window(void* hwnd);
+
+#define kREDRAW_UIVIEW_GL "sk_redraw_uiview_gl_iOS"
+
+#define TITLE_HEIGHT  44
+
+static const float SCALE_FOR_ZOOM_LENS = 4.0;
+#define Y_OFFSET_FOR_ZOOM_LENS           200
+#define SIZE_FOR_ZOOM_LENS               250
+
+static const float MAX_ZOOM_SCALE = 4.0;
+static const float MIN_ZOOM_SCALE = 2.0 / MAX_ZOOM_SCALE;
+
+extern bool gDoTraceDraw;
+#define DO_TRACE_DRAW_MAX   100
+
+#ifdef SHOW_FPS
+struct FPSState {
+    static const int FRAME_COUNT = 60;
+
+    CFTimeInterval fNow0, fNow1;
+    CFTimeInterval fTime0, fTime1, fTotalTime;
+    int fFrameCounter;
+    int fDrawCounter;
+
+    FPSState() {
+        fTime0 = fTime1 = fTotalTime = 0;
+        fFrameCounter = 0;
+    }
+
+    void startDraw() {
+        fNow0 = CACurrentMediaTime();
+        
+        if (0 == fDrawCounter && false) {
+            gDoTraceDraw = true;
+            SkDebugf("\n");
+        }
+    }
+    
+    void endDraw() {
+        fNow1 = CACurrentMediaTime();
+
+        if (0 == fDrawCounter) {
+            gDoTraceDraw = true;
+        }
+        if (DO_TRACE_DRAW_MAX == ++fDrawCounter) {
+            fDrawCounter = 0;
+        }
+    }
+        
+    void flush(SkOSWindow* wind) {
+        CFTimeInterval now2 = CACurrentMediaTime();
+
+        fTime0 += fNow1 - fNow0;
+        fTime1 += now2 - fNow1;
+        
+        if (++fFrameCounter == FRAME_COUNT) {
+            CFTimeInterval totalNow = CACurrentMediaTime();
+            fTotalTime = totalNow - fTotalTime;
+            
+            SkMSec ms0 = (int)(1000 * fTime0 / FRAME_COUNT);
+            SkMSec msTotal = (int)(1000 * fTotalTime / FRAME_COUNT);
+            
+            SkString str;
+            str.printf("ms: %d [%d], fps: %3.1f", msTotal, ms0,
+                       FRAME_COUNT / fTotalTime);
+#ifdef DUMP_FPS_TO_PRINTF
+            SkDebugf("%s\n", str.c_str());
+#else
+            wind->setTitle(str.c_str());
+#endif
+
+            fTotalTime = totalNow;
+            fTime0 = fTime1 = 0;
+            fFrameCounter = 0;
+        }
+    }
+};
+
+static FPSState gFPS;
+
+    #define FPS_StartDraw() gFPS.startDraw()
+    #define FPS_EndDraw()   gFPS.endDraw()
+    #define FPS_Flush(wind)     gFPS.flush(wind)
+#else
+    #define FPS_StartDraw()
+    #define FPS_EndDraw()
+    #define FPS_Flush(wind)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef USE_GL
++ (Class) layerClass
+{
+	return [CAEAGLLayer class];
+}
+#endif
+
+- (id)initWithMyDefaults {
+    fBackend = kGL_Backend;
+    fUseWarp = false;
+    fRedrawRequestPending = false;
+    fWind = create_sk_window(self);
+    fWind->setConfig(SKWIND_CONFIG);
+    fMatrix.reset();
+    fLocalMatrix.reset();
+    fNeedGestureEnded = false;
+    fNeedFirstPinch = true;
+    fZoomAround = false;
+    fComplexClip = false;
+
+    [self initGestures];
+
+#ifdef USE_GL
+    CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
+    eaglLayer.opaque = TRUE;
+    eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
+                                        [NSNumber numberWithBool:NO],
+                                        kEAGLDrawablePropertyRetainedBacking,
+                                        SKGL_CONFIG,
+                                        kEAGLDrawablePropertyColorFormat,
+                                        nil];
+
+#ifdef USE_GL_1
+    fGL.fContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
+#else
+    fGL.fContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+#endif
+
+    if (!fGL.fContext || ![EAGLContext setCurrentContext:fGL.fContext])
+    {
+        [self release];
+        return nil;
+    }
+    
+    // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer
+    glGenFramebuffersOES(1, &fGL.fFramebuffer);
+    glBindFramebufferOES(GL_FRAMEBUFFER_OES, fGL.fFramebuffer);
+
+    glGenRenderbuffersOES(1, &fGL.fRenderbuffer);
+    glGenRenderbuffersOES(1, &fGL.fStencilbuffer);
+
+    glBindRenderbufferOES(GL_RENDERBUFFER_OES, fGL.fRenderbuffer);
+    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, fGL.fRenderbuffer);
+
+    glBindRenderbufferOES(GL_RENDERBUFFER_OES, fGL.fStencilbuffer);
+    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_STENCIL_ATTACHMENT_OES, GL_RENDERBUFFER_OES, fGL.fStencilbuffer);
+#endif
+
+#ifdef USE_ACCEL_TO_ROTATE
+    fRotateMatrix.reset();
+    [UIAccelerometer sharedAccelerometer].delegate = self;
+    [UIAccelerometer sharedAccelerometer].updateInterval = 1 / 30.0;
+#endif
+    return self;
+}
+
+- (id)initWithCoder:(NSCoder*)coder {
+    if ((self = [super initWithCoder:coder])) {
+        self = [self initWithMyDefaults];
+    }
+    return self;
+}
+
+- (id)initWithFrame:(CGRect)frame {
+    if (self = [super initWithFrame:frame]) {
+        self = [self initWithMyDefaults];
+    }
+    return self;
+}
+
+#include "SkImageDecoder.h"
+#include "SkStream_NSData.h"
+
+static void zoom_around(SkCanvas* canvas, float cx, float cy, float zoom) {
+    float clipW = SIZE_FOR_ZOOM_LENS;
+    float clipH = SIZE_FOR_ZOOM_LENS;
+
+    SkRect r;
+    r.set(0, 0, clipW, clipH);
+    r.offset(cx - clipW/2, cy - clipH/2);
+
+    SkPaint paint;
+    paint.setColor(0xFF66AAEE);
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(10);
+
+    // draw our "frame" around the zoom lens
+    canvas->drawRect(r, paint);
+
+    // now clip and scale the lens
+    canvas->clipRect(r);
+    canvas->translate(cx, cy);
+    canvas->scale(zoom, zoom);
+    canvas->translate(-cx, -cy);
+}
+
+- (void)drawWithCanvas:(SkCanvas*)canvas {
+    if (fComplexClip) {
+        canvas->drawColor(SK_ColorBLUE);
+
+        SkPath path;
+        static const SkRect r[] = {
+            { 50, 50, 250, 250 },
+            { 150, 150, 500, 600 }
+        };
+        for (size_t i = 0; i < GR_ARRAY_COUNT(r); i++) {
+            path.addRect(r[i]);
+        }
+        canvas->clipPath(path);
+    }
+    
+    // This is to consolidate multiple inval requests
+    fRedrawRequestPending = false;
+
+    if (fFlingState.isActive()) {
+        if (!fFlingState.evaluateMatrix(&fLocalMatrix)) {
+            [self flushLocalMatrix];
+        }
+    }
+
+    SkMatrix localMatrix = fLocalMatrix;
+#ifdef USE_ACCEL_TO_ROTATE
+    localMatrix.preConcat(fRotateMatrix);
+#endif
+
+    SkMatrix matrix;
+    matrix.setConcat(localMatrix, fMatrix);
+
+    const SkMatrix* localM = NULL;
+    if (localMatrix.getType() & SkMatrix::kScale_Mask) {
+        localM = &localMatrix;
+    }
+#ifdef USE_ACCEL_TO_ROTATE
+    localM = &localMatrix;
+#endif
+    canvas->setExternalMatrix(localM);
+
+#ifdef SHOULD_COUNTER_INIT
+    gShouldCounter = SHOULD_COUNTER_INIT;
+#endif
+
+    {
+        int saveCount = canvas->save();
+        canvas->concat(matrix);
+//        SkRect r = { 10, 10, 500, 600 }; canvas->clipRect(r);
+        fWind->draw(canvas);
+        canvas->restoreToCount(saveCount);
+    }
+
+    if (fZoomAround) {
+        zoom_around(canvas, fZoomAroundX, fZoomAroundY, SCALE_FOR_ZOOM_LENS);
+        canvas->concat(matrix);
+        fWind->draw(canvas);
+    }
+    
+#ifdef FORCE_REDRAW
+    fWind->inval(NULL);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+- (void)layoutSubviews {
+    int W, H;
+
+    gScreenScale = [UIScreen mainScreen].scale;
+
+#ifdef USE_GL
+    
+    CAEAGLLayer* eaglLayer = (CAEAGLLayer*)self.layer;
+    if ([self respondsToSelector:@selector(setContentScaleFactor:)]) {
+        self.contentScaleFactor = gScreenScale;
+    }
+    // Allocate color buffer backing based on the current layer size
+    glBindRenderbufferOES(GL_RENDERBUFFER_OES, fGL.fRenderbuffer);
+    [fGL.fContext renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:eaglLayer];
+    
+    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &fGL.fWidth);
+    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &fGL.fHeight);
+
+    if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
+    {
+        NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
+    }
+
+    glBindRenderbufferOES(GL_RENDERBUFFER_OES, fGL.fStencilbuffer);
+    glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_STENCIL_INDEX8_OES, fGL.fWidth, fGL.fHeight);
+
+    W = fGL.fWidth;
+    H = fGL.fHeight;
+#else
+    CGRect rect = [self bounds];
+    W = (int)CGRectGetWidth(rect);
+    H = (int)CGRectGetHeight(rect) - TITLE_HEIGHT;
+#endif
+
+    printf("---- layoutSubviews %d %d\n", W, H);
+    fWind->resize(W, H);
+    fWind->inval(NULL);
+}
+
+#ifdef USE_GL
+
+static GrContext* gCtx;
+static GrContext* get_global_grctx() {
+    // should be pthread-local at least
+    if (NULL == gCtx) {        
+#ifdef USE_GL_1
+        gCtx = GrContext::Create(GrGpu::kOpenGL_Fixed_Engine, NULL);
+#else
+        gCtx = GrContext::Create(GrGpu::kOpenGL_Shaders_Engine, NULL);
+#endif
+    }
+    return gCtx;
+}
+
+#include "SkDevice.h"
+#include "SkShader.h"
+#include "SkGrTexturePixelRef.h"
+#include "GrMesh.h"
+#include "SkRandom.h"
+
+#include "GrAtlas.h"
+#include "GrTextStrike.h"
+
+static void show_fontcache(GrContext* ctx, SkCanvas* canvas) {
+#if 0
+    SkPaint paint;
+    const int SIZE = 64;
+    GrAtlas* plot[64][64];
+
+    paint.setAntiAlias(true);
+    paint.setTextSize(24);
+    paint.setTextAlign(SkPaint::kCenter_Align);
+
+    Gr_bzero(plot, sizeof(plot));
+
+    GrFontCache* cache = ctx->getFontCache();
+    GrTextStrike* strike = cache->getHeadStrike();
+    int count = 0;
+    while (strike) {
+        GrAtlas* atlas = strike->getAtlas();
+        while (atlas) {
+            int x = atlas->getPlotX();
+            int y = atlas->getPlotY();
+
+            SkRandom rand((intptr_t)strike);
+            SkColor c = rand.nextU() | 0x80808080;
+            paint.setColor(c);
+            paint.setAlpha(0x80);
+
+            SkRect r;
+            r.set(x * SIZE, y * SIZE, (x + 1)*SIZE, (y+1)*SIZE);
+            r.inset(1, 1);
+            canvas->drawRect(r, paint);
+            
+            paint.setColor(0xFF660000);
+            SkString label;
+            label.printf("%d", count);
+            canvas->drawText(label.c_str(), label.size(), r.centerX(),
+                             r.fTop + r.height() * 2 / 3, paint);
+            
+            atlas = atlas->nextAtlas();
+        }
+        strike = strike->fNext;
+        count += 1;
+    }
+#endif
+}
+
+void test_patch(SkCanvas* canvas, const SkBitmap& bm, SkScalar scale);
+
+static void draw_mesh(SkCanvas* canvas, const SkBitmap& bm) {
+    GrMesh fMesh;
+
+    SkRect r;
+    r.set(0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()));
+    
+    //        fMesh.init(bounds, fBitmap.width() / 40, fBitmap.height() / 40, texture);
+    fMesh.init(r, bm.width()/16, bm.height()/16, r);
+
+    SkPaint paint;
+    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+    paint.setShader(s)->unref();
+    fMesh.draw(canvas, paint);
+}
+
+static void scale_about(SkCanvas* canvas, float sx, float sy, float px, float py) {
+    canvas->translate(px, py);
+    canvas->scale(sx, sy);
+    canvas->translate(-px, -py);
+}
+
+static float grInterp(float v0, float v1, float percent) {
+    return v0 + percent * (v1 - v0);
+}
+
+static void draw_device(SkCanvas* canvas, SkDevice* dev, float w, float h, float warp) {
+    canvas->save();
+    float s = grInterp(1, 0.8, warp);
+    scale_about(canvas, s, s, w/2, h/2);
+    test_patch(canvas, dev->accessBitmap(false), warp);
+    canvas->restore();
+}
+
+- (void)drawInGL {
+//    printf("------ drawInGL\n");
+    // This application only creates a single context which is already set current at this point.
+    // This call is redundant, but needed if dealing with multiple contexts.
+    [EAGLContext setCurrentContext:fGL.fContext];
+    
+    // This application only creates a single default framebuffer which is already bound at this point.
+    // This call is redundant, but needed if dealing with multiple framebuffers.
+    glBindFramebufferOES(GL_FRAMEBUFFER_OES, fGL.fFramebuffer);
+    
+    GLint scissorEnable;
+    glGetIntegerv(GL_SCISSOR_TEST, &scissorEnable);
+    glDisable(GL_SCISSOR_TEST);
+    glClearColor(0,0,0,0);
+    glClear(GL_COLOR_BUFFER_BIT);
+    if (scissorEnable) {
+        glEnable(GL_SCISSOR_TEST);
+    }
+    
+    GrContext* ctx = get_global_grctx();
+    SkGpuCanvas origCanvas(ctx);
+    origCanvas.setBitmapDevice(fWind->getBitmap());
+    
+    //    gl->reset();
+    SkGpuCanvas glCanvas(ctx);
+    SkCanvas   rasterCanvas;
+
+    SkCanvas* canvas;
+    SkDevice* dev = NULL;
+    
+    switch (fBackend) {
+        case kRaster_Backend:
+            canvas = &rasterCanvas;
+            break;
+        case kGL_Backend:
+            canvas = &glCanvas;
+            break;
+    }
+
+    if (fUseWarp || fWarpState.isActive()) {
+        if (kGL_Backend == fBackend) {
+            dev = origCanvas.createDevice(fWind->getBitmap(), true);
+            canvas->setDevice(dev)->unref();
+        } else {
+            canvas->setBitmapDevice(fWind->getBitmap());
+            dev = canvas->getDevice();
+        }
+    } else {
+        canvas->setBitmapDevice(fWind->getBitmap());
+        dev = NULL;
+    }
+    
+    canvas->translate(0, TITLE_HEIGHT);
+
+    // if we're not "retained", then we have to always redraw everything.
+    // This call forces us to ignore the fDirtyRgn, and draw everywhere.
+    // If we are "retained", we can skip this call (as the raster case does)
+    fWind->forceInvalAll();
+
+    FPS_StartDraw();
+    [self drawWithCanvas:canvas];
+    FPS_EndDraw();
+
+    if (dev) {
+        draw_device(&origCanvas, dev, fWind->width(), fWind->height(),
+                    fWarpState.evaluate());
+    } else {
+        if (kRaster_Backend == fBackend) {
+            origCanvas.drawBitmap(fWind->getBitmap(), 0, 0, NULL);
+        }
+        // else GL - we're already on screen
+    }
+
+    show_fontcache(ctx, canvas);
+    ctx->flush(false);
+        
+    // This application only creates a single color renderbuffer which is already bound at this point.
+    // This call is redundant, but needed if dealing with multiple renderbuffers.
+    glBindRenderbufferOES(GL_RENDERBUFFER_OES, fGL.fRenderbuffer);
+    [fGL.fContext presentRenderbuffer:GL_RENDERBUFFER_OES];
+    
+#if GR_COLLECT_STATS
+    static int frame = 0;
+    if (!(frame % 100)) {
+        get_global_grctx()->printStats();
+    }
+    get_global_grctx()->resetStats();
+    ++frame;
+#endif
+    
+    FPS_Flush(fWind);
+
+#if 0
+    gCtx->deleteAllTextures(GrTextureCache::kAbandonTexture_DeleteMode);
+    gCtx->unref();
+    gCtx = NULL;
+#endif
+}
+
+#else   // raster case
+
+- (void)drawRect:(CGRect)rect {
+    CGContextRef cg = UIGraphicsGetCurrentContext();
+    SkCanvas* canvas = NULL;
+
+    FPS_StartDraw();
+    [self drawWithCanvas:canvas];
+
+    FPS_EndDraw();
+    SkCGDrawBitmap(cg, fWind->getBitmap(), 0, TITLE_HEIGHT);
+
+    FPS_Flush(fWind);
+
+}
+#endif
+
+- (void)setWarpState:(bool)useWarp {
+    fWarpState.stop();  // we should reverse from where we are if active...
+
+    const float duration = 0.5;
+    fUseWarp = useWarp;
+    if (useWarp) {
+        fWarpState.start(0, 1, duration);
+    } else {
+        fWarpState.start(1, 0, duration);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+- (void)flushLocalMatrix {
+    fMatrix.postConcat(fLocalMatrix);
+    fLocalMatrix.reset();
+    fFlingState.stop();
+    fNeedGestureEnded = false;
+    fNeedFirstPinch = true;
+}
+
+- (void)localMatrixWithGesture:(UIGestureRecognizer*)gesture {
+    fNeedGestureEnded = true;
+
+    switch (gesture.state) {
+        case UIGestureRecognizerStateCancelled:
+        case UIGestureRecognizerStateEnded:
+            [self flushLocalMatrix];
+            break;
+        case UIGestureRecognizerStateChanged: {
+            SkMatrix matrix;
+            matrix.setConcat(fLocalMatrix, fMatrix);
+        } break;
+        default:
+            break;
+    }
+}
+
+- (void)commonHandleGesture:(UIGestureRecognizer*)sender {
+    if (fFlingState.isActive()) {
+        [self flushLocalMatrix];
+    }
+
+    switch (sender.state) {
+        case UIGestureRecognizerStateBegan:
+            [self flushLocalMatrix];
+            break;
+        default:
+            break;
+    }
+}
+
+- (void)handleDTapGesture:(UIGestureRecognizer*)sender {
+    [self flushLocalMatrix];
+    fMatrix.reset();
+}
+
+static float discretize(float x) {
+    return (int)x;
+}
+
+- (void)handlePanGesture:(UIPanGestureRecognizer*)sender {
+    [self commonHandleGesture:sender];
+
+    CGPoint delta = [sender translationInView:self];
+    delta.x *= gScreenScale;
+    delta.y *= gScreenScale;
+    // avoid flickering where the drawing might toggle in and out of a pixel
+    // center if translated by a fractional value
+    delta.x = discretize(delta.x);
+    delta.y = discretize(delta.y);
+    fLocalMatrix.setTranslate(delta.x, delta.y);
+    [self localMatrixWithGesture:sender];
+
+    if (UIGestureRecognizerStateEnded == sender.state) {
+        CGPoint velocity = [sender velocityInView:self];
+        fFlingState.reset(velocity.x, velocity.y);
+        fNeedGestureEnded = true;
+    }
+}
+
+- (float)limitTotalZoom:(float)scale {
+    // this query works 'cause we know that we're square-scale w/ no skew/rotation
+    const float curr = fMatrix[0];
+
+    if (scale > 1 && curr * scale > MAX_ZOOM_SCALE) {
+        scale = MAX_ZOOM_SCALE / curr;
+    } else if (scale < 1 && curr * scale < MIN_ZOOM_SCALE) {
+        scale = MIN_ZOOM_SCALE / curr;
+    }
+    return scale;
+}
+
+- (void)handleScaleGesture:(UIPinchGestureRecognizer*)sender {
+    [self commonHandleGesture:sender];
+
+    if ([sender numberOfTouches] == 2) {
+        float scale = sender.scale;
+        CGPoint p0 = [sender locationOfTouch:0 inView:self];
+        CGPoint p1 = [sender locationOfTouch:0 inView:self];
+        float cx = (p0.x + p1.x) * 0.5;
+        float cy = (p0.y + p1.y) * 0.5;
+
+        if (fNeedFirstPinch) {
+            fFirstPinchX = cx;
+            fFirstPinchY = cy;
+            fNeedFirstPinch = false;
+        }
+
+        scale = [self limitTotalZoom:scale];
+
+        fLocalMatrix.setTranslate(-fFirstPinchX, -fFirstPinchY);
+        fLocalMatrix.postScale(scale, scale);
+        fLocalMatrix.postTranslate(cx, cy);
+        [self localMatrixWithGesture:sender];
+    } else {
+        [self flushLocalMatrix];
+    }
+}
+
+- (void)handleLongPressGesture:(UILongPressGestureRecognizer*)sender {
+    [self commonHandleGesture:sender];
+
+    if ([sender numberOfTouches] == 0) {
+        fZoomAround = false;
+        return;
+    }
+
+    CGPoint pt = [sender locationOfTouch:0 inView:self];
+    switch (sender.state) {
+        case UIGestureRecognizerStateBegan:
+        case UIGestureRecognizerStateChanged:
+            fZoomAround = true;
+            fZoomAroundX = pt.x;
+            fZoomAroundY = pt.y - Y_OFFSET_FOR_ZOOM_LENS;
+            break;
+        case UIGestureRecognizerStateEnded:
+        case UIGestureRecognizerStateCancelled:
+            fZoomAround = false;
+            break;
+        default:
+            break;
+    }
+}
+
+- (void)addAndReleaseGesture:(UIGestureRecognizer*)gesture {
+    [self addGestureRecognizer:gesture];
+    [gesture release];
+}
+
+- (void)initGestures {
+    UITapGestureRecognizer* tapG = [UITapGestureRecognizer alloc];
+    [tapG initWithTarget:self action:@selector(handleDTapGesture:)];
+    tapG.numberOfTapsRequired = 2;
+    [self addAndReleaseGesture:tapG];
+
+    UIPanGestureRecognizer* panG = [UIPanGestureRecognizer alloc];
+    [panG initWithTarget:self action:@selector(handlePanGesture:)];
+    [self addAndReleaseGesture:panG];
+    
+    UIPinchGestureRecognizer* pinchG = [UIPinchGestureRecognizer alloc];
+    [pinchG initWithTarget:self action:@selector(handleScaleGesture:)];
+    [self addAndReleaseGesture:pinchG];
+
+    UILongPressGestureRecognizer* longG = [UILongPressGestureRecognizer alloc];
+    [longG initWithTarget:self action:@selector(handleLongPressGesture:)];
+    [self addAndReleaseGesture:longG];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static float abs(float x) { return x < 0 ? -x : x; }
+
+static bool normalize(UIAcceleration* acc, float xy[]) {
+    float mag2 = acc.x*acc.x + acc.y*acc.y + acc.z*acc.z;
+    if (mag2 < 0.000001) {
+        return false;
+    }
+    if (abs((float)acc.z) > 0.9 * sqrt(mag2)) {
+        return false;
+    }
+
+    mag2 = acc.x*acc.x + acc.y*acc.y;
+    if (mag2 < 0.000001) {
+        return false;
+    }
+    float scale = 1 / sqrt(mag2);
+    xy[0] = acc.x * scale;
+    xy[1] = acc.y * scale;
+    return true;
+}
+
+static void normalize(float xy[]) {
+    float scale = 1 / sqrt(xy[0]*xy[0] + xy[1]*xy[1]);
+    xy[0] *= scale;
+    xy[1] *= scale;
+}
+
+static float weighted_average(float newv, float oldv) {
+    return newv * 0.25 + oldv * 0.75;
+}
+
+- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acc {
+
+    float norm[2];
+    if (normalize(acc, norm)) {
+        float sinv = -norm[0];
+        float cosv = -norm[1];
+        // smooth
+        norm[0] = weighted_average(sinv, -fRotateMatrix[1]);
+        norm[1] = weighted_average(cosv, fRotateMatrix[0]);
+        normalize(norm);
+        fRotateMatrix.setSinCos(norm[0], norm[1], 400, 400);
+    }
+#if 0
+    NSDate *now = [NSDate date];
+    NSTimeInterval intervalDate = [now timeIntervalSinceDate:now_prev];
+    
+    velX += (acceleration.x * intervalDate);
+    distX += (velX * intervalDate);
+    //do other axis here too
+    
+    // setup for next UIAccelerometer event
+    now_prev = now;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+- (void)setSkTitle:(const char *)title {
+    if (fTitleLabel) {
+        fTitleLabel.text = [NSString stringWithUTF8String:title];
+        [fTitleLabel setNeedsDisplay];
+    }
+}
+
+- (BOOL)onHandleEvent:(const SkEvent&)evt {
+    if (evt.isType(kREDRAW_UIVIEW_GL)) {
+        [self drawInGL];
+        return true;
+    }
+    return false;
+}
+
+- (void)postInvalWithRect:(const SkIRect*)r {
+#ifdef USE_GL
+
+#if 1
+    if (!fRedrawRequestPending) {
+        fRedrawRequestPending = true;
+        /*
+            performSelectorOnMainThread seems to starve updating other views
+            (e.g. our FPS view in the titlebar), so we use the afterDelay
+            version
+         */
+        if (0) {
+            [self performSelectorOnMainThread:@selector(drawInGL) withObject:nil waitUntilDone:NO];
+        } else {
+            [self performSelector:@selector(drawInGL) withObject:nil afterDelay:0];
+        }
+    }
+#else
+    if (!fRedrawRequestPending) {
+        SkEvent* evt = new SkEvent(kREDRAW_UIVIEW_GL);
+        evt->post(fWind->getSinkID());
+        fRedrawRequestPending = true;
+    }
+#endif
+
+#else
+    if (r) {
+        [self setNeedsDisplayInRect:CGRectMake(r->fLeft, r->fTop,
+                                               r->width(), r->height())];
+    } else {
+        [self setNeedsDisplay];
+    }
+#endif
+}
+
+@end
diff --git a/gpu/src/skia/skgr_files.mk b/gpu/src/skia/skgr_files.mk
new file mode 100644
index 0000000..41896c0
--- /dev/null
+++ b/gpu/src/skia/skgr_files.mk
@@ -0,0 +1,7 @@
+SOURCE := \
+    SkGpuCanvas.cpp	\
+    SkGpuDevice.cpp \
+    SkGr.cpp \
+    SkGrTexturePixelRef.cpp \
+    SkGrFontScaler.cpp
+