Sanitizing source files in Housekeeper-Nightly

git-svn-id: http://skia.googlecode.com/svn/trunk@12232 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 92ac326..429ee9f 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1,1859 +1,1859 @@
-/*

- * Copyright 2011 Google Inc.

- *

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

- * found in the LICENSE file.

- */

-

-#include "SkGpuDevice.h"

-

-#include "effects/GrTextureDomainEffect.h"

-#include "effects/GrSimpleTextureEffect.h"

-

-#include "GrContext.h"

-#include "GrBitmapTextContext.h"

-#if SK_DISTANCEFIELD_FONTS

-#include "GrDistanceFieldTextContext.h"

-#endif

-

-#include "SkGrTexturePixelRef.h"

-

-#include "SkColorFilter.h"

-#include "SkDeviceImageFilterProxy.h"

-#include "SkDrawProcs.h"

-#include "SkGlyphCache.h"

-#include "SkImageFilter.h"

-#include "SkPathEffect.h"

-#include "SkRRect.h"

-#include "SkStroke.h"

-#include "SkUtils.h"

-#include "SkErrorInternals.h"

-

-#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1

-

-#if 0

-    extern bool (*gShouldDrawProc)();

-    #define CHECK_SHOULD_DRAW(draw, forceI)                     \

-        do {                                                    \

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

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

-        } while (0)

-#else

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

-#endif

-

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

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

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

-#define COLOR_BLEED_TOLERANCE SkFloatToScalar(0.001f)

-

-#define DO_DEFERRED_CLEAR()             \

-    do {                                \

-        if (fNeedClear) {               \

-            this->clear(SK_ColorTRANSPARENT); \

-        }                               \

-    } while (false)                     \

-

-///////////////////////////////////////////////////////////////////////////////

-

-#define CHECK_FOR_ANNOTATION(paint) \

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

-

-///////////////////////////////////////////////////////////////////////////////

-

-

-class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable {

-public:

-    SkAutoCachedTexture()

-        : fDevice(NULL)

-        , fTexture(NULL) {

-    }

-

-    SkAutoCachedTexture(SkGpuDevice* device,

-                        const SkBitmap& bitmap,

-                        const GrTextureParams* params,

-                        GrTexture** texture)

-        : fDevice(NULL)

-        , fTexture(NULL) {

-        SkASSERT(NULL != texture);

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

-    }

-

-    ~SkAutoCachedTexture() {

-        if (NULL != fTexture) {

-            GrUnlockAndUnrefCachedBitmapTexture(fTexture);

-        }

-    }

-

-    GrTexture* set(SkGpuDevice* device,

-                   const SkBitmap& bitmap,

-                   const GrTextureParams* params) {

-        if (NULL != fTexture) {

-            GrUnlockAndUnrefCachedBitmapTexture(fTexture);

-            fTexture = NULL;

-        }

-        fDevice = device;

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

-        if (NULL == result) {

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

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

-            result = fTexture;

-        }

-        return result;

-    }

-

-private:

-    SkGpuDevice* fDevice;

-    GrTexture*   fTexture;

-};

-

-///////////////////////////////////////////////////////////////////////////////

-

-struct GrSkDrawProcs : public SkDrawProcs {

-public:

-    GrContext* fContext;

-    GrTextContext* fTextContext;

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

-};

-

-///////////////////////////////////////////////////////////////////////////////

-

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

-    switch (config) {

-        case kAlpha_8_GrPixelConfig:

-            *isOpaque = false;

-            return SkBitmap::kA8_Config;

-        case kRGB_565_GrPixelConfig:

-            *isOpaque = true;

-            return SkBitmap::kRGB_565_Config;

-        case kRGBA_4444_GrPixelConfig:

-            *isOpaque = false;

-            return SkBitmap::kARGB_4444_Config;

-        case kSkia8888_GrPixelConfig:

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

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

-            *isOpaque = false;

-            return SkBitmap::kARGB_8888_Config;

-        default:

-            *isOpaque = false;

-            return SkBitmap::kNo_Config;

-    }

-}

-

-/*

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

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

- */

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

-    bool isOpaque;

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

-

-    SkBitmap bitmap;

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

-                     isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);

-    return bitmap;

-}

-

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

-    SkASSERT(NULL != surface);

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

-        return NULL;

-    }

-    if (surface->asTexture()) {

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

-    } else {

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

-    }

-}

-

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

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

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

-}

-

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

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

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

-}

-

-void SkGpuDevice::initFromRenderTarget(GrContext* context,

-                                       GrRenderTarget* renderTarget,

-                                       bool cached) {

-    fDrawProcs = NULL;

-

-    fContext = context;

-    fContext->ref();

-

-    fRenderTarget = NULL;

-    fNeedClear = false;

-

-    SkASSERT(NULL != renderTarget);

-    fRenderTarget = renderTarget;

-    fRenderTarget->ref();

-

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

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

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

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

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

-    if (NULL == surface) {

-        surface = fRenderTarget;

-    }

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

-

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

-}

-

-SkGpuDevice::SkGpuDevice(GrContext* context,

-                         SkBitmap::Config config,

-                         int width,

-                         int height,

-                         int sampleCount)

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

-

-    fDrawProcs = NULL;

-

-    fContext = context;

-    fContext->ref();

-

-    fRenderTarget = NULL;

-    fNeedClear = false;

-

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

-        config = SkBitmap::kARGB_8888_Config;

-    }

-

-    GrTextureDesc desc;

-    desc.fFlags = kRenderTarget_GrTextureFlagBit;

-    desc.fWidth = width;

-    desc.fHeight = height;

-    desc.fConfig = SkBitmapConfig2GrPixelConfig(config);

-    desc.fSampleCnt = sampleCount;

-

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

-

-    if (NULL != texture) {

-        fRenderTarget = texture->asRenderTarget();

-        fRenderTarget->ref();

-

-        SkASSERT(NULL != fRenderTarget);

-

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

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

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

-    } else {

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

-                 width, height);

-        SkASSERT(false);

-    }

-}

-

-SkGpuDevice::~SkGpuDevice() {

-    if (fDrawProcs) {

-        delete fDrawProcs;

-    }

-

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

-    // target to be unnecessarily kept alive.

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

-        fContext->setRenderTarget(NULL);

-    }

-

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

-        fContext->setClip(NULL);

-    }

-

-    SkSafeUnref(fRenderTarget);

-    fContext->unref();

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-void SkGpuDevice::makeRenderTargetCurrent() {

-    DO_DEFERRED_CLEAR();

-    fContext->setRenderTarget(fRenderTarget);

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-namespace {

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

-    switch (config8888) {

-        case SkCanvas::kNative_Premul_Config8888:

-            *flags = 0;

-            return kSkia8888_GrPixelConfig;

-        case SkCanvas::kNative_Unpremul_Config8888:

-            *flags = GrContext::kUnpremul_PixelOpsFlag;

-            return kSkia8888_GrPixelConfig;

-        case SkCanvas::kBGRA_Premul_Config8888:

-            *flags = 0;

-            return kBGRA_8888_GrPixelConfig;

-        case SkCanvas::kBGRA_Unpremul_Config8888:

-            *flags = GrContext::kUnpremul_PixelOpsFlag;

-            return kBGRA_8888_GrPixelConfig;

-        case SkCanvas::kRGBA_Premul_Config8888:

-            *flags = 0;

-            return kRGBA_8888_GrPixelConfig;

-        case SkCanvas::kRGBA_Unpremul_Config8888:

-            *flags = GrContext::kUnpremul_PixelOpsFlag;

-            return kRGBA_8888_GrPixelConfig;

-        default:

-            GrCrash("Unexpected Config8888.");

-            *flags = 0; // suppress warning

-            return kSkia8888_GrPixelConfig;

-    }

-}

-}

-

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

-                               int x, int y,

-                               SkCanvas::Config8888 config8888) {

-    DO_DEFERRED_CLEAR();

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

-    SkASSERT(!bitmap.isNull());

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

-

-    SkAutoLockPixels alp(bitmap);

-    GrPixelConfig config;

-    uint32_t flags;

-    config = config8888_to_grconfig_and_flags(config8888, &flags);

-    return fContext->readRenderTargetPixels(fRenderTarget,

-                                            x, y,

-                                            bitmap.width(),

-                                            bitmap.height(),

-                                            config,

-                                            bitmap.getPixels(),

-                                            bitmap.rowBytes(),

-                                            flags);

-}

-

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

-                              SkCanvas::Config8888 config8888) {

-    SkAutoLockPixels alp(bitmap);

-    if (!bitmap.readyToDraw()) {

-        return;

-    }

-

-    GrPixelConfig config;

-    uint32_t flags;

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

-        config = config8888_to_grconfig_and_flags(config8888, &flags);

-    } else {

-        flags = 0;

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

-    }

-

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

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

-}

-

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

-    INHERITED::onAttachToCanvas(canvas);

-

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

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

-}

-

-void SkGpuDevice::onDetachFromCanvas() {

-    INHERITED::onDetachFromCanvas();

-    fClipData.fClipStack = NULL;

-}

-

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

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

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

-    SkASSERT(NULL != fClipData.fClipStack);

-

-    fContext->setRenderTarget(fRenderTarget);

-

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

-

-    if (forceIdentity) {

-        fContext->setIdentityMatrix();

-    } else {

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

-    }

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

-

-    fContext->setClip(&fClipData);

-

-    DO_DEFERRED_CLEAR();

-}

-

-GrRenderTarget* SkGpuDevice::accessRenderTarget() {

-    DO_DEFERRED_CLEAR();

-    return fRenderTarget;

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

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

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

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

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

-SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,

-                  shader_type_mismatch);

-SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5,

-                  shader_type_mismatch);

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

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

-

-namespace {

-

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

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

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

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

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

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

-inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev,

-                                    const SkPaint& skPaint,

-                                    bool justAlpha,

-                                    bool constantColor,

-                                    GrPaint* grPaint) {

-

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

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

-

-    SkXfermode::Coeff sm;

-    SkXfermode::Coeff dm;

-

-    SkXfermode* mode = skPaint.getXfermode();

-    GrEffectRef* xferEffect = NULL;

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

-        if (NULL != xferEffect) {

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

-            sm = SkXfermode::kOne_Coeff;

-            dm = SkXfermode::kZero_Coeff;

-        }

-    } else {

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

-#if 0

-        return false;

-#else

-        // Fall back to src-over

-        sm = SkXfermode::kOne_Coeff;

-        dm = SkXfermode::kISA_Coeff;

-#endif

-    }

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

-

-    if (justAlpha) {

-        uint8_t alpha = skPaint.getAlpha();

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

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

-        // so constantColor should not also be true.

-        SkASSERT(!constantColor);

-    } else {

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

-    }

-

-    SkColorFilter* colorFilter = skPaint.getColorFilter();

-    if (NULL != colorFilter) {

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

-        // in a shader.

-        if (constantColor) {

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

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

-        } else {

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

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

-                grPaint->addColorEffect(effect);

-            }

-        }

-    }

-

-    return true;

-}

-

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

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

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

-// same meaning as in skPaint2GrPaintNoShader.

-inline bool skPaint2GrPaintShader(SkGpuDevice* dev,

-                                  const SkPaint& skPaint,

-                                  bool constantColor,

-                                  GrPaint* grPaint) {

-    SkShader* shader = skPaint.getShader();

-    if (NULL == shader) {

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

-    }

-

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

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

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

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

-

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

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

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

-        grPaint->addColorEffect(effect);

-        // Now setup the rest of the paint.

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

-    } else {

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

-        SkShader::GradientInfo info;

-        SkColor                color;

-

-        info.fColors = &color;

-        info.fColorOffsets = NULL;

-        info.fColorCount = 1;

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

-            SkPaint copy(skPaint);

-            copy.setShader(NULL);

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

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

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

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

-        } else {

-            return false;

-        }

-    }

-}

-}

-

-///////////////////////////////////////////////////////////////////////////////

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

-    if (NULL != bounds) {

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

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

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

-    }

-}

-

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

-    if (NULL == fRenderTarget) {

-        return SkBitmap::kNo_Config;

-    }

-

-    bool isOpaque;

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

-}

-

-void SkGpuDevice::clear(SkColor color) {

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

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

-    fNeedClear = false;

-}

-

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

-    CHECK_SHOULD_DRAW(draw, false);

-

-    GrPaint grPaint;

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

-        return;

-    }

-

-    fContext->drawPaint(grPaint);

-}

-

-// must be in SkCanvas::PointMode order

-static const GrPrimitiveType gPointMode2PrimtiveType[] = {

-    kPoints_GrPrimitiveType,

-    kLines_GrPrimitiveType,

-    kLineStrip_GrPrimitiveType

-};

-

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

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

-    CHECK_FOR_ANNOTATION(paint);

-    CHECK_SHOULD_DRAW(draw, false);

-

-    SkScalar width = paint.getStrokeWidth();

-    if (width < 0) {

-        return;

-    }

-

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

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

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

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

-        return;

-    }

-

-    GrPaint grPaint;

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

-        return;

-    }

-

-    fContext->drawVertices(grPaint,

-                           gPointMode2PrimtiveType[mode],

-                           count,

-                           (GrPoint*)pts,

-                           NULL,

-                           NULL,

-                           NULL,

-                           0);

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

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

-                           const SkPaint& paint) {

-    CHECK_FOR_ANNOTATION(paint);

-    CHECK_SHOULD_DRAW(draw, false);

-

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

-    SkScalar width = paint.getStrokeWidth();

-

-    /*

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

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

-     */

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

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

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

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

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

-        usePath = true;

-    }

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

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

-        if (doStroke) {

-#endif

-            usePath = true;

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

-        } else {

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

-        }

-#endif

-    }

-    // until we can both stroke and fill rectangles

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

-        usePath = true;

-    }

-

-    if (usePath) {

-        SkPath path;

-        path.addRect(rect);

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

-        return;

-    }

-

-    GrPaint grPaint;

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

-        return;

-    }

-

-    if (!doStroke) {

-        fContext->drawRect(grPaint, rect);

-    } else {

-        SkStrokeRec stroke(paint);

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

-    }

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

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

-                           const SkPaint& paint) {

-    CHECK_FOR_ANNOTATION(paint);

-    CHECK_SHOULD_DRAW(draw, false);

-

-    bool usePath = !rect.isSimple();

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

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

-        usePath = true;

-    }

-    // until we can rotate rrects...

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

-        usePath = true;

-    }

-

-    if (usePath) {

-        SkPath path;

-        path.addRRect(rect);

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

-        return;

-    }

-

-    GrPaint grPaint;

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

-        return;

-    }

-

-    SkStrokeRec stroke(paint);

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

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

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

-                           const SkPaint& paint) {

-    CHECK_FOR_ANNOTATION(paint);

-    CHECK_SHOULD_DRAW(draw, false);

-

-    bool usePath = false;

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

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

-        usePath = true;

-    }

-

-    if (usePath) {

-        SkPath path;

-        path.addOval(oval);

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

-        return;

-    }

-

-    GrPaint grPaint;

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

-        return;

-    }

-    SkStrokeRec stroke(paint);

-

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

-}

-

-#include "SkMaskFilter.h"

-#include "SkBounder.h"

-

-///////////////////////////////////////////////////////////////////////////////

-

-// helpers for applying mask filters

-namespace {

-

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

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

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

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

-               GrPaint* grp, GrTexture* mask) {

-    GrContext::AutoMatrix am;

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

-        return false;

-    }

-

-    SkMatrix matrix;

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

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

-

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

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

-    return true;

-}

-

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

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

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

-    SkMask  srcM, dstM;

-

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

-                            SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {

-        return false;

-    }

-    SkAutoMaskFreeImage autoSrc(srcM.fImage);

-

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

-        return false;

-    }

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

-    SkAutoMaskFreeImage autoDst(dstM.fImage);

-

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

-        return false;

-    }

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

-        return false;

-    }

-

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

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

-    GrTextureDesc desc;

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

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

-    desc.fConfig = kAlpha_8_GrPixelConfig;

-

-    GrAutoScratchTexture ast(context, desc);

-    GrTexture* texture = ast.texture();

-

-    if (NULL == texture) {

-        return false;

-    }

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

-                               dstM.fImage, dstM.fRowBytes);

-

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

-

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

-}

-

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

-// success; false otherwise.

-bool create_mask_GPU(GrContext* context,

-                     const SkRect& maskRect,

-                     const SkPath& devPath,

-                     const SkStrokeRec& stroke,

-                     bool doAA,

-                     GrAutoScratchTexture* mask) {

-    GrTextureDesc desc;

-    desc.fFlags = kRenderTarget_GrTextureFlagBit;

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

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

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

-    // render target so default to RGBA_8888

-    desc.fConfig = kRGBA_8888_GrPixelConfig;

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

-        desc.fConfig = kAlpha_8_GrPixelConfig;

-    }

-

-    mask->set(context, desc);

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

-        return false;

-    }

-

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

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

-

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

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

-

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

-

-    GrPaint tempPaint;

-    if (doAA) {

-        tempPaint.setAntiAlias(true);

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

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

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

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

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

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

-        tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);

-    }

-

-    GrContext::AutoMatrix am;

-

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

-    SkMatrix translate;

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

-    am.set(context, translate);

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

-    return true;

-}

-

-SkBitmap wrap_texture(GrTexture* texture) {

-    SkBitmap result;

-    bool dummy;

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

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

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

-    return result;

-}

-

-};

-

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

-                           const SkPaint& paint, const SkMatrix* prePathMatrix,

-                           bool pathIsMutable) {

-    CHECK_FOR_ANNOTATION(paint);

-    CHECK_SHOULD_DRAW(draw, false);

-

-    GrPaint grPaint;

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

-        return;

-    }

-

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

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

-    SkScalar hairlineCoverage;

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

-    if (doHairLine) {

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

-    }

-

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

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

-    // its parameter type is const).

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

-    SkPath  tmpPath, effectPath;

-

-    if (prePathMatrix) {

-        SkPath* result = pathPtr;

-

-        if (!pathIsMutable) {

-            result = &tmpPath;

-            pathIsMutable = true;

-        }

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

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

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

-        pathPtr = result;

-    }

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

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

-

-    SkStrokeRec stroke(paint);

-    SkPathEffect* pathEffect = paint.getPathEffect();

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

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

-                                             cullRect)) {

-        pathPtr = &effectPath;

-    }

-

-    if (!pathEffect && doHairLine) {

-        stroke.setHairlineStyle();

-    }

-

-    if (paint.getMaskFilter()) {

-        if (!stroke.isHairlineStyle()) {

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

-                pathPtr = &tmpPath;

-                pathIsMutable = true;

-                stroke.setFillStyle();

-            }

-        }

-

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

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

-

-        // transform the path into device space

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

-

-        SkRect maskRect;

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

-                                                    draw.fClip->getBounds(),

-                                                    fContext->getMatrix(),

-                                                    &maskRect)) {

-            SkIRect finalIRect;

-            maskRect.roundOut(&finalIRect);

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

-                // clipped out

-                return;

-            }

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

-                // nothing to draw

-                return;

-            }

-

-            GrAutoScratchTexture mask;

-

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

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

-                GrTexture* filtered;

-

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

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

-                    SkAutoTUnref<GrTexture> atu(filtered);

-

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

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

-                    // draw.

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

-                        mask.detach();

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

-                    }

-

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

-                        // This path is completely drawn

-                        return;

-                    }

-                }

-            }

-        }

-

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

-        // GPU path fails

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

-                                                          SkPaint::kFill_Style;

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

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

-        return;

-    }

-

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

-}

-

-static const int kBmpSmallTileSize = 1 << 10;

-

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

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

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

-    return tilesX * tilesY;

-}

-

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

-    if (maxTileSize <= kBmpSmallTileSize) {

-        return maxTileSize;

-    }

-

-    size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);

-    size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);

-

-    maxTileTotalTileSize *= maxTileSize * maxTileSize;

-    smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;

-

-    if (maxTileTotalTileSize > 2 * smallTotalTileSize) {

-        return kBmpSmallTileSize;

-    } else {

-        return maxTileSize;

-    }

-}

-

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

-// pixels from the bitmap are necessary.

-static void determine_clipped_src_rect(const GrContext* context,

-                                       const SkBitmap& bitmap,

-                                       const SkRect* srcRectPtr,

-                                       SkIRect* clippedSrcIRect) {

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

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

-    SkMatrix inv;

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

-        clippedSrcIRect->setEmpty();

-        return;

-    }

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

-    inv.mapRect(&clippedSrcRect);

-    if (NULL != srcRectPtr) {

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

-            clippedSrcIRect->setEmpty();

-            return;

-        }

-    }

-    clippedSrcRect.roundOut(clippedSrcIRect);

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

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

-        clippedSrcIRect->setEmpty();

-    }

-}

-

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

-                                   const GrTextureParams& params,

-                                   const SkRect* srcRectPtr,

-                                   int maxTileSize,

-                                   int* tileSize,

-                                   SkIRect* clippedSrcRect) const {

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

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

-        return false;

-    }

-

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

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

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

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

-        return true;

-    }

-

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

-        return false;

-    }

-

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

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

-        return false;

-    }

-

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

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

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

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

-

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

-    // a texture

-    size_t bmpSize = bitmap.getSize();

-    size_t cacheSize;

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

-    if (bmpSize < cacheSize / 2) {

-        return false;

-    }

-

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

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

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

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

-                           kBmpSmallTileSize * kBmpSmallTileSize;

-

-    return usedTileBytes < 2 * bmpSize;

-}

-

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

-                             const SkBitmap& bitmap,

-                             const SkMatrix& m,

-                             const SkPaint& paint) {

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

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

-                           SkCanvas::kNone_DrawBitmapRectFlag);

-}

-

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

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

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

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

-                                                   const SkIRect& clamp) {

-    iRect->outset(1, 1);

-

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

-        iRect->fLeft = clamp.fLeft;

-    } else {

-        offset->fX -= SK_Scalar1;

-    }

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

-        iRect->fTop = clamp.fTop;

-    } else {

-        offset->fY -= SK_Scalar1;

-    }

-

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

-        iRect->fRight = clamp.fRight;

-    }

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

-        iRect->fBottom = clamp.fBottom;

-    }

-}

-

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

-                                   const SkBitmap& bitmap,

-                                   const SkRect* srcRectPtr,

-                                   const SkMatrix& m,

-                                   const SkPaint& paint,

-                                   SkCanvas::DrawBitmapRectFlags flags) {

-    CHECK_SHOULD_DRAW(draw, false);

-

-    SkRect srcRect;

-    if (NULL == srcRectPtr) {

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

-    } else {

-        srcRect = *srcRectPtr;

-    }

-

-    if (paint.getMaskFilter()){

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

-        // through drawRect, which supports mask filters.

-        SkMatrix        newM(m);

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

-        const SkBitmap* bitmapPtr = &bitmap;

-        if (NULL != srcRectPtr) {

-            SkIRect iSrc;

-            srcRect.roundOut(&iSrc);

-

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

-                                           SkIntToScalar(iSrc.fTop));

-

-            if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {

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

-                // but stay within the bitmap bounds

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

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

-            }

-

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

-                return;     // extraction failed

-            }

-            bitmapPtr = &tmp;

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

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

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

-        }

-

-        SkPaint paintWithTexture(paint);

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

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

-

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

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

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

-        SkMatrix drawMatrix;

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

-        SkDraw transformedDraw(draw);

-        transformedDraw.fMatrix = &drawMatrix;

-

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

-

-        return;

-    }

-

-    fContext->concatMatrix(m);

-

-    GrTextureParams params;

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

-    GrTextureParams::FilterMode textureFilterMode;

-    switch(paintFilterLevel) {

-        case SkPaint::kNone_FilterLevel:

-            textureFilterMode = GrTextureParams::kNone_FilterMode;

-            break;

-        case SkPaint::kLow_FilterLevel:

-            textureFilterMode = GrTextureParams::kBilerp_FilterMode;

-            break;

-        case SkPaint::kMedium_FilterLevel:

-            textureFilterMode = GrTextureParams::kMipMap_FilterMode;

-            break;

-        case SkPaint::kHigh_FilterLevel:

-            // Fall back to mips for now

-            textureFilterMode = GrTextureParams::kMipMap_FilterMode;

-            break;

-        default:

-            SkErrorInternals::SetError( kInvalidPaint_SkError,

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

-                                        "mode you asked for.  Falling back to "

-                                        "MIPMaps.");

-            textureFilterMode = GrTextureParams::kMipMap_FilterMode;

-            break;

-

-    }

-

-    params.setFilterMode(textureFilterMode);

-

-    int maxTileSize = fContext->getMaxTextureSize();

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

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

-        // by 1 pixel all around

-        maxTileSize -= 2;

-    }

-    int tileSize;

-

-    SkIRect clippedSrcRect;

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

-                               &clippedSrcRect)) {

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

-    } else {

-        // take the simple case

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

-    }

-}

-

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

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

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

-                                  const SkRect& srcRect,

-                                  const SkIRect& clippedSrcIRect,

-                                  const GrTextureParams& params,

-                                  const SkPaint& paint,

-                                  SkCanvas::DrawBitmapRectFlags flags,

-                                  int tileSize) {

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

-

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

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

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

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

-            SkRect tileR;

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

-                      SkIntToScalar(y * tileSize),

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

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

-

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

-                continue;

-            }

-

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

-                continue;

-            }

-

-            SkBitmap tmpB;

-            SkIRect iTileR;

-            tileR.roundOut(&iTileR);

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

-                                           SkIntToScalar(iTileR.fTop));

-

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

-                SkIRect iClampRect;

-

-                if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {

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

-                    // but stay within the bitmap bounds

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

-                } else {

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

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

-                    // not bleed across the original clamped edges)

-                    srcRect.roundOut(&iClampRect);

-                }

-

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

-            }

-

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

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

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

-                SkMatrix tmpM;

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

-                GrContext::AutoMatrix am;

-                am.setPreConcat(fContext, tmpM);

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

-            }

-        }

-    }

-}

-

-static bool has_aligned_samples(const SkRect& srcRect,

-                                const SkRect& transformedRect) {

-    // detect pixel disalignment

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

-            transformedRect.left()) < COLOR_BLEED_TOLERANCE &&

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

-            transformedRect.top()) < COLOR_BLEED_TOLERANCE &&

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

-            COLOR_BLEED_TOLERANCE &&

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

-            COLOR_BLEED_TOLERANCE) {

-        return true;

-    }

-    return false;

-}

-

-static bool may_color_bleed(const SkRect& srcRect,

-                            const SkRect& transformedRect,

-                            const SkMatrix& m) {

-    // Only gets called if has_aligned_samples returned false.

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

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

-    SkRect innerSrcRect(srcRect), innerTransformedRect,

-        outerTransformedRect(transformedRect);

-    innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);

-    m.mapRect(&innerTransformedRect, innerSrcRect);

-

-    // The gap between outerTransformedRect and innerTransformedRect

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

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

-    // destination pixels sample the border area.

-    outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);

-    innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);

-    SkIRect outer, inner;

-    outerTransformedRect.round(&outer);

-    innerTransformedRect.round(&inner);

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

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

-    return inner != outer;

-}

-

-

-/*

- *  This is called by drawBitmap(), which has to handle images that may be too

- *  large to be represented by a single texture.

- *

- *  internalDrawBitmap assumes that the specified bitmap will fit in a texture

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

- */

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

-                                     const SkRect& srcRect,

-                                     const GrTextureParams& params,

-                                     const SkPaint& paint,

-                                     SkCanvas::DrawBitmapRectFlags flags) {

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

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

-

-    GrTexture* texture;

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

-    if (NULL == texture) {

-        return;

-    }

-

-    SkRect dstRect(srcRect);

-    SkRect paintRect;

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

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

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

-                      SkScalarMul(srcRect.fTop,    hInv),

-                      SkScalarMul(srcRect.fRight,  wInv),

-                      SkScalarMul(srcRect.fBottom, hInv));

-

-    bool needsTextureDomain = false;

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

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

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

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

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

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

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

-            // sampling is axis-aligned

-            SkRect transformedRect;

-            matrix.mapRect(&transformedRect, srcRect);

-

-            if (has_aligned_samples(srcRect, transformedRect)) {

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

-                // params).

-                needsTextureDomain = false;

-            } else {

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

-            }

-        }

-    }

-

-    SkRect textureDomain = SkRect::MakeEmpty();

-    SkAutoTUnref<GrEffectRef> effect;

-    if (needsTextureDomain) {

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

-        SkScalar left, top, right, bottom;

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

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

-            left = paintRect.left() + border;

-            right = paintRect.right() - border;

-        } else {

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

-        }

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

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

-            top = paintRect.top() + border;

-            bottom = paintRect.bottom() - border;

-        } else {

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

-        }

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

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

-                                                   SkMatrix::I(),

-                                                   textureDomain,

-                                                   GrTextureDomainEffect::kClamp_WrapMode,

-                                                   params.filterMode()));

-    } else {

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

-    }

-

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

-    // the rest from the SkPaint.

-    GrPaint grPaint;

-    grPaint.addColorEffect(effect);

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

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

-        return;

-    }

-

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

-}

-

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

-                           GrTexture* texture, SkImageFilter* filter,

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

-                           SkIPoint* offset) {

-    SkASSERT(filter);

-    SkDeviceImageFilterProxy proxy(device);

-

-    if (filter->canFilterImageGPU()) {

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

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

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

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

-    } else {

-        return false;

-    }

-}

-

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

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

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

-    CHECK_SHOULD_DRAW(draw, true);

-

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

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

-        return;

-    }

-

-    int w = bitmap.width();

-    int h = bitmap.height();

-

-    GrTexture* texture;

-    // draw sprite uses the default texture params

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

-

-    SkImageFilter* filter = paint.getImageFilter();

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

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

-    SkBitmap filteredBitmap;

-

-    if (NULL != filter) {

-        SkMatrix matrix(*draw.fMatrix);

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

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

-                           &offset)) {

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

-            w = filteredBitmap.width();

-            h = filteredBitmap.height();

-        } else {

-            return;

-        }

-    }

-

-    GrPaint grPaint;

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

-

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

-        return;

-    }

-

-    fContext->drawRectToRect(grPaint,

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

-                                              SkIntToScalar(offset.fY),

-                                              SkIntToScalar(w),

-                                              SkIntToScalar(h)),

-                             SkRect::MakeXYWH(0,

-                                              0,

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

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

-}

-

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

-                                 const SkRect* src, const SkRect& dst,

-                                 const SkPaint& paint,

-                                 SkCanvas::DrawBitmapRectFlags flags) {

-    SkMatrix    matrix;

-    SkRect      bitmapBounds, tmpSrc;

-

-    bitmapBounds.set(0, 0,

-                     SkIntToScalar(bitmap.width()),

-                     SkIntToScalar(bitmap.height()));

-

-    // Compute matrix from the two rectangles

-    if (NULL != src) {

-        tmpSrc = *src;

-    } else {

-        tmpSrc = bitmapBounds;

-    }

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

-

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

-    if (NULL != src) {

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

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

-                return; // nothing to draw

-            }

-        }

-    }

-

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

-}

-

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

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

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

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

-    if (dev->fNeedClear) {

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

-        dev->clear(0x0);

-    }

-

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

-    CHECK_SHOULD_DRAW(draw, true);

-

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

-    GrTexture* devTex;

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

-        return;

-    }

-

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

-    int w = bm.width();

-    int h = bm.height();

-

-    SkImageFilter* filter = paint.getImageFilter();

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

-    SkBitmap filteredBitmap;

-

-    if (NULL != filter) {

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

-        SkMatrix matrix(*draw.fMatrix);

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

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

-                           &offset)) {

-            devTex = filteredBitmap.getTexture();

-            w = filteredBitmap.width();

-            h = filteredBitmap.height();

-            x += offset.fX;

-            y += offset.fY;

-        } else {

-            return;

-        }

-    }

-

-    GrPaint grPaint;

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

-

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

-        return;

-    }

-

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

-                                      SkIntToScalar(y),

-                                      SkIntToScalar(w),

-                                      SkIntToScalar(h));

-

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

-    // scratch texture).

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

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

-

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

-}

-

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

-    return filter->canFilterImageGPU();

-}

-

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

-                              const SkMatrix& ctm,

-                              SkBitmap* result, SkIPoint* offset) {

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

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

-        return false;

-    }

-

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

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

-        return false;

-    }

-

-    GrTexture* texture;

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

-    // must be pushed upstack.

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

-

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

-                          offset);

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-// must be in SkCanvas::VertexMode order

-static const GrPrimitiveType gVertexMode2PrimitiveType[] = {

-    kTriangles_GrPrimitiveType,

-    kTriangleStrip_GrPrimitiveType,

-    kTriangleFan_GrPrimitiveType,

-};

-

-void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,

-                              int vertexCount, const SkPoint vertices[],

-                              const SkPoint texs[], const SkColor colors[],

-                              SkXfermode* xmode,

-                              const uint16_t indices[], int indexCount,

-                              const SkPaint& paint) {

-    CHECK_SHOULD_DRAW(draw, false);

-

-    GrPaint grPaint;

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

-    if (NULL == texs) {

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

-            return;

-        }

-    } else {

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

-            return;

-        }

-    }

-

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

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

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

-#if 0

-            return

-#endif

-        }

-    }

-

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

-    if (NULL != colors) {

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

-        convertedColors.reset(vertexCount);

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

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

-        }

-        colors = convertedColors.get();

-    }

-    fContext->drawVertices(grPaint,

-                           gVertexMode2PrimitiveType[vmode],

-                           vertexCount,

-                           (GrPoint*) vertices,

-                           (GrPoint*) texs,

-                           colors,

-                           indices,

-                           indexCount);

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-static void GlyphCacheAuxProc(void* data) {

-    GrFontScaler* scaler = (GrFontScaler*)data;

-    SkSafeUnref(scaler);

-}

-

-static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {

-    void* auxData;

-    GrFontScaler* scaler = NULL;

-    if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {

-        scaler = (GrFontScaler*)auxData;

-    }

-    if (NULL == scaler) {

-        scaler = SkNEW_ARGS(SkGrFontScaler, (cache));

-        cache->setAuxProc(GlyphCacheAuxProc, scaler);

-    }

-    return scaler;

-}

-

-static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state,

-                             SkFixed fx, SkFixed fy,

-                             const SkGlyph& glyph) {

-    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);

-

-    GrSkDrawProcs* procs = static_cast<GrSkDrawProcs*>(state.fDraw->fProcs);

-

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

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

-    }

-

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

-                                                       glyph.getSubXFixed(),

-                                                       glyph.getSubYFixed()),

-                                         SkFixedFloorToFixed(fx),

-                                         SkFixedFloorToFixed(fy),

-                                         procs->fFontScaler);

-}

-

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

-

-    // deferred allocation

-    if (NULL == fDrawProcs) {

-        fDrawProcs = SkNEW(GrSkDrawProcs);

-        fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;

-        fDrawProcs->fContext = fContext;

-#if SK_DISTANCEFIELD_FONTS

-        fDrawProcs->fFlags = 0;

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

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

-#endif

-    }

-

-    // init our (and GL's) state

-    fDrawProcs->fTextContext = context;

-    fDrawProcs->fFontScaler = NULL;

-    return fDrawProcs;

-}

-

-void SkGpuDevice::drawText(const SkDraw& draw, const void* text,

-                          size_t byteLength, SkScalar x, SkScalar y,

-                          const SkPaint& paint) {

-    CHECK_SHOULD_DRAW(draw, false);

-

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

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

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

-    } else {

-        SkDraw myDraw(draw);

-

-        GrPaint grPaint;

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

-            return;

-        }

-#if SK_DISTANCEFIELD_FONTS

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

-                                           paint.getTextSize());

-#else

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

-#endif

-        myDraw.fProcs = this->initDrawForText(&context);

-        this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);

-    }

-}

-

-void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,

-                             size_t byteLength, const SkScalar pos[],

-                             SkScalar constY, int scalarsPerPos,

-                             const SkPaint& paint) {

-    CHECK_SHOULD_DRAW(draw, false);

-

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

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

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

-                         scalarsPerPos, paint);

-    } else {

-        SkDraw myDraw(draw);

-

-        GrPaint grPaint;

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

-            return;

-        }

-#if SK_DISTANCEFIELD_FONTS

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

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

-#else

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

-#endif

-        myDraw.fProcs = this->initDrawForText(&context);

-        this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,

-                                     scalarsPerPos, paint);

-    }

-}

-

-void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,

-                                size_t len, const SkPath& path,

-                                const SkMatrix* m, const SkPaint& paint) {

-    CHECK_SHOULD_DRAW(draw, false);

-

-    SkASSERT(draw.fDevice == this);

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

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

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

-    if (!paint.isLCDRenderText()) {

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

-        return false;

-    }

-

-    if (paint.getShader() ||

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

-        paint.getMaskFilter() ||

-        paint.getRasterizer() ||

-        paint.getColorFilter() ||

-        paint.getPathEffect() ||

-        paint.isFakeBoldText() ||

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

-        // turn off lcd

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

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

-        return true;

-    }

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

-    return false;

-}

-

-void SkGpuDevice::flush() {

-    DO_DEFERRED_CLEAR();

-    fContext->resolveRenderTarget(fRenderTarget);

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

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

-                                                    int width, int height,

-                                                    bool isOpaque,

-                                                    Usage usage) {

-    GrTextureDesc desc;

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

-    desc.fFlags = kRenderTarget_GrTextureFlagBit;

-    desc.fWidth = width;

-    desc.fHeight = height;

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

-

-    SkAutoTUnref<GrTexture> texture;

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

-    bool needClear = !isOpaque;

-

-#if CACHE_COMPATIBLE_DEVICE_TEXTURES

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

-    // match and ignore any padding.

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

-                                                GrContext::kApprox_ScratchTexMatch :

-                                                GrContext::kExact_ScratchTexMatch;

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

-#else

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

-#endif

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

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

-    } else {

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

-        return NULL;

-    }

-}

-

-SkGpuDevice::SkGpuDevice(GrContext* context,

-                         GrTexture* texture,

-                         bool needClear)

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

-

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

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

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

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

-    fNeedClear = needClear;

-}

+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkGpuDevice.h"
+
+#include "effects/GrTextureDomainEffect.h"
+#include "effects/GrSimpleTextureEffect.h"
+
+#include "GrContext.h"
+#include "GrBitmapTextContext.h"
+#if SK_DISTANCEFIELD_FONTS
+#include "GrDistanceFieldTextContext.h"
+#endif
+
+#include "SkGrTexturePixelRef.h"
+
+#include "SkColorFilter.h"
+#include "SkDeviceImageFilterProxy.h"
+#include "SkDrawProcs.h"
+#include "SkGlyphCache.h"
+#include "SkImageFilter.h"
+#include "SkPathEffect.h"
+#include "SkRRect.h"
+#include "SkStroke.h"
+#include "SkUtils.h"
+#include "SkErrorInternals.h"
+
+#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1
+
+#if 0
+    extern bool (*gShouldDrawProc)();
+    #define CHECK_SHOULD_DRAW(draw, forceI)                     \
+        do {                                                    \
+            if (gShouldDrawProc && !gShouldDrawProc()) return;  \
+            this->prepareDraw(draw, forceI);                    \
+        } while (0)
+#else
+    #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI)
+#endif
+
+// This constant represents the screen alignment criterion in texels for
+// requiring texture domain clamping to prevent color bleeding when drawing
+// a sub region of a larger source image.
+#define COLOR_BLEED_TOLERANCE SkFloatToScalar(0.001f)
+
+#define DO_DEFERRED_CLEAR()             \
+    do {                                \
+        if (fNeedClear) {               \
+            this->clear(SK_ColorTRANSPARENT); \
+        }                               \
+    } while (false)                     \
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define CHECK_FOR_ANNOTATION(paint) \
+    do { if (paint.getAnnotation()) { return; } } while (0)
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable {
+public:
+    SkAutoCachedTexture()
+        : fDevice(NULL)
+        , fTexture(NULL) {
+    }
+
+    SkAutoCachedTexture(SkGpuDevice* device,
+                        const SkBitmap& bitmap,
+                        const GrTextureParams* params,
+                        GrTexture** texture)
+        : fDevice(NULL)
+        , fTexture(NULL) {
+        SkASSERT(NULL != texture);
+        *texture = this->set(device, bitmap, params);
+    }
+
+    ~SkAutoCachedTexture() {
+        if (NULL != fTexture) {
+            GrUnlockAndUnrefCachedBitmapTexture(fTexture);
+        }
+    }
+
+    GrTexture* set(SkGpuDevice* device,
+                   const SkBitmap& bitmap,
+                   const GrTextureParams* params) {
+        if (NULL != fTexture) {
+            GrUnlockAndUnrefCachedBitmapTexture(fTexture);
+            fTexture = NULL;
+        }
+        fDevice = device;
+        GrTexture* result = (GrTexture*)bitmap.getTexture();
+        if (NULL == result) {
+            // Cannot return the native texture so look it up in our cache
+            fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params);
+            result = fTexture;
+        }
+        return result;
+    }
+
+private:
+    SkGpuDevice* fDevice;
+    GrTexture*   fTexture;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct GrSkDrawProcs : public SkDrawProcs {
+public:
+    GrContext* fContext;
+    GrTextContext* fTextContext;
+    GrFontScaler* fFontScaler;  // cached in the skia glyphcache
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) {
+    switch (config) {
+        case kAlpha_8_GrPixelConfig:
+            *isOpaque = false;
+            return SkBitmap::kA8_Config;
+        case kRGB_565_GrPixelConfig:
+            *isOpaque = true;
+            return SkBitmap::kRGB_565_Config;
+        case kRGBA_4444_GrPixelConfig:
+            *isOpaque = false;
+            return SkBitmap::kARGB_4444_Config;
+        case kSkia8888_GrPixelConfig:
+            // we don't currently have a way of knowing whether
+            // a 8888 is opaque based on the config.
+            *isOpaque = false;
+            return SkBitmap::kARGB_8888_Config;
+        default:
+            *isOpaque = false;
+            return SkBitmap::kNo_Config;
+    }
+}
+
+/*
+ * GrRenderTarget does not know its opaqueness, only its config, so we have
+ * to make conservative guesses when we return an "equivalent" bitmap.
+ */
+static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) {
+    bool isOpaque;
+    SkBitmap::Config config = grConfig2skConfig(renderTarget->config(), &isOpaque);
+
+    SkBitmap bitmap;
+    bitmap.setConfig(config, renderTarget->width(), renderTarget->height(), 0,
+                     isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
+    return bitmap;
+}
+
+SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) {
+    SkASSERT(NULL != surface);
+    if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) {
+        return NULL;
+    }
+    if (surface->asTexture()) {
+        return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture()));
+    } else {
+        return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget()));
+    }
+}
+
+SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture)
+    : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {
+    this->initFromRenderTarget(context, texture->asRenderTarget(), false);
+}
+
+SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget)
+    : SkBitmapDevice(make_bitmap(context, renderTarget)) {
+    this->initFromRenderTarget(context, renderTarget, false);
+}
+
+void SkGpuDevice::initFromRenderTarget(GrContext* context,
+                                       GrRenderTarget* renderTarget,
+                                       bool cached) {
+    fDrawProcs = NULL;
+
+    fContext = context;
+    fContext->ref();
+
+    fRenderTarget = NULL;
+    fNeedClear = false;
+
+    SkASSERT(NULL != renderTarget);
+    fRenderTarget = renderTarget;
+    fRenderTarget->ref();
+
+    // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref
+    // on the RT but not vice-versa.
+    // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without
+    // busting chrome (for a currently unknown reason).
+    GrSurface* surface = fRenderTarget->asTexture();
+    if (NULL == surface) {
+        surface = fRenderTarget;
+    }
+    SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (surface, cached));
+
+    this->setPixelRef(pr, 0)->unref();
+}
+
+SkGpuDevice::SkGpuDevice(GrContext* context,
+                         SkBitmap::Config config,
+                         int width,
+                         int height,
+                         int sampleCount)
+    : SkBitmapDevice(config, width, height, false /*isOpaque*/) {
+
+    fDrawProcs = NULL;
+
+    fContext = context;
+    fContext->ref();
+
+    fRenderTarget = NULL;
+    fNeedClear = false;
+
+    if (config != SkBitmap::kRGB_565_Config) {
+        config = SkBitmap::kARGB_8888_Config;
+    }
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fConfig = SkBitmapConfig2GrPixelConfig(config);
+    desc.fSampleCnt = sampleCount;
+
+    SkAutoTUnref<GrTexture> texture(fContext->createUncachedTexture(desc, NULL, 0));
+
+    if (NULL != texture) {
+        fRenderTarget = texture->asRenderTarget();
+        fRenderTarget->ref();
+
+        SkASSERT(NULL != fRenderTarget);
+
+        // wrap the bitmap with a pixelref to expose our texture
+        SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (texture));
+        this->setPixelRef(pr, 0)->unref();
+    } else {
+        GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",
+                 width, height);
+        SkASSERT(false);
+    }
+}
+
+SkGpuDevice::~SkGpuDevice() {
+    if (fDrawProcs) {
+        delete fDrawProcs;
+    }
+
+    // The GrContext takes a ref on the target. We don't want to cause the render
+    // target to be unnecessarily kept alive.
+    if (fContext->getRenderTarget() == fRenderTarget) {
+        fContext->setRenderTarget(NULL);
+    }
+
+    if (fContext->getClip() == &fClipData) {
+        fContext->setClip(NULL);
+    }
+
+    SkSafeUnref(fRenderTarget);
+    fContext->unref();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::makeRenderTargetCurrent() {
+    DO_DEFERRED_CLEAR();
+    fContext->setRenderTarget(fRenderTarget);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+GrPixelConfig config8888_to_grconfig_and_flags(SkCanvas::Config8888 config8888, uint32_t* flags) {
+    switch (config8888) {
+        case SkCanvas::kNative_Premul_Config8888:
+            *flags = 0;
+            return kSkia8888_GrPixelConfig;
+        case SkCanvas::kNative_Unpremul_Config8888:
+            *flags = GrContext::kUnpremul_PixelOpsFlag;
+            return kSkia8888_GrPixelConfig;
+        case SkCanvas::kBGRA_Premul_Config8888:
+            *flags = 0;
+            return kBGRA_8888_GrPixelConfig;
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            *flags = GrContext::kUnpremul_PixelOpsFlag;
+            return kBGRA_8888_GrPixelConfig;
+        case SkCanvas::kRGBA_Premul_Config8888:
+            *flags = 0;
+            return kRGBA_8888_GrPixelConfig;
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            *flags = GrContext::kUnpremul_PixelOpsFlag;
+            return kRGBA_8888_GrPixelConfig;
+        default:
+            GrCrash("Unexpected Config8888.");
+            *flags = 0; // suppress warning
+            return kSkia8888_GrPixelConfig;
+    }
+}
+}
+
+bool SkGpuDevice::onReadPixels(const SkBitmap& bitmap,
+                               int x, int y,
+                               SkCanvas::Config8888 config8888) {
+    DO_DEFERRED_CLEAR();
+    SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
+    SkASSERT(!bitmap.isNull());
+    SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height())));
+
+    SkAutoLockPixels alp(bitmap);
+    GrPixelConfig config;
+    uint32_t flags;
+    config = config8888_to_grconfig_and_flags(config8888, &flags);
+    return fContext->readRenderTargetPixels(fRenderTarget,
+                                            x, y,
+                                            bitmap.width(),
+                                            bitmap.height(),
+                                            config,
+                                            bitmap.getPixels(),
+                                            bitmap.rowBytes(),
+                                            flags);
+}
+
+void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y,
+                              SkCanvas::Config8888 config8888) {
+    SkAutoLockPixels alp(bitmap);
+    if (!bitmap.readyToDraw()) {
+        return;
+    }
+
+    GrPixelConfig config;
+    uint32_t flags;
+    if (SkBitmap::kARGB_8888_Config == bitmap.config()) {
+        config = config8888_to_grconfig_and_flags(config8888, &flags);
+    } else {
+        flags = 0;
+        config= SkBitmapConfig2GrPixelConfig(bitmap.config());
+    }
+
+    fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(),
+                               config, bitmap.getPixels(), bitmap.rowBytes(), flags);
+}
+
+void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {
+    INHERITED::onAttachToCanvas(canvas);
+
+    // Canvas promises that this ptr is valid until onDetachFromCanvas is called
+    fClipData.fClipStack = canvas->getClipStack();
+}
+
+void SkGpuDevice::onDetachFromCanvas() {
+    INHERITED::onDetachFromCanvas();
+    fClipData.fClipStack = NULL;
+}
+
+// call this every draw call, to ensure that the context reflects our state,
+// and not the state from some other canvas/device
+void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) {
+    SkASSERT(NULL != fClipData.fClipStack);
+
+    fContext->setRenderTarget(fRenderTarget);
+
+    SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack);
+
+    if (forceIdentity) {
+        fContext->setIdentityMatrix();
+    } else {
+        fContext->setMatrix(*draw.fMatrix);
+    }
+    fClipData.fOrigin = this->getOrigin();
+
+    fContext->setClip(&fClipData);
+
+    DO_DEFERRED_CLEAR();
+}
+
+GrRenderTarget* SkGpuDevice::accessRenderTarget() {
+    DO_DEFERRED_CLEAR();
+    return fRenderTarget;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,
+                  shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5,
+                  shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch);
+
+namespace {
+
+// converts a SkPaint to a GrPaint, ignoring the skPaint's shader
+// justAlpha indicates that skPaint's alpha should be used rather than the color
+// Callers may subsequently modify the GrPaint. Setting constantColor indicates
+// that the final paint will draw the same color at every pixel. This allows
+// an optimization where the the color filter can be applied to the skPaint's
+// color once while converting to GrPaint and then ignored.
+inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev,
+                                    const SkPaint& skPaint,
+                                    bool justAlpha,
+                                    bool constantColor,
+                                    GrPaint* grPaint) {
+
+    grPaint->setDither(skPaint.isDither());
+    grPaint->setAntiAlias(skPaint.isAntiAlias());
+
+    SkXfermode::Coeff sm;
+    SkXfermode::Coeff dm;
+
+    SkXfermode* mode = skPaint.getXfermode();
+    GrEffectRef* xferEffect = NULL;
+    if (SkXfermode::AsNewEffectOrCoeff(mode, &xferEffect, &sm, &dm)) {
+        if (NULL != xferEffect) {
+            grPaint->addColorEffect(xferEffect)->unref();
+            sm = SkXfermode::kOne_Coeff;
+            dm = SkXfermode::kZero_Coeff;
+        }
+    } else {
+        //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)
+#if 0
+        return false;
+#else
+        // Fall back to src-over
+        sm = SkXfermode::kOne_Coeff;
+        dm = SkXfermode::kISA_Coeff;
+#endif
+    }
+    grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
+
+    if (justAlpha) {
+        uint8_t alpha = skPaint.getAlpha();
+        grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha));
+        // justAlpha is currently set to true only if there is a texture,
+        // so constantColor should not also be true.
+        SkASSERT(!constantColor);
+    } else {
+        grPaint->setColor(SkColor2GrColor(skPaint.getColor()));
+    }
+
+    SkColorFilter* colorFilter = skPaint.getColorFilter();
+    if (NULL != colorFilter) {
+        // if the source color is a constant then apply the filter here once rather than per pixel
+        // in a shader.
+        if (constantColor) {
+            SkColor filtered = colorFilter->filterColor(skPaint.getColor());
+            grPaint->setColor(SkColor2GrColor(filtered));
+        } else {
+            SkAutoTUnref<GrEffectRef> effect(colorFilter->asNewEffect(dev->context()));
+            if (NULL != effect.get()) {
+                grPaint->addColorEffect(effect);
+            }
+        }
+    }
+
+    return true;
+}
+
+// This function is similar to skPaint2GrPaintNoShader but also converts
+// skPaint's shader to a GrTexture/GrEffectStage if possible. The texture to
+// be used is set on grPaint and returned in param act. constantColor has the
+// same meaning as in skPaint2GrPaintNoShader.
+inline bool skPaint2GrPaintShader(SkGpuDevice* dev,
+                                  const SkPaint& skPaint,
+                                  bool constantColor,
+                                  GrPaint* grPaint) {
+    SkShader* shader = skPaint.getShader();
+    if (NULL == shader) {
+        return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint);
+    }
+
+    // SkShader::asNewEffect() may do offscreen rendering. Setup default drawing state
+    // Also require shader to set the render target .
+    GrContext::AutoWideOpenIdentityDraw awo(dev->context(), NULL);
+    GrContext::AutoRenderTarget(dev->context(), NULL);
+
+    // setup the shader as the first color effect on the paint
+    SkAutoTUnref<GrEffectRef> effect(shader->asNewEffect(dev->context(), skPaint));
+    if (NULL != effect.get()) {
+        grPaint->addColorEffect(effect);
+        // Now setup the rest of the paint.
+        return skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint);
+    } else {
+        // We still don't have SkColorShader::asNewEffect() implemented.
+        SkShader::GradientInfo info;
+        SkColor                color;
+
+        info.fColors = &color;
+        info.fColorOffsets = NULL;
+        info.fColorCount = 1;
+        if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {
+            SkPaint copy(skPaint);
+            copy.setShader(NULL);
+            // modulate the paint alpha by the shader's solid color alpha
+            U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
+            copy.setColor(SkColorSetA(color, newA));
+            return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint);
+        } else {
+            return false;
+        }
+    }
+}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void SkGpuDevice::getGlobalBounds(SkIRect* bounds) const {
+    if (NULL != bounds) {
+        const SkIPoint& origin = this->getOrigin();
+        bounds->setXYWH(origin.x(), origin.y(),
+                        this->width(), this->height());
+    }
+}
+
+SkBitmap::Config SkGpuDevice::config() const {
+    if (NULL == fRenderTarget) {
+        return SkBitmap::kNo_Config;
+    }
+
+    bool isOpaque;
+    return grConfig2skConfig(fRenderTarget->config(), &isOpaque);
+}
+
+void SkGpuDevice::clear(SkColor color) {
+    SkIRect rect = SkIRect::MakeWH(this->width(), this->height());
+    fContext->clear(&rect, SkColor2GrColor(color), true, fRenderTarget);
+    fNeedClear = false;
+}
+
+void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw, false);
+
+    GrPaint grPaint;
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+        return;
+    }
+
+    fContext->drawPaint(grPaint);
+}
+
+// must be in SkCanvas::PointMode order
+static const GrPrimitiveType gPointMode2PrimtiveType[] = {
+    kPoints_GrPrimitiveType,
+    kLines_GrPrimitiveType,
+    kLineStrip_GrPrimitiveType
+};
+
+void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
+                             size_t count, const SkPoint pts[], const SkPaint& paint) {
+    CHECK_FOR_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw, false);
+
+    SkScalar width = paint.getStrokeWidth();
+    if (width < 0) {
+        return;
+    }
+
+    // we only handle hairlines and paints without path effects or mask filters,
+    // else we let the SkDraw call our drawPath()
+    if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) {
+        draw.drawPoints(mode, count, pts, paint, true);
+        return;
+    }
+
+    GrPaint grPaint;
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+        return;
+    }
+
+    fContext->drawVertices(grPaint,
+                           gPointMode2PrimtiveType[mode],
+                           count,
+                           (GrPoint*)pts,
+                           NULL,
+                           NULL,
+                           NULL,
+                           0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
+                           const SkPaint& paint) {
+    CHECK_FOR_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw, false);
+
+    bool doStroke = paint.getStyle() != SkPaint::kFill_Style;
+    SkScalar width = paint.getStrokeWidth();
+
+    /*
+        We have special code for hairline strokes, miter-strokes, bevel-stroke
+        and fills. Anything else we just call our path code.
+     */
+    bool usePath = doStroke && width > 0 &&
+                   (paint.getStrokeJoin() == SkPaint::kRound_Join ||
+                    (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty()));
+    // another two reasons we might need to call drawPath...
+    if (paint.getMaskFilter() || paint.getPathEffect()) {
+        usePath = true;
+    }
+    if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) {
+#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
+        if (doStroke) {
+#endif
+            usePath = true;
+#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
+        } else {
+            usePath = !fContext->getMatrix().preservesRightAngles();
+        }
+#endif
+    }
+    // until we can both stroke and fill rectangles
+    if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
+        usePath = true;
+    }
+
+    if (usePath) {
+        SkPath path;
+        path.addRect(rect);
+        this->drawPath(draw, path, paint, NULL, true);
+        return;
+    }
+
+    GrPaint grPaint;
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+        return;
+    }
+
+    if (!doStroke) {
+        fContext->drawRect(grPaint, rect);
+    } else {
+        SkStrokeRec stroke(paint);
+        fContext->drawRect(grPaint, rect, &stroke);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect,
+                           const SkPaint& paint) {
+    CHECK_FOR_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw, false);
+
+    bool usePath = !rect.isSimple();
+    // another two reasons we might need to call drawPath...
+    if (paint.getMaskFilter() || paint.getPathEffect()) {
+        usePath = true;
+    }
+    // until we can rotate rrects...
+    if (!usePath && !fContext->getMatrix().rectStaysRect()) {
+        usePath = true;
+    }
+
+    if (usePath) {
+        SkPath path;
+        path.addRRect(rect);
+        this->drawPath(draw, path, paint, NULL, true);
+        return;
+    }
+
+    GrPaint grPaint;
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+        return;
+    }
+
+    SkStrokeRec stroke(paint);
+    fContext->drawRRect(grPaint, rect, stroke);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,
+                           const SkPaint& paint) {
+    CHECK_FOR_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw, false);
+
+    bool usePath = false;
+    // some basic reasons we might need to call drawPath...
+    if (paint.getMaskFilter() || paint.getPathEffect()) {
+        usePath = true;
+    }
+
+    if (usePath) {
+        SkPath path;
+        path.addOval(oval);
+        this->drawPath(draw, path, paint, NULL, true);
+        return;
+    }
+
+    GrPaint grPaint;
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+        return;
+    }
+    SkStrokeRec stroke(paint);
+
+    fContext->drawOval(grPaint, oval, stroke);
+}
+
+#include "SkMaskFilter.h"
+#include "SkBounder.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+// helpers for applying mask filters
+namespace {
+
+// Draw a mask using the supplied paint. Since the coverage/geometry
+// is already burnt into the mask this boils down to a rect draw.
+// Return true if the mask was successfully drawn.
+bool draw_mask(GrContext* context, const SkRect& maskRect,
+               GrPaint* grp, GrTexture* mask) {
+    GrContext::AutoMatrix am;
+    if (!am.setIdentity(context, grp)) {
+        return false;
+    }
+
+    SkMatrix matrix;
+    matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);
+    matrix.postIDiv(mask->width(), mask->height());
+
+    grp->addCoverageEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref();
+    context->drawRect(*grp, maskRect);
+    return true;
+}
+
+bool draw_with_mask_filter(GrContext* context, const SkPath& devPath,
+                           SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder,
+                           GrPaint* grp, SkPaint::Style style) {
+    SkMask  srcM, dstM;
+
+    if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM,
+                            SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
+        return false;
+    }
+    SkAutoMaskFreeImage autoSrc(srcM.fImage);
+
+    if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) {
+        return false;
+    }
+    // this will free-up dstM when we're done (allocated in filterMask())
+    SkAutoMaskFreeImage autoDst(dstM.fImage);
+
+    if (clip.quickReject(dstM.fBounds)) {
+        return false;
+    }
+    if (bounder && !bounder->doIRect(dstM.fBounds)) {
+        return false;
+    }
+
+    // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
+    // the current clip (and identity matrix) and GrPaint settings
+    GrTextureDesc desc;
+    desc.fWidth = dstM.fBounds.width();
+    desc.fHeight = dstM.fBounds.height();
+    desc.fConfig = kAlpha_8_GrPixelConfig;
+
+    GrAutoScratchTexture ast(context, desc);
+    GrTexture* texture = ast.texture();
+
+    if (NULL == texture) {
+        return false;
+    }
+    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+                               dstM.fImage, dstM.fRowBytes);
+
+    SkRect maskRect = SkRect::Make(dstM.fBounds);
+
+    return draw_mask(context, maskRect, grp, texture);
+}
+
+// Create a mask of 'devPath' and place the result in 'mask'. Return true on
+// success; false otherwise.
+bool create_mask_GPU(GrContext* context,
+                     const SkRect& maskRect,
+                     const SkPath& devPath,
+                     const SkStrokeRec& stroke,
+                     bool doAA,
+                     GrAutoScratchTexture* mask) {
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    desc.fWidth = SkScalarCeilToInt(maskRect.width());
+    desc.fHeight = SkScalarCeilToInt(maskRect.height());
+    // We actually only need A8, but it often isn't supported as a
+    // render target so default to RGBA_8888
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
+        desc.fConfig = kAlpha_8_GrPixelConfig;
+    }
+
+    mask->set(context, desc);
+    if (NULL == mask->texture()) {
+        return false;
+    }
+
+    GrTexture* maskTexture = mask->texture();
+    SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
+
+    GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget());
+    GrContext::AutoClip ac(context, clipRect);
+
+    context->clear(NULL, 0x0, true);
+
+    GrPaint tempPaint;
+    if (doAA) {
+        tempPaint.setAntiAlias(true);
+        // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
+        // blend coeff of zero requires dual source blending support in order
+        // to properly blend partially covered pixels. This means the AA
+        // code path may not be taken. So we use a dst blend coeff of ISA. We
+        // could special case AA draws to a dst surface with known alpha=0 to
+        // use a zero dst coeff when dual source blending isn't available.
+        tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
+    }
+
+    GrContext::AutoMatrix am;
+
+    // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint.
+    SkMatrix translate;
+    translate.setTranslate(-maskRect.fLeft, -maskRect.fTop);
+    am.set(context, translate);
+    context->drawPath(tempPaint, devPath, stroke);
+    return true;
+}
+
+SkBitmap wrap_texture(GrTexture* texture) {
+    SkBitmap result;
+    bool dummy;
+    SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy);
+    result.setConfig(config, texture->width(), texture->height());
+    result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();
+    return result;
+}
+
+};
+
+void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
+                           const SkPaint& paint, const SkMatrix* prePathMatrix,
+                           bool pathIsMutable) {
+    CHECK_FOR_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw, false);
+
+    GrPaint grPaint;
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+        return;
+    }
+
+    // can we cheat, and treat a thin stroke as a hairline w/ coverage
+    // if we can, we draw lots faster (raster device does this same test)
+    SkScalar hairlineCoverage;
+    bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage);
+    if (doHairLine) {
+        grPaint.setCoverage(SkScalarRoundToInt(hairlineCoverage * grPaint.getCoverage()));
+    }
+
+    // If we have a prematrix, apply it to the path, optimizing for the case
+    // where the original path can in fact be modified in place (even though
+    // its parameter type is const).
+    SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
+    SkPath  tmpPath, effectPath;
+
+    if (prePathMatrix) {
+        SkPath* result = pathPtr;
+
+        if (!pathIsMutable) {
+            result = &tmpPath;
+            pathIsMutable = true;
+        }
+        // should I push prePathMatrix on our MV stack temporarily, instead
+        // of applying it here? See SkDraw.cpp
+        pathPtr->transform(*prePathMatrix, result);
+        pathPtr = result;
+    }
+    // at this point we're done with prePathMatrix
+    SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
+
+    SkStrokeRec stroke(paint);
+    SkPathEffect* pathEffect = paint.getPathEffect();
+    const SkRect* cullRect = NULL;  // TODO: what is our bounds?
+    if (pathEffect && pathEffect->filterPath(&effectPath, *pathPtr, &stroke,
+                                             cullRect)) {
+        pathPtr = &effectPath;
+    }
+
+    if (!pathEffect && doHairLine) {
+        stroke.setHairlineStyle();
+    }
+
+    if (paint.getMaskFilter()) {
+        if (!stroke.isHairlineStyle()) {
+            if (stroke.applyToPath(&tmpPath, *pathPtr)) {
+                pathPtr = &tmpPath;
+                pathIsMutable = true;
+                stroke.setFillStyle();
+            }
+        }
+
+        // avoid possibly allocating a new path in transform if we can
+        SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
+
+        // transform the path into device space
+        pathPtr->transform(fContext->getMatrix(), devPathPtr);
+
+        SkRect maskRect;
+        if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),
+                                                    draw.fClip->getBounds(),
+                                                    fContext->getMatrix(),
+                                                    &maskRect)) {
+            SkIRect finalIRect;
+            maskRect.roundOut(&finalIRect);
+            if (draw.fClip->quickReject(finalIRect)) {
+                // clipped out
+                return;
+            }
+            if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) {
+                // nothing to draw
+                return;
+            }
+
+            GrAutoScratchTexture mask;
+
+            if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke,
+                                grPaint.isAntiAlias(), &mask)) {
+                GrTexture* filtered;
+
+                if (paint.getMaskFilter()->filterMaskGPU(mask.texture(), maskRect, &filtered, true)) {
+                    // filterMaskGPU gives us ownership of a ref to the result
+                    SkAutoTUnref<GrTexture> atu(filtered);
+
+                    // If the scratch texture that we used as the filter src also holds the filter
+                    // result then we must detach so that this texture isn't recycled for a later
+                    // draw.
+                    if (filtered == mask.texture()) {
+                        mask.detach();
+                        filtered->unref(); // detach transfers GrAutoScratchTexture's ref to us.
+                    }
+
+                    if (draw_mask(fContext, maskRect, &grPaint, filtered)) {
+                        // This path is completely drawn
+                        return;
+                    }
+                }
+            }
+        }
+
+        // draw the mask on the CPU - this is a fallthrough path in case the
+        // GPU path fails
+        SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :
+                                                          SkPaint::kFill_Style;
+        draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(),
+                              *draw.fClip, draw.fBounder, &grPaint, style);
+        return;
+    }
+
+    fContext->drawPath(grPaint, *pathPtr, stroke);
+}
+
+static const int kBmpSmallTileSize = 1 << 10;
+
+static inline int get_tile_count(const SkIRect& srcRect, int tileSize)  {
+    int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1;
+    int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1;
+    return tilesX * tilesY;
+}
+
+static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) {
+    if (maxTileSize <= kBmpSmallTileSize) {
+        return maxTileSize;
+    }
+
+    size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);
+    size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);
+
+    maxTileTotalTileSize *= maxTileSize * maxTileSize;
+    smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;
+
+    if (maxTileTotalTileSize > 2 * smallTotalTileSize) {
+        return kBmpSmallTileSize;
+    } else {
+        return maxTileSize;
+    }
+}
+
+// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what
+// pixels from the bitmap are necessary.
+static void determine_clipped_src_rect(const GrContext* context,
+                                       const SkBitmap& bitmap,
+                                       const SkRect* srcRectPtr,
+                                       SkIRect* clippedSrcIRect) {
+    const GrClipData* clip = context->getClip();
+    clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL);
+    SkMatrix inv;
+    if (!context->getMatrix().invert(&inv)) {
+        clippedSrcIRect->setEmpty();
+        return;
+    }
+    SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect);
+    inv.mapRect(&clippedSrcRect);
+    if (NULL != srcRectPtr) {
+        if (!clippedSrcRect.intersect(*srcRectPtr)) {
+            clippedSrcIRect->setEmpty();
+            return;
+        }
+    }
+    clippedSrcRect.roundOut(clippedSrcIRect);
+    SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
+    if (!clippedSrcIRect->intersect(bmpBounds)) {
+        clippedSrcIRect->setEmpty();
+    }
+}
+
+bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
+                                   const GrTextureParams& params,
+                                   const SkRect* srcRectPtr,
+                                   int maxTileSize,
+                                   int* tileSize,
+                                   SkIRect* clippedSrcRect) const {
+    // if bitmap is explictly texture backed then just use the texture
+    if (NULL != bitmap.getTexture()) {
+        return false;
+    }
+
+    // if it's larger than the max tile size, then we have no choice but tiling.
+    if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) {
+        determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);
+        *tileSize = determine_tile_size(bitmap, *clippedSrcRect, maxTileSize);
+        return true;
+    }
+
+    if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) {
+        return false;
+    }
+
+    // if the entire texture is already in our cache then no reason to tile it
+    if (GrIsBitmapInCache(fContext, bitmap, &params)) {
+        return false;
+    }
+
+    // At this point we know we could do the draw by uploading the entire bitmap
+    // as a texture. However, if the texture would be large compared to the
+    // cache size and we don't require most of it for this draw then tile to
+    // reduce the amount of upload and cache spill.
+
+    // assumption here is that sw bitmap size is a good proxy for its size as
+    // a texture
+    size_t bmpSize = bitmap.getSize();
+    size_t cacheSize;
+    fContext->getTextureCacheLimits(NULL, &cacheSize);
+    if (bmpSize < cacheSize / 2) {
+        return false;
+    }
+
+    // Figure out how much of the src we will need based on the src rect and clipping.
+    determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect);
+    *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile.
+    size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) *
+                           kBmpSmallTileSize * kBmpSmallTileSize;
+
+    return usedTileBytes < 2 * bmpSize;
+}
+
+void SkGpuDevice::drawBitmap(const SkDraw& draw,
+                             const SkBitmap& bitmap,
+                             const SkMatrix& m,
+                             const SkPaint& paint) {
+    // We cannot call drawBitmapRect here since 'm' could be anything
+    this->drawBitmapCommon(draw, bitmap, NULL, m, paint,
+                           SkCanvas::kNone_DrawBitmapRectFlag);
+}
+
+// This method outsets 'iRect' by 1 all around and then clamps its extents to
+// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner
+// of 'iRect' for all possible outsets/clamps.
+static inline void clamped_unit_outset_with_offset(SkIRect* iRect, SkPoint* offset,
+                                                   const SkIRect& clamp) {
+    iRect->outset(1, 1);
+
+    if (iRect->fLeft < clamp.fLeft) {
+        iRect->fLeft = clamp.fLeft;
+    } else {
+        offset->fX -= SK_Scalar1;
+    }
+    if (iRect->fTop < clamp.fTop) {
+        iRect->fTop = clamp.fTop;
+    } else {
+        offset->fY -= SK_Scalar1;
+    }
+
+    if (iRect->fRight > clamp.fRight) {
+        iRect->fRight = clamp.fRight;
+    }
+    if (iRect->fBottom > clamp.fBottom) {
+        iRect->fBottom = clamp.fBottom;
+    }
+}
+
+void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,
+                                   const SkBitmap& bitmap,
+                                   const SkRect* srcRectPtr,
+                                   const SkMatrix& m,
+                                   const SkPaint& paint,
+                                   SkCanvas::DrawBitmapRectFlags flags) {
+    CHECK_SHOULD_DRAW(draw, false);
+
+    SkRect srcRect;
+    if (NULL == srcRectPtr) {
+        srcRect.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
+    } else {
+        srcRect = *srcRectPtr;
+    }
+
+    if (paint.getMaskFilter()){
+        // Convert the bitmap to a shader so that the rect can be drawn
+        // through drawRect, which supports mask filters.
+        SkMatrix        newM(m);
+        SkBitmap        tmp;    // subset of bitmap, if necessary
+        const SkBitmap* bitmapPtr = &bitmap;
+        if (NULL != srcRectPtr) {
+            SkIRect iSrc;
+            srcRect.roundOut(&iSrc);
+
+            SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft),
+                                           SkIntToScalar(iSrc.fTop));
+
+            if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {
+                // In bleed mode we want to expand the src rect on all sides
+                // but stay within the bitmap bounds
+                SkIRect iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());
+                clamped_unit_outset_with_offset(&iSrc, &offset, iClampRect);
+            }
+
+            if (!bitmap.extractSubset(&tmp, iSrc)) {
+                return;     // extraction failed
+            }
+            bitmapPtr = &tmp;
+            srcRect.offset(-offset.fX, -offset.fY);
+            // The source rect has changed so update the matrix
+            newM.preTranslate(offset.fX, offset.fY);
+        }
+
+        SkPaint paintWithTexture(paint);
+        paintWithTexture.setShader(SkShader::CreateBitmapShader(*bitmapPtr,
+            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref();
+
+        // Transform 'newM' needs to be concatenated to the current matrix,
+        // rather than transforming the primitive directly, so that 'newM' will
+        // also affect the behavior of the mask filter.
+        SkMatrix drawMatrix;
+        drawMatrix.setConcat(fContext->getMatrix(), newM);
+        SkDraw transformedDraw(draw);
+        transformedDraw.fMatrix = &drawMatrix;
+
+        this->drawRect(transformedDraw, srcRect, paintWithTexture);
+
+        return;
+    }
+
+    fContext->concatMatrix(m);
+
+    GrTextureParams params;
+    SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel();
+    GrTextureParams::FilterMode textureFilterMode;
+    switch(paintFilterLevel) {
+        case SkPaint::kNone_FilterLevel:
+            textureFilterMode = GrTextureParams::kNone_FilterMode;
+            break;
+        case SkPaint::kLow_FilterLevel:
+            textureFilterMode = GrTextureParams::kBilerp_FilterMode;
+            break;
+        case SkPaint::kMedium_FilterLevel:
+            textureFilterMode = GrTextureParams::kMipMap_FilterMode;
+            break;
+        case SkPaint::kHigh_FilterLevel:
+            // Fall back to mips for now
+            textureFilterMode = GrTextureParams::kMipMap_FilterMode;
+            break;
+        default:
+            SkErrorInternals::SetError( kInvalidPaint_SkError,
+                                        "Sorry, I don't understand the filtering "
+                                        "mode you asked for.  Falling back to "
+                                        "MIPMaps.");
+            textureFilterMode = GrTextureParams::kMipMap_FilterMode;
+            break;
+
+    }
+
+    params.setFilterMode(textureFilterMode);
+
+    int maxTileSize = fContext->getMaxTextureSize();
+    if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) {
+        // We may need a skosh more room if we have to bump out the tile
+        // by 1 pixel all around
+        maxTileSize -= 2;
+    }
+    int tileSize;
+
+    SkIRect clippedSrcRect;
+    if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize,
+                               &clippedSrcRect)) {
+        this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize);
+    } else {
+        // take the simple case
+        this->internalDrawBitmap(bitmap, srcRect, params, paint, flags);
+    }
+}
+
+// Break 'bitmap' into several tiles to draw it since it has already
+// been determined to be too large to fit in VRAM
+void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,
+                                  const SkRect& srcRect,
+                                  const SkIRect& clippedSrcIRect,
+                                  const GrTextureParams& params,
+                                  const SkPaint& paint,
+                                  SkCanvas::DrawBitmapRectFlags flags,
+                                  int tileSize) {
+    SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect);
+
+    int nx = bitmap.width() / tileSize;
+    int ny = bitmap.height() / tileSize;
+    for (int x = 0; x <= nx; x++) {
+        for (int y = 0; y <= ny; y++) {
+            SkRect tileR;
+            tileR.set(SkIntToScalar(x * tileSize),
+                      SkIntToScalar(y * tileSize),
+                      SkIntToScalar((x + 1) * tileSize),
+                      SkIntToScalar((y + 1) * tileSize));
+
+            if (!SkRect::Intersects(tileR, clippedSrcRect)) {
+                continue;
+            }
+
+            if (!tileR.intersect(srcRect)) {
+                continue;
+            }
+
+            SkBitmap tmpB;
+            SkIRect iTileR;
+            tileR.roundOut(&iTileR);
+            SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft),
+                                           SkIntToScalar(iTileR.fTop));
+
+            if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) {
+                SkIRect iClampRect;
+
+                if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) {
+                    // In bleed mode we want to always expand the tile on all edges
+                    // but stay within the bitmap bounds
+                    iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());
+                } else {
+                    // In texture-domain/clamp mode we only want to expand the
+                    // tile on edges interior to "srcRect" (i.e., we want to
+                    // not bleed across the original clamped edges)
+                    srcRect.roundOut(&iClampRect);
+                }
+
+                clamped_unit_outset_with_offset(&iTileR, &offset, iClampRect);
+            }
+
+            if (bitmap.extractSubset(&tmpB, iTileR)) {
+                // now offset it to make it "local" to our tmp bitmap
+                tileR.offset(-offset.fX, -offset.fY);
+                SkMatrix tmpM;
+                tmpM.setTranslate(offset.fX, offset.fY);
+                GrContext::AutoMatrix am;
+                am.setPreConcat(fContext, tmpM);
+                this->internalDrawBitmap(tmpB, tileR, params, paint, flags);
+            }
+        }
+    }
+}
+
+static bool has_aligned_samples(const SkRect& srcRect,
+                                const SkRect& transformedRect) {
+    // detect pixel disalignment
+    if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) -
+            transformedRect.left()) < COLOR_BLEED_TOLERANCE &&
+        SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) -
+            transformedRect.top()) < COLOR_BLEED_TOLERANCE &&
+        SkScalarAbs(transformedRect.width() - srcRect.width()) <
+            COLOR_BLEED_TOLERANCE &&
+        SkScalarAbs(transformedRect.height() - srcRect.height()) <
+            COLOR_BLEED_TOLERANCE) {
+        return true;
+    }
+    return false;
+}
+
+static bool may_color_bleed(const SkRect& srcRect,
+                            const SkRect& transformedRect,
+                            const SkMatrix& m) {
+    // Only gets called if has_aligned_samples returned false.
+    // So we can assume that sampling is axis aligned but not texel aligned.
+    SkASSERT(!has_aligned_samples(srcRect, transformedRect));
+    SkRect innerSrcRect(srcRect), innerTransformedRect,
+        outerTransformedRect(transformedRect);
+    innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
+    m.mapRect(&innerTransformedRect, innerSrcRect);
+
+    // The gap between outerTransformedRect and innerTransformedRect
+    // represents the projection of the source border area, which is
+    // problematic for color bleeding.  We must check whether any
+    // destination pixels sample the border area.
+    outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
+    innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
+    SkIRect outer, inner;
+    outerTransformedRect.round(&outer);
+    innerTransformedRect.round(&inner);
+    // If the inner and outer rects round to the same result, it means the
+    // border does not overlap any pixel centers. Yay!
+    return inner != outer;
+}
+
+
+/*
+ *  This is called by drawBitmap(), which has to handle images that may be too
+ *  large to be represented by a single texture.
+ *
+ *  internalDrawBitmap assumes that the specified bitmap will fit in a texture
+ *  and that non-texture portion of the GrPaint has already been setup.
+ */
+void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
+                                     const SkRect& srcRect,
+                                     const GrTextureParams& params,
+                                     const SkPaint& paint,
+                                     SkCanvas::DrawBitmapRectFlags flags) {
+    SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&
+             bitmap.height() <= fContext->getMaxTextureSize());
+
+    GrTexture* texture;
+    SkAutoCachedTexture act(this, bitmap, &params, &texture);
+    if (NULL == texture) {
+        return;
+    }
+
+    SkRect dstRect(srcRect);
+    SkRect paintRect;
+    SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width()));
+    SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height()));
+    paintRect.setLTRB(SkScalarMul(srcRect.fLeft,   wInv),
+                      SkScalarMul(srcRect.fTop,    hInv),
+                      SkScalarMul(srcRect.fRight,  wInv),
+                      SkScalarMul(srcRect.fBottom, hInv));
+
+    bool needsTextureDomain = false;
+    if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) &&
+        params.filterMode() != GrTextureParams::kNone_FilterMode) {
+        // Need texture domain if drawing a sub rect.
+        needsTextureDomain = srcRect.width() < bitmap.width() ||
+                             srcRect.height() < bitmap.height();
+        if (needsTextureDomain && fContext->getMatrix().rectStaysRect()) {
+            const SkMatrix& matrix = fContext->getMatrix();
+            // sampling is axis-aligned
+            SkRect transformedRect;
+            matrix.mapRect(&transformedRect, srcRect);
+
+            if (has_aligned_samples(srcRect, transformedRect)) {
+                // We could also turn off filtering here (but we already did a cache lookup with
+                // params).
+                needsTextureDomain = false;
+            } else {
+                needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix);
+            }
+        }
+    }
+
+    SkRect textureDomain = SkRect::MakeEmpty();
+    SkAutoTUnref<GrEffectRef> effect;
+    if (needsTextureDomain) {
+        // Use a constrained texture domain to avoid color bleeding
+        SkScalar left, top, right, bottom;
+        if (srcRect.width() > SK_Scalar1) {
+            SkScalar border = SK_ScalarHalf / texture->width();
+            left = paintRect.left() + border;
+            right = paintRect.right() - border;
+        } else {
+            left = right = SkScalarHalf(paintRect.left() + paintRect.right());
+        }
+        if (srcRect.height() > SK_Scalar1) {
+            SkScalar border = SK_ScalarHalf / texture->height();
+            top = paintRect.top() + border;
+            bottom = paintRect.bottom() - border;
+        } else {
+            top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom());
+        }
+        textureDomain.setLTRB(left, top, right, bottom);
+        effect.reset(GrTextureDomainEffect::Create(texture,
+                                                   SkMatrix::I(),
+                                                   textureDomain,
+                                                   GrTextureDomainEffect::kClamp_WrapMode,
+                                                   params.filterMode()));
+    } else {
+        effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params));
+    }
+
+    // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring
+    // the rest from the SkPaint.
+    GrPaint grPaint;
+    grPaint.addColorEffect(effect);
+    bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config());
+    if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) {
+        return;
+    }
+
+    fContext->drawRectToRect(grPaint, dstRect, paintRect, NULL);
+}
+
+static bool filter_texture(SkBaseDevice* device, GrContext* context,
+                           GrTexture* texture, SkImageFilter* filter,
+                           int w, int h, const SkMatrix& ctm, SkBitmap* result,
+                           SkIPoint* offset) {
+    SkASSERT(filter);
+    SkDeviceImageFilterProxy proxy(device);
+
+    if (filter->canFilterImageGPU()) {
+        // Save the render target and set it to NULL, so we don't accidentally draw to it in the
+        // filter.  Also set the clip wide open and the matrix to identity.
+        GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
+        return filter->filterImageGPU(&proxy, wrap_texture(texture), ctm, result, offset);
+    } else {
+        return false;
+    }
+}
+
+void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+                             int left, int top, const SkPaint& paint) {
+    // drawSprite is defined to be in device coords.
+    CHECK_SHOULD_DRAW(draw, true);
+
+    SkAutoLockPixels alp(bitmap, !bitmap.getTexture());
+    if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
+        return;
+    }
+
+    int w = bitmap.width();
+    int h = bitmap.height();
+
+    GrTexture* texture;
+    // draw sprite uses the default texture params
+    SkAutoCachedTexture act(this, bitmap, NULL, &texture);
+
+    SkImageFilter* filter = paint.getImageFilter();
+    SkIPoint offset = SkIPoint::Make(left, top);
+    // This bitmap will own the filtered result as a texture.
+    SkBitmap filteredBitmap;
+
+    if (NULL != filter) {
+        SkMatrix matrix(*draw.fMatrix);
+        matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
+        if (filter_texture(this, fContext, texture, filter, w, h, matrix, &filteredBitmap,
+                           &offset)) {
+            texture = (GrTexture*) filteredBitmap.getTexture();
+            w = filteredBitmap.width();
+            h = filteredBitmap.height();
+        } else {
+            return;
+        }
+    }
+
+    GrPaint grPaint;
+    grPaint.addColorTextureEffect(texture, SkMatrix::I());
+
+    if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
+        return;
+    }
+
+    fContext->drawRectToRect(grPaint,
+                             SkRect::MakeXYWH(SkIntToScalar(offset.fX),
+                                              SkIntToScalar(offset.fY),
+                                              SkIntToScalar(w),
+                                              SkIntToScalar(h)),
+                             SkRect::MakeXYWH(0,
+                                              0,
+                                              SK_Scalar1 * w / texture->width(),
+                                              SK_Scalar1 * h / texture->height()));
+}
+
+void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
+                                 const SkRect* src, const SkRect& dst,
+                                 const SkPaint& paint,
+                                 SkCanvas::DrawBitmapRectFlags flags) {
+    SkMatrix    matrix;
+    SkRect      bitmapBounds, tmpSrc;
+
+    bitmapBounds.set(0, 0,
+                     SkIntToScalar(bitmap.width()),
+                     SkIntToScalar(bitmap.height()));
+
+    // Compute matrix from the two rectangles
+    if (NULL != src) {
+        tmpSrc = *src;
+    } else {
+        tmpSrc = bitmapBounds;
+    }
+    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+
+    // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null.
+    if (NULL != src) {
+        if (!bitmapBounds.contains(tmpSrc)) {
+            if (!tmpSrc.intersect(bitmapBounds)) {
+                return; // nothing to draw
+            }
+        }
+    }
+
+    this->drawBitmapCommon(draw, bitmap, &tmpSrc, matrix, paint, flags);
+}
+
+void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
+                             int x, int y, const SkPaint& paint) {
+    // clear of the source device must occur before CHECK_SHOULD_DRAW
+    SkGpuDevice* dev = static_cast<SkGpuDevice*>(device);
+    if (dev->fNeedClear) {
+        // TODO: could check here whether we really need to draw at all
+        dev->clear(0x0);
+    }
+
+    // drawDevice is defined to be in device coords.
+    CHECK_SHOULD_DRAW(draw, true);
+
+    GrRenderTarget* devRT = dev->accessRenderTarget();
+    GrTexture* devTex;
+    if (NULL == (devTex = devRT->asTexture())) {
+        return;
+    }
+
+    const SkBitmap& bm = dev->accessBitmap(false);
+    int w = bm.width();
+    int h = bm.height();
+
+    SkImageFilter* filter = paint.getImageFilter();
+    // This bitmap will own the filtered result as a texture.
+    SkBitmap filteredBitmap;
+
+    if (NULL != filter) {
+        SkIPoint offset = SkIPoint::Make(0, 0);
+        SkMatrix matrix(*draw.fMatrix);
+        matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+        if (filter_texture(this, fContext, devTex, filter, w, h, matrix, &filteredBitmap,
+                           &offset)) {
+            devTex = filteredBitmap.getTexture();
+            w = filteredBitmap.width();
+            h = filteredBitmap.height();
+            x += offset.fX;
+            y += offset.fY;
+        } else {
+            return;
+        }
+    }
+
+    GrPaint grPaint;
+    grPaint.addColorTextureEffect(devTex, SkMatrix::I());
+
+    if (!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
+        return;
+    }
+
+    SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x),
+                                      SkIntToScalar(y),
+                                      SkIntToScalar(w),
+                                      SkIntToScalar(h));
+
+    // The device being drawn may not fill up its texture (e.g. saveLayer uses approximate
+    // scratch texture).
+    SkRect srcRect = SkRect::MakeWH(SK_Scalar1 * w / devTex->width(),
+                                    SK_Scalar1 * h / devTex->height());
+
+    fContext->drawRectToRect(grPaint, dstRect, srcRect);
+}
+
+bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) {
+    return filter->canFilterImageGPU();
+}
+
+bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
+                              const SkMatrix& ctm,
+                              SkBitmap* result, SkIPoint* offset) {
+    // want explicitly our impl, so guard against a subclass of us overriding it
+    if (!this->SkGpuDevice::canHandleImageFilter(filter)) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(src, !src.getTexture());
+    if (!src.getTexture() && !src.readyToDraw()) {
+        return false;
+    }
+
+    GrTexture* texture;
+    // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
+    // must be pushed upstack.
+    SkAutoCachedTexture act(this, src, NULL, &texture);
+
+    return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctm, result,
+                          offset);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// must be in SkCanvas::VertexMode order
+static const GrPrimitiveType gVertexMode2PrimitiveType[] = {
+    kTriangles_GrPrimitiveType,
+    kTriangleStrip_GrPrimitiveType,
+    kTriangleFan_GrPrimitiveType,
+};
+
+void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+                              int vertexCount, const SkPoint vertices[],
+                              const SkPoint texs[], const SkColor colors[],
+                              SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw, false);
+
+    GrPaint grPaint;
+    // we ignore the shader if texs is null.
+    if (NULL == texs) {
+        if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) {
+            return;
+        }
+    } else {
+        if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) {
+            return;
+        }
+    }
+
+    if (NULL != xmode && NULL != texs && NULL != colors) {
+        if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) {
+            SkDebugf("Unsupported vertex-color/texture xfer mode.\n");
+#if 0
+            return
+#endif
+        }
+    }
+
+    SkAutoSTMalloc<128, GrColor> convertedColors(0);
+    if (NULL != colors) {
+        // need to convert byte order and from non-PM to PM
+        convertedColors.reset(vertexCount);
+        for (int i = 0; i < vertexCount; ++i) {
+            convertedColors[i] = SkColor2GrColor(colors[i]);
+        }
+        colors = convertedColors.get();
+    }
+    fContext->drawVertices(grPaint,
+                           gVertexMode2PrimitiveType[vmode],
+                           vertexCount,
+                           (GrPoint*) vertices,
+                           (GrPoint*) texs,
+                           colors,
+                           indices,
+                           indexCount);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void GlyphCacheAuxProc(void* data) {
+    GrFontScaler* scaler = (GrFontScaler*)data;
+    SkSafeUnref(scaler);
+}
+
+static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {
+    void* auxData;
+    GrFontScaler* scaler = NULL;
+    if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
+        scaler = (GrFontScaler*)auxData;
+    }
+    if (NULL == scaler) {
+        scaler = SkNEW_ARGS(SkGrFontScaler, (cache));
+        cache->setAuxProc(GlyphCacheAuxProc, scaler);
+    }
+    return scaler;
+}
+
+static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state,
+                             SkFixed fx, SkFixed fy,
+                             const SkGlyph& glyph) {
+    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+
+    GrSkDrawProcs* procs = static_cast<GrSkDrawProcs*>(state.fDraw->fProcs);
+
+    if (NULL == procs->fFontScaler) {
+        procs->fFontScaler = get_gr_font_scaler(state.fCache);
+    }
+
+    procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed()),
+                                         SkFixedFloorToFixed(fx),
+                                         SkFixedFloorToFixed(fy),
+                                         procs->fFontScaler);
+}
+
+SkDrawProcs* SkGpuDevice::initDrawForText(GrTextContext* context) {
+
+    // deferred allocation
+    if (NULL == fDrawProcs) {
+        fDrawProcs = SkNEW(GrSkDrawProcs);
+        fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;
+        fDrawProcs->fContext = fContext;
+#if SK_DISTANCEFIELD_FONTS
+        fDrawProcs->fFlags = 0;
+        fDrawProcs->fFlags |= SkDrawProcs::kSkipBakedGlyphTransform_Flag;
+        fDrawProcs->fFlags |= SkDrawProcs::kUseScaledGlyphs_Flag;
+#endif
+    }
+
+    // init our (and GL's) state
+    fDrawProcs->fTextContext = context;
+    fDrawProcs->fFontScaler = NULL;
+    return fDrawProcs;
+}
+
+void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
+                          size_t byteLength, SkScalar x, SkScalar y,
+                          const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw, false);
+
+    if (fContext->getMatrix().hasPerspective()) {
+        // this guy will just call our drawPath()
+        draw.drawText((const char*)text, byteLength, x, y, paint);
+    } else {
+        SkDraw myDraw(draw);
+
+        GrPaint grPaint;
+        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+            return;
+        }
+#if SK_DISTANCEFIELD_FONTS
+        GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(),
+                                           paint.getTextSize());
+#else
+        GrBitmapTextContext context(fContext, grPaint, paint.getColor());
+#endif
+        myDraw.fProcs = this->initDrawForText(&context);
+        this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
+    }
+}
+
+void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
+                             size_t byteLength, const SkScalar pos[],
+                             SkScalar constY, int scalarsPerPos,
+                             const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw, false);
+
+    if (fContext->getMatrix().hasPerspective()) {
+        // this guy will just call our drawPath()
+        draw.drawPosText((const char*)text, byteLength, pos, constY,
+                         scalarsPerPos, paint);
+    } else {
+        SkDraw myDraw(draw);
+
+        GrPaint grPaint;
+        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+            return;
+        }
+#if SK_DISTANCEFIELD_FONTS
+        GrDistanceFieldTextContext context(fContext, grPaint, paint.getColor(),
+                                           paint.getTextSize()/SkDrawProcs::kBaseDFFontSize);
+#else
+        GrBitmapTextContext context(fContext, grPaint, paint.getColor());
+#endif
+        myDraw.fProcs = this->initDrawForText(&context);
+        this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
+                                     scalarsPerPos, paint);
+    }
+}
+
+void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,
+                                size_t len, const SkPath& path,
+                                const SkMatrix* m, const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw, false);
+
+    SkASSERT(draw.fDevice == this);
+    draw.drawTextOnPath((const char*)text, len, path, m, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
+    if (!paint.isLCDRenderText()) {
+        // we're cool with the paint as is
+        return false;
+    }
+
+    if (paint.getShader() ||
+        paint.getXfermode() || // unless its srcover
+        paint.getMaskFilter() ||
+        paint.getRasterizer() ||
+        paint.getColorFilter() ||
+        paint.getPathEffect() ||
+        paint.isFakeBoldText() ||
+        paint.getStyle() != SkPaint::kFill_Style) {
+        // turn off lcd
+        flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
+        flags->fHinting = paint.getHinting();
+        return true;
+    }
+    // we're cool with the paint as is
+    return false;
+}
+
+void SkGpuDevice::flush() {
+    DO_DEFERRED_CLEAR();
+    fContext->resolveRenderTarget(fRenderTarget);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBaseDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config,
+                                                    int width, int height,
+                                                    bool isOpaque,
+                                                    Usage usage) {
+    GrTextureDesc desc;
+    desc.fConfig = fRenderTarget->config();
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fSampleCnt = fRenderTarget->numSamples();
+
+    SkAutoTUnref<GrTexture> texture;
+    // Skia's convention is to only clear a device if it is non-opaque.
+    bool needClear = !isOpaque;
+
+#if CACHE_COMPATIBLE_DEVICE_TEXTURES
+    // layers are never draw in repeat modes, so we can request an approx
+    // match and ignore any padding.
+    const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ?
+                                                GrContext::kApprox_ScratchTexMatch :
+                                                GrContext::kExact_ScratchTexMatch;
+    texture.reset(fContext->lockAndRefScratchTexture(desc, match));
+#else
+    texture.reset(fContext->createUncachedTexture(desc, NULL, 0));
+#endif
+    if (NULL != texture.get()) {
+        return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear));
+    } else {
+        GrPrintf("---- failed to create compatible device texture [%d %d]\n", width, height);
+        return NULL;
+    }
+}
+
+SkGpuDevice::SkGpuDevice(GrContext* context,
+                         GrTexture* texture,
+                         bool needClear)
+    : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) {
+
+    SkASSERT(texture && texture->asRenderTarget());
+    // This constructor is called from onCreateCompatibleDevice. It has locked the RT in the texture
+    // cache. We pass true for the third argument so that it will get unlocked.
+    this->initFromRenderTarget(context, texture->asRenderTarget(), true);
+    fNeedClear = needClear;
+}