Hook in rough distance field support for fonts
authorjvanverth@google.com <jvanverth@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 11 Nov 2013 20:54:09 +0000 (20:54 +0000)
committerjvanverth@google.com <jvanverth@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 11 Nov 2013 20:54:09 +0000 (20:54 +0000)
R=bsalomon@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@12229 2bbb7eff-a529-9590-31e7-b0007b416f81

19 files changed:
gyp/common_conditions.gypi
gyp/common_variables.gypi
gyp/edtaa.gyp [new file with mode: 0755]
gyp/gpu.gyp
include/gpu/GrDistanceFieldTextContext.h [new file with mode: 0755]
src/core/SkDraw.cpp
src/core/SkDrawProcs.h
src/device/xps/SkXPSDevice.cpp
src/gpu/GrBitmapTextContext.cpp
src/gpu/GrDistanceFieldTextContext.cpp [new file with mode: 0755]
src/gpu/GrTextStrike.cpp
src/gpu/GrTextStrike.h
src/gpu/GrTextStrike_impl.h
src/gpu/SkGpuDevice.cpp
src/gpu/effects/GrDistanceFieldTextureEffect.cpp [new file with mode: 0755]
src/gpu/effects/GrDistanceFieldTextureEffect.h [new file with mode: 0755]
third_party/edtaa/LICENSE [new file with mode: 0755]
third_party/edtaa/edtaa3.h [new file with mode: 0755]
third_party/edtaa/edtaa3func.cpp [new file with mode: 0755]

index 4aefdc1..770269f 100644 (file)
         ],
       },
     ],
+    [ 'skia_distancefield_fonts == 1',
+      {
+        'defines': [
+          'SK_DISTANCEFIELD_FONTS=1',
+        ],
+      }, {
+        'defines': [
+          'SK_DISTANCEFIELD_FONTS=0',
+        ],
+      },
+    ],
     [ 'skia_os == "win"',
       {
         'defines': [
index 23aaff9..425760e 100644 (file)
@@ -94,6 +94,7 @@
       'skia_win_debuggers_path%': '',
       'skia_shared_lib%': 0,
       'skia_opencl%': 0,
+      'skia_distancefield_fonts%': 0,
 
       # These variables determine the default optimization level for different
       # compilers.
     'skia_profile_enabled%': '<(skia_profile_enabled)',
     'skia_shared_lib%': '<(skia_shared_lib)',
     'skia_opencl%': '<(skia_opencl)',
+    'skia_distancefield_fonts%': '<(skia_distancefield_fonts)',
     'skia_static_initializers%': '<(skia_static_initializers)',
     'ios_sdk_version%': '6.0',
     'skia_win_debuggers_path%': '<(skia_win_debuggers_path)',
diff --git a/gyp/edtaa.gyp b/gyp/edtaa.gyp
new file mode 100755 (executable)
index 0000000..5c04ce1
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# Copyright 2013 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+
+
+{
+  'targets': [
+    {
+      'target_name': 'edtaa',
+      'type': 'none',
+      'conditions': [
+        [ 'skia_distancefield_fonts', {
+          'type': 'static_library',
+          'sources': [
+            '../third_party/edtaa/edtaa3func.cpp',
+          ],
+          'include_dirs': [
+            '../third_party/edtaa/',
+          ],
+          'all_dependent_settings': {
+            'include_dirs': [
+              '../third_party/edtaa/',
+            ],
+          },
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
index 057c2fb..29889f1 100644 (file)
@@ -83,6 +83,7 @@
       'dependencies': [
         'angle.gyp:*',
         'core.gyp:*',
+        'edtaa.gyp:*',
         'utils.gyp:*',
       ],
       'includes': [
             'GR_ANDROID_PATH_RENDERING=1',
           ],
         }],
+        [ 'skia_distancefield_fonts', {
+          'sources': [
+            '<(skia_include_path)/gpu/GrDistanceFieldTextContext.h',
+            '<(skia_src_path)/gpu/GrDistanceFieldTextContext.cpp',
+            '<(skia_src_path)/gpu/effects/GrDistanceFieldTextureEffect.cpp',
+            '<(skia_src_path)/gpu/effects/GrDistanceFieldTextureEffect.h',
+          ],
+          'defines': [
+            'GR_DISTANCEFIELD_FONTS=1',
+          ],
+        }],
         [ 'skia_os == "linux" or skia_os == "chromeos"', {
           'sources!': [
             '../src/gpu/gl/GrGLDefaultInterface_none.cpp',
diff --git a/include/gpu/GrDistanceFieldTextContext.h b/include/gpu/GrDistanceFieldTextContext.h
new file mode 100755 (executable)
index 0000000..217faf3
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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 GrDistanceFieldTextContext_DEFINED
+#define GrDistanceFieldTextContext_DEFINED
+
+#include "GrTextContext.h"
+
+class GrTextStrike;
+
+/*
+ * This class implements GrTextContext using distance field fonts
+ */
+class GrDistanceFieldTextContext : public GrTextContext {
+public:
+    GrDistanceFieldTextContext(GrContext*, const GrPaint&, SkColor, SkScalar textRatio);
+    virtual ~GrDistanceFieldTextContext();
+
+    virtual void drawPackedGlyph(GrGlyph::PackedID, GrFixed left, GrFixed top,
+                                 GrFontScaler*) SK_OVERRIDE;
+
+private:
+    GrTextStrike*           fStrike;
+    SkScalar                fTextRatio;
+
+    void flushGlyphs();                 // automatically called by destructor
+
+    enum {
+        kMinRequestedGlyphs      = 1,
+        kDefaultRequestedGlyphs  = 64,
+        kMinRequestedVerts       = kMinRequestedGlyphs * 4,
+        kDefaultRequestedVerts   = kDefaultRequestedGlyphs * 4,
+    };
+
+    SkColor                 fSkPaintColor;
+    SkPoint*                fVertices;
+    int32_t                 fMaxVertices;
+    GrTexture*              fCurrTexture;
+    int                     fCurrVertex;
+};
+
+#endif
index f282bf5..9b6c426 100644 (file)
@@ -1727,10 +1727,30 @@ void SkDraw::drawText(const char text[], size_t byteLength,
 
     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 @@ void SkDraw::drawText(const char text[], size_t byteLength,
     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 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
     }
 
     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 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
 
         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;
 
index 8b8c382..cc2f3ed 100644 (file)
@@ -57,6 +57,22 @@ struct SkDraw1Glyph {
 
 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
 };
 
 /**
index f837655..86b8c7d 100644 (file)
@@ -2234,6 +2234,9 @@ static void text_draw_init(const SkPaint& paint,
                            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:
index d389ed6..433eda1 100755 (executable)
@@ -123,7 +123,11 @@ void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
         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/src/gpu/GrDistanceFieldTextContext.cpp b/src/gpu/GrDistanceFieldTextContext.cpp
new file mode 100755 (executable)
index 0000000..87be8ca
--- /dev/null
@@ -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;
+}
index a96aab1..609cb5b 100644 (file)
 #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 @@ void GrFontCache::dump() const {
     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* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
     }
 
     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 @@ bool GrTextStrike::removeUnusedPlots() {
     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 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
     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;
+#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
 
-    GrPlot* plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(),
-                                         glyph->height(), storage.get(),
-                                         &glyph->fAtlasLocation);
     if (NULL == plot) {
         return false;
     }
@@ -289,3 +408,4 @@ bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
     glyph->fPlot = plot;
     return true;
 }
+
index 26a3632..422ae0c 100644 (file)
@@ -61,9 +61,12 @@ private:
 
     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 @@ public:
     GrFontCache(GrGpu*);
     ~GrFontCache();
 
+#if SK_DISTANCEFIELD_FONTS
+    inline GrTextStrike* getStrike(GrFontScaler*, bool useDistanceField);
+#else
     inline GrTextStrike* getStrike(GrFontScaler*);
+#endif
 
     void freeAll();
 
index 130509c..4297185 100644 (file)
@@ -48,7 +48,11 @@ void GrFontCache::detachStrikeFromList(GrTextStrike* strike) {
     }
 }
 
+#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 @@ GrTextStrike* GrFontCache::getStrike(GrFontScaler* scaler) {
         strike->fPrev = NULL;
         fHead = strike;
     }
-
+#if SK_DISTANCEFIELD_FONTS
+    strike->fUseDistanceField = useDistanceField;
+#endif
     this->validate();
     return strike;
 }
index ca86b84..92ac326 100644 (file)
-/*
- * 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;
-}
+/*\r
+ * Copyright 2011 Google Inc.\r
+ *\r
+ * Use of this source code is governed by a BSD-style license that can be\r
+ * found in the LICENSE file.\r
+ */\r
+\r
+#include "SkGpuDevice.h"\r
+\r
+#include "effects/GrTextureDomainEffect.h"\r
+#include "effects/GrSimpleTextureEffect.h"\r
+\r
+#include "GrContext.h"\r
+#include "GrBitmapTextContext.h"\r
+#if SK_DISTANCEFIELD_FONTS\r
+#include "GrDistanceFieldTextContext.h"\r
+#endif\r
+\r
+#include "SkGrTexturePixelRef.h"\r
+\r
+#include "SkColorFilter.h"\r
+#include "SkDeviceImageFilterProxy.h"\r
+#include "SkDrawProcs.h"\r
+#include "SkGlyphCache.h"\r
+#include "SkImageFilter.h"\r
+#include "SkPathEffect.h"\r
+#include "SkRRect.h"\r
+#include "SkStroke.h"\r
+#include "SkUtils.h"\r
+#include "SkErrorInternals.h"\r
+\r
+#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1\r
+\r
+#if 0\r
+    extern bool (*gShouldDrawProc)();\r
+    #define CHECK_SHOULD_DRAW(draw, forceI)                     \\r
+        do {                                                    \\r
+            if (gShouldDrawProc && !gShouldDrawProc()) return;  \\r
+            this->prepareDraw(draw, forceI);                    \\r
+        } while (0)\r
+#else\r
+    #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI)\r
+#endif\r
+\r
+// This constant represents the screen alignment criterion in texels for\r
+// requiring texture domain clamping to prevent color bleeding when drawing\r
+// a sub region of a larger source image.\r
+#define COLOR_BLEED_TOLERANCE SkFloatToScalar(0.001f)\r
+\r
+#define DO_DEFERRED_CLEAR()             \\r
+    do {                                \\r
+        if (fNeedClear) {               \\r
+            this->clear(SK_ColorTRANSPARENT); \\r
+        }                               \\r
+    } while (false)                     \\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+#define CHECK_FOR_ANNOTATION(paint) \\r
+    do { if (paint.getAnnotation()) { return; } } while (0)\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+\r
+class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable {\r
+public:\r
+    SkAutoCachedTexture()\r
+        : fDevice(NULL)\r
+        , fTexture(NULL) {\r
+    }\r
+\r
+    SkAutoCachedTexture(SkGpuDevice* device,\r
+                        const SkBitmap& bitmap,\r
+                        const GrTextureParams* params,\r
+                        GrTexture** texture)\r
+        : fDevice(NULL)\r
+        , fTexture(NULL) {\r
+        SkASSERT(NULL != texture);\r
+        *texture = this->set(device, bitmap, params);\r
+    }\r
+\r
+    ~SkAutoCachedTexture() {\r
+        if (NULL != fTexture) {\r
+            GrUnlockAndUnrefCachedBitmapTexture(fTexture);\r
+        }\r
+    }\r
+\r
+    GrTexture* set(SkGpuDevice* device,\r
+                   const SkBitmap& bitmap,\r
+                   const GrTextureParams* params) {\r
+        if (NULL != fTexture) {\r
+            GrUnlockAndUnrefCachedBitmapTexture(fTexture);\r
+            fTexture = NULL;\r
+        }\r
+        fDevice = device;\r
+        GrTexture* result = (GrTexture*)bitmap.getTexture();\r
+        if (NULL == result) {\r
+            // Cannot return the native texture so look it up in our cache\r
+            fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params);\r
+            result = fTexture;\r
+        }\r
+        return result;\r
+    }\r
+\r
+private:\r
+    SkGpuDevice* fDevice;\r
+    GrTexture*   fTexture;\r
+};\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+struct GrSkDrawProcs : public SkDrawProcs {\r
+public:\r
+    GrContext* fContext;\r
+    GrTextContext* fTextContext;\r
+    GrFontScaler* fFontScaler;  // cached in the skia glyphcache\r
+};\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) {\r
+    switch (config) {\r
+        case kAlpha_8_GrPixelConfig:\r
+            *isOpaque = false;\r
+            return SkBitmap::kA8_Config;\r
+        case kRGB_565_GrPixelConfig:\r
+            *isOpaque = true;\r
+            return SkBitmap::kRGB_565_Config;\r
+        case kRGBA_4444_GrPixelConfig:\r
+            *isOpaque = false;\r
+            return SkBitmap::kARGB_4444_Config;\r
+        case kSkia8888_GrPixelConfig:\r
+            // we don't currently have a way of knowing whether\r
+            // a 8888 is opaque based on the config.\r
+            *isOpaque = false;\r
+            return SkBitmap::kARGB_8888_Config;\r
+        default:\r
+            *isOpaque = false;\r
+            return SkBitmap::kNo_Config;\r
+    }\r
+}\r
+\r
+/*\r
+ * GrRenderTarget does not know its opaqueness, only its config, so we have\r
+ * to make conservative guesses when we return an "equivalent" bitmap.\r
+ */\r
+static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) {\r
+    bool isOpaque;\r
+    SkBitmap::Config config = grConfig2skConfig(renderTarget->config(), &isOpaque);\r
+\r
+    SkBitmap bitmap;\r
+    bitmap.setConfig(config, renderTarget->width(), renderTarget->height(), 0,\r
+                     isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);\r
+    return bitmap;\r
+}\r
+\r
+SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) {\r
+    SkASSERT(NULL != surface);\r
+    if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) {\r
+        return NULL;\r
+    }\r
+    if (surface->asTexture()) {\r
+        return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture()));\r
+    } else {\r
+        return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget()));\r
+    }\r
+}\r
+\r
+SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture)\r
+    : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {\r
+    this->initFromRenderTarget(context, texture->asRenderTarget(), false);\r
+}\r
+\r
+SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget)\r
+    : SkBitmapDevice(make_bitmap(context, renderTarget)) {\r
+    this->initFromRenderTarget(context, renderTarget, false);\r
+}\r
+\r
+void SkGpuDevice::initFromRenderTarget(GrContext* context,\r
+                                       GrRenderTarget* renderTarget,\r
+                                       bool cached) {\r
+    fDrawProcs = NULL;\r
+\r
+    fContext = context;\r
+    fContext->ref();\r
+\r
+    fRenderTarget = NULL;\r
+    fNeedClear = false;\r
+\r
+    SkASSERT(NULL != renderTarget);\r
+    fRenderTarget = renderTarget;\r
+    fRenderTarget->ref();\r
+\r
+    // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref\r
+    // on the RT but not vice-versa.\r
+    // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without\r
+    // busting chrome (for a currently unknown reason).\r
+    GrSurface* surface = fRenderTarget->asTexture();\r
+    if (NULL == surface) {\r
+        surface = fRenderTarget;\r
+    }\r
+    SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (surface, cached));\r
+\r
+    this->setPixelRef(pr, 0)->unref();\r
+}\r
+\r
+SkGpuDevice::SkGpuDevice(GrContext* context,\r
+                         SkBitmap::Config config,\r
+                         int width,\r
+                         int height,\r
+                         int sampleCount)\r
+    : SkBitmapDevice(config, width, height, false /*isOpaque*/) {\r
+\r
+    fDrawProcs = NULL;\r
+\r
+    fContext = context;\r
+    fContext->ref();\r
+\r
+    fRenderTarget = NULL;\r
+    fNeedClear = false;\r
+\r
+    if (config != SkBitmap::kRGB_565_Config) {\r
+        config = SkBitmap::kARGB_8888_Config;\r
+    }\r
+\r
+    GrTextureDesc desc;\r
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;\r
+    desc.fWidth = width;\r
+    desc.fHeight = height;\r
+    desc.fConfig = SkBitmapConfig2GrPixelConfig(config);\r
+    desc.fSampleCnt = sampleCount;\r
+\r
+    SkAutoTUnref<GrTexture> texture(fContext->createUncachedTexture(desc, NULL, 0));\r
+\r
+    if (NULL != texture) {\r
+        fRenderTarget = texture->asRenderTarget();\r
+        fRenderTarget->ref();\r
+\r
+        SkASSERT(NULL != fRenderTarget);\r
+\r
+        // wrap the bitmap with a pixelref to expose our texture\r
+        SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (texture));\r
+        this->setPixelRef(pr, 0)->unref();\r
+    } else {\r
+        GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",\r
+                 width, height);\r
+        SkASSERT(false);\r
+    }\r
+}\r
+\r
+SkGpuDevice::~SkGpuDevice() {\r
+    if (fDrawProcs) {\r
+        delete fDrawProcs;\r
+    }\r
+\r
+    // The GrContext takes a ref on the target. We don't want to cause the render\r
+    // target to be unnecessarily kept alive.\r
+    if (fContext->getRenderTarget() == fRenderTarget) {\r
+        fContext->setRenderTarget(NULL);\r
+    }\r
+\r
+    if (fContext->getClip() == &fClipData) {\r
+        fContext->setClip(NULL);\r
+    }\r
+\r
+    SkSafeUnref(fRenderTarget);\r
+    fContext->unref();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+void SkGpuDevice::makeRenderTargetCurrent() {\r
+    DO_DEFERRED_CLEAR();\r
+    fContext->setRenderTarget(fRenderTarget);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace {\r
+GrPixelConfig config8888_to_grconfig_and_flags(SkCanvas::Config8888 config8888, uint32_t* flags) {\r
+    switch (config8888) {\r
+        case SkCanvas::kNative_Premul_Config8888:\r
+            *flags = 0;\r
+            return kSkia8888_GrPixelConfig;\r
+        case SkCanvas::kNative_Unpremul_Config8888:\r
+            *flags = GrContext::kUnpremul_PixelOpsFlag;\r
+            return kSkia8888_GrPixelConfig;\r
+        case SkCanvas::kBGRA_Premul_Config8888:\r
+            *flags = 0;\r
+            return kBGRA_8888_GrPixelConfig;\r
+        case SkCanvas::kBGRA_Unpremul_Config8888:\r
+            *flags = GrContext::kUnpremul_PixelOpsFlag;\r
+            return kBGRA_8888_GrPixelConfig;\r
+        case SkCanvas::kRGBA_Premul_Config8888:\r
+            *flags = 0;\r
+            return kRGBA_8888_GrPixelConfig;\r
+        case SkCanvas::kRGBA_Unpremul_Config8888:\r
+            *flags = GrContext::kUnpremul_PixelOpsFlag;\r
+            return kRGBA_8888_GrPixelConfig;\r
+        default:\r
+            GrCrash("Unexpected Config8888.");\r
+            *flags = 0; // suppress warning\r
+            return kSkia8888_GrPixelConfig;\r
+    }\r
+}\r
+}\r
+\r
+bool SkGpuDevice::onReadPixels(const SkBitmap& bitmap,\r
+                               int x, int y,\r
+                               SkCanvas::Config8888 config8888) {\r
+    DO_DEFERRED_CLEAR();\r
+    SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());\r
+    SkASSERT(!bitmap.isNull());\r
+    SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height())));\r
+\r
+    SkAutoLockPixels alp(bitmap);\r
+    GrPixelConfig config;\r
+    uint32_t flags;\r
+    config = config8888_to_grconfig_and_flags(config8888, &flags);\r
+    return fContext->readRenderTargetPixels(fRenderTarget,\r
+                                            x, y,\r
+                                            bitmap.width(),\r
+                                            bitmap.height(),\r
+                                            config,\r
+                                            bitmap.getPixels(),\r
+                                            bitmap.rowBytes(),\r
+                                            flags);\r
+}\r
+\r
+void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y,\r
+                              SkCanvas::Config8888 config8888) {\r
+    SkAutoLockPixels alp(bitmap);\r
+    if (!bitmap.readyToDraw()) {\r
+        return;\r
+    }\r
+\r
+    GrPixelConfig config;\r
+    uint32_t flags;\r
+    if (SkBitmap::kARGB_8888_Config == bitmap.config()) {\r
+        config = config8888_to_grconfig_and_flags(config8888, &flags);\r
+    } else {\r
+        flags = 0;\r
+        config= SkBitmapConfig2GrPixelConfig(bitmap.config());\r
+    }\r
+\r
+    fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(),\r
+                               config, bitmap.getPixels(), bitmap.rowBytes(), flags);\r
+}\r
+\r
+void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {\r
+    INHERITED::onAttachToCanvas(canvas);\r
+\r
+    // Canvas promises that this ptr is valid until onDetachFromCanvas is called\r
+    fClipData.fClipStack = canvas->getClipStack();\r
+}\r
+\r
+void SkGpuDevice::onDetachFromCanvas() {\r
+    INHERITED::onDetachFromCanvas();\r
+    fClipData.fClipStack = NULL;\r
+}\r
+\r
+// call this every draw call, to ensure that the context reflects our state,\r
+// and not the state from some other canvas/device\r
+void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) {\r
+    SkASSERT(NULL != fClipData.fClipStack);\r
+\r
+    fContext->setRenderTarget(fRenderTarget);\r
+\r
+    SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack);\r
+\r
+    if (forceIdentity) {\r
+        fContext->setIdentityMatrix();\r
+    } else {\r
+        fContext->setMatrix(*draw.fMatrix);\r
+    }\r
+    fClipData.fOrigin = this->getOrigin();\r
+\r
+    fContext->setClip(&fClipData);\r
+\r
+    DO_DEFERRED_CLEAR();\r
+}\r
+\r
+GrRenderTarget* SkGpuDevice::accessRenderTarget() {\r
+    DO_DEFERRED_CLEAR();\r
+    return fRenderTarget;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);\r
+SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch);\r
+SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch);\r
+SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);\r
+SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,\r
+                  shader_type_mismatch);\r
+SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5,\r
+                  shader_type_mismatch);\r
+SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch);\r
+SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch);\r
+\r
+namespace {\r
+\r
+// converts a SkPaint to a GrPaint, ignoring the skPaint's shader\r
+// justAlpha indicates that skPaint's alpha should be used rather than the color\r
+// Callers may subsequently modify the GrPaint. Setting constantColor indicates\r
+// that the final paint will draw the same color at every pixel. This allows\r
+// an optimization where the the color filter can be applied to the skPaint's\r
+// color once while converting to GrPaint and then ignored.\r
+inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev,\r
+                                    const SkPaint& skPaint,\r
+                                    bool justAlpha,\r
+                                    bool constantColor,\r
+                                    GrPaint* grPaint) {\r
+\r
+    grPaint->setDither(skPaint.isDither());\r
+    grPaint->setAntiAlias(skPaint.isAntiAlias());\r
+\r
+    SkXfermode::Coeff sm;\r
+    SkXfermode::Coeff dm;\r
+\r
+    SkXfermode* mode = skPaint.getXfermode();\r
+    GrEffectRef* xferEffect = NULL;\r
+    if (SkXfermode::AsNewEffectOrCoeff(mode, &xferEffect, &sm, &dm)) {\r
+        if (NULL != xferEffect) {\r
+            grPaint->addColorEffect(xferEffect)->unref();\r
+            sm = SkXfermode::kOne_Coeff;\r
+            dm = SkXfermode::kZero_Coeff;\r
+        }\r
+    } else {\r
+        //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)\r
+#if 0\r
+        return false;\r
+#else\r
+        // Fall back to src-over\r
+        sm = SkXfermode::kOne_Coeff;\r
+        dm = SkXfermode::kISA_Coeff;\r
+#endif\r
+    }\r
+    grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));\r
+\r
+    if (justAlpha) {\r
+        uint8_t alpha = skPaint.getAlpha();\r
+        grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha));\r
+        // justAlpha is currently set to true only if there is a texture,\r
+        // so constantColor should not also be true.\r
+        SkASSERT(!constantColor);\r
+    } else {\r
+        grPaint->setColor(SkColor2GrColor(skPaint.getColor()));\r
+    }\r
+\r
+    SkColorFilter* colorFilter = skPaint.getColorFilter();\r
+    if (NULL != colorFilter) {\r
+        // if the source color is a constant then apply the filter here once rather than per pixel\r
+        // in a shader.\r
+        if (constantColor) {\r
+            SkColor filtered = colorFilter->filterColor(skPaint.getColor());\r
+            grPaint->setColor(SkColor2GrColor(filtered));\r
+        } else {\r
+            SkAutoTUnref<GrEffectRef> effect(colorFilter->asNewEffect(dev->context()));\r
+            if (NULL != effect.get()) {\r
+                grPaint->addColorEffect(effect);\r
+            }\r
+        }\r
+    }\r
+\r
+    return true;\r
+}\r
+\r
+// This function is similar to skPaint2GrPaintNoShader but also converts\r
+// skPaint's shader to a GrTexture/GrEffectStage if possible. The texture to\r
+// be used is set on grPaint and returned in param act. constantColor has the\r
+// same meaning as in skPaint2GrPaintNoShader.\r
+inline bool skPaint2GrPaintShader(SkGpuDevice* dev,\r
+                                  const SkPaint& skPaint,\r
+                                  bool constantColor,\r
+                                  GrPaint* grPaint) {\r
+    SkShader* shader = skPaint.getShader();\r
+    if (NULL == shader) {\r
+        return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint);\r
+    }\r
+\r
+    // SkShader::asNewEffect() may do offscreen rendering. Setup default drawing state\r
+    // Also require shader to set the render target .\r
+    GrContext::AutoWideOpenIdentityDraw awo(dev->context(), NULL);\r
+    GrContext::AutoRenderTarget(dev->context(), NULL);\r
+\r
+    // setup the shader as the first color effect on the paint\r
+    SkAutoTUnref<GrEffectRef> effect(shader->asNewEffect(dev->context(), skPaint));\r
+    if (NULL != effect.get()) {\r
+        grPaint->addColorEffect(effect);\r
+        // Now setup the rest of the paint.\r
+        return skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint);\r
+    } else {\r
+        // We still don't have SkColorShader::asNewEffect() implemented.\r
+        SkShader::GradientInfo info;\r
+        SkColor                color;\r
+\r
+        info.fColors = &color;\r
+        info.fColorOffsets = NULL;\r
+        info.fColorCount = 1;\r
+        if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {\r
+            SkPaint copy(skPaint);\r
+            copy.setShader(NULL);\r
+            // modulate the paint alpha by the shader's solid color alpha\r
+            U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());\r
+            copy.setColor(SkColorSetA(color, newA));\r
+            return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint);\r
+        } else {\r
+            return false;\r
+        }\r
+    }\r
+}\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+void SkGpuDevice::getGlobalBounds(SkIRect* bounds) const {\r
+    if (NULL != bounds) {\r
+        const SkIPoint& origin = this->getOrigin();\r
+        bounds->setXYWH(origin.x(), origin.y(),\r
+                        this->width(), this->height());\r
+    }\r
+}\r
+\r
+SkBitmap::Config SkGpuDevice::config() const {\r
+    if (NULL == fRenderTarget) {\r
+        return SkBitmap::kNo_Config;\r
+    }\r
+\r
+    bool isOpaque;\r
+    return grConfig2skConfig(fRenderTarget->config(), &isOpaque);\r
+}\r
+\r
+void SkGpuDevice::clear(SkColor color) {\r
+    SkIRect rect = SkIRect::MakeWH(this->width(), this->height());\r
+    fContext->clear(&rect, SkColor2GrColor(color), true, fRenderTarget);\r
+    fNeedClear = false;\r
+}\r
+\r
+void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {\r
+    CHECK_SHOULD_DRAW(draw, false);\r
+\r
+    GrPaint grPaint;\r
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {\r
+        return;\r
+    }\r
+\r
+    fContext->drawPaint(grPaint);\r
+}\r
+\r
+// must be in SkCanvas::PointMode order\r
+static const GrPrimitiveType gPointMode2PrimtiveType[] = {\r
+    kPoints_GrPrimitiveType,\r
+    kLines_GrPrimitiveType,\r
+    kLineStrip_GrPrimitiveType\r
+};\r
+\r
+void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,\r
+                             size_t count, const SkPoint pts[], const SkPaint& paint) {\r
+    CHECK_FOR_ANNOTATION(paint);\r
+    CHECK_SHOULD_DRAW(draw, false);\r
+\r
+    SkScalar width = paint.getStrokeWidth();\r
+    if (width < 0) {\r
+        return;\r
+    }\r
+\r
+    // we only handle hairlines and paints without path effects or mask filters,\r
+    // else we let the SkDraw call our drawPath()\r
+    if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) {\r
+        draw.drawPoints(mode, count, pts, paint, true);\r
+        return;\r
+    }\r
+\r
+    GrPaint grPaint;\r
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {\r
+        return;\r
+    }\r
+\r
+    fContext->drawVertices(grPaint,\r
+                           gPointMode2PrimtiveType[mode],\r
+                           count,\r
+                           (GrPoint*)pts,\r
+                           NULL,\r
+                           NULL,\r
+                           NULL,\r
+                           0);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,\r
+                           const SkPaint& paint) {\r
+    CHECK_FOR_ANNOTATION(paint);\r
+    CHECK_SHOULD_DRAW(draw, false);\r
+\r
+    bool doStroke = paint.getStyle() != SkPaint::kFill_Style;\r
+    SkScalar width = paint.getStrokeWidth();\r
+\r
+    /*\r
+        We have special code for hairline strokes, miter-strokes, bevel-stroke\r
+        and fills. Anything else we just call our path code.\r
+     */\r
+    bool usePath = doStroke && width > 0 &&\r
+                   (paint.getStrokeJoin() == SkPaint::kRound_Join ||\r
+                    (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty()));\r
+    // another two reasons we might need to call drawPath...\r
+    if (paint.getMaskFilter() || paint.getPathEffect()) {\r
+        usePath = true;\r
+    }\r
+    if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) {\r
+#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)\r
+        if (doStroke) {\r
+#endif\r
+            usePath = true;\r
+#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)\r
+        } else {\r
+            usePath = !fContext->getMatrix().preservesRightAngles();\r
+        }\r
+#endif\r
+    }\r
+    // until we can both stroke and fill rectangles\r
+    if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {\r
+        usePath = true;\r
+    }\r
+\r
+    if (usePath) {\r
+        SkPath path;\r
+        path.addRect(rect);\r
+        this->drawPath(draw, path, paint, NULL, true);\r
+        return;\r
+    }\r
+\r
+    GrPaint grPaint;\r
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {\r
+        return;\r
+    }\r
+\r
+    if (!doStroke) {\r
+        fContext->drawRect(grPaint, rect);\r
+    } else {\r
+        SkStrokeRec stroke(paint);\r
+        fContext->drawRect(grPaint, rect, &stroke);\r
+    }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect,\r
+                           const SkPaint& paint) {\r
+    CHECK_FOR_ANNOTATION(paint);\r
+    CHECK_SHOULD_DRAW(draw, false);\r
+\r
+    bool usePath = !rect.isSimple();\r
+    // another two reasons we might need to call drawPath...\r
+    if (paint.getMaskFilter() || paint.getPathEffect()) {\r
+        usePath = true;\r
+    }\r
+    // until we can rotate rrects...\r
+    if (!usePath && !fContext->getMatrix().rectStaysRect()) {\r
+        usePath = true;\r
+    }\r
+\r
+    if (usePath) {\r
+        SkPath path;\r
+        path.addRRect(rect);\r
+        this->drawPath(draw, path, paint, NULL, true);\r
+        return;\r
+    }\r
+\r
+    GrPaint grPaint;\r
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {\r
+        return;\r
+    }\r
+\r
+    SkStrokeRec stroke(paint);\r
+    fContext->drawRRect(grPaint, rect, stroke);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,\r
+                           const SkPaint& paint) {\r
+    CHECK_FOR_ANNOTATION(paint);\r
+    CHECK_SHOULD_DRAW(draw, false);\r
+\r
+    bool usePath = false;\r
+    // some basic reasons we might need to call drawPath...\r
+    if (paint.getMaskFilter() || paint.getPathEffect()) {\r
+        usePath = true;\r
+    }\r
+\r
+    if (usePath) {\r
+        SkPath path;\r
+        path.addOval(oval);\r
+        this->drawPath(draw, path, paint, NULL, true);\r
+        return;\r
+    }\r
+\r
+    GrPaint grPaint;\r
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {\r
+        return;\r
+    }\r
+    SkStrokeRec stroke(paint);\r
+\r
+    fContext->drawOval(grPaint, oval, stroke);\r
+}\r
+\r
+#include "SkMaskFilter.h"\r
+#include "SkBounder.h"\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+// helpers for applying mask filters\r
+namespace {\r
+\r
+// Draw a mask using the supplied paint. Since the coverage/geometry\r
+// is already burnt into the mask this boils down to a rect draw.\r
+// Return true if the mask was successfully drawn.\r
+bool draw_mask(GrContext* context, const SkRect& maskRect,\r
+               GrPaint* grp, GrTexture* mask) {\r
+    GrContext::AutoMatrix am;\r
+    if (!am.setIdentity(context, grp)) {\r
+        return false;\r
+    }\r
+\r
+    SkMatrix matrix;\r
+    matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);\r
+    matrix.postIDiv(mask->width(), mask->height());\r
+\r
+    grp->addCoverageEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref();\r
+    context->drawRect(*grp, maskRect);\r
+    return true;\r
+}\r
+\r
+bool draw_with_mask_filter(GrContext* context, const SkPath& devPath,\r
+                           SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder,\r
+                           GrPaint* grp, SkPaint::Style style) {\r
+    SkMask  srcM, dstM;\r
+\r
+    if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM,\r
+                            SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {\r
+        return false;\r
+    }\r
+    SkAutoMaskFreeImage autoSrc(srcM.fImage);\r
+\r
+    if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) {\r
+        return false;\r
+    }\r
+    // this will free-up dstM when we're done (allocated in filterMask())\r
+    SkAutoMaskFreeImage autoDst(dstM.fImage);\r
+\r
+    if (clip.quickReject(dstM.fBounds)) {\r
+        return false;\r
+    }\r
+    if (bounder && !bounder->doIRect(dstM.fBounds)) {\r
+        return false;\r
+    }\r
+\r
+    // we now have a device-aligned 8bit mask in dstM, ready to be drawn using\r
+    // the current clip (and identity matrix) and GrPaint settings\r
+    GrTextureDesc desc;\r
+    desc.fWidth = dstM.fBounds.width();\r
+    desc.fHeight = dstM.fBounds.height();\r
+    desc.fConfig = kAlpha_8_GrPixelConfig;\r
+\r
+    GrAutoScratchTexture ast(context, desc);\r
+    GrTexture* texture = ast.texture();\r
+\r
+    if (NULL == texture) {\r
+        return false;\r
+    }\r
+    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,\r
+                               dstM.fImage, dstM.fRowBytes);\r
+\r
+    SkRect maskRect = SkRect::Make(dstM.fBounds);\r
+\r
+    return draw_mask(context, maskRect, grp, texture);\r
+}\r
+\r
+// Create a mask of 'devPath' and place the result in 'mask'. Return true on\r
+// success; false otherwise.\r
+bool create_mask_GPU(GrContext* context,\r
+                     const SkRect& maskRect,\r
+                     const SkPath& devPath,\r
+                     const SkStrokeRec& stroke,\r
+                     bool doAA,\r
+                     GrAutoScratchTexture* mask) {\r
+    GrTextureDesc desc;\r
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;\r
+    desc.fWidth = SkScalarCeilToInt(maskRect.width());\r
+    desc.fHeight = SkScalarCeilToInt(maskRect.height());\r
+    // We actually only need A8, but it often isn't supported as a\r
+    // render target so default to RGBA_8888\r
+    desc.fConfig = kRGBA_8888_GrPixelConfig;\r
+    if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {\r
+        desc.fConfig = kAlpha_8_GrPixelConfig;\r
+    }\r
+\r
+    mask->set(context, desc);\r
+    if (NULL == mask->texture()) {\r
+        return false;\r
+    }\r
+\r
+    GrTexture* maskTexture = mask->texture();\r
+    SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());\r
+\r
+    GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget());\r
+    GrContext::AutoClip ac(context, clipRect);\r
+\r
+    context->clear(NULL, 0x0, true);\r
+\r
+    GrPaint tempPaint;\r
+    if (doAA) {\r
+        tempPaint.setAntiAlias(true);\r
+        // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst\r
+        // blend coeff of zero requires dual source blending support in order\r
+        // to properly blend partially covered pixels. This means the AA\r
+        // code path may not be taken. So we use a dst blend coeff of ISA. We\r
+        // could special case AA draws to a dst surface with known alpha=0 to\r
+        // use a zero dst coeff when dual source blending isn't available.\r
+        tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);\r
+    }\r
+\r
+    GrContext::AutoMatrix am;\r
+\r
+    // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint.\r
+    SkMatrix translate;\r
+    translate.setTranslate(-maskRect.fLeft, -maskRect.fTop);\r
+    am.set(context, translate);\r
+    context->drawPath(tempPaint, devPath, stroke);\r
+    return true;\r
+}\r
+\r
+SkBitmap wrap_texture(GrTexture* texture) {\r
+    SkBitmap result;\r
+    bool dummy;\r
+    SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy);\r
+    result.setConfig(config, texture->width(), texture->height());\r
+    result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();\r
+    return result;\r
+}\r
+\r
+};\r
+\r
+void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,\r
+                           const SkPaint& paint, const SkMatrix* prePathMatrix,\r
+                           bool pathIsMutable) {\r
+    CHECK_FOR_ANNOTATION(paint);\r
+    CHECK_SHOULD_DRAW(draw, false);\r
+\r
+    GrPaint grPaint;\r
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {\r
+        return;\r
+    }\r
+\r
+    // can we cheat, and treat a thin stroke as a hairline w/ coverage\r
+    // if we can, we draw lots faster (raster device does this same test)\r
+    SkScalar hairlineCoverage;\r
+    bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage);\r
+    if (doHairLine) {\r
+        grPaint.setCoverage(SkScalarRoundToInt(hairlineCoverage * grPaint.getCoverage()));\r
+    }\r
+\r
+    // If we have a prematrix, apply it to the path, optimizing for the case\r
+    // where the original path can in fact be modified in place (even though\r
+    // its parameter type is const).\r
+    SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);\r
+    SkPath  tmpPath, effectPath;\r
+\r
+    if (prePathMatrix) {\r
+        SkPath* result = pathPtr;\r
+\r
+        if (!pathIsMutable) {\r
+            result = &tmpPath;\r
+            pathIsMutable = true;\r
+        }\r
+        // should I push prePathMatrix on our MV stack temporarily, instead\r
+        // of applying it here? See SkDraw.cpp\r
+        pathPtr->transform(*prePathMatrix, result);\r
+        pathPtr = result;\r
+    }\r
+    // at this point we're done with prePathMatrix\r
+    SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)\r
+\r
+    SkStrokeRec stroke(paint);\r
+    SkPathEffect* pathEffect = paint.getPathEffect();\r
+    const SkRect* cullRect = NULL;  // TODO: what is our bounds?\r
+    if (pathEffect && pathEffect->filterPath(&effectPath, *pathPtr, &stroke,\r
+                                             cullRect)) {\r
+        pathPtr = &effectPath;\r
+    }\r
+\r
+    if (!pathEffect && doHairLine) {\r
+        stroke.setHairlineStyle();\r
+    }\r
+\r
+    if (paint.getMaskFilter()) {\r
+        if (!stroke.isHairlineStyle()) {\r
+            if (stroke.applyToPath(&tmpPath, *pathPtr)) {\r
+                pathPtr = &tmpPath;\r
+                pathIsMutable = true;\r
+                stroke.setFillStyle();\r
+            }\r
+        }\r
+\r
+        // avoid possibly allocating a new path in transform if we can\r
+        SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;\r
+\r
+        // transform the path into device space\r
+        pathPtr->transform(fContext->getMatrix(), devPathPtr);\r
+\r
+        SkRect maskRect;\r
+        if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),\r
+                                                    draw.fClip->getBounds(),\r
+                                                    fContext->getMatrix(),\r
+                                                    &maskRect)) {\r
+            SkIRect finalIRect;\r
+            maskRect.roundOut(&finalIRect);\r
+            if (draw.fClip->quickReject(finalIRect)) {\r
+                // clipped out\r
+                return;\r
+            }\r
+            if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) {\r
+                // nothing to draw\r
+                return;\r
+            }\r
+\r
+            GrAutoScratchTexture mask;\r
+\r
+            if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke,\r
+                                grPaint.isAntiAlias(), &mask)) {\r
+                GrTexture* filtered;\r
+\r
+                if (paint.getMaskFilter()->filterMaskGPU(mask.texture(), maskRect, &filtered, true)) {\r
+                    // filterMaskGPU gives us ownership of a ref to the result\r
+                    SkAutoTUnref<GrTexture> atu(filtered);\r
+\r
+                    // If the scratch texture that we used as the filter src also holds the filter\r
+                    // result then we must detach so that this texture isn't recycled for a later\r
+                    // draw.\r
+                    if (filtered == mask.texture()) {\r
+                        mask.detach();\r
+                        filtered->unref(); // detach transfers GrAutoScratchTexture's ref to us.\r
+                    }\r
+\r
+                    if (draw_mask(fContext, maskRect, &grPaint, filtered)) {\r
+                        // This path is completely drawn\r
+                        return;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        // draw the mask on the CPU - this is a fallthrough path in case the\r
+        // GPU path fails\r
+        SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :\r
+                                                          SkPaint::kFill_Style;\r
+        draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(),\r
+                              *draw.fClip, draw.fBounder, &grPaint, style);\r
+        return;\r
+    }\r
+\r
+    fContext->drawPath(grPaint, *pathPtr, stroke);\r
+}\r
+\r
+static const int kBmpSmallTileSize = 1 << 10;\r
+\r
+static inline int get_tile_count(const SkIRect& srcRect, int tileSize)  {\r
+    int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1;\r
+    int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1;\r
+    return tilesX * tilesY;\r
+}\r
+\r
+static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) {\r
+    if (maxTileSize <= kBmpSmallTileSize) {\r
+        return maxTileSize;\r
+    }\r
+\r
+    size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);\r
+    size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);\r
+\r
+    maxTileTotalTileSize *= maxTileSize * maxTileSize;\r
+    smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;\r
+\r
+    if (maxTileTotalTileSize > 2 * smallTotalTileSize) {\r
+        return kBmpSmallTileSize;\r
+    } else {\r
+        return maxTileSize;\r
+    }\r
+}\r
+\r
+// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what\r
+// pixels from the bitmap are necessary.\r
+static void determine_clipped_src_rect(const GrContext* context,\r
+                                       const SkBitmap& bitmap,\r
+                                       const SkRect* srcRectPtr,\r
+                                       SkIRect* clippedSrcIRect) {\r
+    const GrClipData* clip = context->getClip();\r
+    clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL);\r
+    SkMatrix inv;\r
+    if (!context->getMatrix().invert(&inv)) {\r
+        clippedSrcIRect->setEmpty();\r
+        return;\r
+    }\r
+    SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect);\r
+    inv.mapRect(&clippedSrcRect);\r
+    if (NULL != srcRectPtr) {\r
+        if (!clippedSrcRect.intersect(*srcRectPtr)) {\r
+            clippedSrcIRect->setEmpty();\r
+            return;\r
+        }\r
+    }\r
+    clippedSrcRect.roundOut(clippedSrcIRect);\r
+    SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());\r
+    if (!clippedSrcIRect->intersect(bmpBounds)) {\r
+        clippedSrcIRect->setEmpty();\r
+    }\r
+}\r
+\r
+bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,\r
+                                   const GrTextureParams& params,\r
+                                   const SkRect* srcRectPtr,\r
+                                   int maxTileSize,\r
+                                   int* tileSize,\r
+                                   SkIRect* clippedSrcRect) const {\r
+    // if bitmap is explictly texture backed then just use the texture\r
+    if (NULL != bitmap.getTexture()) {\r
+        return false;\r
+    }\r
+\r
+    // if it's larger than the max tile size, then we have no choice but tiling.\r
+    if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) {\r
+        determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);\r
+        *tileSize = determine_tile_size(bitmap, *clippedSrcRect, maxTileSize);\r
+        return true;\r
+    }\r
+\r
+    if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) {\r
+        return false;\r
+    }\r
+\r
+    // if the entire texture is already in our cache then no reason to tile it\r
+    if (GrIsBitmapInCache(fContext, bitmap, &params)) {\r
+        return false;\r
+    }\r
+\r
+    // At this point we know we could do the draw by uploading the entire bitmap\r
+    // as a texture. However, if the texture would be large compared to the\r
+    // cache size and we don't require most of it for this draw then tile to\r
+    // reduce the amount of upload and cache spill.\r
+\r
+    // assumption here is that sw bitmap size is a good proxy for its size as\r
+    // a texture\r
+    size_t bmpSize = bitmap.getSize();\r
+    size_t cacheSize;\r
+    fContext->getTextureCacheLimits(NULL, &cacheSize);\r
+    if (bmpSize < cacheSize / 2) {\r
+        return false;\r
+    }\r
+\r
+    // Figure out how much of the src we will need based on the src rect and clipping.\r
+    determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);\r
+    *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile.\r
+    size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) *\r
+                           kBmpSmallTileSize * kBmpSmallTileSize;\r
+\r
+    return usedTileBytes < 2 * bmpSize;\r
+}\r
+\r
+void SkGpuDevice::drawBitmap(const SkDraw& draw,\r
+                             const SkBitmap& bitmap,\r
+                             const SkMatrix& m,\r
+                             const SkPaint& paint) {\r
+    // We cannot call drawBitmapRect here since 'm' could be anything\r
+    this->drawBitmapCommon(draw, bitmap, NULL, m, paint,\r
+                           SkCanvas::kNone_DrawBitmapRectFlag);\r
+}\r
+\r
+// This method outsets 'iRect' by 1 all around and then clamps its extents to\r
+// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner\r
+// of 'iRect' for all possible outsets/clamps.\r
+static inline void clamped_unit_outset_with_offset(SkIRect* iRect, SkPoint* offset,\r
+                                                   const SkIRect& clamp) {\r
+    iRect->outset(1, 1);\r
+\r
+    if (iRect->fLeft < clamp.fLeft) {\r
+        iRect->fLeft = clamp.fLeft;\r
+    } else {\r
+        offset->fX -= SK_Scalar1;\r
+    }\r
+    if (iRect->fTop < clamp.fTop) {\r
+        iRect->fTop = clamp.fTop;\r
+    } else {\r
+        offset->fY -= SK_Scalar1;\r
+    }\r
+\r
+    if (iRect->fRight > clamp.fRight) {\r
+        iRect->fRight = clamp.fRight;\r
+    }\r
+    if (iRect->fBottom > clamp.fBottom) {\r
+        iRect->fBottom = clamp.fBottom;\r
+    }\r
+}\r
+\r
+void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,\r
+                                   const SkBitmap& bitmap,\r
+                                   const SkRect* srcRectPtr,\r
+                                   const SkMatrix& m,\r
+                                   const SkPaint& paint,\r
+                                   SkCanvas::DrawBitmapRectFlags flags) {\r
+    CHECK_SHOULD_DRAW(draw, false);\r
+\r
+    SkRect srcRect;\r
+    if (NULL == srcRectPtr) {\r
+        srcRect.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));\r
+    } else {\r
+        srcRect = *srcRectPtr;\r
+    }\r
+\r
+    if (paint.getMaskFilter()){\r
+        // Convert the bitmap to a shader so that the rect can be drawn\r
+        // through drawRect, which supports mask filters.\r
+        SkMatrix        newM(m);\r
+        SkBitmap        tmp;    // subset of bitmap, if necessary\r
+        const SkBitmap* bitmapPtr = &bitmap;\r
+        if (NULL != srcRectPtr) {\r
+            SkIRect iSrc;\r
+            srcRect.roundOut(&iSrc);\r
+\r
+            SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft),\r
+                                           SkIntToScalar(iSrc.fTop));\r
+\r
+            if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {\r
+                // In bleed mode we want to expand the src rect on all sides\r
+                // but stay within the bitmap bounds\r
+                SkIRect iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());\r
+                clamped_unit_outset_with_offset(&iSrc, &offset, iClampRect);\r
+            }\r
+\r
+            if (!bitmap.extractSubset(&tmp, iSrc)) {\r
+                return;     // extraction failed\r
+            }\r
+            bitmapPtr = &tmp;\r
+            srcRect.offset(-offset.fX, -offset.fY);\r
+            // The source rect has changed so update the matrix\r
+            newM.preTranslate(offset.fX, offset.fY);\r
+        }\r
+\r
+        SkPaint paintWithTexture(paint);\r
+        paintWithTexture.setShader(SkShader::CreateBitmapShader(*bitmapPtr,\r
+            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref();\r
+\r
+        // Transform 'newM' needs to be concatenated to the current matrix,\r
+        // rather than transforming the primitive directly, so that 'newM' will\r
+        // also affect the behavior of the mask filter.\r
+        SkMatrix drawMatrix;\r
+        drawMatrix.setConcat(fContext->getMatrix(), newM);\r
+        SkDraw transformedDraw(draw);\r
+        transformedDraw.fMatrix = &drawMatrix;\r
+\r
+        this->drawRect(transformedDraw, srcRect, paintWithTexture);\r
+\r
+        return;\r
+    }\r
+\r
+    fContext->concatMatrix(m);\r
+\r
+    GrTextureParams params;\r
+    SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel();\r
+    GrTextureParams::FilterMode textureFilterMode;\r
+    switch(paintFilterLevel) {\r
+        case SkPaint::kNone_FilterLevel:\r
+            textureFilterMode = GrTextureParams::kNone_FilterMode;\r
+            break;\r
+        case SkPaint::kLow_FilterLevel:\r
+            textureFilterMode = GrTextureParams::kBilerp_FilterMode;\r
+            break;\r
+        case SkPaint::kMedium_FilterLevel:\r
+            textureFilterMode = GrTextureParams::kMipMap_FilterMode;\r
+            break;\r
+        case SkPaint::kHigh_FilterLevel:\r
+            // Fall back to mips for now\r
+            textureFilterMode = GrTextureParams::kMipMap_FilterMode;\r
+            break;\r
+        default:\r
+            SkErrorInternals::SetError( kInvalidPaint_SkError,\r
+                                        "Sorry, I don't understand the filtering "\r
+                                        "mode you asked for.  Falling back to "\r
+                                        "MIPMaps.");\r
+            textureFilterMode = GrTextureParams::kMipMap_FilterMode;\r
+            break;\r
+\r
+    }\r
+\r
+    params.setFilterMode(textureFilterMode);\r
+\r
+    int maxTileSize = fContext->getMaxTextureSize();\r
+    if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) {\r
+        // We may need a skosh more room if we have to bump out the tile\r
+        // by 1 pixel all around\r
+        maxTileSize -= 2;\r
+    }\r
+    int tileSize;\r
+\r
+    SkIRect clippedSrcRect;\r
+    if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize,\r
+                               &clippedSrcRect)) {\r
+        this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize);\r
+    } else {\r
+        // take the simple case\r
+        this->internalDrawBitmap(bitmap, srcRect, params, paint, flags);\r
+    }\r
+}\r
+\r
+// Break 'bitmap' into several tiles to draw it since it has already\r
+// been determined to be too large to fit in VRAM\r
+void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,\r
+                                  const SkRect& srcRect,\r
+                                  const SkIRect& clippedSrcIRect,\r
+                                  const GrTextureParams& params,\r
+                                  const SkPaint& paint,\r
+                                  SkCanvas::DrawBitmapRectFlags flags,\r
+                                  int tileSize) {\r
+    SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect);\r
+\r
+    int nx = bitmap.width() / tileSize;\r
+    int ny = bitmap.height() / tileSize;\r
+    for (int x = 0; x <= nx; x++) {\r
+        for (int y = 0; y <= ny; y++) {\r
+            SkRect tileR;\r
+            tileR.set(SkIntToScalar(x * tileSize),\r
+                      SkIntToScalar(y * tileSize),\r
+                      SkIntToScalar((x + 1) * tileSize),\r
+                      SkIntToScalar((y + 1) * tileSize));\r
+\r
+            if (!SkRect::Intersects(tileR, clippedSrcRect)) {\r
+                continue;\r
+            }\r
+\r
+            if (!tileR.intersect(srcRect)) {\r
+                continue;\r
+            }\r
+\r
+            SkBitmap tmpB;\r
+            SkIRect iTileR;\r
+            tileR.roundOut(&iTileR);\r
+            SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft),\r
+                                           SkIntToScalar(iTileR.fTop));\r
+\r
+            if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) {\r
+                SkIRect iClampRect;\r
+\r
+                if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {\r
+                    // In bleed mode we want to always expand the tile on all edges\r
+                    // but stay within the bitmap bounds\r
+                    iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());\r
+                } else {\r
+                    // In texture-domain/clamp mode we only want to expand the\r
+                    // tile on edges interior to "srcRect" (i.e., we want to\r
+                    // not bleed across the original clamped edges)\r
+                    srcRect.roundOut(&iClampRect);\r
+                }\r
+\r
+                clamped_unit_outset_with_offset(&iTileR, &offset, iClampRect);\r
+            }\r
+\r
+            if (bitmap.extractSubset(&tmpB, iTileR)) {\r
+                // now offset it to make it "local" to our tmp bitmap\r
+                tileR.offset(-offset.fX, -offset.fY);\r
+                SkMatrix tmpM;\r
+                tmpM.setTranslate(offset.fX, offset.fY);\r
+                GrContext::AutoMatrix am;\r
+                am.setPreConcat(fContext, tmpM);\r
+                this->internalDrawBitmap(tmpB, tileR, params, paint, flags);\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+static bool has_aligned_samples(const SkRect& srcRect,\r
+                                const SkRect& transformedRect) {\r
+    // detect pixel disalignment\r
+    if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) -\r
+            transformedRect.left()) < COLOR_BLEED_TOLERANCE &&\r
+        SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) -\r
+            transformedRect.top()) < COLOR_BLEED_TOLERANCE &&\r
+        SkScalarAbs(transformedRect.width() - srcRect.width()) <\r
+            COLOR_BLEED_TOLERANCE &&\r
+        SkScalarAbs(transformedRect.height() - srcRect.height()) <\r
+            COLOR_BLEED_TOLERANCE) {\r
+        return true;\r
+    }\r
+    return false;\r
+}\r
+\r
+static bool may_color_bleed(const SkRect& srcRect,\r
+                            const SkRect& transformedRect,\r
+                            const SkMatrix& m) {\r
+    // Only gets called if has_aligned_samples returned false.\r
+    // So we can assume that sampling is axis aligned but not texel aligned.\r
+    SkASSERT(!has_aligned_samples(srcRect, transformedRect));\r
+    SkRect innerSrcRect(srcRect), innerTransformedRect,\r
+        outerTransformedRect(transformedRect);\r
+    innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);\r
+    m.mapRect(&innerTransformedRect, innerSrcRect);\r
+\r
+    // The gap between outerTransformedRect and innerTransformedRect\r
+    // represents the projection of the source border area, which is\r
+    // problematic for color bleeding.  We must check whether any\r
+    // destination pixels sample the border area.\r
+    outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);\r
+    innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);\r
+    SkIRect outer, inner;\r
+    outerTransformedRect.round(&outer);\r
+    innerTransformedRect.round(&inner);\r
+    // If the inner and outer rects round to the same result, it means the\r
+    // border does not overlap any pixel centers. Yay!\r
+    return inner != outer;\r
+}\r
+\r
+\r
+/*\r
+ *  This is called by drawBitmap(), which has to handle images that may be too\r
+ *  large to be represented by a single texture.\r
+ *\r
+ *  internalDrawBitmap assumes that the specified bitmap will fit in a texture\r
+ *  and that non-texture portion of the GrPaint has already been setup.\r
+ */\r
+void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,\r
+                                     const SkRect& srcRect,\r
+                                     const GrTextureParams& params,\r
+                                     const SkPaint& paint,\r
+                                     SkCanvas::DrawBitmapRectFlags flags) {\r
+    SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&\r
+             bitmap.height() <= fContext->getMaxTextureSize());\r
+\r
+    GrTexture* texture;\r
+    SkAutoCachedTexture act(this, bitmap, &params, &texture);\r
+    if (NULL == texture) {\r
+        return;\r
+    }\r
+\r
+    SkRect dstRect(srcRect);\r
+    SkRect paintRect;\r
+    SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width()));\r
+    SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height()));\r
+    paintRect.setLTRB(SkScalarMul(srcRect.fLeft,   wInv),\r
+                      SkScalarMul(srcRect.fTop,    hInv),\r
+                      SkScalarMul(srcRect.fRight,  wInv),\r
+                      SkScalarMul(srcRect.fBottom, hInv));\r
+\r
+    bool needsTextureDomain = false;\r
+    if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) &&\r
+        params.filterMode() != GrTextureParams::kNone_FilterMode) {\r
+        // Need texture domain if drawing a sub rect.\r
+        needsTextureDomain = srcRect.width() < bitmap.width() ||\r
+                             srcRect.height() < bitmap.height();\r
+        if (needsTextureDomain && fContext->getMatrix().rectStaysRect()) {\r
+            const SkMatrix& matrix = fContext->getMatrix();\r
+            // sampling is axis-aligned\r
+            SkRect transformedRect;\r
+            matrix.mapRect(&transformedRect, srcRect);\r
+\r
+            if (has_aligned_samples(srcRect, transformedRect)) {\r
+                // We could also turn off filtering here (but we already did a cache lookup with\r
+                // params).\r
+                needsTextureDomain = false;\r
+            } else {\r
+                needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix);\r
+            }\r
+        }\r
+    }\r
+\r
+    SkRect textureDomain = SkRect::MakeEmpty();\r
+    SkAutoTUnref<GrEffectRef> effect;\r
+    if (needsTextureDomain) {\r
+        // Use a constrained texture domain to avoid color bleeding\r
+        SkScalar left, top, right, bottom;\r
+        if (srcRect.width() > SK_Scalar1) {\r
+            SkScalar border = SK_ScalarHalf / texture->width();\r
+            left = paintRect.left() + border;\r
+            right = paintRect.right() - border;\r
+        } else {\r
+            left = right = SkScalarHalf(paintRect.left() + paintRect.right());\r
+        }\r
+        if (srcRect.height() > SK_Scalar1) {\r
+            SkScalar border = SK_ScalarHalf / texture->height();\r
+            top = paintRect.top() + border;\r
+            bottom = paintRect.bottom() - border;\r
+        } else {\r
+            top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom());\r
+        }\r
+        textureDomain.setLTRB(left, top, right, bottom);\r
+        effect.reset(GrTextureDomainEffect::Create(texture,\r
+                                                   SkMatrix::I(),\r
+                                                   textureDomain,\r
+                                                   GrTextureDomainEffect::kClamp_WrapMode,\r
+                                                   params.filterMode()));\r
+    } else {\r
+        effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params));\r
+    }\r
+\r
+    // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring\r
+    // the rest from the SkPaint.\r
+    GrPaint grPaint;\r
+    grPaint.addColorEffect(effect);\r
+    bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config());\r
+    if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) {\r
+        return;\r
+    }\r
+\r
+    fContext->drawRectToRect(grPaint, dstRect, paintRect, NULL);\r
+}\r
+\r
+static bool filter_texture(SkBaseDevice* device, GrContext* context,\r
+                           GrTexture* texture, SkImageFilter* filter,\r
+                           int w, int h, const SkMatrix& ctm, SkBitmap* result,\r
+                           SkIPoint* offset) {\r
+    SkASSERT(filter);\r
+    SkDeviceImageFilterProxy proxy(device);\r
+\r
+    if (filter->canFilterImageGPU()) {\r
+        // Save the render target and set it to NULL, so we don't accidentally draw to it in the\r
+        // filter.  Also set the clip wide open and the matrix to identity.\r
+        GrContext::AutoWideOpenIdentityDraw awo(context, NULL);\r
+        return filter->filterImageGPU(&proxy, wrap_texture(texture), ctm, result, offset);\r
+    } else {\r
+        return false;\r
+    }\r
+}\r
+\r
+void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,\r
+                             int left, int top, const SkPaint& paint) {\r
+    // drawSprite is defined to be in device coords.\r
+    CHECK_SHOULD_DRAW(draw, true);\r
+\r
+    SkAutoLockPixels alp(bitmap, !bitmap.getTexture());\r
+    if (!bitmap.getTexture() && !bitmap.readyToDraw()) {\r
+        return;\r
+    }\r
+\r
+    int w = bitmap.width();\r
+    int h = bitmap.height();\r
+\r
+    GrTexture* texture;\r
+    // draw sprite uses the default texture params\r
+    SkAutoCachedTexture act(this, bitmap, NULL, &texture);\r
+\r
+    SkImageFilter* filter = paint.getImageFilter();\r
+    SkIPoint offset = SkIPoint::Make(left, top);\r
+    // This bitmap will own the filtered result as a texture.\r
+    SkBitmap filteredBitmap;\r
+\r
+    if (NULL != filter) {\r
+        SkMatrix matrix(*draw.fMatrix);\r
+        matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));\r
+        if (filter_texture(this, fContext, texture, filter, w, h, matrix, &filteredBitmap,\r
+                           &offset)) {\r
+            texture = (GrTexture*) filteredBitmap.getTexture();\r
+            w = filteredBitmap.width();\r
+            h = filteredBitmap.height();\r
+        } else {\r
+            return;\r
+        }\r
+    }\r
+\r
+    GrPaint grPaint;\r
+    grPaint.addColorTextureEffect(texture, SkMatrix::I());\r
+\r
+    if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {\r
+        return;\r
+    }\r
+\r
+    fContext->drawRectToRect(grPaint,\r
+                             SkRect::MakeXYWH(SkIntToScalar(offset.fX),\r
+                                              SkIntToScalar(offset.fY),\r
+                                              SkIntToScalar(w),\r
+                                              SkIntToScalar(h)),\r
+                             SkRect::MakeXYWH(0,\r
+                                              0,\r
+                                              SK_Scalar1 * w / texture->width(),\r
+                                              SK_Scalar1 * h / texture->height()));\r
+}\r
+\r
+void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,\r
+                                 const SkRect* src, const SkRect& dst,\r
+                                 const SkPaint& paint,\r
+                                 SkCanvas::DrawBitmapRectFlags flags) {\r
+    SkMatrix    matrix;\r
+    SkRect      bitmapBounds, tmpSrc;\r
+\r
+    bitmapBounds.set(0, 0,\r
+                     SkIntToScalar(bitmap.width()),\r
+                     SkIntToScalar(bitmap.height()));\r
+\r
+    // Compute matrix from the two rectangles\r
+    if (NULL != src) {\r
+        tmpSrc = *src;\r
+    } else {\r
+        tmpSrc = bitmapBounds;\r
+    }\r
+    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);\r
+\r
+    // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null.\r
+    if (NULL != src) {\r
+        if (!bitmapBounds.contains(tmpSrc)) {\r
+            if (!tmpSrc.intersect(bitmapBounds)) {\r
+                return; // nothing to draw\r
+            }\r
+        }\r
+    }\r
+\r
+    this->drawBitmapCommon(draw, bitmap, &tmpSrc, matrix, paint, flags);\r
+}\r
+\r
+void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,\r
+                             int x, int y, const SkPaint& paint) {\r
+    // clear of the source device must occur before CHECK_SHOULD_DRAW\r
+    SkGpuDevice* dev = static_cast<SkGpuDevice*>(device);\r
+    if (dev->fNeedClear) {\r
+        // TODO: could check here whether we really need to draw at all\r
+        dev->clear(0x0);\r
+    }\r
+\r
+    // drawDevice is defined to be in device coords.\r
+    CHECK_SHOULD_DRAW(draw, true);\r
+\r
+    GrRenderTarget* devRT = dev->accessRenderTarget();\r
+    GrTexture* devTex;\r
+    if (NULL == (devTex = devRT->asTexture())) {\r
+        return;\r
+    }\r
+\r
+    const SkBitmap& bm = dev->accessBitmap(false);\r
+    int w = bm.width();\r
+    int h = bm.height();\r
+\r
+    SkImageFilter* filter = paint.getImageFilter();\r
+    // This bitmap will own the filtered result as a texture.\r
+    SkBitmap filteredBitmap;\r
+\r
+    if (NULL != filter) {\r
+        SkIPoint offset = SkIPoint::Make(0, 0);\r
+        SkMatrix matrix(*draw.fMatrix);\r
+        matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));\r
+        if (filter_texture(this, fContext, devTex, filter, w, h, matrix, &filteredBitmap,\r
+                           &offset)) {\r
+            devTex = filteredBitmap.getTexture();\r
+            w = filteredBitmap.width();\r
+            h = filteredBitmap.height();\r
+            x += offset.fX;\r
+            y += offset.fY;\r
+        } else {\r
+            return;\r
+        }\r
+    }\r
+\r
+    GrPaint grPaint;\r
+    grPaint.addColorTextureEffect(devTex, SkMatrix::I());\r
+\r
+    if (!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {\r
+        return;\r
+    }\r
+\r
+    SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x),\r
+                                      SkIntToScalar(y),\r
+                                      SkIntToScalar(w),\r
+                                      SkIntToScalar(h));\r
+\r
+    // The device being drawn may not fill up its texture (e.g. saveLayer uses approximate\r
+    // scratch texture).\r
+    SkRect srcRect = SkRect::MakeWH(SK_Scalar1 * w / devTex->width(),\r
+                                    SK_Scalar1 * h / devTex->height());\r
+\r
+    fContext->drawRectToRect(grPaint, dstRect, srcRect);\r
+}\r
+\r
+bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) {\r
+    return filter->canFilterImageGPU();\r
+}\r
+\r
+bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,\r
+                              const SkMatrix& ctm,\r
+                              SkBitmap* result, SkIPoint* offset) {\r
+    // want explicitly our impl, so guard against a subclass of us overriding it\r
+    if (!this->SkGpuDevice::canHandleImageFilter(filter)) {\r
+        return false;\r
+    }\r
+\r
+    SkAutoLockPixels alp(src, !src.getTexture());\r
+    if (!src.getTexture() && !src.readyToDraw()) {\r
+        return false;\r
+    }\r
+\r
+    GrTexture* texture;\r
+    // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup\r
+    // must be pushed upstack.\r
+    SkAutoCachedTexture act(this, src, NULL, &texture);\r
+\r
+    return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctm, result,\r
+                          offset);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+// must be in SkCanvas::VertexMode order\r
+static const GrPrimitiveType gVertexMode2PrimitiveType[] = {\r
+    kTriangles_GrPrimitiveType,\r
+    kTriangleStrip_GrPrimitiveType,\r
+    kTriangleFan_GrPrimitiveType,\r
+};\r
+\r
+void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,\r
+                              int vertexCount, const SkPoint vertices[],\r
+                              const SkPoint texs[], const SkColor colors[],\r
+                              SkXfermode* xmode,\r
+                              const uint16_t indices[], int indexCount,\r
+                              const SkPaint& paint) {\r
+    CHECK_SHOULD_DRAW(draw, false);\r
+\r
+    GrPaint grPaint;\r
+    // we ignore the shader if texs is null.\r
+    if (NULL == texs) {\r
+        if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) {\r
+            return;\r
+        }\r
+    } else {\r
+        if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) {\r
+            return;\r
+        }\r
+    }\r
+\r
+    if (NULL != xmode && NULL != texs && NULL != colors) {\r
+        if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) {\r
+            SkDebugf("Unsupported vertex-color/texture xfer mode.\n");\r
+#if 0\r
+            return\r
+#endif\r
+        }\r
+    }\r
+\r
+    SkAutoSTMalloc<128, GrColor> convertedColors(0);\r
+    if (NULL != colors) {\r
+        // need to convert byte order and from non-PM to PM\r
+        convertedColors.reset(vertexCount);\r
+        for (int i = 0; i < vertexCount; ++i) {\r
+            convertedColors[i] = SkColor2GrColor(colors[i]);\r
+        }\r
+        colors = convertedColors.get();\r
+    }\r
+    fContext->drawVertices(grPaint,\r
+                           gVertexMode2PrimitiveType[vmode],\r
+                           vertexCount,\r
+                           (GrPoint*) vertices,\r
+                           (GrPoint*) texs,\r
+                           colors,\r
+                           indices,\r
+                           indexCount);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+static void GlyphCacheAuxProc(void* data) {\r
+    GrFontScaler* scaler = (GrFontScaler*)data;\r
+    SkSafeUnref(scaler);\r
+}\r
+\r
+static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {\r
+    void* auxData;\r
+    GrFontScaler* scaler = NULL;\r
+    if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {\r
+        scaler = (GrFontScaler*)auxData;\r
+    }\r
+    if (NULL == scaler) {\r
+        scaler = SkNEW_ARGS(SkGrFontScaler, (cache));\r
+        cache->setAuxProc(GlyphCacheAuxProc, scaler);\r
+    }\r
+    return scaler;\r
+}\r
+\r
+static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state,\r
+                             SkFixed fx, SkFixed fy,\r
+                             const SkGlyph& glyph) {\r
+    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);\r
+\r
+    GrSkDrawProcs* procs = static_cast<GrSkDrawProcs*>(state.fDraw->fProcs);\r
+\r
+    if (NULL == procs->fFontScaler) {\r
+        procs->fFontScaler = get_gr_font_scaler(state.fCache);\r
+    }\r
+\r
+    procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),\r
+                                                       glyph.getSubXFixed(),\r
+                                                       glyph.getSubYFixed()),\r
+                                         SkFixedFloorToFixed(fx),\r
+                                         SkFixedFloorToFixed(fy),\r
+                                         procs->fFontScaler);\r
+}\r
+\r
+SkDrawProcs* SkGpuDevice::initDrawForText(GrTextContext* context) {\r
+\r
+    // deferred allocation\r
+    if (NULL == fDrawProcs) {\r
+        fDrawProcs = SkNEW(GrSkDrawProcs);\r
+        fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;\r
+        fDrawProcs->fContext = fContext;\r
+#if SK_DISTANCEFIELD_FONTS\r
+        fDrawProcs->fFlags = 0;\r
+        fDrawProcs->fFlags |= SkDrawProcs::kSkipBakedGlyphTransform_Flag;\r
+        fDrawProcs->fFlags |= SkDrawProcs::kUseScaledGlyphs_Flag;\r
+#endif\r
+    }\r
+\r
+    // init our (and GL's) state\r
+    fDrawProcs->fTextContext = context;\r
+    fDrawProcs->fFontScaler = NULL;\r
+    return fDrawProcs;\r
+}\r
+\r
+void SkGpuDevice::drawText(const SkDraw& draw, const void* text,\r
+                          size_t byteLength, SkScalar x, SkScalar y,\r
+                          const SkPaint& paint) {\r
+    CHECK_SHOULD_DRAW(draw, false);\r
+\r
+    if (fContext->getMatrix().hasPerspective()) {\r
+        // this guy will just call our drawPath()\r
+        draw.drawText((const char*)text, byteLength, x, y, paint);\r
+    } else {\r
+        SkDraw myDraw(draw);\r
+\r
+        GrPaint grPaint;\r
+        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {\r
+            return;\r
+        }\r
+#if SK_DISTANCEFIELD_FONTS\r
+        GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(), \r
+                                           paint.getTextSize());\r
+#else\r
+        GrBitmapTextContext context(fContext, grPaint, paint.getColor());\r
+#endif\r
+        myDraw.fProcs = this->initDrawForText(&context);\r
+        this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);\r
+    }\r
+}\r
+\r
+void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,\r
+                             size_t byteLength, const SkScalar pos[],\r
+                             SkScalar constY, int scalarsPerPos,\r
+                             const SkPaint& paint) {\r
+    CHECK_SHOULD_DRAW(draw, false);\r
+\r
+    if (fContext->getMatrix().hasPerspective()) {\r
+        // this guy will just call our drawPath()\r
+        draw.drawPosText((const char*)text, byteLength, pos, constY,\r
+                         scalarsPerPos, paint);\r
+    } else {\r
+        SkDraw myDraw(draw);\r
+\r
+        GrPaint grPaint;\r
+        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {\r
+            return;\r
+        }\r
+#if SK_DISTANCEFIELD_FONTS\r
+        GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(), \r
+                                           paint.getTextSize()/SkDrawProcs::kBaseDFFontSize);\r
+#else\r
+        GrBitmapTextContext context(fContext, grPaint, paint.getColor());\r
+#endif\r
+        myDraw.fProcs = this->initDrawForText(&context);\r
+        this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,\r
+                                     scalarsPerPos, paint);\r
+    }\r
+}\r
+\r
+void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,\r
+                                size_t len, const SkPath& path,\r
+                                const SkMatrix* m, const SkPaint& paint) {\r
+    CHECK_SHOULD_DRAW(draw, false);\r
+\r
+    SkASSERT(draw.fDevice == this);\r
+    draw.drawTextOnPath((const char*)text, len, path, m, paint);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {\r
+    if (!paint.isLCDRenderText()) {\r
+        // we're cool with the paint as is\r
+        return false;\r
+    }\r
+\r
+    if (paint.getShader() ||\r
+        paint.getXfermode() || // unless its srcover\r
+        paint.getMaskFilter() ||\r
+        paint.getRasterizer() ||\r
+        paint.getColorFilter() ||\r
+        paint.getPathEffect() ||\r
+        paint.isFakeBoldText() ||\r
+        paint.getStyle() != SkPaint::kFill_Style) {\r
+        // turn off lcd\r
+        flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;\r
+        flags->fHinting = paint.getHinting();\r
+        return true;\r
+    }\r
+    // we're cool with the paint as is\r
+    return false;\r
+}\r
+\r
+void SkGpuDevice::flush() {\r
+    DO_DEFERRED_CLEAR();\r
+    fContext->resolveRenderTarget(fRenderTarget);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+SkBaseDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config,\r
+                                                    int width, int height,\r
+                                                    bool isOpaque,\r
+                                                    Usage usage) {\r
+    GrTextureDesc desc;\r
+    desc.fConfig = fRenderTarget->config();\r
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;\r
+    desc.fWidth = width;\r
+    desc.fHeight = height;\r
+    desc.fSampleCnt = fRenderTarget->numSamples();\r
+\r
+    SkAutoTUnref<GrTexture> texture;\r
+    // Skia's convention is to only clear a device if it is non-opaque.\r
+    bool needClear = !isOpaque;\r
+\r
+#if CACHE_COMPATIBLE_DEVICE_TEXTURES\r
+    // layers are never draw in repeat modes, so we can request an approx\r
+    // match and ignore any padding.\r
+    const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ?\r
+                                                GrContext::kApprox_ScratchTexMatch :\r
+                                                GrContext::kExact_ScratchTexMatch;\r
+    texture.reset(fContext->lockAndRefScratchTexture(desc, match));\r
+#else\r
+    texture.reset(fContext->createUncachedTexture(desc, NULL, 0));\r
+#endif\r
+    if (NULL != texture.get()) {\r
+        return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear));\r
+    } else {\r
+        GrPrintf("---- failed to create compatible device texture [%d %d]\n", width, height);\r
+        return NULL;\r
+    }\r
+}\r
+\r
+SkGpuDevice::SkGpuDevice(GrContext* context,\r
+                         GrTexture* texture,\r
+                         bool needClear)\r
+    : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {\r
+\r
+    SkASSERT(texture && texture->asRenderTarget());\r
+    // This constructor is called from onCreateCompatibleDevice. It has locked the RT in the texture\r
+    // cache. We pass true for the third argument so that it will get unlocked.\r
+    this->initFromRenderTarget(context, texture->asRenderTarget(), true);\r
+    fNeedClear = needClear;\r
+}\r
diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
new file mode 100755 (executable)
index 0000000..fd27f2b
--- /dev/null
@@ -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/src/gpu/effects/GrDistanceFieldTextureEffect.h b/src/gpu/effects/GrDistanceFieldTextureEffect.h
new file mode 100755 (executable)
index 0000000..34d4ae5
--- /dev/null
@@ -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
diff --git a/third_party/edtaa/LICENSE b/third_party/edtaa/LICENSE
new file mode 100755 (executable)
index 0000000..a558805
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License (MIT)\r
+\r
+Copyright (c) 2009-2012 Stefan Gustavson (stefan.gustavson@gmail.com)\r
+\r
+Permission is hereby granted, free of charge, to any person obtaining a copy\r
+of this software and associated documentation files (the "Software"), to deal\r
+in the Software without restriction, including without limitation the rights\r
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+copies of the Software, and to permit persons to whom the Software is\r
+furnished to do so, subject to the following conditions:\r
+\r
+The above copyright notice and this permission notice shall be included in\r
+all copies or substantial portions of the Software.\r
+\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+THE SOFTWARE.\r
diff --git a/third_party/edtaa/edtaa3.h b/third_party/edtaa/edtaa3.h
new file mode 100755 (executable)
index 0000000..4e30677
--- /dev/null
@@ -0,0 +1,35 @@
+/* \r
+ Permission is hereby granted, free of charge, to any person obtaining a copy\r
+ of this software and associated documentation files (the "Software"), to deal\r
+ in the Software without restriction, including without limitation the rights\r
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+ copies of the Software, and to permit persons to whom the Software is\r
+ furnished to do so, subject to the following conditions:\r
+\r
+ The above copyright notice and this permission notice shall be included in\r
+ all copies or substantial portions of the Software.\r
+\r
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+ THE SOFTWARE.\r
+ */\r
+\r
+#define EDTAA_UNSIGNED_CHAR_INPUT 1\r
+\r
+namespace EDTAA {\r
+\r
+#if EDTAA_UNSIGNED_CHAR_INPUT\r
+typedef unsigned char EdtaaImageType;\r
+#else\r
+typedef double EdtaaImageType;\r
+#endif\r
+\r
+void computegradient(EdtaaImageType *img, int w, int h, double *gx, double *gy);\r
+void edtaa3(EdtaaImageType *img, double *gx, double *gy, int w, int h, \r
+                   short *distx, short *disty, double *dist);\r
+\r
+}\r
diff --git a/third_party/edtaa/edtaa3func.cpp b/third_party/edtaa/edtaa3func.cpp
new file mode 100755 (executable)
index 0000000..beed198
--- /dev/null
@@ -0,0 +1,586 @@
+/*\r
+ * edtaa3()\r
+ *\r
+ * Sweep-and-update Euclidean distance transform of an\r
+ * image. Positive pixels are treated as object pixels,\r
+ * zero or negative pixels are treated as background.\r
+ * An attempt is made to treat antialiased edges correctly.\r
+ * The input image must have pixels in the range [0,1],\r
+ * and the antialiased image should be a box-filter\r
+ * sampling of the ideal, crisp edge.\r
+ * If the antialias region is more than 1 pixel wide,\r
+ * the result from this transform will be inaccurate.\r
+ *\r
+ * By Stefan Gustavson (stefan.gustavson@gmail.com).\r
+ *\r
+ * Originally written in 1994, based on a verbal\r
+ * description of the SSED8 algorithm published in the\r
+ * PhD dissertation of Ingemar Ragnemalm. This is his\r
+ * algorithm, I only implemented it in C.\r
+ *\r
+ * Updated in 2004 to treat border pixels correctly,\r
+ * and cleaned up the code to improve readability.\r
+ *\r
+ * Updated in 2009 to handle anti-aliased edges.\r
+ *\r
+ * Updated in 2011 to avoid a corner case infinite loop.\r
+ *\r
+ * Updated 2012 to change license from LGPL to MIT.\r
+ */\r
+\r
+/*\r
+ Copyright (C) 2009-2012 Stefan Gustavson (stefan.gustavson@gmail.com)\r
+ The code in this file is distributed under the MIT license:\r
+\r
+ Permission is hereby granted, free of charge, to any person obtaining a copy\r
+ of this software and associated documentation files (the "Software"), to deal\r
+ in the Software without restriction, including without limitation the rights\r
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+ copies of the Software, and to permit persons to whom the Software is\r
+ furnished to do so, subject to the following conditions:\r
+\r
+ The above copyright notice and this permission notice shall be included in\r
+ all copies or substantial portions of the Software.\r
+\r
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+ THE SOFTWARE.\r
+ */\r
+\r
+#include "edtaa3.h"\r
+\r
+#include <math.h>\r
+\r
+#if EDTAA_UNSIGNED_CHAR_INPUT\r
+#define IMG(i) ((double)(img[i] & 0xff)/256.0)\r
+#else\r
+#define IMG(i) (img[i])\r
+#endif\r
+\r
+namespace EDTAA {\r
+\r
+/*\r
+ * Compute the local gradient at edge pixels using convolution filters.\r
+ * The gradient is computed only at edge pixels. At other places in the\r
+ * image, it is never used, and it's mostly zero anyway.\r
+ */\r
+void computegradient(EdtaaImageType *img, int w, int h, double *gx, double *gy)\r
+{\r
+    int i,j,k;\r
+    double glength;\r
+#define SQRT2 1.4142136\r
+    for(i = 1; i < h-1; i++) { // Avoid edges where the kernels would spill over\r
+        for(j = 1; j < w-1; j++) {\r
+            k = i*w + j;\r
+            if((IMG(k)>0.0) && (IMG(k)<1.0)) { // Compute gradient for edge pixels only\r
+                gx[k] = -IMG(k-w-1) - SQRT2*IMG(k-1) - IMG(k+w-1) + IMG(k-w+1) + SQRT2*IMG(k+1) + IMG(k+w+1);\r
+                gy[k] = -IMG(k-w-1) - SQRT2*IMG(k-w) - IMG(k+w-1) + IMG(k-w+1) + SQRT2*IMG(k+w) + IMG(k+w+1);\r
+                glength = gx[k]*gx[k] + gy[k]*gy[k];\r
+                if(glength > 0.0) { // Avoid division by zero\r
+                    glength = sqrt(glength);\r
+                    gx[k]=gx[k]/glength;\r
+                    gy[k]=gy[k]/glength;\r
+                }\r
+            }\r
+        }\r
+    }\r
+    // TODO: Compute reasonable values for gx, gy also around the image edges.\r
+    // (These are zero now, which reduces the accuracy for a 1-pixel wide region\r
+       // around the image edge.) 2x2 kernels would be suitable for this.\r
+}\r
+\r
+/*\r
+ * A somewhat tricky function to approximate the distance to an edge in a\r
+ * certain pixel, with consideration to either the local gradient (gx,gy)\r
+ * or the direction to the pixel (dx,dy) and the pixel greyscale value a.\r
+ * The latter alternative, using (dx,dy), is the metric used by edtaa2().\r
+ * Using a local estimate of the edge gradient (gx,gy) yields much better\r
+ * accuracy at and near edges, and reduces the error even at distant pixels\r
+ * provided that the gradient direction is accurately estimated.\r
+ */\r
+static double edgedf(double gx, double gy, double a)\r
+{\r
+    double df, glength, temp, a1;\r
+\r
+    if ((gx == 0) || (gy == 0)) { // Either A) gu or gv are zero, or B) both\r
+        df = 0.5-a;  // Linear approximation is A) correct or B) a fair guess\r
+    } else {\r
+        glength = sqrt(gx*gx + gy*gy);\r
+        if(glength>0) {\r
+            gx = gx/glength;\r
+            gy = gy/glength;\r
+        }\r
+        /* Everything is symmetric wrt sign and transposition,\r
+         * so move to first octant (gx>=0, gy>=0, gx>=gy) to\r
+         * avoid handling all possible edge directions.\r
+         */\r
+        gx = fabs(gx);\r
+        gy = fabs(gy);\r
+        if(gx<gy) {\r
+            temp = gx;\r
+            gx = gy;\r
+            gy = temp;\r
+        }\r
+        a1 = 0.5*gy/gx;\r
+        if (a < a1) { // 0 <= a < a1\r
+            df = 0.5*(gx + gy) - sqrt(2.0*gx*gy*a);\r
+        } else if (a < (1.0-a1)) { // a1 <= a <= 1-a1\r
+            df = (0.5-a)*gx;\r
+        } else { // 1-a1 < a <= 1\r
+            df = -0.5*(gx + gy) + sqrt(2.0*gx*gy*(1.0-a));\r
+        }\r
+    }    \r
+    return df;\r
+}\r
+\r
+static double distaa3(EdtaaImageType *img, double *gximg, double *gyimg, int w, int c, int xc, int yc, int xi, int yi)\r
+{\r
+  double di, df, dx, dy, gx, gy, a;\r
+  int closest;\r
+  \r
+  closest = c-xc-yc*w; // Index to the edge pixel pointed to from c\r
+  a = IMG(closest);    // Grayscale value at the edge pixel\r
+  gx = gximg[closest]; // X gradient component at the edge pixel\r
+  gy = gyimg[closest]; // Y gradient component at the edge pixel\r
+  \r
+  if(a > 1.0) a = 1.0;\r
+  if(a < 0.0) a = 0.0; // Clip grayscale values outside the range [0,1]\r
+  if(a == 0.0) return 1000000.0; // Not an object pixel, return "very far" ("don't know yet")\r
+\r
+  dx = (double)xi;\r
+  dy = (double)yi;\r
+  di = sqrt(dx*dx + dy*dy); // Length of integer vector, like a traditional EDT\r
+  if(di==0) { // Use local gradient only at edges\r
+      // Estimate based on local gradient only\r
+      df = edgedf(gx, gy, a);\r
+  } else {\r
+      // Estimate gradient based on direction to edge (accurate for large di)\r
+      df = edgedf(dx, dy, a);\r
+  }\r
+  return di + df; // Same metric as edtaa2, except at edges (where di=0)\r
+}\r
+\r
+// Shorthand macro: add ubiquitous parameters dist, gx, gy, img and w and call distaa3()\r
+#define DISTAA(c,xc,yc,xi,yi) (distaa3(img, gx, gy, w, c, xc, yc, xi, yi))\r
+\r
+void edtaa3(EdtaaImageType *img, double *gx, double *gy, int w, int h, short *distx, short *disty, double *dist)\r
+{\r
+  int x, y, i, c;\r
+  int offset_u, offset_ur, offset_r, offset_rd,\r
+  offset_d, offset_dl, offset_l, offset_lu;\r
+  double olddist, newdist;\r
+  int cdistx, cdisty, newdistx, newdisty;\r
+  int changed;\r
+  double epsilon = 1e-3;\r
+  double a;\r
+\r
+  /* Initialize index offsets for the current image width */\r
+  offset_u = -w;\r
+  offset_ur = -w+1;\r
+  offset_r = 1;\r
+  offset_rd = w+1;\r
+  offset_d = w;\r
+  offset_dl = w-1;\r
+  offset_l = -1;\r
+  offset_lu = -w-1;\r
+\r
+  /* Initialize the distance images */\r
+  for(i=0; i<w*h; i++) {\r
+    distx[i] = 0; // At first, all pixels point to\r
+    disty[i] = 0; // themselves as the closest known.\r
+    a = IMG(i);\r
+    if(a <= 0.0)\r
+      {\r
+       dist[i]= 1000000.0; // Big value, means "not set yet"\r
+      }\r
+    else if (a<1.0) {\r
+      dist[i] = edgedf(gx[i], gy[i], a); // Gradient-assisted estimate\r
+    }\r
+    else {\r
+      dist[i]= 0.0; // Inside the object\r
+    }\r
+  }\r
+\r
+  /* Perform the transformation */\r
+  do\r
+    {\r
+      changed = 0;\r
+\r
+      /* Scan rows, except first row */\r
+      for(y=1; y<h; y++)\r
+        {\r
+\r
+          /* move index to leftmost pixel of current row */\r
+          i = y*w;\r
+\r
+          /* scan right, propagate distances from above & left */\r
+\r
+          /* Leftmost pixel is special, has no left neighbors */\r
+          olddist = dist[i];\r
+          if(olddist > 0) // If non-zero distance or not set yet\r
+            {\r
+             c = i + offset_u; // Index of candidate for testing\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx;\r
+              newdisty = cdisty+1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_ur;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx-1;\r
+              newdisty = cdisty+1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  changed = 1;\r
+                }\r
+            }\r
+          i++;\r
+\r
+          /* Middle pixels have all neighbors */\r
+          for(x=1; x<w-1; x++, i++)\r
+            {\r
+              olddist = dist[i];\r
+              if(olddist <= 0) continue; // No need to update further\r
+\r
+             c = i+offset_l;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx+1;\r
+              newdisty = cdisty;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_lu;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx+1;\r
+              newdisty = cdisty+1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_u;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx;\r
+              newdisty = cdisty+1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_ur;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx-1;\r
+              newdisty = cdisty+1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  changed = 1;\r
+                }\r
+            }\r
+\r
+          /* Rightmost pixel of row is special, has no right neighbors */\r
+          olddist = dist[i];\r
+          if(olddist > 0) // If not already zero distance\r
+            {\r
+             c = i+offset_l;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx+1;\r
+              newdisty = cdisty;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_lu;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx+1;\r
+              newdisty = cdisty+1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_u;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx;\r
+              newdisty = cdisty+1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  changed = 1;\r
+                }\r
+            }\r
+\r
+          /* Move index to second rightmost pixel of current row. */\r
+          /* Rightmost pixel is skipped, it has no right neighbor. */\r
+          i = y*w + w-2;\r
+\r
+          /* scan left, propagate distance from right */\r
+          for(x=w-2; x>=0; x--, i--)\r
+            {\r
+              olddist = dist[i];\r
+              if(olddist <= 0) continue; // Already zero distance\r
+\r
+             c = i+offset_r;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx-1;\r
+              newdisty = cdisty;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  changed = 1;\r
+                }\r
+            }\r
+        }\r
+      \r
+      /* Scan rows in reverse order, except last row */\r
+      for(y=h-2; y>=0; y--)\r
+        {\r
+          /* move index to rightmost pixel of current row */\r
+          i = y*w + w-1;\r
+\r
+          /* Scan left, propagate distances from below & right */\r
+\r
+          /* Rightmost pixel is special, has no right neighbors */\r
+          olddist = dist[i];\r
+          if(olddist > 0) // If not already zero distance\r
+            {\r
+             c = i+offset_d;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx;\r
+              newdisty = cdisty-1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_dl;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx+1;\r
+              newdisty = cdisty-1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  changed = 1;\r
+                }\r
+            }\r
+          i--;\r
+\r
+          /* Middle pixels have all neighbors */\r
+          for(x=w-2; x>0; x--, i--)\r
+            {\r
+              olddist = dist[i];\r
+              if(olddist <= 0) continue; // Already zero distance\r
+\r
+             c = i+offset_r;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx-1;\r
+              newdisty = cdisty;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_rd;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx-1;\r
+              newdisty = cdisty-1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_d;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx;\r
+              newdisty = cdisty-1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                  dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_dl;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx+1;\r
+              newdisty = cdisty-1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                  dist[i]=newdist;\r
+                  changed = 1;\r
+                }\r
+            }\r
+          /* Leftmost pixel is special, has no left neighbors */\r
+          olddist = dist[i];\r
+          if(olddist > 0) // If not already zero distance\r
+            {\r
+             c = i+offset_r;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx-1;\r
+              newdisty = cdisty;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                  dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_rd;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx-1;\r
+              newdisty = cdisty-1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                 dist[i]=newdist;\r
+                  olddist=newdist;\r
+                  changed = 1;\r
+                }\r
+\r
+             c = i+offset_d;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx;\r
+              newdisty = cdisty-1;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                  dist[i]=newdist;\r
+                  changed = 1;\r
+                }\r
+            }\r
+\r
+          /* Move index to second leftmost pixel of current row. */\r
+          /* Leftmost pixel is skipped, it has no left neighbor. */\r
+          i = y*w + 1;\r
+          for(x=1; x<w; x++, i++)\r
+            {\r
+              /* scan right, propagate distance from left */\r
+              olddist = dist[i];\r
+              if(olddist <= 0) continue; // Already zero distance\r
+\r
+             c = i+offset_l;\r
+             cdistx = distx[c];\r
+             cdisty = disty[c];\r
+              newdistx = cdistx+1;\r
+              newdisty = cdisty;\r
+              newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty);\r
+              if(newdist < olddist-epsilon)\r
+                {\r
+                  distx[i]=newdistx;\r
+                  disty[i]=newdisty;\r
+                  dist[i]=newdist;\r
+                  changed = 1;\r
+                }\r
+            }\r
+        }\r
+    }\r
+  while(changed); // Sweep until no more updates are made\r
+\r
+  /* The transformation is completed. */\r
+\r
+}\r
+\r
+} // namespace EDTAA\r