Hook in rough distance field support for fonts

R=bsalomon@google.com

Review URL: https://codereview.chromium.org/41213003

git-svn-id: http://skia.googlecode.com/svn/trunk/src@12229 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/core/SkDraw.cpp b/core/SkDraw.cpp
index f282bf5..9b6c426 100644
--- a/core/SkDraw.cpp
+++ b/core/SkDraw.cpp
@@ -1727,10 +1727,30 @@
 
     SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
 
+#if SK_DISTANCEFIELD_FONTS
+    const SkMatrix* ctm = fMatrix;
+    const SkPaint* paintRef = &paint;
+    SkPaint paintCopy;
+    uint32_t procFlags = fProcs ? fProcs->fFlags : 0;
+    if (procFlags & SkDrawProcs::kUseScaledGlyphs_Flag) {
+        paintCopy = paint;
+        paintCopy.setTextSize(SkDrawProcs::kBaseDFFontSize);
+        paintCopy.setLCDRenderText(false);
+        paintRef = &paintCopy;
+    }
+    if (procFlags & SkDrawProcs::kSkipBakedGlyphTransform_Flag) {
+        ctm = NULL;
+    }
+    SkAutoGlyphCache    autoCache(*paintRef, &fDevice->fLeakyProperties, ctm);
+#else
     SkAutoGlyphCache    autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
+#endif
     SkGlyphCache*       cache = autoCache.getCache();
 
     // transform our starting point
+#if SK_DISTANCEFIELD_FONTS
+    if (!(procFlags & SkDrawProcs::kSkipBakedGlyphTransform_Flag)) 
+#endif
     {
         SkPoint loc;
         fMatrix->mapXY(x, y, &loc);
@@ -1789,16 +1809,41 @@
     SkFixed fx = SkScalarToFixed(x) + d1g.fHalfSampleX;
     SkFixed fy = SkScalarToFixed(y) + d1g.fHalfSampleY;
 
+#if SK_DISTANCEFIELD_FONTS
+    SkFixed fixedScale;
+    if (procFlags & SkDrawProcs::kUseScaledGlyphs_Flag) {
+        fixedScale = SkScalarToFixed(paint.getTextSize()/(float)SkDrawProcs::kBaseDFFontSize);
+    }
+#endif
     while (text < stop) {
         const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
 
+#if SK_DISTANCEFIELD_FONTS
+        if (procFlags & SkDrawProcs::kUseScaledGlyphs_Flag) {
+            fx += SkFixedMul_portable(autokern.adjust(glyph), fixedScale);
+        } else {
+            fx += autokern.adjust(glyph);
+        }
+#else
         fx += autokern.adjust(glyph);
+#endif
 
         if (glyph.fWidth) {
             proc(d1g, fx, fy, glyph);
         }
+
+#if SK_DISTANCEFIELD_FONTS
+        if (procFlags & SkDrawProcs::kUseScaledGlyphs_Flag) {
+            fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
+            fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
+        } else {
+            fx += glyph.fAdvanceX;
+            fy += glyph.fAdvanceY;
+        }
+#else
         fx += glyph.fAdvanceX;
         fy += glyph.fAdvanceY;
+#endif
     }
 }
 
@@ -1956,7 +2001,23 @@
     }
 
     SkDrawCacheProc     glyphCacheProc = paint.getDrawCacheProc();
+#if SK_DISTANCEFIELD_FONTS
+    const SkMatrix* ctm = fMatrix;
+    const SkPaint* paintRef = &paint;
+    SkPaint paintCopy;
+    uint32_t procFlags = fProcs ? fProcs->fFlags : 0;
+    if (procFlags & SkDrawProcs::kUseScaledGlyphs_Flag) {
+        paintCopy = paint;
+        paintCopy.setTextSize(SkDrawProcs::kBaseDFFontSize);
+        paintRef = &paintCopy;
+    }
+    if (procFlags & SkDrawProcs::kSkipBakedGlyphTransform_Flag) {
+        ctm = NULL;
+    }
+    SkAutoGlyphCache    autoCache(*paintRef, &fDevice->fLeakyProperties, ctm);
+#else
     SkAutoGlyphCache    autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
+#endif
     SkGlyphCache*       cache = autoCache.getCache();
 
     SkAAClipBlitterWrapper wrapper;
@@ -1998,8 +2059,16 @@
 
         if (SkPaint::kLeft_Align == paint.getTextAlign()) {
             while (text < stop) {
+#if SK_DISTANCEFIELD_FONTS
+                if (procFlags & SkDrawProcs::kSkipBakedGlyphTransform_Flag) {
+                    tms.fLoc.fX = *pos;
+                    tms.fLoc.fY = *(pos+1);
+                } else {
+                    tmsProc(tms, pos);
+                }
+#else
                 tmsProc(tms, pos);
-
+#endif
                 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + d1g.fHalfSampleX;
                 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + d1g.fHalfSampleY;
 
diff --git a/core/SkDrawProcs.h b/core/SkDrawProcs.h
index 8b8c382..cc2f3ed 100644
--- a/core/SkDrawProcs.h
+++ b/core/SkDrawProcs.h
@@ -57,6 +57,22 @@
 
 struct SkDrawProcs {
     SkDraw1Glyph::Proc  fD1GProc;
+#if SK_DISTANCEFIELD_FONTS
+    uint32_t            fFlags;
+
+    enum Flags {
+        /**
+         * Disable baked glyph transforms
+         */
+        kSkipBakedGlyphTransform_Flag = 0x1,
+        /**
+         * Scale glyphs to get different point sizes
+         */
+        kUseScaledGlyphs_Flag         = 0x2,
+    };
+
+    static const int kBaseDFFontSize = 32;
+#endif
 };
 
 /**
diff --git a/device/xps/SkXPSDevice.cpp b/device/xps/SkXPSDevice.cpp
index f837655..86b8c7d 100644
--- a/device/xps/SkXPSDevice.cpp
+++ b/device/xps/SkXPSDevice.cpp
@@ -2234,6 +2234,9 @@
                            SkBitSet& glyphsUsed,
                            SkDraw& myDraw, SkXPSDrawProcs& procs) {
     procs.fD1GProc = xps_draw_1_glyph;
+#if SK_DISTANCEFIELD_FONTS
+    procs.fFlags = 0;
+#endif
     size_t numGlyphGuess;
     switch (paint.getTextEncoding()) {
         case SkPaint::kUTF8_TextEncoding:
diff --git a/gpu/GrBitmapTextContext.cpp b/gpu/GrBitmapTextContext.cpp
index d389ed6..433eda1 100755
--- a/gpu/GrBitmapTextContext.cpp
+++ b/gpu/GrBitmapTextContext.cpp
@@ -123,7 +123,11 @@
         return;
     }
     if (NULL == fStrike) {
+#if SK_DISTANCEFIELD_FONTS
+        fStrike = fContext->getFontCache()->getStrike(scaler, true);
+#else
         fStrike = fContext->getFontCache()->getStrike(scaler);
+#endif
     }
 
     GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
diff --git a/gpu/GrDistanceFieldTextContext.cpp b/gpu/GrDistanceFieldTextContext.cpp
new file mode 100755
index 0000000..87be8ca
--- /dev/null
+++ b/gpu/GrDistanceFieldTextContext.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrDistanceFieldTextContext.h"
+#include "GrAtlas.h"
+#include "GrDrawTarget.h"
+#include "GrFontScaler.h"
+#include "GrIndexBuffer.h"
+#include "GrTextStrike.h"
+#include "GrTextStrike_impl.h"
+#include "SkPath.h"
+#include "SkRTConf.h"
+#include "SkStrokeRec.h"
+#include "effects/GrDistanceFieldTextureEffect.h"
+
+static const int kGlyphCoordsAttributeIndex = 1;
+
+SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
+                "Dump the contents of the font cache before every purge.");
+
+
+GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, 
+                                                       const GrPaint& paint,
+                                                       SkColor color,
+                                                       SkScalar textRatio)
+                                                     : GrTextContext(context, paint)
+                                                     , fTextRatio(textRatio) {
+    fSkPaintColor = color;
+
+    fStrike = NULL;
+
+    fCurrTexture = NULL;
+    fCurrVertex = 0;
+
+    fVertices = NULL;
+    fMaxVertices = 0;
+}
+
+GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
+    this->flushGlyphs();
+}
+
+static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
+    unsigned r = SkColorGetR(c);
+    unsigned g = SkColorGetG(c);
+    unsigned b = SkColorGetB(c);
+    return GrColorPackRGBA(r, g, b, 0xff);
+}
+
+void GrDistanceFieldTextContext::flushGlyphs() {
+    if (NULL == fDrawTarget) {
+        return;
+    }
+
+    GrDrawState* drawState = fDrawTarget->drawState();
+    GrDrawState::AutoRestoreEffects are(drawState);
+    drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
+
+    if (fCurrVertex > 0) {
+        // setup our sampler state for our text texture/atlas
+        SkASSERT(GrIsALIGN4(fCurrVertex));
+        SkASSERT(fCurrTexture);
+        GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
+
+        // This effect could be stored with one of the cache objects (atlas?)
+        drawState->addCoverageEffect(
+                                GrDistanceFieldTextureEffect::Create(fCurrTexture, params),
+                                kGlyphCoordsAttributeIndex)->unref();
+
+        if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
+            if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
+                kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
+                fPaint.numColorStages()) {
+                GrPrintf("LCD Text will not draw correctly.\n");
+            }
+            // We don't use the GrPaint's color in this case because it's been premultiplied by
+            // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
+            // the mask texture color. The end result is that we get
+            //            mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
+            int a = SkColorGetA(fSkPaintColor);
+            // paintAlpha
+            drawState->setColor(SkColorSetARGB(a, a, a, a));
+            // paintColor
+            drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaintColor));
+            drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
+        } else {
+            // set back to normal in case we took LCD path previously.
+            drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
+            drawState->setColor(fPaint.getColor());
+        }
+
+        int nGlyphs = fCurrVertex / 4;
+        fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
+        fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
+                                          nGlyphs,
+                                          4, 6);
+        fDrawTarget->resetVertexSource();
+        fVertices = NULL;
+        fMaxVertices = 0;
+        fCurrVertex = 0;
+        SkSafeSetNull(fCurrTexture);
+    }
+}
+
+namespace {
+
+// position + texture coord
+extern const GrVertexAttrib gTextVertexAttribs[] = {
+    {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
+    {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
+};
+
+};
+
+void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
+                                                 GrFixed vx, GrFixed vy,
+                                                 GrFontScaler* scaler) {
+    if (NULL == fDrawTarget) {
+        return;
+    }
+    if (NULL == fStrike) {
+        fStrike = fContext->getFontCache()->getStrike(scaler, true);
+    }
+
+    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
+    if (NULL == glyph || glyph->fBounds.isEmpty()) {
+        return;
+    }
+
+    SkScalar sx = SkFixedToScalar(vx);
+    SkScalar sy = SkFixedToScalar(vy);
+/*
+    // not valid, need to find a different solution for this
+    vx += SkIntToFixed(glyph->fBounds.fLeft);
+    vy += SkIntToFixed(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->fPlot) {
+        int x = vx >> 16;
+        int y = vy >> 16;
+        if (fClipRect.quickReject(x, y, x + width, y + height)) {
+//            SkCLZ(3);    // so we can set a break-point in the debugger
+            return;
+        }
+    }
+*/
+    if (NULL == glyph->fPlot) {
+        if (fStrike->getGlyphAtlas(glyph, scaler)) {
+            goto HAS_ATLAS;
+        }
+
+        // try to clear out an unused plot before we flush
+        fContext->getFontCache()->freePlotExceptFor(fStrike);
+        if (fStrike->getGlyphAtlas(glyph, scaler)) {
+            goto HAS_ATLAS;
+        }
+
+        if (c_DumpFontCache) {
+#ifdef SK_DEVELOPER
+            fContext->getFontCache()->dump();
+#endif
+        }
+
+        // before we purge the cache, we must flush any accumulated draws
+        this->flushGlyphs();
+        fContext->flush();
+
+        // try to purge
+        fContext->getFontCache()->purgeExceptFor(fStrike);
+        // need to use new flush count here
+        if (fStrike->getGlyphAtlas(glyph, scaler)) {
+            goto HAS_ATLAS;
+        }
+
+        if (NULL == glyph->fPath) {
+            SkPath* path = SkNEW(SkPath);
+            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
+                // flag the glyph as being dead?
+                delete path;
+                return;
+            }
+            glyph->fPath = path;
+        }
+
+        GrContext::AutoMatrix am;
+        SkMatrix translate;
+        translate.setTranslate(sx, sy);
+        GrPaint tmpPaint(fPaint);
+        am.setPreConcat(fContext, translate, &tmpPaint);
+        SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+        fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
+        return;
+    }
+
+HAS_ATLAS:
+    SkASSERT(glyph->fPlot);
+    GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
+    glyph->fPlot->setDrawToken(drawToken);
+
+    GrTexture* texture = glyph->fPlot->texture();
+    SkASSERT(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;
+        fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
+            SK_ARRAY_COUNT(gTextVertexAttribs));
+        bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
+        if (flush) {
+            this->flushGlyphs();
+            fContext->flush();
+            fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
+                SK_ARRAY_COUNT(gTextVertexAttribs));
+        }
+        fMaxVertices = kDefaultRequestedVerts;
+        // ignore return, no point in flushing again.
+        fDrawTarget->geometryHints(&fMaxVertices, NULL);
+
+        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
+        if (fMaxVertices < kMinRequestedVerts) {
+            fMaxVertices = kDefaultRequestedVerts;
+        } else if (fMaxVertices > maxQuadVertices) {
+            // don't exceed the limit of the index buffer
+            fMaxVertices = maxQuadVertices;
+        }
+        bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
+                                                               0,
+                                                               GrTCast<void**>(&fVertices),
+                                                               NULL);
+        GrAlwaysAssert(success);
+        SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
+    }
+
+    SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft);
+    SkScalar dy = SkIntToScalar(glyph->fBounds.fTop);
+    SkScalar width = SkIntToScalar(glyph->fBounds.width());
+    SkScalar height = SkIntToScalar(glyph->fBounds.height());
+
+    SkScalar scale = fTextRatio;
+    dx *= scale;
+    dy *= scale;
+    sx += dx;
+    sy += dy;
+    width *= scale;
+    height *= scale;
+
+    GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
+    GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
+    GrFixed tw = SkIntToFixed(glyph->fBounds.width());
+    GrFixed th = SkIntToFixed(glyph->fBounds.height());
+
+    fVertices[2*fCurrVertex].setRectFan(sx,
+                                        sy,
+                                        sx + width,
+                                        sy + height,
+                                        2 * sizeof(SkPoint));
+    fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
+                                          SkFixedToFloat(texture->normalizeFixedY(ty)),
+                                          SkFixedToFloat(texture->normalizeFixedX(tx + tw)),
+                                          SkFixedToFloat(texture->normalizeFixedY(ty + th)),
+                                          2 * sizeof(SkPoint));
+    fCurrVertex += 4;
+}
diff --git a/gpu/GrTextStrike.cpp b/gpu/GrTextStrike.cpp
index a96aab1..609cb5b 100644
--- a/gpu/GrTextStrike.cpp
+++ b/gpu/GrTextStrike.cpp
@@ -12,6 +12,10 @@
 #include "GrTextStrike_impl.h"
 #include "SkString.h"
 
+#if SK_DISTANCEFIELD_FONTS
+#include "edtaa3.h"
+#endif
+
 SK_DEFINE_INST_COUNT(GrFontScaler)
 SK_DEFINE_INST_COUNT(GrKey)
 
@@ -193,6 +197,11 @@
     static int gCounter;
 #endif
 
+#if SK_DISTANCEFIELD_FONTS
+#define DISTANCE_FIELD_PAD   4
+#define DISTANCE_FIELD_RANGE (4.0)
+#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().
@@ -246,6 +255,15 @@
     }
 
     GrGlyph* glyph = fPool.alloc();
+#if SK_DISTANCEFIELD_FONTS
+    // expand bounds to hold full distance field data
+    if (fUseDistanceField) {
+        bounds.fLeft   -= DISTANCE_FIELD_PAD;
+        bounds.fRight  += DISTANCE_FIELD_PAD;
+        bounds.fTop    -= DISTANCE_FIELD_PAD;
+        bounds.fBottom += DISTANCE_FIELD_PAD;
+    }
+#endif
     glyph->init(packed, bounds);
     fCache.insert(packed, glyph);
     return glyph;
@@ -256,6 +274,7 @@
     return fAtlasMgr->removeUnusedPlots(&fAtlas);
 }
 
+
 bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
 #if 0   // testing hack to force us to flush our cache often
     static int gCounter;
@@ -270,18 +289,118 @@
     SkAutoRef ar(scaler);
 
     int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
-    size_t size = glyph->fBounds.area() * bytesPerPixel;
-    SkAutoSMalloc<1024> storage(size);
-    if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
-                                     glyph->height(),
-                                     glyph->width() * bytesPerPixel,
-                                     storage.get())) {
-        return false;
-    }
 
-    GrPlot* plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(),
-                                         glyph->height(), storage.get(),
-                                         &glyph->fAtlasLocation);
+    GrPlot* plot;
+#if SK_DISTANCEFIELD_FONTS
+    if (fUseDistanceField) {
+        SkASSERT(1 == bytesPerPixel);
+
+        // we've already expanded the glyph dimensions to match the final size
+        // but must shrink back down to get the packed glyph data
+        int dfWidth = glyph->width();
+        int dfHeight = glyph->height();
+        int width = dfWidth - 2*DISTANCE_FIELD_PAD;
+        int height = dfHeight - 2*DISTANCE_FIELD_PAD;
+        size_t stride = width*bytesPerPixel;
+
+        size_t size = width * height * bytesPerPixel;
+        SkAutoSMalloc<1024> storage(size);
+        if (!scaler->getPackedGlyphImage(glyph->fPackedID, width, height, stride, storage.get())) {
+            return false;
+        }
+
+        // alloc storage for distance field glyph
+        size_t dfSize = dfWidth * dfHeight * bytesPerPixel;
+        SkAutoSMalloc<1024> dfStorage(dfSize);
+
+        // copy glyph into distance field storage
+        sk_bzero(dfStorage.get(), dfSize);
+
+        unsigned char* ptr = (unsigned char*) storage.get();
+        unsigned char* dfPtr = (unsigned char*) dfStorage.get();
+        size_t dfStride = dfWidth*bytesPerPixel;
+        dfPtr += DISTANCE_FIELD_PAD*dfStride;
+        dfPtr += DISTANCE_FIELD_PAD*bytesPerPixel;
+
+        for (int i = 0; i < height; ++i) {
+            memcpy(dfPtr, ptr, stride);
+
+            dfPtr += dfStride;
+            ptr += stride;
+        }
+
+        // generate distance field data
+        SkAutoSMalloc<1024> distXStorage(dfWidth*dfHeight*sizeof(short));
+        SkAutoSMalloc<1024> distYStorage(dfWidth*dfHeight*sizeof(short));
+        SkAutoSMalloc<1024> outerDistStorage(dfWidth*dfHeight*sizeof(double));
+        SkAutoSMalloc<1024> innerDistStorage(dfWidth*dfHeight*sizeof(double));
+        SkAutoSMalloc<1024> gxStorage(dfWidth*dfHeight*sizeof(double));
+        SkAutoSMalloc<1024> gyStorage(dfWidth*dfHeight*sizeof(double));
+
+        short* distX = (short*) distXStorage.get();
+        short* distY = (short*) distYStorage.get();
+        double* outerDist = (double*) outerDistStorage.get();
+        double* innerDist = (double*) innerDistStorage.get();
+        double* gx = (double*) gxStorage.get();
+        double* gy = (double*) gyStorage.get();
+
+        dfPtr = (unsigned char*) dfStorage.get();
+        EDTAA::computegradient(dfPtr, dfWidth, dfHeight, gx, gy);
+        EDTAA::edtaa3(dfPtr, gx, gy, dfWidth, dfHeight, distX, distY, outerDist);
+
+        for (int i = 0; i < dfWidth*dfHeight; ++i) {
+            *dfPtr = 255 - *dfPtr;
+            dfPtr++;
+        }
+        dfPtr = (unsigned char*) dfStorage.get();
+        sk_bzero(gx, sizeof(double)*dfWidth*dfHeight);
+        sk_bzero(gy, sizeof(double)*dfWidth*dfHeight);
+        EDTAA::computegradient(dfPtr, dfWidth, dfHeight, gx, gy);
+        EDTAA::edtaa3(dfPtr, gx, gy, dfWidth, dfHeight, distX, distY, innerDist);
+
+        for (int i = 0; i < dfWidth*dfHeight; ++i) {
+            unsigned char val;
+            double outerval = outerDist[i];
+            if (outerval < 0.0) { 
+                outerval = 0.0; 
+            }
+            double innerval = innerDist[i];
+            if (innerval < 0.0) { 
+                innerval = 0.0; 
+            }
+            double dist = outerval - innerval;
+            if (dist <= -DISTANCE_FIELD_RANGE) {
+                val = 255;
+            } else if (dist > DISTANCE_FIELD_RANGE) {
+                val = 0;
+            } else {
+                val = (unsigned char)((DISTANCE_FIELD_RANGE-dist)*128.0/DISTANCE_FIELD_RANGE);
+            }
+            *dfPtr++ = val;
+        }
+        
+        // copy to atlas
+        plot = fAtlasMgr->addToAtlas(&fAtlas, dfWidth, dfHeight, dfStorage.get(), 
+                                     &glyph->fAtlasLocation);
+
+    } else {
+#endif
+        size_t size = glyph->fBounds.area() * bytesPerPixel;
+        SkAutoSMalloc<1024> storage(size);
+        if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
+                                         glyph->height(),
+                                         glyph->width() * bytesPerPixel,
+                                         storage.get())) {
+            return false;
+        }
+
+        plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(), 
+                                     glyph->height(), storage.get(), 
+                                     &glyph->fAtlasLocation);
+#if SK_DISTANCEFIELD_FONTS
+    }
+#endif
+
     if (NULL == plot) {
         return false;
     }
@@ -289,3 +408,4 @@
     glyph->fPlot = plot;
     return true;
 }
+
diff --git a/gpu/GrTextStrike.h b/gpu/GrTextStrike.h
index 26a3632..422ae0c 100644
--- a/gpu/GrTextStrike.h
+++ b/gpu/GrTextStrike.h
@@ -61,9 +61,12 @@
 
     GrFontCache*    fFontCache;
     GrAtlasMgr*     fAtlasMgr;
-    GrAtlas         fAtlas;
-
     GrMaskFormat    fMaskFormat;
+#if SK_DISTANCEFIELD_FONTS
+    bool            fUseDistanceField;
+#endif
+
+    GrAtlas         fAtlas;
 
     GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
 
@@ -75,7 +78,11 @@
     GrFontCache(GrGpu*);
     ~GrFontCache();
 
+#if SK_DISTANCEFIELD_FONTS
+    inline GrTextStrike* getStrike(GrFontScaler*, bool useDistanceField);
+#else
     inline GrTextStrike* getStrike(GrFontScaler*);
+#endif
 
     void freeAll();
 
diff --git a/gpu/GrTextStrike_impl.h b/gpu/GrTextStrike_impl.h
index 130509c..4297185 100644
--- a/gpu/GrTextStrike_impl.h
+++ b/gpu/GrTextStrike_impl.h
@@ -48,7 +48,11 @@
     }
 }
 
+#if SK_DISTANCEFIELD_FONTS
+GrTextStrike* GrFontCache::getStrike(GrFontScaler* scaler, bool useDistanceField) {
+#else
 GrTextStrike* GrFontCache::getStrike(GrFontScaler* scaler) {
+#endif
     this->validate();
 
     const Key key(scaler->getKey());
@@ -65,7 +69,9 @@
         strike->fPrev = NULL;
         fHead = strike;
     }
-
+#if SK_DISTANCEFIELD_FONTS
+    strike->fUseDistanceField = useDistanceField;
+#endif
     this->validate();
     return strike;
 }
diff --git a/gpu/SkGpuDevice.cpp b/gpu/SkGpuDevice.cpp
index ca86b84..92ac326 100644
--- a/gpu/SkGpuDevice.cpp
+++ b/gpu/SkGpuDevice.cpp
@@ -1,1842 +1,1859 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkGpuDevice.h"
-
-#include "effects/GrTextureDomainEffect.h"
-#include "effects/GrSimpleTextureEffect.h"
-
-#include "GrContext.h"
-#include "GrBitmapTextContext.h"
-
-#include "SkGrTexturePixelRef.h"
-
-#include "SkColorFilter.h"
-#include "SkDeviceImageFilterProxy.h"
-#include "SkDrawProcs.h"
-#include "SkGlyphCache.h"
-#include "SkImageFilter.h"
-#include "SkPathEffect.h"
-#include "SkRRect.h"
-#include "SkStroke.h"
-#include "SkUtils.h"
-#include "SkErrorInternals.h"
-
-#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1
-
-#if 0
-    extern bool (*gShouldDrawProc)();
-    #define CHECK_SHOULD_DRAW(draw, forceI)                     \
-        do {                                                    \
-            if (gShouldDrawProc && !gShouldDrawProc()) return;  \
-            this->prepareDraw(draw, forceI);                    \
-        } while (0)
-#else
-    #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI)
-#endif
-
-// This constant represents the screen alignment criterion in texels for
-// requiring texture domain clamping to prevent color bleeding when drawing
-// a sub region of a larger source image.
-#define COLOR_BLEED_TOLERANCE SkFloatToScalar(0.001f)
-
-#define DO_DEFERRED_CLEAR()             \
-    do {                                \
-        if (fNeedClear) {               \
-            this->clear(SK_ColorTRANSPARENT); \
-        }                               \
-    } while (false)                     \
-
-///////////////////////////////////////////////////////////////////////////////
-
-#define CHECK_FOR_ANNOTATION(paint) \
-    do { if (paint.getAnnotation()) { return; } } while (0)
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable {
-public:
-    SkAutoCachedTexture()
-        : fDevice(NULL)
-        , fTexture(NULL) {
-    }
-
-    SkAutoCachedTexture(SkGpuDevice* device,
-                        const SkBitmap& bitmap,
-                        const GrTextureParams* params,
-                        GrTexture** texture)
-        : fDevice(NULL)
-        , fTexture(NULL) {
-        SkASSERT(NULL != texture);
-        *texture = this->set(device, bitmap, params);
-    }
-
-    ~SkAutoCachedTexture() {
-        if (NULL != fTexture) {
-            GrUnlockAndUnrefCachedBitmapTexture(fTexture);
-        }
-    }
-
-    GrTexture* set(SkGpuDevice* device,
-                   const SkBitmap& bitmap,
-                   const GrTextureParams* params) {
-        if (NULL != fTexture) {
-            GrUnlockAndUnrefCachedBitmapTexture(fTexture);
-            fTexture = NULL;
-        }
-        fDevice = device;
-        GrTexture* result = (GrTexture*)bitmap.getTexture();
-        if (NULL == result) {
-            // Cannot return the native texture so look it up in our cache
-            fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params);
-            result = fTexture;
-        }
-        return result;
-    }
-
-private:
-    SkGpuDevice* fDevice;
-    GrTexture*   fTexture;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct GrSkDrawProcs : public SkDrawProcs {
-public:
-    GrContext* fContext;
-    GrTextContext* fTextContext;
-    GrFontScaler* fFontScaler;  // cached in the skia glyphcache
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) {
-    switch (config) {
-        case kAlpha_8_GrPixelConfig:
-            *isOpaque = false;
-            return SkBitmap::kA8_Config;
-        case kRGB_565_GrPixelConfig:
-            *isOpaque = true;
-            return SkBitmap::kRGB_565_Config;
-        case kRGBA_4444_GrPixelConfig:
-            *isOpaque = false;
-            return SkBitmap::kARGB_4444_Config;
-        case kSkia8888_GrPixelConfig:
-            // we don't currently have a way of knowing whether
-            // a 8888 is opaque based on the config.
-            *isOpaque = false;
-            return SkBitmap::kARGB_8888_Config;
-        default:
-            *isOpaque = false;
-            return SkBitmap::kNo_Config;
-    }
-}
-
-/*
- * GrRenderTarget does not know its opaqueness, only its config, so we have
- * to make conservative guesses when we return an "equivalent" bitmap.
- */
-static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) {
-    bool isOpaque;
-    SkBitmap::Config config = grConfig2skConfig(renderTarget->config(), &isOpaque);
-
-    SkBitmap bitmap;
-    bitmap.setConfig(config, renderTarget->width(), renderTarget->height(), 0,
-                     isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
-    return bitmap;
-}
-
-SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) {
-    SkASSERT(NULL != surface);
-    if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) {
-        return NULL;
-    }
-    if (surface->asTexture()) {
-        return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture()));
-    } else {
-        return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget()));
-    }
-}
-
-SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture)
-    : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {
-    this->initFromRenderTarget(context, texture->asRenderTarget(), false);
-}
-
-SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget)
-    : SkBitmapDevice(make_bitmap(context, renderTarget)) {
-    this->initFromRenderTarget(context, renderTarget, false);
-}
-
-void SkGpuDevice::initFromRenderTarget(GrContext* context,
-                                       GrRenderTarget* renderTarget,
-                                       bool cached) {
-    fDrawProcs = NULL;
-
-    fContext = context;
-    fContext->ref();
-
-    fRenderTarget = NULL;
-    fNeedClear = false;
-
-    SkASSERT(NULL != renderTarget);
-    fRenderTarget = renderTarget;
-    fRenderTarget->ref();
-
-    // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref
-    // on the RT but not vice-versa.
-    // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without
-    // busting chrome (for a currently unknown reason).
-    GrSurface* surface = fRenderTarget->asTexture();
-    if (NULL == surface) {
-        surface = fRenderTarget;
-    }
-    SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (surface, cached));
-
-    this->setPixelRef(pr, 0)->unref();
-}
-
-SkGpuDevice::SkGpuDevice(GrContext* context,
-                         SkBitmap::Config config,
-                         int width,
-                         int height,
-                         int sampleCount)
-    : SkBitmapDevice(config, width, height, false /*isOpaque*/) {
-
-    fDrawProcs = NULL;
-
-    fContext = context;
-    fContext->ref();
-
-    fRenderTarget = NULL;
-    fNeedClear = false;
-
-    if (config != SkBitmap::kRGB_565_Config) {
-        config = SkBitmap::kARGB_8888_Config;
-    }
-
-    GrTextureDesc desc;
-    desc.fFlags = kRenderTarget_GrTextureFlagBit;
-    desc.fWidth = width;
-    desc.fHeight = height;
-    desc.fConfig = SkBitmapConfig2GrPixelConfig(config);
-    desc.fSampleCnt = sampleCount;
-
-    SkAutoTUnref<GrTexture> texture(fContext->createUncachedTexture(desc, NULL, 0));
-
-    if (NULL != texture) {
-        fRenderTarget = texture->asRenderTarget();
-        fRenderTarget->ref();
-
-        SkASSERT(NULL != fRenderTarget);
-
-        // wrap the bitmap with a pixelref to expose our texture
-        SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (texture));
-        this->setPixelRef(pr, 0)->unref();
-    } else {
-        GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",
-                 width, height);
-        SkASSERT(false);
-    }
-}
-
-SkGpuDevice::~SkGpuDevice() {
-    if (fDrawProcs) {
-        delete fDrawProcs;
-    }
-
-    // The GrContext takes a ref on the target. We don't want to cause the render
-    // target to be unnecessarily kept alive.
-    if (fContext->getRenderTarget() == fRenderTarget) {
-        fContext->setRenderTarget(NULL);
-    }
-
-    if (fContext->getClip() == &fClipData) {
-        fContext->setClip(NULL);
-    }
-
-    SkSafeUnref(fRenderTarget);
-    fContext->unref();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkGpuDevice::makeRenderTargetCurrent() {
-    DO_DEFERRED_CLEAR();
-    fContext->setRenderTarget(fRenderTarget);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-namespace {
-GrPixelConfig config8888_to_grconfig_and_flags(SkCanvas::Config8888 config8888, uint32_t* flags) {
-    switch (config8888) {
-        case SkCanvas::kNative_Premul_Config8888:
-            *flags = 0;
-            return kSkia8888_GrPixelConfig;
-        case SkCanvas::kNative_Unpremul_Config8888:
-            *flags = GrContext::kUnpremul_PixelOpsFlag;
-            return kSkia8888_GrPixelConfig;
-        case SkCanvas::kBGRA_Premul_Config8888:
-            *flags = 0;
-            return kBGRA_8888_GrPixelConfig;
-        case SkCanvas::kBGRA_Unpremul_Config8888:
-            *flags = GrContext::kUnpremul_PixelOpsFlag;
-            return kBGRA_8888_GrPixelConfig;
-        case SkCanvas::kRGBA_Premul_Config8888:
-            *flags = 0;
-            return kRGBA_8888_GrPixelConfig;
-        case SkCanvas::kRGBA_Unpremul_Config8888:
-            *flags = GrContext::kUnpremul_PixelOpsFlag;
-            return kRGBA_8888_GrPixelConfig;
-        default:
-            GrCrash("Unexpected Config8888.");
-            *flags = 0; // suppress warning
-            return kSkia8888_GrPixelConfig;
-    }
-}
-}
-
-bool SkGpuDevice::onReadPixels(const SkBitmap& bitmap,
-                               int x, int y,
-                               SkCanvas::Config8888 config8888) {
-    DO_DEFERRED_CLEAR();
-    SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
-    SkASSERT(!bitmap.isNull());
-    SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height())));
-
-    SkAutoLockPixels alp(bitmap);
-    GrPixelConfig config;
-    uint32_t flags;
-    config = config8888_to_grconfig_and_flags(config8888, &flags);
-    return fContext->readRenderTargetPixels(fRenderTarget,
-                                            x, y,
-                                            bitmap.width(),
-                                            bitmap.height(),
-                                            config,
-                                            bitmap.getPixels(),
-                                            bitmap.rowBytes(),
-                                            flags);
-}
-
-void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y,
-                              SkCanvas::Config8888 config8888) {
-    SkAutoLockPixels alp(bitmap);
-    if (!bitmap.readyToDraw()) {
-        return;
-    }
-
-    GrPixelConfig config;
-    uint32_t flags;
-    if (SkBitmap::kARGB_8888_Config == bitmap.config()) {
-        config = config8888_to_grconfig_and_flags(config8888, &flags);
-    } else {
-        flags = 0;
-        config= SkBitmapConfig2GrPixelConfig(bitmap.config());
-    }
-
-    fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(),
-                               config, bitmap.getPixels(), bitmap.rowBytes(), flags);
-}
-
-void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {
-    INHERITED::onAttachToCanvas(canvas);
-
-    // Canvas promises that this ptr is valid until onDetachFromCanvas is called
-    fClipData.fClipStack = canvas->getClipStack();
-}
-
-void SkGpuDevice::onDetachFromCanvas() {
-    INHERITED::onDetachFromCanvas();
-    fClipData.fClipStack = NULL;
-}
-
-// call this every draw call, to ensure that the context reflects our state,
-// and not the state from some other canvas/device
-void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) {
-    SkASSERT(NULL != fClipData.fClipStack);
-
-    fContext->setRenderTarget(fRenderTarget);
-
-    SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack);
-
-    if (forceIdentity) {
-        fContext->setIdentityMatrix();
-    } else {
-        fContext->setMatrix(*draw.fMatrix);
-    }
-    fClipData.fOrigin = this->getOrigin();
-
-    fContext->setClip(&fClipData);
-
-    DO_DEFERRED_CLEAR();
-}
-
-GrRenderTarget* SkGpuDevice::accessRenderTarget() {
-    DO_DEFERRED_CLEAR();
-    return fRenderTarget;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,
-                  shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5,
-                  shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch);
-
-namespace {
-
-// converts a SkPaint to a GrPaint, ignoring the skPaint's shader
-// justAlpha indicates that skPaint's alpha should be used rather than the color
-// Callers may subsequently modify the GrPaint. Setting constantColor indicates
-// that the final paint will draw the same color at every pixel. This allows
-// an optimization where the the color filter can be applied to the skPaint's
-// color once while converting to GrPaint and then ignored.
-inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev,
-                                    const SkPaint& skPaint,
-                                    bool justAlpha,
-                                    bool constantColor,
-                                    GrPaint* grPaint) {
-
-    grPaint->setDither(skPaint.isDither());
-    grPaint->setAntiAlias(skPaint.isAntiAlias());
-
-    SkXfermode::Coeff sm;
-    SkXfermode::Coeff dm;
-
-    SkXfermode* mode = skPaint.getXfermode();
-    GrEffectRef* xferEffect = NULL;
-    if (SkXfermode::AsNewEffectOrCoeff(mode, &xferEffect, &sm, &dm)) {
-        if (NULL != xferEffect) {
-            grPaint->addColorEffect(xferEffect)->unref();
-            sm = SkXfermode::kOne_Coeff;
-            dm = SkXfermode::kZero_Coeff;
-        }
-    } else {
-        //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)
-#if 0
-        return false;
-#else
-        // Fall back to src-over
-        sm = SkXfermode::kOne_Coeff;
-        dm = SkXfermode::kISA_Coeff;
-#endif
-    }
-    grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
-
-    if (justAlpha) {
-        uint8_t alpha = skPaint.getAlpha();
-        grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha));
-        // justAlpha is currently set to true only if there is a texture,
-        // so constantColor should not also be true.
-        SkASSERT(!constantColor);
-    } else {
-        grPaint->setColor(SkColor2GrColor(skPaint.getColor()));
-    }
-
-    SkColorFilter* colorFilter = skPaint.getColorFilter();
-    if (NULL != colorFilter) {
-        // if the source color is a constant then apply the filter here once rather than per pixel
-        // in a shader.
-        if (constantColor) {
-            SkColor filtered = colorFilter->filterColor(skPaint.getColor());
-            grPaint->setColor(SkColor2GrColor(filtered));
-        } else {
-            SkAutoTUnref<GrEffectRef> effect(colorFilter->asNewEffect(dev->context()));
-            if (NULL != effect.get()) {
-                grPaint->addColorEffect(effect);
-            }
-        }
-    }
-
-    return true;
-}
-
-// This function is similar to skPaint2GrPaintNoShader but also converts
-// skPaint's shader to a GrTexture/GrEffectStage if possible. The texture to
-// be used is set on grPaint and returned in param act. constantColor has the
-// same meaning as in skPaint2GrPaintNoShader.
-inline bool skPaint2GrPaintShader(SkGpuDevice* dev,
-                                  const SkPaint& skPaint,
-                                  bool constantColor,
-                                  GrPaint* grPaint) {
-    SkShader* shader = skPaint.getShader();
-    if (NULL == shader) {
-        return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint);
-    }
-
-    // SkShader::asNewEffect() may do offscreen rendering. Setup default drawing state
-    // Also require shader to set the render target .
-    GrContext::AutoWideOpenIdentityDraw awo(dev->context(), NULL);
-    GrContext::AutoRenderTarget(dev->context(), NULL);
-
-    // setup the shader as the first color effect on the paint
-    SkAutoTUnref<GrEffectRef> effect(shader->asNewEffect(dev->context(), skPaint));
-    if (NULL != effect.get()) {
-        grPaint->addColorEffect(effect);
-        // Now setup the rest of the paint.
-        return skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint);
-    } else {
-        // We still don't have SkColorShader::asNewEffect() implemented.
-        SkShader::GradientInfo info;
-        SkColor                color;
-
-        info.fColors = &color;
-        info.fColorOffsets = NULL;
-        info.fColorCount = 1;
-        if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {
-            SkPaint copy(skPaint);
-            copy.setShader(NULL);
-            // modulate the paint alpha by the shader's solid color alpha
-            U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
-            copy.setColor(SkColorSetA(color, newA));
-            return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint);
-        } else {
-            return false;
-        }
-    }
-}
-}
-
-///////////////////////////////////////////////////////////////////////////////
-void SkGpuDevice::getGlobalBounds(SkIRect* bounds) const {
-    if (NULL != bounds) {
-        const SkIPoint& origin = this->getOrigin();
-        bounds->setXYWH(origin.x(), origin.y(),
-                        this->width(), this->height());
-    }
-}
-
-SkBitmap::Config SkGpuDevice::config() const {
-    if (NULL == fRenderTarget) {
-        return SkBitmap::kNo_Config;
-    }
-
-    bool isOpaque;
-    return grConfig2skConfig(fRenderTarget->config(), &isOpaque);
-}
-
-void SkGpuDevice::clear(SkColor color) {
-    SkIRect rect = SkIRect::MakeWH(this->width(), this->height());
-    fContext->clear(&rect, SkColor2GrColor(color), true, fRenderTarget);
-    fNeedClear = false;
-}
-
-void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
-    CHECK_SHOULD_DRAW(draw, false);
-
-    GrPaint grPaint;
-    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
-        return;
-    }
-
-    fContext->drawPaint(grPaint);
-}
-
-// must be in SkCanvas::PointMode order
-static const GrPrimitiveType gPointMode2PrimtiveType[] = {
-    kPoints_GrPrimitiveType,
-    kLines_GrPrimitiveType,
-    kLineStrip_GrPrimitiveType
-};
-
-void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
-                             size_t count, const SkPoint pts[], const SkPaint& paint) {
-    CHECK_FOR_ANNOTATION(paint);
-    CHECK_SHOULD_DRAW(draw, false);
-
-    SkScalar width = paint.getStrokeWidth();
-    if (width < 0) {
-        return;
-    }
-
-    // we only handle hairlines and paints without path effects or mask filters,
-    // else we let the SkDraw call our drawPath()
-    if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) {
-        draw.drawPoints(mode, count, pts, paint, true);
-        return;
-    }
-
-    GrPaint grPaint;
-    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
-        return;
-    }
-
-    fContext->drawVertices(grPaint,
-                           gPointMode2PrimtiveType[mode],
-                           count,
-                           (GrPoint*)pts,
-                           NULL,
-                           NULL,
-                           NULL,
-                           0);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
-                           const SkPaint& paint) {
-    CHECK_FOR_ANNOTATION(paint);
-    CHECK_SHOULD_DRAW(draw, false);
-
-    bool doStroke = paint.getStyle() != SkPaint::kFill_Style;
-    SkScalar width = paint.getStrokeWidth();
-
-    /*
-        We have special code for hairline strokes, miter-strokes, bevel-stroke
-        and fills. Anything else we just call our path code.
-     */
-    bool usePath = doStroke && width > 0 &&
-                   (paint.getStrokeJoin() == SkPaint::kRound_Join ||
-                    (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty()));
-    // another two reasons we might need to call drawPath...
-    if (paint.getMaskFilter() || paint.getPathEffect()) {
-        usePath = true;
-    }
-    if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) {
-#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
-        if (doStroke) {
-#endif
-            usePath = true;
-#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
-        } else {
-            usePath = !fContext->getMatrix().preservesRightAngles();
-        }
-#endif
-    }
-    // until we can both stroke and fill rectangles
-    if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
-        usePath = true;
-    }
-
-    if (usePath) {
-        SkPath path;
-        path.addRect(rect);
-        this->drawPath(draw, path, paint, NULL, true);
-        return;
-    }
-
-    GrPaint grPaint;
-    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
-        return;
-    }
-
-    if (!doStroke) {
-        fContext->drawRect(grPaint, rect);
-    } else {
-        SkStrokeRec stroke(paint);
-        fContext->drawRect(grPaint, rect, &stroke);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect,
-                           const SkPaint& paint) {
-    CHECK_FOR_ANNOTATION(paint);
-    CHECK_SHOULD_DRAW(draw, false);
-
-    bool usePath = !rect.isSimple();
-    // another two reasons we might need to call drawPath...
-    if (paint.getMaskFilter() || paint.getPathEffect()) {
-        usePath = true;
-    }
-    // until we can rotate rrects...
-    if (!usePath && !fContext->getMatrix().rectStaysRect()) {
-        usePath = true;
-    }
-
-    if (usePath) {
-        SkPath path;
-        path.addRRect(rect);
-        this->drawPath(draw, path, paint, NULL, true);
-        return;
-    }
-
-    GrPaint grPaint;
-    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
-        return;
-    }
-
-    SkStrokeRec stroke(paint);
-    fContext->drawRRect(grPaint, rect, stroke);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,
-                           const SkPaint& paint) {
-    CHECK_FOR_ANNOTATION(paint);
-    CHECK_SHOULD_DRAW(draw, false);
-
-    bool usePath = false;
-    // some basic reasons we might need to call drawPath...
-    if (paint.getMaskFilter() || paint.getPathEffect()) {
-        usePath = true;
-    }
-
-    if (usePath) {
-        SkPath path;
-        path.addOval(oval);
-        this->drawPath(draw, path, paint, NULL, true);
-        return;
-    }
-
-    GrPaint grPaint;
-    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
-        return;
-    }
-    SkStrokeRec stroke(paint);
-
-    fContext->drawOval(grPaint, oval, stroke);
-}
-
-#include "SkMaskFilter.h"
-#include "SkBounder.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-// helpers for applying mask filters
-namespace {
-
-// Draw a mask using the supplied paint. Since the coverage/geometry
-// is already burnt into the mask this boils down to a rect draw.
-// Return true if the mask was successfully drawn.
-bool draw_mask(GrContext* context, const SkRect& maskRect,
-               GrPaint* grp, GrTexture* mask) {
-    GrContext::AutoMatrix am;
-    if (!am.setIdentity(context, grp)) {
-        return false;
-    }
-
-    SkMatrix matrix;
-    matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);
-    matrix.postIDiv(mask->width(), mask->height());
-
-    grp->addCoverageEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref();
-    context->drawRect(*grp, maskRect);
-    return true;
-}
-
-bool draw_with_mask_filter(GrContext* context, const SkPath& devPath,
-                           SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder,
-                           GrPaint* grp, SkPaint::Style style) {
-    SkMask  srcM, dstM;
-
-    if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM,
-                            SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
-        return false;
-    }
-    SkAutoMaskFreeImage autoSrc(srcM.fImage);
-
-    if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) {
-        return false;
-    }
-    // this will free-up dstM when we're done (allocated in filterMask())
-    SkAutoMaskFreeImage autoDst(dstM.fImage);
-
-    if (clip.quickReject(dstM.fBounds)) {
-        return false;
-    }
-    if (bounder && !bounder->doIRect(dstM.fBounds)) {
-        return false;
-    }
-
-    // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
-    // the current clip (and identity matrix) and GrPaint settings
-    GrTextureDesc desc;
-    desc.fWidth = dstM.fBounds.width();
-    desc.fHeight = dstM.fBounds.height();
-    desc.fConfig = kAlpha_8_GrPixelConfig;
-
-    GrAutoScratchTexture ast(context, desc);
-    GrTexture* texture = ast.texture();
-
-    if (NULL == texture) {
-        return false;
-    }
-    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                               dstM.fImage, dstM.fRowBytes);
-
-    SkRect maskRect = SkRect::Make(dstM.fBounds);
-
-    return draw_mask(context, maskRect, grp, texture);
-}
-
-// Create a mask of 'devPath' and place the result in 'mask'. Return true on
-// success; false otherwise.
-bool create_mask_GPU(GrContext* context,
-                     const SkRect& maskRect,
-                     const SkPath& devPath,
-                     const SkStrokeRec& stroke,
-                     bool doAA,
-                     GrAutoScratchTexture* mask) {
-    GrTextureDesc desc;
-    desc.fFlags = kRenderTarget_GrTextureFlagBit;
-    desc.fWidth = SkScalarCeilToInt(maskRect.width());
-    desc.fHeight = SkScalarCeilToInt(maskRect.height());
-    // We actually only need A8, but it often isn't supported as a
-    // render target so default to RGBA_8888
-    desc.fConfig = kRGBA_8888_GrPixelConfig;
-    if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
-        desc.fConfig = kAlpha_8_GrPixelConfig;
-    }
-
-    mask->set(context, desc);
-    if (NULL == mask->texture()) {
-        return false;
-    }
-
-    GrTexture* maskTexture = mask->texture();
-    SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
-
-    GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget());
-    GrContext::AutoClip ac(context, clipRect);
-
-    context->clear(NULL, 0x0, true);
-
-    GrPaint tempPaint;
-    if (doAA) {
-        tempPaint.setAntiAlias(true);
-        // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
-        // blend coeff of zero requires dual source blending support in order
-        // to properly blend partially covered pixels. This means the AA
-        // code path may not be taken. So we use a dst blend coeff of ISA. We
-        // could special case AA draws to a dst surface with known alpha=0 to
-        // use a zero dst coeff when dual source blending isn't available.
-        tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
-    }
-
-    GrContext::AutoMatrix am;
-
-    // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint.
-    SkMatrix translate;
-    translate.setTranslate(-maskRect.fLeft, -maskRect.fTop);
-    am.set(context, translate);
-    context->drawPath(tempPaint, devPath, stroke);
-    return true;
-}
-
-SkBitmap wrap_texture(GrTexture* texture) {
-    SkBitmap result;
-    bool dummy;
-    SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy);
-    result.setConfig(config, texture->width(), texture->height());
-    result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();
-    return result;
-}
-
-};
-
-void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
-                           const SkPaint& paint, const SkMatrix* prePathMatrix,
-                           bool pathIsMutable) {
-    CHECK_FOR_ANNOTATION(paint);
-    CHECK_SHOULD_DRAW(draw, false);
-
-    GrPaint grPaint;
-    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
-        return;
-    }
-
-    // can we cheat, and treat a thin stroke as a hairline w/ coverage
-    // if we can, we draw lots faster (raster device does this same test)
-    SkScalar hairlineCoverage;
-    bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage);
-    if (doHairLine) {
-        grPaint.setCoverage(SkScalarRoundToInt(hairlineCoverage * grPaint.getCoverage()));
-    }
-
-    // If we have a prematrix, apply it to the path, optimizing for the case
-    // where the original path can in fact be modified in place (even though
-    // its parameter type is const).
-    SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
-    SkPath  tmpPath, effectPath;
-
-    if (prePathMatrix) {
-        SkPath* result = pathPtr;
-
-        if (!pathIsMutable) {
-            result = &tmpPath;
-            pathIsMutable = true;
-        }
-        // should I push prePathMatrix on our MV stack temporarily, instead
-        // of applying it here? See SkDraw.cpp
-        pathPtr->transform(*prePathMatrix, result);
-        pathPtr = result;
-    }
-    // at this point we're done with prePathMatrix
-    SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
-
-    SkStrokeRec stroke(paint);
-    SkPathEffect* pathEffect = paint.getPathEffect();
-    const SkRect* cullRect = NULL;  // TODO: what is our bounds?
-    if (pathEffect && pathEffect->filterPath(&effectPath, *pathPtr, &stroke,
-                                             cullRect)) {
-        pathPtr = &effectPath;
-    }
-
-    if (!pathEffect && doHairLine) {
-        stroke.setHairlineStyle();
-    }
-
-    if (paint.getMaskFilter()) {
-        if (!stroke.isHairlineStyle()) {
-            if (stroke.applyToPath(&tmpPath, *pathPtr)) {
-                pathPtr = &tmpPath;
-                pathIsMutable = true;
-                stroke.setFillStyle();
-            }
-        }
-
-        // avoid possibly allocating a new path in transform if we can
-        SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
-
-        // transform the path into device space
-        pathPtr->transform(fContext->getMatrix(), devPathPtr);
-
-        SkRect maskRect;
-        if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),
-                                                    draw.fClip->getBounds(),
-                                                    fContext->getMatrix(),
-                                                    &maskRect)) {
-            SkIRect finalIRect;
-            maskRect.roundOut(&finalIRect);
-            if (draw.fClip->quickReject(finalIRect)) {
-                // clipped out
-                return;
-            }
-            if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) {
-                // nothing to draw
-                return;
-            }
-
-            GrAutoScratchTexture mask;
-
-            if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke,
-                                grPaint.isAntiAlias(), &mask)) {
-                GrTexture* filtered;
-
-                if (paint.getMaskFilter()->filterMaskGPU(mask.texture(), maskRect, &filtered, true)) {
-                    // filterMaskGPU gives us ownership of a ref to the result
-                    SkAutoTUnref<GrTexture> atu(filtered);
-
-                    // If the scratch texture that we used as the filter src also holds the filter
-                    // result then we must detach so that this texture isn't recycled for a later
-                    // draw.
-                    if (filtered == mask.texture()) {
-                        mask.detach();
-                        filtered->unref(); // detach transfers GrAutoScratchTexture's ref to us.
-                    }
-
-                    if (draw_mask(fContext, maskRect, &grPaint, filtered)) {
-                        // This path is completely drawn
-                        return;
-                    }
-                }
-            }
-        }
-
-        // draw the mask on the CPU - this is a fallthrough path in case the
-        // GPU path fails
-        SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :
-                                                          SkPaint::kFill_Style;
-        draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(),
-                              *draw.fClip, draw.fBounder, &grPaint, style);
-        return;
-    }
-
-    fContext->drawPath(grPaint, *pathPtr, stroke);
-}
-
-static const int kBmpSmallTileSize = 1 << 10;
-
-static inline int get_tile_count(const SkIRect& srcRect, int tileSize)  {
-    int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1;
-    int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1;
-    return tilesX * tilesY;
-}
-
-static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) {
-    if (maxTileSize <= kBmpSmallTileSize) {
-        return maxTileSize;
-    }
-
-    size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);
-    size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);
-
-    maxTileTotalTileSize *= maxTileSize * maxTileSize;
-    smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;
-
-    if (maxTileTotalTileSize > 2 * smallTotalTileSize) {
-        return kBmpSmallTileSize;
-    } else {
-        return maxTileSize;
-    }
-}
-
-// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what
-// pixels from the bitmap are necessary.
-static void determine_clipped_src_rect(const GrContext* context,
-                                       const SkBitmap& bitmap,
-                                       const SkRect* srcRectPtr,
-                                       SkIRect* clippedSrcIRect) {
-    const GrClipData* clip = context->getClip();
-    clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL);
-    SkMatrix inv;
-    if (!context->getMatrix().invert(&inv)) {
-        clippedSrcIRect->setEmpty();
-        return;
-    }
-    SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect);
-    inv.mapRect(&clippedSrcRect);
-    if (NULL != srcRectPtr) {
-        if (!clippedSrcRect.intersect(*srcRectPtr)) {
-            clippedSrcIRect->setEmpty();
-            return;
-        }
-    }
-    clippedSrcRect.roundOut(clippedSrcIRect);
-    SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
-    if (!clippedSrcIRect->intersect(bmpBounds)) {
-        clippedSrcIRect->setEmpty();
-    }
-}
-
-bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
-                                   const GrTextureParams& params,
-                                   const SkRect* srcRectPtr,
-                                   int maxTileSize,
-                                   int* tileSize,
-                                   SkIRect* clippedSrcRect) const {
-    // if bitmap is explictly texture backed then just use the texture
-    if (NULL != bitmap.getTexture()) {
-        return false;
-    }
-
-    // if it's larger than the max tile size, then we have no choice but tiling.
-    if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) {
-        determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);
-        *tileSize = determine_tile_size(bitmap, *clippedSrcRect, maxTileSize);
-        return true;
-    }
-
-    if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) {
-        return false;
-    }
-
-    // if the entire texture is already in our cache then no reason to tile it
-    if (GrIsBitmapInCache(fContext, bitmap, &params)) {
-        return false;
-    }
-
-    // At this point we know we could do the draw by uploading the entire bitmap
-    // as a texture. However, if the texture would be large compared to the
-    // cache size and we don't require most of it for this draw then tile to
-    // reduce the amount of upload and cache spill.
-
-    // assumption here is that sw bitmap size is a good proxy for its size as
-    // a texture
-    size_t bmpSize = bitmap.getSize();
-    size_t cacheSize;
-    fContext->getTextureCacheLimits(NULL, &cacheSize);
-    if (bmpSize < cacheSize / 2) {
-        return false;
-    }
-
-    // Figure out how much of the src we will need based on the src rect and clipping.
-    determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);
-    *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile.
-    size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) *
-                           kBmpSmallTileSize * kBmpSmallTileSize;
-
-    return usedTileBytes < 2 * bmpSize;
-}
-
-void SkGpuDevice::drawBitmap(const SkDraw& draw,
-                             const SkBitmap& bitmap,
-                             const SkMatrix& m,
-                             const SkPaint& paint) {
-    // We cannot call drawBitmapRect here since 'm' could be anything
-    this->drawBitmapCommon(draw, bitmap, NULL, m, paint,
-                           SkCanvas::kNone_DrawBitmapRectFlag);
-}
-
-// This method outsets 'iRect' by 1 all around and then clamps its extents to
-// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner
-// of 'iRect' for all possible outsets/clamps.
-static inline void clamped_unit_outset_with_offset(SkIRect* iRect, SkPoint* offset,
-                                                   const SkIRect& clamp) {
-    iRect->outset(1, 1);
-
-    if (iRect->fLeft < clamp.fLeft) {
-        iRect->fLeft = clamp.fLeft;
-    } else {
-        offset->fX -= SK_Scalar1;
-    }
-    if (iRect->fTop < clamp.fTop) {
-        iRect->fTop = clamp.fTop;
-    } else {
-        offset->fY -= SK_Scalar1;
-    }
-
-    if (iRect->fRight > clamp.fRight) {
-        iRect->fRight = clamp.fRight;
-    }
-    if (iRect->fBottom > clamp.fBottom) {
-        iRect->fBottom = clamp.fBottom;
-    }
-}
-
-void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,
-                                   const SkBitmap& bitmap,
-                                   const SkRect* srcRectPtr,
-                                   const SkMatrix& m,
-                                   const SkPaint& paint,
-                                   SkCanvas::DrawBitmapRectFlags flags) {
-    CHECK_SHOULD_DRAW(draw, false);
-
-    SkRect srcRect;
-    if (NULL == srcRectPtr) {
-        srcRect.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
-    } else {
-        srcRect = *srcRectPtr;
-    }
-
-    if (paint.getMaskFilter()){
-        // Convert the bitmap to a shader so that the rect can be drawn
-        // through drawRect, which supports mask filters.
-        SkMatrix        newM(m);
-        SkBitmap        tmp;    // subset of bitmap, if necessary
-        const SkBitmap* bitmapPtr = &bitmap;
-        if (NULL != srcRectPtr) {
-            SkIRect iSrc;
-            srcRect.roundOut(&iSrc);
-
-            SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft),
-                                           SkIntToScalar(iSrc.fTop));
-
-            if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {
-                // In bleed mode we want to expand the src rect on all sides
-                // but stay within the bitmap bounds
-                SkIRect iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());
-                clamped_unit_outset_with_offset(&iSrc, &offset, iClampRect);
-            }
-
-            if (!bitmap.extractSubset(&tmp, iSrc)) {
-                return;     // extraction failed
-            }
-            bitmapPtr = &tmp;
-            srcRect.offset(-offset.fX, -offset.fY);
-            // The source rect has changed so update the matrix
-            newM.preTranslate(offset.fX, offset.fY);
-        }
-
-        SkPaint paintWithTexture(paint);
-        paintWithTexture.setShader(SkShader::CreateBitmapShader(*bitmapPtr,
-            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref();
-
-        // Transform 'newM' needs to be concatenated to the current matrix,
-        // rather than transforming the primitive directly, so that 'newM' will
-        // also affect the behavior of the mask filter.
-        SkMatrix drawMatrix;
-        drawMatrix.setConcat(fContext->getMatrix(), newM);
-        SkDraw transformedDraw(draw);
-        transformedDraw.fMatrix = &drawMatrix;
-
-        this->drawRect(transformedDraw, srcRect, paintWithTexture);
-
-        return;
-    }
-
-    fContext->concatMatrix(m);
-
-    GrTextureParams params;
-    SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel();
-    GrTextureParams::FilterMode textureFilterMode;
-    switch(paintFilterLevel) {
-        case SkPaint::kNone_FilterLevel:
-            textureFilterMode = GrTextureParams::kNone_FilterMode;
-            break;
-        case SkPaint::kLow_FilterLevel:
-            textureFilterMode = GrTextureParams::kBilerp_FilterMode;
-            break;
-        case SkPaint::kMedium_FilterLevel:
-            textureFilterMode = GrTextureParams::kMipMap_FilterMode;
-            break;
-        case SkPaint::kHigh_FilterLevel:
-            // Fall back to mips for now
-            textureFilterMode = GrTextureParams::kMipMap_FilterMode;
-            break;
-        default:
-            SkErrorInternals::SetError( kInvalidPaint_SkError,
-                                        "Sorry, I don't understand the filtering "
-                                        "mode you asked for.  Falling back to "
-                                        "MIPMaps.");
-            textureFilterMode = GrTextureParams::kMipMap_FilterMode;
-            break;
-
-    }
-
-    params.setFilterMode(textureFilterMode);
-
-    int maxTileSize = fContext->getMaxTextureSize();
-    if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) {
-        // We may need a skosh more room if we have to bump out the tile
-        // by 1 pixel all around
-        maxTileSize -= 2;
-    }
-    int tileSize;
-
-    SkIRect clippedSrcRect;
-    if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize,
-                               &clippedSrcRect)) {
-        this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize);
-    } else {
-        // take the simple case
-        this->internalDrawBitmap(bitmap, srcRect, params, paint, flags);
-    }
-}
-
-// Break 'bitmap' into several tiles to draw it since it has already
-// been determined to be too large to fit in VRAM
-void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,
-                                  const SkRect& srcRect,
-                                  const SkIRect& clippedSrcIRect,
-                                  const GrTextureParams& params,
-                                  const SkPaint& paint,
-                                  SkCanvas::DrawBitmapRectFlags flags,
-                                  int tileSize) {
-    SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect);
-
-    int nx = bitmap.width() / tileSize;
-    int ny = bitmap.height() / tileSize;
-    for (int x = 0; x <= nx; x++) {
-        for (int y = 0; y <= ny; y++) {
-            SkRect tileR;
-            tileR.set(SkIntToScalar(x * tileSize),
-                      SkIntToScalar(y * tileSize),
-                      SkIntToScalar((x + 1) * tileSize),
-                      SkIntToScalar((y + 1) * tileSize));
-
-            if (!SkRect::Intersects(tileR, clippedSrcRect)) {
-                continue;
-            }
-
-            if (!tileR.intersect(srcRect)) {
-                continue;
-            }
-
-            SkBitmap tmpB;
-            SkIRect iTileR;
-            tileR.roundOut(&iTileR);
-            SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft),
-                                           SkIntToScalar(iTileR.fTop));
-
-            if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) {
-                SkIRect iClampRect;
-
-                if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {
-                    // In bleed mode we want to always expand the tile on all edges
-                    // but stay within the bitmap bounds
-                    iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());
-                } else {
-                    // In texture-domain/clamp mode we only want to expand the
-                    // tile on edges interior to "srcRect" (i.e., we want to
-                    // not bleed across the original clamped edges)
-                    srcRect.roundOut(&iClampRect);
-                }
-
-                clamped_unit_outset_with_offset(&iTileR, &offset, iClampRect);
-            }
-
-            if (bitmap.extractSubset(&tmpB, iTileR)) {
-                // now offset it to make it "local" to our tmp bitmap
-                tileR.offset(-offset.fX, -offset.fY);
-                SkMatrix tmpM;
-                tmpM.setTranslate(offset.fX, offset.fY);
-                GrContext::AutoMatrix am;
-                am.setPreConcat(fContext, tmpM);
-                this->internalDrawBitmap(tmpB, tileR, params, paint, flags);
-            }
-        }
-    }
-}
-
-static bool has_aligned_samples(const SkRect& srcRect,
-                                const SkRect& transformedRect) {
-    // detect pixel disalignment
-    if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) -
-            transformedRect.left()) < COLOR_BLEED_TOLERANCE &&
-        SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) -
-            transformedRect.top()) < COLOR_BLEED_TOLERANCE &&
-        SkScalarAbs(transformedRect.width() - srcRect.width()) <
-            COLOR_BLEED_TOLERANCE &&
-        SkScalarAbs(transformedRect.height() - srcRect.height()) <
-            COLOR_BLEED_TOLERANCE) {
-        return true;
-    }
-    return false;
-}
-
-static bool may_color_bleed(const SkRect& srcRect,
-                            const SkRect& transformedRect,
-                            const SkMatrix& m) {
-    // Only gets called if has_aligned_samples returned false.
-    // So we can assume that sampling is axis aligned but not texel aligned.
-    SkASSERT(!has_aligned_samples(srcRect, transformedRect));
-    SkRect innerSrcRect(srcRect), innerTransformedRect,
-        outerTransformedRect(transformedRect);
-    innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
-    m.mapRect(&innerTransformedRect, innerSrcRect);
-
-    // The gap between outerTransformedRect and innerTransformedRect
-    // represents the projection of the source border area, which is
-    // problematic for color bleeding.  We must check whether any
-    // destination pixels sample the border area.
-    outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
-    innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
-    SkIRect outer, inner;
-    outerTransformedRect.round(&outer);
-    innerTransformedRect.round(&inner);
-    // If the inner and outer rects round to the same result, it means the
-    // border does not overlap any pixel centers. Yay!
-    return inner != outer;
-}
-
-
-/*
- *  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
- *  and that non-texture portion of the GrPaint has already been setup.
- */
-void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
-                                     const SkRect& srcRect,
-                                     const GrTextureParams& params,
-                                     const SkPaint& paint,
-                                     SkCanvas::DrawBitmapRectFlags flags) {
-    SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&
-             bitmap.height() <= fContext->getMaxTextureSize());
-
-    GrTexture* texture;
-    SkAutoCachedTexture act(this, bitmap, &params, &texture);
-    if (NULL == texture) {
-        return;
-    }
-
-    SkRect dstRect(srcRect);
-    SkRect paintRect;
-    SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width()));
-    SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height()));
-    paintRect.setLTRB(SkScalarMul(srcRect.fLeft,   wInv),
-                      SkScalarMul(srcRect.fTop,    hInv),
-                      SkScalarMul(srcRect.fRight,  wInv),
-                      SkScalarMul(srcRect.fBottom, hInv));
-
-    bool needsTextureDomain = false;
-    if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) &&
-        params.filterMode() != GrTextureParams::kNone_FilterMode) {
-        // Need texture domain if drawing a sub rect.
-        needsTextureDomain = srcRect.width() < bitmap.width() ||
-                             srcRect.height() < bitmap.height();
-        if (needsTextureDomain && fContext->getMatrix().rectStaysRect()) {
-            const SkMatrix& matrix = fContext->getMatrix();
-            // sampling is axis-aligned
-            SkRect transformedRect;
-            matrix.mapRect(&transformedRect, srcRect);
-
-            if (has_aligned_samples(srcRect, transformedRect)) {
-                // We could also turn off filtering here (but we already did a cache lookup with
-                // params).
-                needsTextureDomain = false;
-            } else {
-                needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix);
-            }
-        }
-    }
-
-    SkRect textureDomain = SkRect::MakeEmpty();
-    SkAutoTUnref<GrEffectRef> effect;
-    if (needsTextureDomain) {
-        // Use a constrained texture domain to avoid color bleeding
-        SkScalar left, top, right, bottom;
-        if (srcRect.width() > SK_Scalar1) {
-            SkScalar border = SK_ScalarHalf / texture->width();
-            left = paintRect.left() + border;
-            right = paintRect.right() - border;
-        } else {
-            left = right = SkScalarHalf(paintRect.left() + paintRect.right());
-        }
-        if (srcRect.height() > SK_Scalar1) {
-            SkScalar border = SK_ScalarHalf / texture->height();
-            top = paintRect.top() + border;
-            bottom = paintRect.bottom() - border;
-        } else {
-            top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom());
-        }
-        textureDomain.setLTRB(left, top, right, bottom);
-        effect.reset(GrTextureDomainEffect::Create(texture,
-                                                   SkMatrix::I(),
-                                                   textureDomain,
-                                                   GrTextureDomainEffect::kClamp_WrapMode,
-                                                   params.filterMode()));
-    } else {
-        effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params));
-    }
-
-    // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring
-    // the rest from the SkPaint.
-    GrPaint grPaint;
-    grPaint.addColorEffect(effect);
-    bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config());
-    if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) {
-        return;
-    }
-
-    fContext->drawRectToRect(grPaint, dstRect, paintRect, NULL);
-}
-
-static bool filter_texture(SkBaseDevice* device, GrContext* context,
-                           GrTexture* texture, SkImageFilter* filter,
-                           int w, int h, const SkMatrix& ctm, SkBitmap* result,
-                           SkIPoint* offset) {
-    SkASSERT(filter);
-    SkDeviceImageFilterProxy proxy(device);
-
-    if (filter->canFilterImageGPU()) {
-        // Save the render target and set it to NULL, so we don't accidentally draw to it in the
-        // filter.  Also set the clip wide open and the matrix to identity.
-        GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
-        return filter->filterImageGPU(&proxy, wrap_texture(texture), ctm, result, offset);
-    } else {
-        return false;
-    }
-}
-
-void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
-                             int left, int top, const SkPaint& paint) {
-    // drawSprite is defined to be in device coords.
-    CHECK_SHOULD_DRAW(draw, true);
-
-    SkAutoLockPixels alp(bitmap, !bitmap.getTexture());
-    if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
-        return;
-    }
-
-    int w = bitmap.width();
-    int h = bitmap.height();
-
-    GrTexture* texture;
-    // draw sprite uses the default texture params
-    SkAutoCachedTexture act(this, bitmap, NULL, &texture);
-
-    SkImageFilter* filter = paint.getImageFilter();
-    SkIPoint offset = SkIPoint::Make(left, top);
-    // This bitmap will own the filtered result as a texture.
-    SkBitmap filteredBitmap;
-
-    if (NULL != filter) {
-        SkMatrix matrix(*draw.fMatrix);
-        matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
-        if (filter_texture(this, fContext, texture, filter, w, h, matrix, &filteredBitmap,
-                           &offset)) {
-            texture = (GrTexture*) filteredBitmap.getTexture();
-            w = filteredBitmap.width();
-            h = filteredBitmap.height();
-        } else {
-            return;
-        }
-    }
-
-    GrPaint grPaint;
-    grPaint.addColorTextureEffect(texture, SkMatrix::I());
-
-    if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
-        return;
-    }
-
-    fContext->drawRectToRect(grPaint,
-                             SkRect::MakeXYWH(SkIntToScalar(offset.fX),
-                                              SkIntToScalar(offset.fY),
-                                              SkIntToScalar(w),
-                                              SkIntToScalar(h)),
-                             SkRect::MakeXYWH(0,
-                                              0,
-                                              SK_Scalar1 * w / texture->width(),
-                                              SK_Scalar1 * h / texture->height()));
-}
-
-void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
-                                 const SkRect* src, const SkRect& dst,
-                                 const SkPaint& paint,
-                                 SkCanvas::DrawBitmapRectFlags flags) {
-    SkMatrix    matrix;
-    SkRect      bitmapBounds, tmpSrc;
-
-    bitmapBounds.set(0, 0,
-                     SkIntToScalar(bitmap.width()),
-                     SkIntToScalar(bitmap.height()));
-
-    // Compute matrix from the two rectangles
-    if (NULL != src) {
-        tmpSrc = *src;
-    } else {
-        tmpSrc = bitmapBounds;
-    }
-    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
-
-    // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null.
-    if (NULL != src) {
-        if (!bitmapBounds.contains(tmpSrc)) {
-            if (!tmpSrc.intersect(bitmapBounds)) {
-                return; // nothing to draw
-            }
-        }
-    }
-
-    this->drawBitmapCommon(draw, bitmap, &tmpSrc, matrix, paint, flags);
-}
-
-void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
-                             int x, int y, const SkPaint& paint) {
-    // clear of the source device must occur before CHECK_SHOULD_DRAW
-    SkGpuDevice* dev = static_cast<SkGpuDevice*>(device);
-    if (dev->fNeedClear) {
-        // TODO: could check here whether we really need to draw at all
-        dev->clear(0x0);
-    }
-
-    // drawDevice is defined to be in device coords.
-    CHECK_SHOULD_DRAW(draw, true);
-
-    GrRenderTarget* devRT = dev->accessRenderTarget();
-    GrTexture* devTex;
-    if (NULL == (devTex = devRT->asTexture())) {
-        return;
-    }
-
-    const SkBitmap& bm = dev->accessBitmap(false);
-    int w = bm.width();
-    int h = bm.height();
-
-    SkImageFilter* filter = paint.getImageFilter();
-    // This bitmap will own the filtered result as a texture.
-    SkBitmap filteredBitmap;
-
-    if (NULL != filter) {
-        SkIPoint offset = SkIPoint::Make(0, 0);
-        SkMatrix matrix(*draw.fMatrix);
-        matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
-        if (filter_texture(this, fContext, devTex, filter, w, h, matrix, &filteredBitmap,
-                           &offset)) {
-            devTex = filteredBitmap.getTexture();
-            w = filteredBitmap.width();
-            h = filteredBitmap.height();
-            x += offset.fX;
-            y += offset.fY;
-        } else {
-            return;
-        }
-    }
-
-    GrPaint grPaint;
-    grPaint.addColorTextureEffect(devTex, SkMatrix::I());
-
-    if (!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
-        return;
-    }
-
-    SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x),
-                                      SkIntToScalar(y),
-                                      SkIntToScalar(w),
-                                      SkIntToScalar(h));
-
-    // The device being drawn may not fill up its texture (e.g. saveLayer uses approximate
-    // scratch texture).
-    SkRect srcRect = SkRect::MakeWH(SK_Scalar1 * w / devTex->width(),
-                                    SK_Scalar1 * h / devTex->height());
-
-    fContext->drawRectToRect(grPaint, dstRect, srcRect);
-}
-
-bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) {
-    return filter->canFilterImageGPU();
-}
-
-bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
-                              const SkMatrix& ctm,
-                              SkBitmap* result, SkIPoint* offset) {
-    // want explicitly our impl, so guard against a subclass of us overriding it
-    if (!this->SkGpuDevice::canHandleImageFilter(filter)) {
-        return false;
-    }
-
-    SkAutoLockPixels alp(src, !src.getTexture());
-    if (!src.getTexture() && !src.readyToDraw()) {
-        return false;
-    }
-
-    GrTexture* texture;
-    // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
-    // must be pushed upstack.
-    SkAutoCachedTexture act(this, src, NULL, &texture);
-
-    return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctm, result,
-                          offset);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-// must be in SkCanvas::VertexMode order
-static const GrPrimitiveType gVertexMode2PrimitiveType[] = {
-    kTriangles_GrPrimitiveType,
-    kTriangleStrip_GrPrimitiveType,
-    kTriangleFan_GrPrimitiveType,
-};
-
-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, false);
-
-    GrPaint grPaint;
-    // we ignore the shader if texs is null.
-    if (NULL == texs) {
-        if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) {
-            return;
-        }
-    } else {
-        if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) {
-            return;
-        }
-    }
-
-    if (NULL != xmode && NULL != texs && NULL != colors) {
-        if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) {
-            SkDebugf("Unsupported vertex-color/texture xfer mode.\n");
-#if 0
-            return
-#endif
-        }
-    }
-
-    SkAutoSTMalloc<128, GrColor> convertedColors(0);
-    if (NULL != colors) {
-        // need to convert byte order and from non-PM to PM
-        convertedColors.reset(vertexCount);
-        for (int i = 0; i < vertexCount; ++i) {
-            convertedColors[i] = SkColor2GrColor(colors[i]);
-        }
-        colors = convertedColors.get();
-    }
-    fContext->drawVertices(grPaint,
-                           gVertexMode2PrimitiveType[vmode],
-                           vertexCount,
-                           (GrPoint*) vertices,
-                           (GrPoint*) texs,
-                           colors,
-                           indices,
-                           indexCount);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static void GlyphCacheAuxProc(void* data) {
-    GrFontScaler* scaler = (GrFontScaler*)data;
-    SkSafeUnref(scaler);
-}
-
-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 = SkNEW_ARGS(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 = static_cast<GrSkDrawProcs*>(state.fDraw->fProcs);
-
-    if (NULL == procs->fFontScaler) {
-        procs->fFontScaler = get_gr_font_scaler(state.fCache);
-    }
-
-    procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
-                                                       glyph.getSubXFixed(),
-                                                       glyph.getSubYFixed()),
-                                         SkFixedFloorToFixed(fx),
-                                         SkFixedFloorToFixed(fy),
-                                         procs->fFontScaler);
-}
-
-SkDrawProcs* SkGpuDevice::initDrawForText(GrTextContext* context) {
-
-    // deferred allocation
-    if (NULL == fDrawProcs) {
-        fDrawProcs = SkNEW(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, false);
-
-    if (fContext->getMatrix().hasPerspective()) {
-        // this guy will just call our drawPath()
-        draw.drawText((const char*)text, byteLength, x, y, paint);
-    } else {
-        SkDraw myDraw(draw);
-
-        GrPaint grPaint;
-        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
-            return;
-        }
-
-        GrBitmapTextContext context(fContext, grPaint, paint.getColor());
-        myDraw.fProcs = this->initDrawForText(&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, false);
-
-    if (fContext->getMatrix().hasPerspective()) {
-        // this guy will just call our drawPath()
-        draw.drawPosText((const char*)text, byteLength, pos, constY,
-                         scalarsPerPos, paint);
-    } else {
-        SkDraw myDraw(draw);
-
-        GrPaint grPaint;
-        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
-            return;
-        }
-        GrBitmapTextContext context(fContext, grPaint, paint.getColor());
-        myDraw.fProcs = this->initDrawForText(&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, false);
-
-    SkASSERT(draw.fDevice == this);
-    draw.drawTextOnPath((const char*)text, len, path, m, paint);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
-    if (!paint.isLCDRenderText()) {
-        // we're cool with the paint as is
-        return false;
-    }
-
-    if (paint.getShader() ||
-        paint.getXfermode() || // unless its srcover
-        paint.getMaskFilter() ||
-        paint.getRasterizer() ||
-        paint.getColorFilter() ||
-        paint.getPathEffect() ||
-        paint.isFakeBoldText() ||
-        paint.getStyle() != SkPaint::kFill_Style) {
-        // turn off lcd
-        flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
-        flags->fHinting = paint.getHinting();
-        return true;
-    }
-    // we're cool with the paint as is
-    return false;
-}
-
-void SkGpuDevice::flush() {
-    DO_DEFERRED_CLEAR();
-    fContext->resolveRenderTarget(fRenderTarget);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkBaseDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config,
-                                                    int width, int height,
-                                                    bool isOpaque,
-                                                    Usage usage) {
-    GrTextureDesc desc;
-    desc.fConfig = fRenderTarget->config();
-    desc.fFlags = kRenderTarget_GrTextureFlagBit;
-    desc.fWidth = width;
-    desc.fHeight = height;
-    desc.fSampleCnt = fRenderTarget->numSamples();
-
-    SkAutoTUnref<GrTexture> texture;
-    // Skia's convention is to only clear a device if it is non-opaque.
-    bool needClear = !isOpaque;
-
-#if CACHE_COMPATIBLE_DEVICE_TEXTURES
-    // layers are never draw in repeat modes, so we can request an approx
-    // match and ignore any padding.
-    const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ?
-                                                GrContext::kApprox_ScratchTexMatch :
-                                                GrContext::kExact_ScratchTexMatch;
-    texture.reset(fContext->lockAndRefScratchTexture(desc, match));
-#else
-    texture.reset(fContext->createUncachedTexture(desc, NULL, 0));
-#endif
-    if (NULL != texture.get()) {
-        return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear));
-    } else {
-        GrPrintf("---- failed to create compatible device texture [%d %d]\n", width, height);
-        return NULL;
-    }
-}
-
-SkGpuDevice::SkGpuDevice(GrContext* context,
-                         GrTexture* texture,
-                         bool needClear)
-    : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {
-
-    SkASSERT(texture && texture->asRenderTarget());
-    // This constructor is called from onCreateCompatibleDevice. It has locked the RT in the texture
-    // cache. We pass true for the third argument so that it will get unlocked.
-    this->initFromRenderTarget(context, texture->asRenderTarget(), true);
-    fNeedClear = needClear;
-}
+/*

+ * Copyright 2011 Google Inc.

+ *

+ * Use of this source code is governed by a BSD-style license that can be

+ * found in the LICENSE file.

+ */

+

+#include "SkGpuDevice.h"

+

+#include "effects/GrTextureDomainEffect.h"

+#include "effects/GrSimpleTextureEffect.h"

+

+#include "GrContext.h"

+#include "GrBitmapTextContext.h"

+#if SK_DISTANCEFIELD_FONTS

+#include "GrDistanceFieldTextContext.h"

+#endif

+

+#include "SkGrTexturePixelRef.h"

+

+#include "SkColorFilter.h"

+#include "SkDeviceImageFilterProxy.h"

+#include "SkDrawProcs.h"

+#include "SkGlyphCache.h"

+#include "SkImageFilter.h"

+#include "SkPathEffect.h"

+#include "SkRRect.h"

+#include "SkStroke.h"

+#include "SkUtils.h"

+#include "SkErrorInternals.h"

+

+#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1

+

+#if 0

+    extern bool (*gShouldDrawProc)();

+    #define CHECK_SHOULD_DRAW(draw, forceI)                     \

+        do {                                                    \

+            if (gShouldDrawProc && !gShouldDrawProc()) return;  \

+            this->prepareDraw(draw, forceI);                    \

+        } while (0)

+#else

+    #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI)

+#endif

+

+// This constant represents the screen alignment criterion in texels for

+// requiring texture domain clamping to prevent color bleeding when drawing

+// a sub region of a larger source image.

+#define COLOR_BLEED_TOLERANCE SkFloatToScalar(0.001f)

+

+#define DO_DEFERRED_CLEAR()             \

+    do {                                \

+        if (fNeedClear) {               \

+            this->clear(SK_ColorTRANSPARENT); \

+        }                               \

+    } while (false)                     \

+

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

+

+#define CHECK_FOR_ANNOTATION(paint) \

+    do { if (paint.getAnnotation()) { return; } } while (0)

+

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

+

+

+class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable {

+public:

+    SkAutoCachedTexture()

+        : fDevice(NULL)

+        , fTexture(NULL) {

+    }

+

+    SkAutoCachedTexture(SkGpuDevice* device,

+                        const SkBitmap& bitmap,

+                        const GrTextureParams* params,

+                        GrTexture** texture)

+        : fDevice(NULL)

+        , fTexture(NULL) {

+        SkASSERT(NULL != texture);

+        *texture = this->set(device, bitmap, params);

+    }

+

+    ~SkAutoCachedTexture() {

+        if (NULL != fTexture) {

+            GrUnlockAndUnrefCachedBitmapTexture(fTexture);

+        }

+    }

+

+    GrTexture* set(SkGpuDevice* device,

+                   const SkBitmap& bitmap,

+                   const GrTextureParams* params) {

+        if (NULL != fTexture) {

+            GrUnlockAndUnrefCachedBitmapTexture(fTexture);

+            fTexture = NULL;

+        }

+        fDevice = device;

+        GrTexture* result = (GrTexture*)bitmap.getTexture();

+        if (NULL == result) {

+            // Cannot return the native texture so look it up in our cache

+            fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params);

+            result = fTexture;

+        }

+        return result;

+    }

+

+private:

+    SkGpuDevice* fDevice;

+    GrTexture*   fTexture;

+};

+

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

+

+struct GrSkDrawProcs : public SkDrawProcs {

+public:

+    GrContext* fContext;

+    GrTextContext* fTextContext;

+    GrFontScaler* fFontScaler;  // cached in the skia glyphcache

+};

+

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

+

+static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) {

+    switch (config) {

+        case kAlpha_8_GrPixelConfig:

+            *isOpaque = false;

+            return SkBitmap::kA8_Config;

+        case kRGB_565_GrPixelConfig:

+            *isOpaque = true;

+            return SkBitmap::kRGB_565_Config;

+        case kRGBA_4444_GrPixelConfig:

+            *isOpaque = false;

+            return SkBitmap::kARGB_4444_Config;

+        case kSkia8888_GrPixelConfig:

+            // we don't currently have a way of knowing whether

+            // a 8888 is opaque based on the config.

+            *isOpaque = false;

+            return SkBitmap::kARGB_8888_Config;

+        default:

+            *isOpaque = false;

+            return SkBitmap::kNo_Config;

+    }

+}

+

+/*

+ * GrRenderTarget does not know its opaqueness, only its config, so we have

+ * to make conservative guesses when we return an "equivalent" bitmap.

+ */

+static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) {

+    bool isOpaque;

+    SkBitmap::Config config = grConfig2skConfig(renderTarget->config(), &isOpaque);

+

+    SkBitmap bitmap;

+    bitmap.setConfig(config, renderTarget->width(), renderTarget->height(), 0,

+                     isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);

+    return bitmap;

+}

+

+SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) {

+    SkASSERT(NULL != surface);

+    if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) {

+        return NULL;

+    }

+    if (surface->asTexture()) {

+        return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture()));

+    } else {

+        return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget()));

+    }

+}

+

+SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture)

+    : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {

+    this->initFromRenderTarget(context, texture->asRenderTarget(), false);

+}

+

+SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget)

+    : SkBitmapDevice(make_bitmap(context, renderTarget)) {

+    this->initFromRenderTarget(context, renderTarget, false);

+}

+

+void SkGpuDevice::initFromRenderTarget(GrContext* context,

+                                       GrRenderTarget* renderTarget,

+                                       bool cached) {

+    fDrawProcs = NULL;

+

+    fContext = context;

+    fContext->ref();

+

+    fRenderTarget = NULL;

+    fNeedClear = false;

+

+    SkASSERT(NULL != renderTarget);

+    fRenderTarget = renderTarget;

+    fRenderTarget->ref();

+

+    // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref

+    // on the RT but not vice-versa.

+    // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without

+    // busting chrome (for a currently unknown reason).

+    GrSurface* surface = fRenderTarget->asTexture();

+    if (NULL == surface) {

+        surface = fRenderTarget;

+    }

+    SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (surface, cached));

+

+    this->setPixelRef(pr, 0)->unref();

+}

+

+SkGpuDevice::SkGpuDevice(GrContext* context,

+                         SkBitmap::Config config,

+                         int width,

+                         int height,

+                         int sampleCount)

+    : SkBitmapDevice(config, width, height, false /*isOpaque*/) {

+

+    fDrawProcs = NULL;

+

+    fContext = context;

+    fContext->ref();

+

+    fRenderTarget = NULL;

+    fNeedClear = false;

+

+    if (config != SkBitmap::kRGB_565_Config) {

+        config = SkBitmap::kARGB_8888_Config;

+    }

+

+    GrTextureDesc desc;

+    desc.fFlags = kRenderTarget_GrTextureFlagBit;

+    desc.fWidth = width;

+    desc.fHeight = height;

+    desc.fConfig = SkBitmapConfig2GrPixelConfig(config);

+    desc.fSampleCnt = sampleCount;

+

+    SkAutoTUnref<GrTexture> texture(fContext->createUncachedTexture(desc, NULL, 0));

+

+    if (NULL != texture) {

+        fRenderTarget = texture->asRenderTarget();

+        fRenderTarget->ref();

+

+        SkASSERT(NULL != fRenderTarget);

+

+        // wrap the bitmap with a pixelref to expose our texture

+        SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (texture));

+        this->setPixelRef(pr, 0)->unref();

+    } else {

+        GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",

+                 width, height);

+        SkASSERT(false);

+    }

+}

+

+SkGpuDevice::~SkGpuDevice() {

+    if (fDrawProcs) {

+        delete fDrawProcs;

+    }

+

+    // The GrContext takes a ref on the target. We don't want to cause the render

+    // target to be unnecessarily kept alive.

+    if (fContext->getRenderTarget() == fRenderTarget) {

+        fContext->setRenderTarget(NULL);

+    }

+

+    if (fContext->getClip() == &fClipData) {

+        fContext->setClip(NULL);

+    }

+

+    SkSafeUnref(fRenderTarget);

+    fContext->unref();

+}

+

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

+

+void SkGpuDevice::makeRenderTargetCurrent() {

+    DO_DEFERRED_CLEAR();

+    fContext->setRenderTarget(fRenderTarget);

+}

+

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

+

+namespace {

+GrPixelConfig config8888_to_grconfig_and_flags(SkCanvas::Config8888 config8888, uint32_t* flags) {

+    switch (config8888) {

+        case SkCanvas::kNative_Premul_Config8888:

+            *flags = 0;

+            return kSkia8888_GrPixelConfig;

+        case SkCanvas::kNative_Unpremul_Config8888:

+            *flags = GrContext::kUnpremul_PixelOpsFlag;

+            return kSkia8888_GrPixelConfig;

+        case SkCanvas::kBGRA_Premul_Config8888:

+            *flags = 0;

+            return kBGRA_8888_GrPixelConfig;

+        case SkCanvas::kBGRA_Unpremul_Config8888:

+            *flags = GrContext::kUnpremul_PixelOpsFlag;

+            return kBGRA_8888_GrPixelConfig;

+        case SkCanvas::kRGBA_Premul_Config8888:

+            *flags = 0;

+            return kRGBA_8888_GrPixelConfig;

+        case SkCanvas::kRGBA_Unpremul_Config8888:

+            *flags = GrContext::kUnpremul_PixelOpsFlag;

+            return kRGBA_8888_GrPixelConfig;

+        default:

+            GrCrash("Unexpected Config8888.");

+            *flags = 0; // suppress warning

+            return kSkia8888_GrPixelConfig;

+    }

+}

+}

+

+bool SkGpuDevice::onReadPixels(const SkBitmap& bitmap,

+                               int x, int y,

+                               SkCanvas::Config8888 config8888) {

+    DO_DEFERRED_CLEAR();

+    SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());

+    SkASSERT(!bitmap.isNull());

+    SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height())));

+

+    SkAutoLockPixels alp(bitmap);

+    GrPixelConfig config;

+    uint32_t flags;

+    config = config8888_to_grconfig_and_flags(config8888, &flags);

+    return fContext->readRenderTargetPixels(fRenderTarget,

+                                            x, y,

+                                            bitmap.width(),

+                                            bitmap.height(),

+                                            config,

+                                            bitmap.getPixels(),

+                                            bitmap.rowBytes(),

+                                            flags);

+}

+

+void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y,

+                              SkCanvas::Config8888 config8888) {

+    SkAutoLockPixels alp(bitmap);

+    if (!bitmap.readyToDraw()) {

+        return;

+    }

+

+    GrPixelConfig config;

+    uint32_t flags;

+    if (SkBitmap::kARGB_8888_Config == bitmap.config()) {

+        config = config8888_to_grconfig_and_flags(config8888, &flags);

+    } else {

+        flags = 0;

+        config= SkBitmapConfig2GrPixelConfig(bitmap.config());

+    }

+

+    fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(),

+                               config, bitmap.getPixels(), bitmap.rowBytes(), flags);

+}

+

+void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {

+    INHERITED::onAttachToCanvas(canvas);

+

+    // Canvas promises that this ptr is valid until onDetachFromCanvas is called

+    fClipData.fClipStack = canvas->getClipStack();

+}

+

+void SkGpuDevice::onDetachFromCanvas() {

+    INHERITED::onDetachFromCanvas();

+    fClipData.fClipStack = NULL;

+}

+

+// call this every draw call, to ensure that the context reflects our state,

+// and not the state from some other canvas/device

+void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) {

+    SkASSERT(NULL != fClipData.fClipStack);

+

+    fContext->setRenderTarget(fRenderTarget);

+

+    SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack);

+

+    if (forceIdentity) {

+        fContext->setIdentityMatrix();

+    } else {

+        fContext->setMatrix(*draw.fMatrix);

+    }

+    fClipData.fOrigin = this->getOrigin();

+

+    fContext->setClip(&fClipData);

+

+    DO_DEFERRED_CLEAR();

+}

+

+GrRenderTarget* SkGpuDevice::accessRenderTarget() {

+    DO_DEFERRED_CLEAR();

+    return fRenderTarget;

+}

+

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

+

+SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);

+SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch);

+SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch);

+SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);

+SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,

+                  shader_type_mismatch);

+SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5,

+                  shader_type_mismatch);

+SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch);

+SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch);

+

+namespace {

+

+// converts a SkPaint to a GrPaint, ignoring the skPaint's shader

+// justAlpha indicates that skPaint's alpha should be used rather than the color

+// Callers may subsequently modify the GrPaint. Setting constantColor indicates

+// that the final paint will draw the same color at every pixel. This allows

+// an optimization where the the color filter can be applied to the skPaint's

+// color once while converting to GrPaint and then ignored.

+inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev,

+                                    const SkPaint& skPaint,

+                                    bool justAlpha,

+                                    bool constantColor,

+                                    GrPaint* grPaint) {

+

+    grPaint->setDither(skPaint.isDither());

+    grPaint->setAntiAlias(skPaint.isAntiAlias());

+

+    SkXfermode::Coeff sm;

+    SkXfermode::Coeff dm;

+

+    SkXfermode* mode = skPaint.getXfermode();

+    GrEffectRef* xferEffect = NULL;

+    if (SkXfermode::AsNewEffectOrCoeff(mode, &xferEffect, &sm, &dm)) {

+        if (NULL != xferEffect) {

+            grPaint->addColorEffect(xferEffect)->unref();

+            sm = SkXfermode::kOne_Coeff;

+            dm = SkXfermode::kZero_Coeff;

+        }

+    } else {

+        //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)

+#if 0

+        return false;

+#else

+        // Fall back to src-over

+        sm = SkXfermode::kOne_Coeff;

+        dm = SkXfermode::kISA_Coeff;

+#endif

+    }

+    grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));

+

+    if (justAlpha) {

+        uint8_t alpha = skPaint.getAlpha();

+        grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha));

+        // justAlpha is currently set to true only if there is a texture,

+        // so constantColor should not also be true.

+        SkASSERT(!constantColor);

+    } else {

+        grPaint->setColor(SkColor2GrColor(skPaint.getColor()));

+    }

+

+    SkColorFilter* colorFilter = skPaint.getColorFilter();

+    if (NULL != colorFilter) {

+        // if the source color is a constant then apply the filter here once rather than per pixel

+        // in a shader.

+        if (constantColor) {

+            SkColor filtered = colorFilter->filterColor(skPaint.getColor());

+            grPaint->setColor(SkColor2GrColor(filtered));

+        } else {

+            SkAutoTUnref<GrEffectRef> effect(colorFilter->asNewEffect(dev->context()));

+            if (NULL != effect.get()) {

+                grPaint->addColorEffect(effect);

+            }

+        }

+    }

+

+    return true;

+}

+

+// This function is similar to skPaint2GrPaintNoShader but also converts

+// skPaint's shader to a GrTexture/GrEffectStage if possible. The texture to

+// be used is set on grPaint and returned in param act. constantColor has the

+// same meaning as in skPaint2GrPaintNoShader.

+inline bool skPaint2GrPaintShader(SkGpuDevice* dev,

+                                  const SkPaint& skPaint,

+                                  bool constantColor,

+                                  GrPaint* grPaint) {

+    SkShader* shader = skPaint.getShader();

+    if (NULL == shader) {

+        return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint);

+    }

+

+    // SkShader::asNewEffect() may do offscreen rendering. Setup default drawing state

+    // Also require shader to set the render target .

+    GrContext::AutoWideOpenIdentityDraw awo(dev->context(), NULL);

+    GrContext::AutoRenderTarget(dev->context(), NULL);

+

+    // setup the shader as the first color effect on the paint

+    SkAutoTUnref<GrEffectRef> effect(shader->asNewEffect(dev->context(), skPaint));

+    if (NULL != effect.get()) {

+        grPaint->addColorEffect(effect);

+        // Now setup the rest of the paint.

+        return skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint);

+    } else {

+        // We still don't have SkColorShader::asNewEffect() implemented.

+        SkShader::GradientInfo info;

+        SkColor                color;

+

+        info.fColors = &color;

+        info.fColorOffsets = NULL;

+        info.fColorCount = 1;

+        if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {

+            SkPaint copy(skPaint);

+            copy.setShader(NULL);

+            // modulate the paint alpha by the shader's solid color alpha

+            U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());

+            copy.setColor(SkColorSetA(color, newA));

+            return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint);

+        } else {

+            return false;

+        }

+    }

+}

+}

+

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

+void SkGpuDevice::getGlobalBounds(SkIRect* bounds) const {

+    if (NULL != bounds) {

+        const SkIPoint& origin = this->getOrigin();

+        bounds->setXYWH(origin.x(), origin.y(),

+                        this->width(), this->height());

+    }

+}

+

+SkBitmap::Config SkGpuDevice::config() const {

+    if (NULL == fRenderTarget) {

+        return SkBitmap::kNo_Config;

+    }

+

+    bool isOpaque;

+    return grConfig2skConfig(fRenderTarget->config(), &isOpaque);

+}

+

+void SkGpuDevice::clear(SkColor color) {

+    SkIRect rect = SkIRect::MakeWH(this->width(), this->height());

+    fContext->clear(&rect, SkColor2GrColor(color), true, fRenderTarget);

+    fNeedClear = false;

+}

+

+void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {

+    CHECK_SHOULD_DRAW(draw, false);

+

+    GrPaint grPaint;

+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {

+        return;

+    }

+

+    fContext->drawPaint(grPaint);

+}

+

+// must be in SkCanvas::PointMode order

+static const GrPrimitiveType gPointMode2PrimtiveType[] = {

+    kPoints_GrPrimitiveType,

+    kLines_GrPrimitiveType,

+    kLineStrip_GrPrimitiveType

+};

+

+void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,

+                             size_t count, const SkPoint pts[], const SkPaint& paint) {

+    CHECK_FOR_ANNOTATION(paint);

+    CHECK_SHOULD_DRAW(draw, false);

+

+    SkScalar width = paint.getStrokeWidth();

+    if (width < 0) {

+        return;

+    }

+

+    // we only handle hairlines and paints without path effects or mask filters,

+    // else we let the SkDraw call our drawPath()

+    if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) {

+        draw.drawPoints(mode, count, pts, paint, true);

+        return;

+    }

+

+    GrPaint grPaint;

+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {

+        return;

+    }

+

+    fContext->drawVertices(grPaint,

+                           gPointMode2PrimtiveType[mode],

+                           count,

+                           (GrPoint*)pts,

+                           NULL,

+                           NULL,

+                           NULL,

+                           0);

+}

+

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

+

+void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,

+                           const SkPaint& paint) {

+    CHECK_FOR_ANNOTATION(paint);

+    CHECK_SHOULD_DRAW(draw, false);

+

+    bool doStroke = paint.getStyle() != SkPaint::kFill_Style;

+    SkScalar width = paint.getStrokeWidth();

+

+    /*

+        We have special code for hairline strokes, miter-strokes, bevel-stroke

+        and fills. Anything else we just call our path code.

+     */

+    bool usePath = doStroke && width > 0 &&

+                   (paint.getStrokeJoin() == SkPaint::kRound_Join ||

+                    (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty()));

+    // another two reasons we might need to call drawPath...

+    if (paint.getMaskFilter() || paint.getPathEffect()) {

+        usePath = true;

+    }

+    if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) {

+#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)

+        if (doStroke) {

+#endif

+            usePath = true;

+#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)

+        } else {

+            usePath = !fContext->getMatrix().preservesRightAngles();

+        }

+#endif

+    }

+    // until we can both stroke and fill rectangles

+    if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {

+        usePath = true;

+    }

+

+    if (usePath) {

+        SkPath path;

+        path.addRect(rect);

+        this->drawPath(draw, path, paint, NULL, true);

+        return;

+    }

+

+    GrPaint grPaint;

+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {

+        return;

+    }

+

+    if (!doStroke) {

+        fContext->drawRect(grPaint, rect);

+    } else {

+        SkStrokeRec stroke(paint);

+        fContext->drawRect(grPaint, rect, &stroke);

+    }

+}

+

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

+

+void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect,

+                           const SkPaint& paint) {

+    CHECK_FOR_ANNOTATION(paint);

+    CHECK_SHOULD_DRAW(draw, false);

+

+    bool usePath = !rect.isSimple();

+    // another two reasons we might need to call drawPath...

+    if (paint.getMaskFilter() || paint.getPathEffect()) {

+        usePath = true;

+    }

+    // until we can rotate rrects...

+    if (!usePath && !fContext->getMatrix().rectStaysRect()) {

+        usePath = true;

+    }

+

+    if (usePath) {

+        SkPath path;

+        path.addRRect(rect);

+        this->drawPath(draw, path, paint, NULL, true);

+        return;

+    }

+

+    GrPaint grPaint;

+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {

+        return;

+    }

+

+    SkStrokeRec stroke(paint);

+    fContext->drawRRect(grPaint, rect, stroke);

+}

+

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

+

+void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,

+                           const SkPaint& paint) {

+    CHECK_FOR_ANNOTATION(paint);

+    CHECK_SHOULD_DRAW(draw, false);

+

+    bool usePath = false;

+    // some basic reasons we might need to call drawPath...

+    if (paint.getMaskFilter() || paint.getPathEffect()) {

+        usePath = true;

+    }

+

+    if (usePath) {

+        SkPath path;

+        path.addOval(oval);

+        this->drawPath(draw, path, paint, NULL, true);

+        return;

+    }

+

+    GrPaint grPaint;

+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {

+        return;

+    }

+    SkStrokeRec stroke(paint);

+

+    fContext->drawOval(grPaint, oval, stroke);

+}

+

+#include "SkMaskFilter.h"

+#include "SkBounder.h"

+

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

+

+// helpers for applying mask filters

+namespace {

+

+// Draw a mask using the supplied paint. Since the coverage/geometry

+// is already burnt into the mask this boils down to a rect draw.

+// Return true if the mask was successfully drawn.

+bool draw_mask(GrContext* context, const SkRect& maskRect,

+               GrPaint* grp, GrTexture* mask) {

+    GrContext::AutoMatrix am;

+    if (!am.setIdentity(context, grp)) {

+        return false;

+    }

+

+    SkMatrix matrix;

+    matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);

+    matrix.postIDiv(mask->width(), mask->height());

+

+    grp->addCoverageEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref();

+    context->drawRect(*grp, maskRect);

+    return true;

+}

+

+bool draw_with_mask_filter(GrContext* context, const SkPath& devPath,

+                           SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder,

+                           GrPaint* grp, SkPaint::Style style) {

+    SkMask  srcM, dstM;

+

+    if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM,

+                            SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {

+        return false;

+    }

+    SkAutoMaskFreeImage autoSrc(srcM.fImage);

+

+    if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) {

+        return false;

+    }

+    // this will free-up dstM when we're done (allocated in filterMask())

+    SkAutoMaskFreeImage autoDst(dstM.fImage);

+

+    if (clip.quickReject(dstM.fBounds)) {

+        return false;

+    }

+    if (bounder && !bounder->doIRect(dstM.fBounds)) {

+        return false;

+    }

+

+    // we now have a device-aligned 8bit mask in dstM, ready to be drawn using

+    // the current clip (and identity matrix) and GrPaint settings

+    GrTextureDesc desc;

+    desc.fWidth = dstM.fBounds.width();

+    desc.fHeight = dstM.fBounds.height();

+    desc.fConfig = kAlpha_8_GrPixelConfig;

+

+    GrAutoScratchTexture ast(context, desc);

+    GrTexture* texture = ast.texture();

+

+    if (NULL == texture) {

+        return false;

+    }

+    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,

+                               dstM.fImage, dstM.fRowBytes);

+

+    SkRect maskRect = SkRect::Make(dstM.fBounds);

+

+    return draw_mask(context, maskRect, grp, texture);

+}

+

+// Create a mask of 'devPath' and place the result in 'mask'. Return true on

+// success; false otherwise.

+bool create_mask_GPU(GrContext* context,

+                     const SkRect& maskRect,

+                     const SkPath& devPath,

+                     const SkStrokeRec& stroke,

+                     bool doAA,

+                     GrAutoScratchTexture* mask) {

+    GrTextureDesc desc;

+    desc.fFlags = kRenderTarget_GrTextureFlagBit;

+    desc.fWidth = SkScalarCeilToInt(maskRect.width());

+    desc.fHeight = SkScalarCeilToInt(maskRect.height());

+    // We actually only need A8, but it often isn't supported as a

+    // render target so default to RGBA_8888

+    desc.fConfig = kRGBA_8888_GrPixelConfig;

+    if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {

+        desc.fConfig = kAlpha_8_GrPixelConfig;

+    }

+

+    mask->set(context, desc);

+    if (NULL == mask->texture()) {

+        return false;

+    }

+

+    GrTexture* maskTexture = mask->texture();

+    SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());

+

+    GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget());

+    GrContext::AutoClip ac(context, clipRect);

+

+    context->clear(NULL, 0x0, true);

+

+    GrPaint tempPaint;

+    if (doAA) {

+        tempPaint.setAntiAlias(true);

+        // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst

+        // blend coeff of zero requires dual source blending support in order

+        // to properly blend partially covered pixels. This means the AA

+        // code path may not be taken. So we use a dst blend coeff of ISA. We

+        // could special case AA draws to a dst surface with known alpha=0 to

+        // use a zero dst coeff when dual source blending isn't available.

+        tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);

+    }

+

+    GrContext::AutoMatrix am;

+

+    // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint.

+    SkMatrix translate;

+    translate.setTranslate(-maskRect.fLeft, -maskRect.fTop);

+    am.set(context, translate);

+    context->drawPath(tempPaint, devPath, stroke);

+    return true;

+}

+

+SkBitmap wrap_texture(GrTexture* texture) {

+    SkBitmap result;

+    bool dummy;

+    SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy);

+    result.setConfig(config, texture->width(), texture->height());

+    result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();

+    return result;

+}

+

+};

+

+void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,

+                           const SkPaint& paint, const SkMatrix* prePathMatrix,

+                           bool pathIsMutable) {

+    CHECK_FOR_ANNOTATION(paint);

+    CHECK_SHOULD_DRAW(draw, false);

+

+    GrPaint grPaint;

+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {

+        return;

+    }

+

+    // can we cheat, and treat a thin stroke as a hairline w/ coverage

+    // if we can, we draw lots faster (raster device does this same test)

+    SkScalar hairlineCoverage;

+    bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage);

+    if (doHairLine) {

+        grPaint.setCoverage(SkScalarRoundToInt(hairlineCoverage * grPaint.getCoverage()));

+    }

+

+    // If we have a prematrix, apply it to the path, optimizing for the case

+    // where the original path can in fact be modified in place (even though

+    // its parameter type is const).

+    SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);

+    SkPath  tmpPath, effectPath;

+

+    if (prePathMatrix) {

+        SkPath* result = pathPtr;

+

+        if (!pathIsMutable) {

+            result = &tmpPath;

+            pathIsMutable = true;

+        }

+        // should I push prePathMatrix on our MV stack temporarily, instead

+        // of applying it here? See SkDraw.cpp

+        pathPtr->transform(*prePathMatrix, result);

+        pathPtr = result;

+    }

+    // at this point we're done with prePathMatrix

+    SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)

+

+    SkStrokeRec stroke(paint);

+    SkPathEffect* pathEffect = paint.getPathEffect();

+    const SkRect* cullRect = NULL;  // TODO: what is our bounds?

+    if (pathEffect && pathEffect->filterPath(&effectPath, *pathPtr, &stroke,

+                                             cullRect)) {

+        pathPtr = &effectPath;

+    }

+

+    if (!pathEffect && doHairLine) {

+        stroke.setHairlineStyle();

+    }

+

+    if (paint.getMaskFilter()) {

+        if (!stroke.isHairlineStyle()) {

+            if (stroke.applyToPath(&tmpPath, *pathPtr)) {

+                pathPtr = &tmpPath;

+                pathIsMutable = true;

+                stroke.setFillStyle();

+            }

+        }

+

+        // avoid possibly allocating a new path in transform if we can

+        SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;

+

+        // transform the path into device space

+        pathPtr->transform(fContext->getMatrix(), devPathPtr);

+

+        SkRect maskRect;

+        if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),

+                                                    draw.fClip->getBounds(),

+                                                    fContext->getMatrix(),

+                                                    &maskRect)) {

+            SkIRect finalIRect;

+            maskRect.roundOut(&finalIRect);

+            if (draw.fClip->quickReject(finalIRect)) {

+                // clipped out

+                return;

+            }

+            if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) {

+                // nothing to draw

+                return;

+            }

+

+            GrAutoScratchTexture mask;

+

+            if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke,

+                                grPaint.isAntiAlias(), &mask)) {

+                GrTexture* filtered;

+

+                if (paint.getMaskFilter()->filterMaskGPU(mask.texture(), maskRect, &filtered, true)) {

+                    // filterMaskGPU gives us ownership of a ref to the result

+                    SkAutoTUnref<GrTexture> atu(filtered);

+

+                    // If the scratch texture that we used as the filter src also holds the filter

+                    // result then we must detach so that this texture isn't recycled for a later

+                    // draw.

+                    if (filtered == mask.texture()) {

+                        mask.detach();

+                        filtered->unref(); // detach transfers GrAutoScratchTexture's ref to us.

+                    }

+

+                    if (draw_mask(fContext, maskRect, &grPaint, filtered)) {

+                        // This path is completely drawn

+                        return;

+                    }

+                }

+            }

+        }

+

+        // draw the mask on the CPU - this is a fallthrough path in case the

+        // GPU path fails

+        SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :

+                                                          SkPaint::kFill_Style;

+        draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(),

+                              *draw.fClip, draw.fBounder, &grPaint, style);

+        return;

+    }

+

+    fContext->drawPath(grPaint, *pathPtr, stroke);

+}

+

+static const int kBmpSmallTileSize = 1 << 10;

+

+static inline int get_tile_count(const SkIRect& srcRect, int tileSize)  {

+    int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1;

+    int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1;

+    return tilesX * tilesY;

+}

+

+static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) {

+    if (maxTileSize <= kBmpSmallTileSize) {

+        return maxTileSize;

+    }

+

+    size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);

+    size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);

+

+    maxTileTotalTileSize *= maxTileSize * maxTileSize;

+    smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;

+

+    if (maxTileTotalTileSize > 2 * smallTotalTileSize) {

+        return kBmpSmallTileSize;

+    } else {

+        return maxTileSize;

+    }

+}

+

+// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what

+// pixels from the bitmap are necessary.

+static void determine_clipped_src_rect(const GrContext* context,

+                                       const SkBitmap& bitmap,

+                                       const SkRect* srcRectPtr,

+                                       SkIRect* clippedSrcIRect) {

+    const GrClipData* clip = context->getClip();

+    clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL);

+    SkMatrix inv;

+    if (!context->getMatrix().invert(&inv)) {

+        clippedSrcIRect->setEmpty();

+        return;

+    }

+    SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect);

+    inv.mapRect(&clippedSrcRect);

+    if (NULL != srcRectPtr) {

+        if (!clippedSrcRect.intersect(*srcRectPtr)) {

+            clippedSrcIRect->setEmpty();

+            return;

+        }

+    }

+    clippedSrcRect.roundOut(clippedSrcIRect);

+    SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());

+    if (!clippedSrcIRect->intersect(bmpBounds)) {

+        clippedSrcIRect->setEmpty();

+    }

+}

+

+bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,

+                                   const GrTextureParams& params,

+                                   const SkRect* srcRectPtr,

+                                   int maxTileSize,

+                                   int* tileSize,

+                                   SkIRect* clippedSrcRect) const {

+    // if bitmap is explictly texture backed then just use the texture

+    if (NULL != bitmap.getTexture()) {

+        return false;

+    }

+

+    // if it's larger than the max tile size, then we have no choice but tiling.

+    if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) {

+        determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);

+        *tileSize = determine_tile_size(bitmap, *clippedSrcRect, maxTileSize);

+        return true;

+    }

+

+    if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) {

+        return false;

+    }

+

+    // if the entire texture is already in our cache then no reason to tile it

+    if (GrIsBitmapInCache(fContext, bitmap, &params)) {

+        return false;

+    }

+

+    // At this point we know we could do the draw by uploading the entire bitmap

+    // as a texture. However, if the texture would be large compared to the

+    // cache size and we don't require most of it for this draw then tile to

+    // reduce the amount of upload and cache spill.

+

+    // assumption here is that sw bitmap size is a good proxy for its size as

+    // a texture

+    size_t bmpSize = bitmap.getSize();

+    size_t cacheSize;

+    fContext->getTextureCacheLimits(NULL, &cacheSize);

+    if (bmpSize < cacheSize / 2) {

+        return false;

+    }

+

+    // Figure out how much of the src we will need based on the src rect and clipping.

+    determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);

+    *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile.

+    size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) *

+                           kBmpSmallTileSize * kBmpSmallTileSize;

+

+    return usedTileBytes < 2 * bmpSize;

+}

+

+void SkGpuDevice::drawBitmap(const SkDraw& draw,

+                             const SkBitmap& bitmap,

+                             const SkMatrix& m,

+                             const SkPaint& paint) {

+    // We cannot call drawBitmapRect here since 'm' could be anything

+    this->drawBitmapCommon(draw, bitmap, NULL, m, paint,

+                           SkCanvas::kNone_DrawBitmapRectFlag);

+}

+

+// This method outsets 'iRect' by 1 all around and then clamps its extents to

+// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner

+// of 'iRect' for all possible outsets/clamps.

+static inline void clamped_unit_outset_with_offset(SkIRect* iRect, SkPoint* offset,

+                                                   const SkIRect& clamp) {

+    iRect->outset(1, 1);

+

+    if (iRect->fLeft < clamp.fLeft) {

+        iRect->fLeft = clamp.fLeft;

+    } else {

+        offset->fX -= SK_Scalar1;

+    }

+    if (iRect->fTop < clamp.fTop) {

+        iRect->fTop = clamp.fTop;

+    } else {

+        offset->fY -= SK_Scalar1;

+    }

+

+    if (iRect->fRight > clamp.fRight) {

+        iRect->fRight = clamp.fRight;

+    }

+    if (iRect->fBottom > clamp.fBottom) {

+        iRect->fBottom = clamp.fBottom;

+    }

+}

+

+void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,

+                                   const SkBitmap& bitmap,

+                                   const SkRect* srcRectPtr,

+                                   const SkMatrix& m,

+                                   const SkPaint& paint,

+                                   SkCanvas::DrawBitmapRectFlags flags) {

+    CHECK_SHOULD_DRAW(draw, false);

+

+    SkRect srcRect;

+    if (NULL == srcRectPtr) {

+        srcRect.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));

+    } else {

+        srcRect = *srcRectPtr;

+    }

+

+    if (paint.getMaskFilter()){

+        // Convert the bitmap to a shader so that the rect can be drawn

+        // through drawRect, which supports mask filters.

+        SkMatrix        newM(m);

+        SkBitmap        tmp;    // subset of bitmap, if necessary

+        const SkBitmap* bitmapPtr = &bitmap;

+        if (NULL != srcRectPtr) {

+            SkIRect iSrc;

+            srcRect.roundOut(&iSrc);

+

+            SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft),

+                                           SkIntToScalar(iSrc.fTop));

+

+            if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {

+                // In bleed mode we want to expand the src rect on all sides

+                // but stay within the bitmap bounds

+                SkIRect iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());

+                clamped_unit_outset_with_offset(&iSrc, &offset, iClampRect);

+            }

+

+            if (!bitmap.extractSubset(&tmp, iSrc)) {

+                return;     // extraction failed

+            }

+            bitmapPtr = &tmp;

+            srcRect.offset(-offset.fX, -offset.fY);

+            // The source rect has changed so update the matrix

+            newM.preTranslate(offset.fX, offset.fY);

+        }

+

+        SkPaint paintWithTexture(paint);

+        paintWithTexture.setShader(SkShader::CreateBitmapShader(*bitmapPtr,

+            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref();

+

+        // Transform 'newM' needs to be concatenated to the current matrix,

+        // rather than transforming the primitive directly, so that 'newM' will

+        // also affect the behavior of the mask filter.

+        SkMatrix drawMatrix;

+        drawMatrix.setConcat(fContext->getMatrix(), newM);

+        SkDraw transformedDraw(draw);

+        transformedDraw.fMatrix = &drawMatrix;

+

+        this->drawRect(transformedDraw, srcRect, paintWithTexture);

+

+        return;

+    }

+

+    fContext->concatMatrix(m);

+

+    GrTextureParams params;

+    SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel();

+    GrTextureParams::FilterMode textureFilterMode;

+    switch(paintFilterLevel) {

+        case SkPaint::kNone_FilterLevel:

+            textureFilterMode = GrTextureParams::kNone_FilterMode;

+            break;

+        case SkPaint::kLow_FilterLevel:

+            textureFilterMode = GrTextureParams::kBilerp_FilterMode;

+            break;

+        case SkPaint::kMedium_FilterLevel:

+            textureFilterMode = GrTextureParams::kMipMap_FilterMode;

+            break;

+        case SkPaint::kHigh_FilterLevel:

+            // Fall back to mips for now

+            textureFilterMode = GrTextureParams::kMipMap_FilterMode;

+            break;

+        default:

+            SkErrorInternals::SetError( kInvalidPaint_SkError,

+                                        "Sorry, I don't understand the filtering "

+                                        "mode you asked for.  Falling back to "

+                                        "MIPMaps.");

+            textureFilterMode = GrTextureParams::kMipMap_FilterMode;

+            break;

+

+    }

+

+    params.setFilterMode(textureFilterMode);

+

+    int maxTileSize = fContext->getMaxTextureSize();

+    if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) {

+        // We may need a skosh more room if we have to bump out the tile

+        // by 1 pixel all around

+        maxTileSize -= 2;

+    }

+    int tileSize;

+

+    SkIRect clippedSrcRect;

+    if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize,

+                               &clippedSrcRect)) {

+        this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize);

+    } else {

+        // take the simple case

+        this->internalDrawBitmap(bitmap, srcRect, params, paint, flags);

+    }

+}

+

+// Break 'bitmap' into several tiles to draw it since it has already

+// been determined to be too large to fit in VRAM

+void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,

+                                  const SkRect& srcRect,

+                                  const SkIRect& clippedSrcIRect,

+                                  const GrTextureParams& params,

+                                  const SkPaint& paint,

+                                  SkCanvas::DrawBitmapRectFlags flags,

+                                  int tileSize) {

+    SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect);

+

+    int nx = bitmap.width() / tileSize;

+    int ny = bitmap.height() / tileSize;

+    for (int x = 0; x <= nx; x++) {

+        for (int y = 0; y <= ny; y++) {

+            SkRect tileR;

+            tileR.set(SkIntToScalar(x * tileSize),

+                      SkIntToScalar(y * tileSize),

+                      SkIntToScalar((x + 1) * tileSize),

+                      SkIntToScalar((y + 1) * tileSize));

+

+            if (!SkRect::Intersects(tileR, clippedSrcRect)) {

+                continue;

+            }

+

+            if (!tileR.intersect(srcRect)) {

+                continue;

+            }

+

+            SkBitmap tmpB;

+            SkIRect iTileR;

+            tileR.roundOut(&iTileR);

+            SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft),

+                                           SkIntToScalar(iTileR.fTop));

+

+            if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) {

+                SkIRect iClampRect;

+

+                if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {

+                    // In bleed mode we want to always expand the tile on all edges

+                    // but stay within the bitmap bounds

+                    iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());

+                } else {

+                    // In texture-domain/clamp mode we only want to expand the

+                    // tile on edges interior to "srcRect" (i.e., we want to

+                    // not bleed across the original clamped edges)

+                    srcRect.roundOut(&iClampRect);

+                }

+

+                clamped_unit_outset_with_offset(&iTileR, &offset, iClampRect);

+            }

+

+            if (bitmap.extractSubset(&tmpB, iTileR)) {

+                // now offset it to make it "local" to our tmp bitmap

+                tileR.offset(-offset.fX, -offset.fY);

+                SkMatrix tmpM;

+                tmpM.setTranslate(offset.fX, offset.fY);

+                GrContext::AutoMatrix am;

+                am.setPreConcat(fContext, tmpM);

+                this->internalDrawBitmap(tmpB, tileR, params, paint, flags);

+            }

+        }

+    }

+}

+

+static bool has_aligned_samples(const SkRect& srcRect,

+                                const SkRect& transformedRect) {

+    // detect pixel disalignment

+    if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) -

+            transformedRect.left()) < COLOR_BLEED_TOLERANCE &&

+        SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) -

+            transformedRect.top()) < COLOR_BLEED_TOLERANCE &&

+        SkScalarAbs(transformedRect.width() - srcRect.width()) <

+            COLOR_BLEED_TOLERANCE &&

+        SkScalarAbs(transformedRect.height() - srcRect.height()) <

+            COLOR_BLEED_TOLERANCE) {

+        return true;

+    }

+    return false;

+}

+

+static bool may_color_bleed(const SkRect& srcRect,

+                            const SkRect& transformedRect,

+                            const SkMatrix& m) {

+    // Only gets called if has_aligned_samples returned false.

+    // So we can assume that sampling is axis aligned but not texel aligned.

+    SkASSERT(!has_aligned_samples(srcRect, transformedRect));

+    SkRect innerSrcRect(srcRect), innerTransformedRect,

+        outerTransformedRect(transformedRect);

+    innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);

+    m.mapRect(&innerTransformedRect, innerSrcRect);

+

+    // The gap between outerTransformedRect and innerTransformedRect

+    // represents the projection of the source border area, which is

+    // problematic for color bleeding.  We must check whether any

+    // destination pixels sample the border area.

+    outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);

+    innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);

+    SkIRect outer, inner;

+    outerTransformedRect.round(&outer);

+    innerTransformedRect.round(&inner);

+    // If the inner and outer rects round to the same result, it means the

+    // border does not overlap any pixel centers. Yay!

+    return inner != outer;

+}

+

+

+/*

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

+ *  and that non-texture portion of the GrPaint has already been setup.

+ */

+void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,

+                                     const SkRect& srcRect,

+                                     const GrTextureParams& params,

+                                     const SkPaint& paint,

+                                     SkCanvas::DrawBitmapRectFlags flags) {

+    SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&

+             bitmap.height() <= fContext->getMaxTextureSize());

+

+    GrTexture* texture;

+    SkAutoCachedTexture act(this, bitmap, &params, &texture);

+    if (NULL == texture) {

+        return;

+    }

+

+    SkRect dstRect(srcRect);

+    SkRect paintRect;

+    SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width()));

+    SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height()));

+    paintRect.setLTRB(SkScalarMul(srcRect.fLeft,   wInv),

+                      SkScalarMul(srcRect.fTop,    hInv),

+                      SkScalarMul(srcRect.fRight,  wInv),

+                      SkScalarMul(srcRect.fBottom, hInv));

+

+    bool needsTextureDomain = false;

+    if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) &&

+        params.filterMode() != GrTextureParams::kNone_FilterMode) {

+        // Need texture domain if drawing a sub rect.

+        needsTextureDomain = srcRect.width() < bitmap.width() ||

+                             srcRect.height() < bitmap.height();

+        if (needsTextureDomain && fContext->getMatrix().rectStaysRect()) {

+            const SkMatrix& matrix = fContext->getMatrix();

+            // sampling is axis-aligned

+            SkRect transformedRect;

+            matrix.mapRect(&transformedRect, srcRect);

+

+            if (has_aligned_samples(srcRect, transformedRect)) {

+                // We could also turn off filtering here (but we already did a cache lookup with

+                // params).

+                needsTextureDomain = false;

+            } else {

+                needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix);

+            }

+        }

+    }

+

+    SkRect textureDomain = SkRect::MakeEmpty();

+    SkAutoTUnref<GrEffectRef> effect;

+    if (needsTextureDomain) {

+        // Use a constrained texture domain to avoid color bleeding

+        SkScalar left, top, right, bottom;

+        if (srcRect.width() > SK_Scalar1) {

+            SkScalar border = SK_ScalarHalf / texture->width();

+            left = paintRect.left() + border;

+            right = paintRect.right() - border;

+        } else {

+            left = right = SkScalarHalf(paintRect.left() + paintRect.right());

+        }

+        if (srcRect.height() > SK_Scalar1) {

+            SkScalar border = SK_ScalarHalf / texture->height();

+            top = paintRect.top() + border;

+            bottom = paintRect.bottom() - border;

+        } else {

+            top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom());

+        }

+        textureDomain.setLTRB(left, top, right, bottom);

+        effect.reset(GrTextureDomainEffect::Create(texture,

+                                                   SkMatrix::I(),

+                                                   textureDomain,

+                                                   GrTextureDomainEffect::kClamp_WrapMode,

+                                                   params.filterMode()));

+    } else {

+        effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params));

+    }

+

+    // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring

+    // the rest from the SkPaint.

+    GrPaint grPaint;

+    grPaint.addColorEffect(effect);

+    bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config());

+    if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) {

+        return;

+    }

+

+    fContext->drawRectToRect(grPaint, dstRect, paintRect, NULL);

+}

+

+static bool filter_texture(SkBaseDevice* device, GrContext* context,

+                           GrTexture* texture, SkImageFilter* filter,

+                           int w, int h, const SkMatrix& ctm, SkBitmap* result,

+                           SkIPoint* offset) {

+    SkASSERT(filter);

+    SkDeviceImageFilterProxy proxy(device);

+

+    if (filter->canFilterImageGPU()) {

+        // Save the render target and set it to NULL, so we don't accidentally draw to it in the

+        // filter.  Also set the clip wide open and the matrix to identity.

+        GrContext::AutoWideOpenIdentityDraw awo(context, NULL);

+        return filter->filterImageGPU(&proxy, wrap_texture(texture), ctm, result, offset);

+    } else {

+        return false;

+    }

+}

+

+void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,

+                             int left, int top, const SkPaint& paint) {

+    // drawSprite is defined to be in device coords.

+    CHECK_SHOULD_DRAW(draw, true);

+

+    SkAutoLockPixels alp(bitmap, !bitmap.getTexture());

+    if (!bitmap.getTexture() && !bitmap.readyToDraw()) {

+        return;

+    }

+

+    int w = bitmap.width();

+    int h = bitmap.height();

+

+    GrTexture* texture;

+    // draw sprite uses the default texture params

+    SkAutoCachedTexture act(this, bitmap, NULL, &texture);

+

+    SkImageFilter* filter = paint.getImageFilter();

+    SkIPoint offset = SkIPoint::Make(left, top);

+    // This bitmap will own the filtered result as a texture.

+    SkBitmap filteredBitmap;

+

+    if (NULL != filter) {

+        SkMatrix matrix(*draw.fMatrix);

+        matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));

+        if (filter_texture(this, fContext, texture, filter, w, h, matrix, &filteredBitmap,

+                           &offset)) {

+            texture = (GrTexture*) filteredBitmap.getTexture();

+            w = filteredBitmap.width();

+            h = filteredBitmap.height();

+        } else {

+            return;

+        }

+    }

+

+    GrPaint grPaint;

+    grPaint.addColorTextureEffect(texture, SkMatrix::I());

+

+    if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {

+        return;

+    }

+

+    fContext->drawRectToRect(grPaint,

+                             SkRect::MakeXYWH(SkIntToScalar(offset.fX),

+                                              SkIntToScalar(offset.fY),

+                                              SkIntToScalar(w),

+                                              SkIntToScalar(h)),

+                             SkRect::MakeXYWH(0,

+                                              0,

+                                              SK_Scalar1 * w / texture->width(),

+                                              SK_Scalar1 * h / texture->height()));

+}

+

+void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,

+                                 const SkRect* src, const SkRect& dst,

+                                 const SkPaint& paint,

+                                 SkCanvas::DrawBitmapRectFlags flags) {

+    SkMatrix    matrix;

+    SkRect      bitmapBounds, tmpSrc;

+

+    bitmapBounds.set(0, 0,

+                     SkIntToScalar(bitmap.width()),

+                     SkIntToScalar(bitmap.height()));

+

+    // Compute matrix from the two rectangles

+    if (NULL != src) {

+        tmpSrc = *src;

+    } else {

+        tmpSrc = bitmapBounds;

+    }

+    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);

+

+    // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null.

+    if (NULL != src) {

+        if (!bitmapBounds.contains(tmpSrc)) {

+            if (!tmpSrc.intersect(bitmapBounds)) {

+                return; // nothing to draw

+            }

+        }

+    }

+

+    this->drawBitmapCommon(draw, bitmap, &tmpSrc, matrix, paint, flags);

+}

+

+void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,

+                             int x, int y, const SkPaint& paint) {

+    // clear of the source device must occur before CHECK_SHOULD_DRAW

+    SkGpuDevice* dev = static_cast<SkGpuDevice*>(device);

+    if (dev->fNeedClear) {

+        // TODO: could check here whether we really need to draw at all

+        dev->clear(0x0);

+    }

+

+    // drawDevice is defined to be in device coords.

+    CHECK_SHOULD_DRAW(draw, true);

+

+    GrRenderTarget* devRT = dev->accessRenderTarget();

+    GrTexture* devTex;

+    if (NULL == (devTex = devRT->asTexture())) {

+        return;

+    }

+

+    const SkBitmap& bm = dev->accessBitmap(false);

+    int w = bm.width();

+    int h = bm.height();

+

+    SkImageFilter* filter = paint.getImageFilter();

+    // This bitmap will own the filtered result as a texture.

+    SkBitmap filteredBitmap;

+

+    if (NULL != filter) {

+        SkIPoint offset = SkIPoint::Make(0, 0);

+        SkMatrix matrix(*draw.fMatrix);

+        matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));

+        if (filter_texture(this, fContext, devTex, filter, w, h, matrix, &filteredBitmap,

+                           &offset)) {

+            devTex = filteredBitmap.getTexture();

+            w = filteredBitmap.width();

+            h = filteredBitmap.height();

+            x += offset.fX;

+            y += offset.fY;

+        } else {

+            return;

+        }

+    }

+

+    GrPaint grPaint;

+    grPaint.addColorTextureEffect(devTex, SkMatrix::I());

+

+    if (!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {

+        return;

+    }

+

+    SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x),

+                                      SkIntToScalar(y),

+                                      SkIntToScalar(w),

+                                      SkIntToScalar(h));

+

+    // The device being drawn may not fill up its texture (e.g. saveLayer uses approximate

+    // scratch texture).

+    SkRect srcRect = SkRect::MakeWH(SK_Scalar1 * w / devTex->width(),

+                                    SK_Scalar1 * h / devTex->height());

+

+    fContext->drawRectToRect(grPaint, dstRect, srcRect);

+}

+

+bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) {

+    return filter->canFilterImageGPU();

+}

+

+bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,

+                              const SkMatrix& ctm,

+                              SkBitmap* result, SkIPoint* offset) {

+    // want explicitly our impl, so guard against a subclass of us overriding it

+    if (!this->SkGpuDevice::canHandleImageFilter(filter)) {

+        return false;

+    }

+

+    SkAutoLockPixels alp(src, !src.getTexture());

+    if (!src.getTexture() && !src.readyToDraw()) {

+        return false;

+    }

+

+    GrTexture* texture;

+    // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup

+    // must be pushed upstack.

+    SkAutoCachedTexture act(this, src, NULL, &texture);

+

+    return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctm, result,

+                          offset);

+}

+

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

+

+// must be in SkCanvas::VertexMode order

+static const GrPrimitiveType gVertexMode2PrimitiveType[] = {

+    kTriangles_GrPrimitiveType,

+    kTriangleStrip_GrPrimitiveType,

+    kTriangleFan_GrPrimitiveType,

+};

+

+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, false);

+

+    GrPaint grPaint;

+    // we ignore the shader if texs is null.

+    if (NULL == texs) {

+        if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) {

+            return;

+        }

+    } else {

+        if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) {

+            return;

+        }

+    }

+

+    if (NULL != xmode && NULL != texs && NULL != colors) {

+        if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) {

+            SkDebugf("Unsupported vertex-color/texture xfer mode.\n");

+#if 0

+            return

+#endif

+        }

+    }

+

+    SkAutoSTMalloc<128, GrColor> convertedColors(0);

+    if (NULL != colors) {

+        // need to convert byte order and from non-PM to PM

+        convertedColors.reset(vertexCount);

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

+            convertedColors[i] = SkColor2GrColor(colors[i]);

+        }

+        colors = convertedColors.get();

+    }

+    fContext->drawVertices(grPaint,

+                           gVertexMode2PrimitiveType[vmode],

+                           vertexCount,

+                           (GrPoint*) vertices,

+                           (GrPoint*) texs,

+                           colors,

+                           indices,

+                           indexCount);

+}

+

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

+

+static void GlyphCacheAuxProc(void* data) {

+    GrFontScaler* scaler = (GrFontScaler*)data;

+    SkSafeUnref(scaler);

+}

+

+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 = SkNEW_ARGS(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 = static_cast<GrSkDrawProcs*>(state.fDraw->fProcs);

+

+    if (NULL == procs->fFontScaler) {

+        procs->fFontScaler = get_gr_font_scaler(state.fCache);

+    }

+

+    procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),

+                                                       glyph.getSubXFixed(),

+                                                       glyph.getSubYFixed()),

+                                         SkFixedFloorToFixed(fx),

+                                         SkFixedFloorToFixed(fy),

+                                         procs->fFontScaler);

+}

+

+SkDrawProcs* SkGpuDevice::initDrawForText(GrTextContext* context) {

+

+    // deferred allocation

+    if (NULL == fDrawProcs) {

+        fDrawProcs = SkNEW(GrSkDrawProcs);

+        fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;

+        fDrawProcs->fContext = fContext;

+#if SK_DISTANCEFIELD_FONTS

+        fDrawProcs->fFlags = 0;

+        fDrawProcs->fFlags |= SkDrawProcs::kSkipBakedGlyphTransform_Flag;

+        fDrawProcs->fFlags |= SkDrawProcs::kUseScaledGlyphs_Flag;

+#endif

+    }

+

+    // 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, false);

+

+    if (fContext->getMatrix().hasPerspective()) {

+        // this guy will just call our drawPath()

+        draw.drawText((const char*)text, byteLength, x, y, paint);

+    } else {

+        SkDraw myDraw(draw);

+

+        GrPaint grPaint;

+        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {

+            return;

+        }

+#if SK_DISTANCEFIELD_FONTS

+        GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(), 

+                                           paint.getTextSize());

+#else

+        GrBitmapTextContext context(fContext, grPaint, paint.getColor());

+#endif

+        myDraw.fProcs = this->initDrawForText(&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, false);

+

+    if (fContext->getMatrix().hasPerspective()) {

+        // this guy will just call our drawPath()

+        draw.drawPosText((const char*)text, byteLength, pos, constY,

+                         scalarsPerPos, paint);

+    } else {

+        SkDraw myDraw(draw);

+

+        GrPaint grPaint;

+        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {

+            return;

+        }

+#if SK_DISTANCEFIELD_FONTS

+        GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(), 

+                                           paint.getTextSize()/SkDrawProcs::kBaseDFFontSize);

+#else

+        GrBitmapTextContext context(fContext, grPaint, paint.getColor());

+#endif

+        myDraw.fProcs = this->initDrawForText(&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, false);

+

+    SkASSERT(draw.fDevice == this);

+    draw.drawTextOnPath((const char*)text, len, path, m, paint);

+}

+

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

+

+bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {

+    if (!paint.isLCDRenderText()) {

+        // we're cool with the paint as is

+        return false;

+    }

+

+    if (paint.getShader() ||

+        paint.getXfermode() || // unless its srcover

+        paint.getMaskFilter() ||

+        paint.getRasterizer() ||

+        paint.getColorFilter() ||

+        paint.getPathEffect() ||

+        paint.isFakeBoldText() ||

+        paint.getStyle() != SkPaint::kFill_Style) {

+        // turn off lcd

+        flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;

+        flags->fHinting = paint.getHinting();

+        return true;

+    }

+    // we're cool with the paint as is

+    return false;

+}

+

+void SkGpuDevice::flush() {

+    DO_DEFERRED_CLEAR();

+    fContext->resolveRenderTarget(fRenderTarget);

+}

+

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

+

+SkBaseDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config,

+                                                    int width, int height,

+                                                    bool isOpaque,

+                                                    Usage usage) {

+    GrTextureDesc desc;

+    desc.fConfig = fRenderTarget->config();

+    desc.fFlags = kRenderTarget_GrTextureFlagBit;

+    desc.fWidth = width;

+    desc.fHeight = height;

+    desc.fSampleCnt = fRenderTarget->numSamples();

+

+    SkAutoTUnref<GrTexture> texture;

+    // Skia's convention is to only clear a device if it is non-opaque.

+    bool needClear = !isOpaque;

+

+#if CACHE_COMPATIBLE_DEVICE_TEXTURES

+    // layers are never draw in repeat modes, so we can request an approx

+    // match and ignore any padding.

+    const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ?

+                                                GrContext::kApprox_ScratchTexMatch :

+                                                GrContext::kExact_ScratchTexMatch;

+    texture.reset(fContext->lockAndRefScratchTexture(desc, match));

+#else

+    texture.reset(fContext->createUncachedTexture(desc, NULL, 0));

+#endif

+    if (NULL != texture.get()) {

+        return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear));

+    } else {

+        GrPrintf("---- failed to create compatible device texture [%d %d]\n", width, height);

+        return NULL;

+    }

+}

+

+SkGpuDevice::SkGpuDevice(GrContext* context,

+                         GrTexture* texture,

+                         bool needClear)

+    : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {

+

+    SkASSERT(texture && texture->asRenderTarget());

+    // This constructor is called from onCreateCompatibleDevice. It has locked the RT in the texture

+    // cache. We pass true for the third argument so that it will get unlocked.

+    this->initFromRenderTarget(context, texture->asRenderTarget(), true);

+    fNeedClear = needClear;

+}

diff --git a/gpu/effects/GrDistanceFieldTextureEffect.cpp b/gpu/effects/GrDistanceFieldTextureEffect.cpp
new file mode 100755
index 0000000..fd27f2b
--- /dev/null
+++ b/gpu/effects/GrDistanceFieldTextureEffect.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrDistanceFieldTextureEffect.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "gl/GrGLVertexEffect.h"
+#include "GrTBackendEffectFactory.h"
+#include "GrTexture.h"
+
+// The distance field is constructed as unsigned char values, so that the zero value is at 128.
+// Hence our zero threshold is 128/255. 
+#define THRESHOLD "0.50196078431"
+
+class GrGLDistanceFieldTextureEffect : public GrGLVertexEffect {
+public:
+    GrGLDistanceFieldTextureEffect(const GrBackendEffectFactory& factory, const GrDrawEffect& drawEffect)
+        : INHERITED (factory) {}
+
+    virtual void emitCode(GrGLFullShaderBuilder* builder,
+                          const GrDrawEffect& drawEffect,
+                          EffectKey key,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TransformedCoordsArray&,
+                          const TextureSamplerArray& samplers) SK_OVERRIDE {
+        SkASSERT(1 == drawEffect.castEffect<GrDistanceFieldTextureEffect>().numVertexAttribs());
+
+        SkString fsCoordName;
+        const char* vsVaryingName;
+        const char* fsVaryingNamePtr;
+        builder->addVarying(kVec2f_GrSLType, "textureCoords", &vsVaryingName, &fsVaryingNamePtr);
+        fsCoordName = fsVaryingNamePtr;
+
+        const char* attrName =
+            builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0])->c_str();
+        builder->vsCodeAppendf("\t%s = %s;\n", vsVaryingName, attrName);
+
+        builder->fsCodeAppend("\tvec4 texColor = ");
+        builder->fsAppendTextureLookup(samplers[0],
+                                       fsCoordName.c_str(),
+                                       kVec2f_GrSLType);
+        builder->fsCodeAppend(";\n");
+        builder->fsCodeAppend("\tfloat distance = texColor.r;\n");
+        // this gives us a smooth step across approximately one fragment 
+        // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
+        builder->fsCodeAppend("\tfloat afwidth = 0.7071*length(vec2(dFdx(distance), dFdy(distance)));\n");
+        builder->fsCodeAppend("\tfloat val = smoothstep("THRESHOLD"-afwidth, "THRESHOLD"+afwidth, distance);\n");
+
+        builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
+                                   (GrGLSLExpr4(inputColor) * GrGLSLExpr1("val")).c_str());
+    }
+
+    virtual void setData(const GrGLUniformManager& uman,
+                         const GrDrawEffect& drawEffect) SK_OVERRIDE {}
+
+private:
+    typedef GrGLVertexEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrDistanceFieldTextureEffect::GrDistanceFieldTextureEffect(GrTexture* texture,
+                                                         const GrTextureParams& params)
+    : fTextureAccess(texture, params) {
+    this->addTextureAccess(&fTextureAccess);
+    this->addVertexAttrib(kVec2f_GrSLType);
+}
+
+bool GrDistanceFieldTextureEffect::onIsEqual(const GrEffect& other) const {
+    const GrDistanceFieldTextureEffect& cte = CastEffect<GrDistanceFieldTextureEffect>(other);
+    return fTextureAccess == cte.fTextureAccess;
+}
+
+void GrDistanceFieldTextureEffect::getConstantColorComponents(GrColor* color,
+                                                             uint32_t* validFlags) const {
+    if ((*validFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
+        GrPixelConfigIsOpaque(this->texture(0)->config())) {
+        *validFlags = kA_GrColorComponentFlag;
+    } else {
+        *validFlags = 0;
+    }
+}
+
+const GrBackendEffectFactory& GrDistanceFieldTextureEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrDistanceFieldTextureEffect>::getInstance();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrDistanceFieldTextureEffect);
+
+GrEffectRef* GrDistanceFieldTextureEffect::TestCreate(SkRandom* random,
+                                                     GrContext*,
+                                                     const GrDrawTargetCaps&,
+                                                     GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    static const SkShader::TileMode kTileModes[] = {
+        SkShader::kClamp_TileMode,
+        SkShader::kRepeat_TileMode,
+        SkShader::kMirror_TileMode,
+    };
+    SkShader::TileMode tileModes[] = {
+        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+    };
+    GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
+                                                           GrTextureParams::kNone_FilterMode);
+
+    return GrDistanceFieldTextureEffect::Create(textures[texIdx], params);
+}
diff --git a/gpu/effects/GrDistanceFieldTextureEffect.h b/gpu/effects/GrDistanceFieldTextureEffect.h
new file mode 100755
index 0000000..34d4ae5
--- /dev/null
+++ b/gpu/effects/GrDistanceFieldTextureEffect.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrDistanceFieldTextureEffect_DEFINED
+#define GrDistanceFieldTextureEffect_DEFINED
+
+#include "GrEffect.h"
+#include "GrVertexEffect.h"
+
+class GrGLDistanceFieldTextureEffect;
+
+/**
+ * The output color of this effect is a modulation of the input color and a sample from a 
+ * distance field texture (using a smoothed step function near 0.5).
+ * It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input
+ * coords are a custom attribute.
+ */
+class GrDistanceFieldTextureEffect : public GrVertexEffect {
+public:
+    static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& p) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, p)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrDistanceFieldTextureEffect() {}
+
+    static const char* Name() { return "Texture"; }
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    typedef GrGLDistanceFieldTextureEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+private:
+    GrDistanceFieldTextureEffect(GrTexture* texture, const GrTextureParams& params);
+
+    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
+
+    GrTextureAccess fTextureAccess;
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrVertexEffect INHERITED;
+};
+
+#endif