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/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
+