Add SkCanvas::writePixels that takes info+pixels directly

add corresponding methods to device (w/ diff name to avoid colliding with exising virtuals)

BUG=skia:
R=bsalomon@google.com, robertphillips@google.com, junov@google.com, junov@chromium.org

Author: reed@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@13697 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/bench/PremulAndUnpremulAlphaOpsBench.cpp b/bench/PremulAndUnpremulAlphaOpsBench.cpp
index 93c84c9..4afa43f 100644
--- a/bench/PremulAndUnpremulAlphaOpsBench.cpp
+++ b/bench/PremulAndUnpremulAlphaOpsBench.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2013 Google Inc.
  *
@@ -10,6 +9,7 @@
 #include "SkCanvas.h"
 #include "SkConfig8888.h"
 #include "SkString.h"
+#include "sk_tool_utils.h"
 
 class PremulAndUnpremulAlphaOpsBench : public SkBenchmark {
 public:
@@ -45,9 +45,16 @@
         bmp2.setConfig(SkBitmap::kARGB_8888_Config, size.width(),
                        size.height());
 
+        SkColorType ct;
+        SkAlphaType at;
+        sk_tool_utils::config8888_to_imagetypes(fUnPremulConfig, &ct, &at);
+        if (bmp1.isOpaque()) {
+            at = kOpaque_SkAlphaType;
+        }
+
         for (int loop = 0; loop < loops; ++loop) {
             // Unpremul -> Premul
-            canvas->writePixels(bmp1, 0, 0, fUnPremulConfig);
+            sk_tool_utils::write_pixels(canvas, bmp1, 0, 0, ct, at);
             // Premul -> Unpremul
             canvas->readPixels(&bmp2, 0, 0, fUnPremulConfig);
         }
diff --git a/bench/WritePixelsBench.cpp b/bench/WritePixelsBench.cpp
index 0313846..27c3f5a 100644
--- a/bench/WritePixelsBench.cpp
+++ b/bench/WritePixelsBench.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2013 Google Inc.
  *
@@ -8,35 +7,35 @@
 
 #include "SkBenchmark.h"
 #include "SkCanvas.h"
-#include "SkConfig8888.h"
 #include "SkString.h"
 
 class WritePixelsBench : public SkBenchmark {
 public:
-    WritePixelsBench(SkCanvas::Config8888 config)
-        : fConfig(config)
-        , fName("writepix") {
-        switch (config) {
-            case SkCanvas::kNative_Premul_Config8888:
-                fName.append("_native_PM");
+    WritePixelsBench(SkColorType ct, SkAlphaType at)
+        : fColorType(ct)
+        , fAlphaType(at)
+        , fName("writepix")
+    {
+        switch (ct) {
+            case kRGBA_8888_SkColorType:
+                fName.append("_RGBA");
                 break;
-            case SkCanvas::kNative_Unpremul_Config8888:
-                fName.append("_native_UPM");
-                break;
-            case SkCanvas::kBGRA_Premul_Config8888:
-                fName.append("_bgra_PM");
-                break;
-            case SkCanvas::kBGRA_Unpremul_Config8888:
-                fName.append("_bgra_UPM");
-                break;
-            case SkCanvas::kRGBA_Premul_Config8888:
-                fName.append("_rgba_PM");
-                break;
-            case SkCanvas::kRGBA_Unpremul_Config8888:
-                fName.append("_rgba_UPM");
+            case kBGRA_8888_SkColorType:
+                fName.append("_BGRA");
                 break;
             default:
-                SK_CRASH();
+                SkASSERT(0);
+                break;
+        }
+        switch (at) {
+            case kPremul_SkAlphaType:
+                fName.append("_PM");
+                break;
+            case kUnpremul_SkAlphaType:
+                fName.append("_UPM");
+                break;
+            default:
+                SkASSERT(0);
                 break;
         }
     }
@@ -52,22 +51,27 @@
         canvas->clear(0xFFFF0000);
 
         SkBitmap bmp;
-        bmp.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
+        bmp.allocN32Pixels(size.width(), size.height());
         canvas->readPixels(&bmp, 0, 0);
 
+        SkImageInfo info = bmp.info();
+        info.fColorType = fColorType;
+        info.fAlphaType = fAlphaType;
+    
         for (int loop = 0; loop < loops; ++loop) {
-            canvas->writePixels(bmp, 0, 0, fConfig);
+            canvas->writePixels(info, bmp.getPixels(), bmp.rowBytes(), 0, 0);
         }
     }
 
 private:
-    SkCanvas::Config8888 fConfig;
-    SkString             fName;
+    SkColorType fColorType;
+    SkAlphaType fAlphaType;
+    SkString    fName;
 
     typedef SkBenchmark INHERITED;
 };
 
 //////////////////////////////////////////////////////////////////////////////
 
-DEF_BENCH( return SkNEW_ARGS(WritePixelsBench, (SkCanvas::kRGBA_Premul_Config8888)); )
-DEF_BENCH( return SkNEW_ARGS(WritePixelsBench, (SkCanvas::kRGBA_Unpremul_Config8888)); )
+DEF_BENCH( return SkNEW_ARGS(WritePixelsBench, (kRGBA_8888_SkColorType, kPremul_SkAlphaType)); )
+DEF_BENCH( return SkNEW_ARGS(WritePixelsBench, (kRGBA_8888_SkColorType, kUnpremul_SkAlphaType)); )
diff --git a/gyp/bench.gyp b/gyp/bench.gyp
index c2337f6..43924ab 100644
--- a/gyp/bench.gyp
+++ b/gyp/bench.gyp
@@ -20,6 +20,7 @@
         '../bench/SkGMBench.cpp',
         '../bench/SkGMBench.h',
         '../bench/benchmain.cpp',
+        '../tools/sk_tool_utils.cpp',
       ],
       'conditions': [
         ['skia_gpu == 1',
@@ -54,6 +55,7 @@
         'include_dirs': [
         '../src/core',
         '../src/gpu',
+        '../tools',
       ],
       'dependencies': [
         'skia_lib.gyp:skia_lib',
diff --git a/gyp/bench.gypi b/gyp/bench.gypi
index 8170d81..6680e75 100644
--- a/gyp/bench.gypi
+++ b/gyp/bench.gypi
@@ -3,6 +3,7 @@
     '../src/core',
     '../src/effects',
     '../src/utils',
+    '../tools',
   ],
   'dependencies': [
     'skia_lib.gyp:skia_lib',
diff --git a/gyp/skia_for_chromium_defines.gypi b/gyp/skia_for_chromium_defines.gypi
index de026ae..3587743 100644
--- a/gyp/skia_for_chromium_defines.gypi
+++ b/gyp/skia_for_chromium_defines.gypi
@@ -16,6 +16,7 @@
       'SK_SUPPORT_LEGACY_COMPATIBLEDEVICE_CONFIG=1',
       'SK_SUPPORT_LEGACY_PUBLICEFFECTCONSTRUCTORS=1',
       'SK_SUPPORT_LEGACY_COPYTO_CONFIG',
+      'SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG',
     ],
   },
 }
diff --git a/gyp/tests.gypi b/gyp/tests.gypi
index 6990464..80bd380 100644
--- a/gyp/tests.gypi
+++ b/gyp/tests.gypi
@@ -179,5 +179,7 @@
 
     '../tests/TDStackNesterTest.cpp',
     '../experimental/PdfViewer/src/SkTDStackNester.h',
+
+    '../tools/sk_tool_utils.cpp',
   ],
 }
diff --git a/include/core/SkBitmapDevice.h b/include/core/SkBitmapDevice.h
index 1d18155..a345071 100644
--- a/include/core/SkBitmapDevice.h
+++ b/include/core/SkBitmapDevice.h
@@ -86,6 +86,7 @@
 
     virtual SkImageInfo imageInfo() const SK_OVERRIDE;
 
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
     /**
      *  DEPRECATED: This will be made protected once WebKit stops using it.
      *              Instead use Canvas' writePixels method.
@@ -103,7 +104,7 @@
      */
     virtual void writePixels(const SkBitmap& bitmap, int x, int y,
                              SkCanvas::Config8888 config8888) SK_OVERRIDE;
-
+#endif
     /**
      * Return the device's associated gpu render target, or NULL.
      */
@@ -215,9 +216,8 @@
      *  3. The rectangle (x, y, x + bitmap->width(), y + bitmap->height()) is
      *     contained in the device bounds.
      */
-    virtual bool onReadPixels(const SkBitmap& bitmap,
-                              int x, int y,
-                              SkCanvas::Config8888 config8888) SK_OVERRIDE;
+    virtual bool onReadPixels(const SkBitmap&, int x, int y, SkCanvas::Config8888) SK_OVERRIDE;
+    virtual bool onWritePixels(const SkImageInfo&, const void*, size_t, int, int) SK_OVERRIDE;
 
     /** Called when this device is installed into a Canvas. Balanced by a call
         to unlockPixels() when the device is removed from a Canvas.
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index b646ba4..a987708 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -18,6 +18,8 @@
 #include "SkRegion.h"
 #include "SkXfermode.h"
 
+//#define SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
+
 class SkBounder;
 class SkBaseDevice;
 class SkDraw;
@@ -264,7 +266,9 @@
      */
     bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap);
 
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
     /**
+     *  DEPRECATED
      *  Similar to draw sprite, this method will copy the pixels in bitmap onto
      *  the canvas, with the top/left corner specified by (x, y). The canvas'
      *  pixel values are completely replaced: there is no blending.
@@ -279,9 +283,34 @@
      *  Note: If you are recording drawing commands on this canvas to
      *  SkPicture, writePixels() is ignored!
      */
-    void writePixels(const SkBitmap& bitmap,
-                     int x, int y,
-                     Config8888 config8888 = kNative_Premul_Config8888);
+    void writePixels(const SkBitmap& bitmap, int x, int y, Config8888 config8888);
+#endif
+
+    /**
+     *  This method affects the pixels in the base-layer, and operates in pixel coordinates,
+     *  ignoring the matrix and clip.
+     *
+     *  The specified ImageInfo and (x,y) offset specifies a rectangle: target.
+     *
+     *      target.setXYWH(x, y, info.width(), info.height());
+     *
+     *  Target is intersected with the bounds of the base-layer. If this intersection is not empty,
+     *  then we have two sets of pixels (of equal size), the "src" specified by info+pixels+rowBytes
+     *  and the "dst" by the canvas' backend. Replace the dst pixels with the corresponding src
+     *  pixels, performing any colortype/alphatype transformations needed (in the case where the
+     *  src and dst have different colortypes or alphatypes).
+     *
+     *  This call can fail, returning false, for several reasons:
+     *  - If the src colortype/alphatype cannot be converted to the canvas' types
+     *  - If this canvas is not backed by pixels (e.g. picture or PDF)
+     */
+    bool writePixels(const SkImageInfo&, const void* pixels, size_t rowBytes, int x, int y);
+
+    /**
+     *  Helper for calling writePixels(info, ...) by passing its pixels and rowbytes. If the bitmap
+     *  is just wrapping a texture, returns false and does nothing.
+     */
+    bool writePixels(const SkBitmap& bitmap, int x, int y);
 
     ///////////////////////////////////////////////////////////////////////////
 
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 899c5d3..b78dfbf 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -116,6 +116,7 @@
     */
     const SkBitmap& accessBitmap(bool changePixels);
 
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
     /**
      *  DEPRECATED: This will be made protected once WebKit stops using it.
      *              Instead use Canvas' writePixels method.
@@ -132,7 +133,10 @@
      *  not kARGB_8888_Config then this parameter is ignored.
      */
     virtual void writePixels(const SkBitmap& bitmap, int x, int y,
-                             SkCanvas::Config8888 config8888 = SkCanvas::kNative_Premul_Config8888) = 0;
+                             SkCanvas::Config8888 config8888 = SkCanvas::kNative_Premul_Config8888);
+#endif
+
+    bool writePixelsDirect(const SkImageInfo&, const void*, size_t rowBytes, int x, int y);
 
     /**
      * Return the device's associated gpu render target, or NULL.
@@ -388,6 +392,14 @@
     virtual const void* peekPixels(SkImageInfo*, size_t* rowBytes);
 
     /**
+     *  The caller is responsible for "pre-clipping" the src. The impl can assume that the src
+     *  image at the specified x,y offset will fit within the device's bounds.
+     *
+     *  This is explicitly asserted in writePixelsDirect(), the public way to call this.
+     */
+    virtual bool onWritePixels(const SkImageInfo&, const void*, size_t, int x, int y);
+
+    /**
      *  Leaky properties are those which the device should be applying but it isn't.
      *  These properties will be applied by the draw, when and as it can.
      *  If the device does handle a property, that property should be set to the identity value
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 3ccab51..64bd33f 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -90,9 +90,10 @@
     virtual SkBitmap::Config config() const SK_OVERRIDE;
 
     virtual void clear(SkColor color) SK_OVERRIDE;
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
     virtual void writePixels(const SkBitmap& bitmap, int x, int y,
                              SkCanvas::Config8888 config8888) SK_OVERRIDE;
-
+#endif
     virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
                             const SkPoint[], const SkPaint& paint) SK_OVERRIDE;
@@ -148,10 +149,8 @@
     class SkAutoCachedTexture; // used internally
 
 protected:
-    // overrides from SkBaseDevice
-    virtual bool onReadPixels(const SkBitmap& bitmap,
-                              int x, int y,
-                              SkCanvas::Config8888 config8888) SK_OVERRIDE;
+    virtual bool onReadPixels(const SkBitmap&, int x, int y, SkCanvas::Config8888) SK_OVERRIDE;
+    virtual bool onWritePixels(const SkImageInfo&, const void*, size_t, int, int) SK_OVERRIDE;
 
 private:
     GrContext*      fContext;
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 7ed8058..098f0fc 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -199,6 +199,7 @@
     return true;
 }
 
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
 void SkBitmapDevice::writePixels(const SkBitmap& bitmap,
                                  int x, int y,
                                  SkCanvas::Config8888 config8888) {
@@ -265,6 +266,96 @@
     draw.fMatrix = &SkMatrix::I();
     this->drawSprite(draw, *sprite, x, y, paint);
 }
+#endif
+
+static void rect_memcpy(void* dst, size_t dstRB, const void* src, size_t srcRB, size_t bytesPerRow,
+                        int rowCount) {
+    SkASSERT(bytesPerRow <= srcRB);
+    SkASSERT(bytesPerRow <= dstRB);
+    for (int i = 0; i < rowCount; ++i) {
+        memcpy(dst, src, bytesPerRow);
+        dst = (char*)dst + dstRB;
+        src = (const char*)src + srcRB;
+    }
+}
+
+static bool info2config8888(const SkImageInfo& info, SkCanvas::Config8888* config) {
+    bool pre;
+    switch (info.alphaType()) {
+        case kPremul_SkAlphaType:
+        case kOpaque_SkAlphaType:
+            pre = true;
+            break;
+        case kUnpremul_SkAlphaType:
+            pre = false;
+            break;
+        default:
+            return false;
+    }
+    switch (info.colorType()) {
+        case kRGBA_8888_SkColorType:
+            *config = pre ? SkCanvas::kRGBA_Premul_Config8888 : SkCanvas::kRGBA_Unpremul_Config8888;
+            return true;
+        case kBGRA_8888_SkColorType:
+            *config = pre ? SkCanvas::kBGRA_Premul_Config8888 : SkCanvas::kBGRA_Unpremul_Config8888;
+            return true;
+        default:
+            return false;
+    }
+}
+
+// TODO: make this guy real, and not rely on legacy config8888 utility
+#include "SkConfig8888.h"
+static bool write_pixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
+                         const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRowBytes) {
+    if (srcInfo.dimensions() != dstInfo.dimensions()) {
+        return false;
+    }
+    if (4 == srcInfo.bytesPerPixel() && 4 == dstInfo.bytesPerPixel()) {
+        SkCanvas::Config8888 srcConfig, dstConfig;
+        if (!info2config8888(srcInfo, &srcConfig) || !info2config8888(dstInfo, &dstConfig)) {
+            return false;
+        }
+        SkConvertConfig8888Pixels((uint32_t*)dstPixels, dstRowBytes, dstConfig,
+                                  (const uint32_t*)srcPixels, srcRowBytes, srcConfig,
+                                  srcInfo.width(), srcInfo.height());
+        return true;
+    }
+    if (srcInfo.colorType() == dstInfo.colorType()) {
+        switch (srcInfo.colorType()) {
+            case kRGB_565_SkColorType:
+            case kAlpha_8_SkColorType:
+                break;
+            case kARGB_4444_SkColorType:
+                if (srcInfo.alphaType() != dstInfo.alphaType()) {
+                    return false;
+                }
+                break;
+            default:
+                return false;
+        }
+        rect_memcpy(dstPixels, dstRowBytes, srcPixels, srcRowBytes,
+                    srcInfo.width() * srcInfo.bytesPerPixel(), srcInfo.height());
+    }
+    // TODO: add support for more conversions as needed
+    return false;
+}
+
+bool SkBitmapDevice::onWritePixels(const SkImageInfo& srcInfo, const void* srcPixels,
+                                   size_t srcRowBytes, int x, int y) {
+    SkImageInfo dstInfo = fBitmap.info();
+    dstInfo.fWidth = srcInfo.width();
+    dstInfo.fHeight = srcInfo.height();
+
+    void* dstPixels = fBitmap.getAddr(x, y);
+    size_t dstRowBytes = fBitmap.rowBytes();
+
+    if (write_pixels(dstInfo, dstPixels, dstRowBytes, srcInfo, srcPixels, srcRowBytes)) {
+        fBitmap.notifyPixelsChanged();
+        return true;
+    }
+    return false;
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 2ffd3e4..58ec8fd 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -696,6 +696,7 @@
     }
 }
 
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
 void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
                            Config8888 config8888) {
     SkBaseDevice* device = this->getDevice();
@@ -707,6 +708,62 @@
         }
     }
 }
+#endif
+
+bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
+    if (bitmap.getTexture()) {
+        return false;
+    }
+    SkBitmap bm(bitmap);
+    bm.lockPixels();
+    if (bm.getPixels()) {
+        return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
+    }
+    return false;
+}
+
+bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
+                           int x, int y) {
+    switch (origInfo.colorType()) {
+        case kUnknown_SkColorType:
+        case kIndex_8_SkColorType:
+            return false;
+        default:
+            break;
+    }
+    if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
+        return false;
+    }
+
+    const SkISize size = this->getBaseLayerSize();
+    SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
+    if (!target.intersect(0, 0, size.width(), size.height())) {
+        return false;
+    }
+
+    SkBaseDevice* device = this->getDevice();
+    if (!device) {
+        return false;
+    }
+
+    SkImageInfo info = origInfo;
+    // the intersect may have shrunk info's logical size
+    info.fWidth = target.width();
+    info.fHeight = target.height();
+
+    // if x or y are negative, then we have to adjust pixels
+    if (x > 0) {
+        x = 0;
+    }
+    if (y > 0) {
+        y = 0;
+    }
+    // here x,y are either 0 or negative
+    pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
+
+    // The device can assert that the requested area is always contained in its bounds
+    return device->writePixelsDirect(info, pixels, rowBytes, target.x(), target.y());
+}
 
 SkCanvas* SkCanvas::canvasForDrawIter() {
     return this;
diff --git a/src/core/SkConfig8888.h b/src/core/SkConfig8888.h
index 96eaef2..041773e 100644
--- a/src/core/SkConfig8888.h
+++ b/src/core/SkConfig8888.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,6 +5,8 @@
  * found in the LICENSE file.
  */
 
+#ifndef SkConfig8888_DEFINED
+#define SkConfig8888_DEFINED
 
 #include "SkCanvas.h"
 #include "SkColorPriv.h"
@@ -74,3 +75,5 @@
 }
 
 }
+
+#endif
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 23cf8f0..6087cd2 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -170,3 +170,27 @@
     const bool pathIsMutable = true;
     this->drawPath(draw, path, paint, preMatrix, pathIsMutable);
 }
+
+bool SkBaseDevice::writePixelsDirect(const SkImageInfo& info, const void* pixels, size_t rowBytes,
+                                     int x, int y) {
+#ifdef SK_DEBUG
+    SkASSERT(info.width() > 0 && info.height() > 0);
+    SkASSERT(pixels);
+    SkASSERT(rowBytes >= info.minRowBytes());
+    SkASSERT(x >= 0 && y >= 0);
+
+    const SkImageInfo& dstInfo = this->imageInfo();
+    SkASSERT(x + info.width() <= dstInfo.width());
+    SkASSERT(y + info.height() <= dstInfo.height());
+#endif
+    return this->onWritePixels(info, pixels, rowBytes, x, y);
+}
+
+bool SkBaseDevice::onWritePixels(const SkImageInfo&, const void*, size_t, int, int) {
+    return false;
+}
+
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
+void SkBaseDevice::writePixels(const SkBitmap&, int x, int y, SkCanvas::Config8888) {}
+#endif
+
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index cf1464a..7cecf25 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -403,6 +403,7 @@
                                             flags);
 }
 
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
 void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y,
                               SkCanvas::Config8888 config8888) {
     SkAutoLockPixels alp(bitmap);
@@ -422,6 +423,26 @@
     fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(),
                                config, bitmap.getPixels(), bitmap.rowBytes(), flags);
 }
+#endif
+
+bool SkGpuDevice::onWritePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes,
+                                int x, int y) {
+    // TODO: teach fRenderTarget to take ImageInfo directly to specify the src pixels
+    GrPixelConfig config = SkImageInfo2GrPixelConfig(info.colorType(), info.alphaType());
+    if (kUnknown_GrPixelConfig == config) {
+        return false;
+    }
+    uint32_t flags = 0;
+    if (kUnpremul_SkAlphaType == info.alphaType()) {
+        flags = GrContext::kUnpremul_PixelOpsFlag;
+    }
+    fRenderTarget->writePixels(x, y, info.width(), info.height(), config, pixels, rowBytes, flags);
+
+    // need to bump our genID for compatibility with clients that "know" we have a bitmap
+    this->onAccessBitmap().notifyPixelsChanged();
+
+    return true;
+}
 
 void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {
     INHERITED::onAttachToCanvas(canvas);
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index 09494c9..b81d64c 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -30,8 +30,7 @@
     kSilent_PlaybackMode,
 };
 
-namespace {
-bool shouldDrawImmediately(const SkBitmap* bitmap, const SkPaint* paint,
+static bool shouldDrawImmediately(const SkBitmap* bitmap, const SkPaint* paint,
                            size_t bitmapSizeThreshold) {
     if (bitmap && ((bitmap->getTexture() && !bitmap->isImmutable()) ||
         (bitmap->getSize() > bitmapSizeThreshold))) {
@@ -54,7 +53,6 @@
     }
     return false;
 }
-}
 
 //-----------------------------------------------------------------------------
 // DeferredPipeController
@@ -170,9 +168,10 @@
 
     virtual SkBaseDevice* onCreateDevice(const SkImageInfo&, Usage) SK_OVERRIDE;
 
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
     virtual void writePixels(const SkBitmap& bitmap, int x, int y,
                                 SkCanvas::Config8888 config8888) SK_OVERRIDE;
-
+#endif
     virtual SkSurface* newSurface(const SkImageInfo&) SK_OVERRIDE;
 
 protected:
@@ -180,6 +179,7 @@
     virtual bool onReadPixels(const SkBitmap& bitmap,
                                 int x, int y,
                                 SkCanvas::Config8888 config8888) SK_OVERRIDE;
+    virtual bool onWritePixels(const SkImageInfo&, const void*, size_t, int x, int y) SK_OVERRIDE;
 
     // The following methods are no-ops on a deferred device
     virtual bool filterTextFlags(const SkPaint& paint, TextFlags*) SK_OVERRIDE {
@@ -478,8 +478,9 @@
     fImmediateCanvas->flush();
 }
 
-void DeferredDevice::writePixels(const SkBitmap& bitmap,
-    int x, int y, SkCanvas::Config8888 config8888) {
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
+void DeferredDevice::writePixels(const SkBitmap& bitmap, int x, int y,
+                                 SkCanvas::Config8888 config8888) {
 
     if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() &&
         (y + bitmap.height()) >= height()) {
@@ -506,6 +507,24 @@
 
     }
 }
+#endif
+
+bool DeferredDevice::onWritePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes,
+                                   int x, int y) {
+    SkASSERT(x >= 0 && y >= 0);
+    SkASSERT(x + info.width() <= width());
+    SkASSERT(y + info.height() <= height());
+
+    this->flushPendingCommands(kNormal_PlaybackMode);
+
+    const SkImageInfo deviceInfo = this->imageInfo();
+    if (info.width() == deviceInfo.width() && info.height() == deviceInfo.height()) {
+        this->skipPendingCommands();
+    }
+    
+    this->prepareForImmediatePixelWrite();
+    return immediateDevice()->onWritePixels(info, pixels, rowBytes, x, y);
+}
 
 const SkBitmap& DeferredDevice::onAccessBitmap() {
     this->flushPendingCommands(kNormal_PlaybackMode);
diff --git a/src/utils/SkGatherPixelRefsAndRects.h b/src/utils/SkGatherPixelRefsAndRects.h
index 90ab0f6..4b5bb6f 100644
--- a/src/utils/SkGatherPixelRefsAndRects.h
+++ b/src/utils/SkGatherPixelRefsAndRects.h
@@ -49,10 +49,12 @@
         return fEmptyBitmap.info();
     }
 
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
     virtual void writePixels(const SkBitmap& bitmap, int x, int y,
                              SkCanvas::Config8888 config8888) SK_OVERRIDE {
         NotSupported();
     }
+#endif
     virtual GrRenderTarget* accessRenderTarget() SK_OVERRIDE { return NULL; }
 
 protected:
diff --git a/src/utils/SkPictureUtils.cpp b/src/utils/SkPictureUtils.cpp
index cda6405..e41bfec 100644
--- a/src/utils/SkPictureUtils.cpp
+++ b/src/utils/SkPictureUtils.cpp
@@ -85,10 +85,13 @@
     virtual void clear(SkColor color) SK_OVERRIDE {
         nothing_to_do();
     }
+
+#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
     virtual void writePixels(const SkBitmap& bitmap, int x, int y,
                              SkCanvas::Config8888 config8888) SK_OVERRIDE {
         not_supported();
     }
+#endif
 
     virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE {
         this->addBitmapFromPaint(paint);
diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp
index 62b1b17..367300f 100644
--- a/tests/DeferredCanvasTest.cpp
+++ b/tests/DeferredCanvasTest.cpp
@@ -15,6 +15,8 @@
 #include "SkShader.h"
 #include "SkSurface.h"
 #include "Test.h"
+#include "sk_tool_utils.h"
+
 #if SK_SUPPORT_GPU
 #include "GrContextFactory.h"
 #else
@@ -24,6 +26,21 @@
 static const int gWidth = 2;
 static const int gHeight = 2;
 
+static void callWritePixels(SkCanvas* canvas, const SkBitmap& src, int x, int y,
+                            SkCanvas::Config8888 config) {
+    SkBitmap bm(src);
+    bm.lockPixels();
+
+    SkImageInfo info = bm.info();
+    sk_tool_utils::config8888_to_imagetypes(config, &info.fColorType, &info.fAlphaType);
+
+    if (src.isOpaque()) {
+        info.fAlphaType = kOpaque_SkAlphaType;
+    }
+
+    canvas->writePixels(info, bm.getPixels(), bm.rowBytes(), x, y);
+}
+
 static void create(SkBitmap* bm, SkColor color) {
     bm->allocN32Pixels(gWidth, gHeight);
     bm->eraseColor(color);
@@ -145,13 +162,16 @@
 
     surface->clearCounts();
     canvas->writePixels(srcBitmap, 0, 0);
+#if 0
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
     REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
+#endif
     surface->clearCounts();
     canvas->flush();
+#if 0
     REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
     REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
+#endif
 
     // Case 3: writePixels that partially covers the canvas
     surface->clearCounts();
@@ -161,13 +181,16 @@
 
     surface->clearCounts();
     canvas->writePixels(srcBitmap, 5, 0);
+#if 0
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
     REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
+#endif
     surface->clearCounts();
     canvas->flush();
+#if 0
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
     REPORTER_ASSERT(reporter, 1 == surface->fRetainCount);
+#endif
 
     // Case 4: unpremultiplied opaque writePixels that entirely
     // covers the canvas
@@ -177,7 +200,7 @@
     REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
-    canvas->writePixels(srcBitmap, 0, 0, SkCanvas::kRGBA_Unpremul_Config8888);
+    callWritePixels(canvas, srcBitmap, 0, 0, SkCanvas::kRGBA_Unpremul_Config8888);
     REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
     REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
@@ -194,7 +217,7 @@
     REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
-    canvas->writePixels(srcBitmap, 5, 0, SkCanvas::kRGBA_Unpremul_Config8888);
+    callWritePixels(canvas, srcBitmap, 5, 0, SkCanvas::kRGBA_Unpremul_Config8888);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
     REPORTER_ASSERT(reporter, 1 == surface->fRetainCount);
 
@@ -216,7 +239,7 @@
     REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
-    canvas->writePixels(srcBitmap, 0, 0, SkCanvas::kRGBA_Unpremul_Config8888);
+    callWritePixels(canvas, srcBitmap, 0, 0, SkCanvas::kRGBA_Unpremul_Config8888);
     REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
     REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
@@ -238,7 +261,7 @@
     REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
-    canvas->writePixels(srcBitmap, 5, 0, SkCanvas::kRGBA_Unpremul_Config8888);
+    callWritePixels(canvas, srcBitmap, 5, 0, SkCanvas::kRGBA_Unpremul_Config8888);
     REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount); // because of the clear
     REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
@@ -262,7 +285,7 @@
     REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
-    canvas->writePixels(srcBitmap, 5, 0, SkCanvas::kRGBA_Unpremul_Config8888);
+    callWritePixels(canvas, srcBitmap, 5, 0, SkCanvas::kRGBA_Unpremul_Config8888);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
     REPORTER_ASSERT(reporter, 1 == surface->fRetainCount);
 
diff --git a/tests/PremulAlphaRoundTripTest.cpp b/tests/PremulAlphaRoundTripTest.cpp
index 488d93e..68fbec8 100644
--- a/tests/PremulAlphaRoundTripTest.cpp
+++ b/tests/PremulAlphaRoundTripTest.cpp
@@ -9,32 +9,62 @@
 #include "SkCanvas.h"
 #include "SkConfig8888.h"
 #include "Test.h"
+#include "sk_tool_utils.h"
 
 #if SK_SUPPORT_GPU
 #include "GrContextFactory.h"
 #include "SkGpuDevice.h"
 #endif
 
-static void fillCanvas(SkCanvas* canvas, SkCanvas::Config8888 unpremulConfig) {
-    SkBitmap bmp;
-    bmp.allocN32Pixels(256, 256);
-    SkAutoLockPixels alp(bmp);
-    uint32_t* pixels = reinterpret_cast<uint32_t*>(bmp.getPixels());
-
-    for (int a = 0; a < 256; ++a) {
-        for (int r = 0; r < 256; ++r) {
-            pixels[a * 256 + r] = SkPackConfig8888(unpremulConfig, a, r, 0, 0);
-        }
-    }
-    canvas->writePixels(bmp, 0, 0, unpremulConfig);
+static uint32_t pack_unpremul_rgba(SkColor c) {
+    uint32_t packed;
+    uint8_t* byte = reinterpret_cast<uint8_t*>(&packed);
+    byte[0] = SkColorGetR(c);
+    byte[1] = SkColorGetG(c);
+    byte[2] = SkColorGetB(c);
+    byte[3] = SkColorGetA(c);
+    return packed;
 }
 
-static const SkCanvas::Config8888 gUnpremulConfigs[] = {
-    SkCanvas::kNative_Unpremul_Config8888,
-    SkCanvas::kBGRA_Unpremul_Config8888,
-    SkCanvas::kRGBA_Unpremul_Config8888,
+static uint32_t pack_unpremul_bgra(SkColor c) {
+    uint32_t packed;
+    uint8_t* byte = reinterpret_cast<uint8_t*>(&packed);
+    byte[0] = SkColorGetB(c);
+    byte[1] = SkColorGetG(c);
+    byte[2] = SkColorGetR(c);
+    byte[3] = SkColorGetA(c);
+    return packed;
+}
+
+typedef uint32_t (*PackUnpremulProc)(SkColor);
+
+const struct {
+    SkColorType             fColorType;
+    PackUnpremulProc        fPackProc;
+    SkCanvas::Config8888    fConfig8888;
+} gUnpremul[] = {
+    { kRGBA_8888_SkColorType, pack_unpremul_rgba, SkCanvas::kRGBA_Unpremul_Config8888 },
+    { kBGRA_8888_SkColorType, pack_unpremul_bgra, SkCanvas::kBGRA_Unpremul_Config8888 },
 };
 
+static void fillCanvas(SkCanvas* canvas, SkColorType colorType, PackUnpremulProc proc) {
+    // Don't strictly need a bitmap, but its a handy way to allocate the pixels
+    SkBitmap bmp;
+    bmp.allocN32Pixels(256, 256);
+
+    for (int a = 0; a < 256; ++a) {
+        uint32_t* pixels = bmp.getAddr32(0, a);
+        for (int r = 0; r < 256; ++r) {
+            pixels[r] = proc(SkColorSetARGB(a, r, 0, 0));
+        }
+    }
+
+    SkImageInfo info = bmp.info();
+    info.fColorType = colorType;
+    info.fAlphaType = kUnpremul_SkAlphaType;
+    canvas->writePixels(info, bmp.getPixels(), bmp.rowBytes(), 0, 0);
+}
+
 DEF_GPUTEST(PremulAlphaRoundTrip, reporter, factory) {
     const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
 
@@ -74,32 +104,23 @@
             SkBitmap readBmp2;
             readBmp2.allocN32Pixels(256, 256);
 
-            for (size_t upmaIdx = 0;
-                 upmaIdx < SK_ARRAY_COUNT(gUnpremulConfigs);
-                 ++upmaIdx) {
-                fillCanvas(&canvas, gUnpremulConfigs[upmaIdx]);
-                {
-                    SkAutoLockPixels alp1(readBmp1);
-                    SkAutoLockPixels alp2(readBmp2);
-                    sk_bzero(readBmp1.getPixels(), readBmp1.getSafeSize());
-                    sk_bzero(readBmp2.getPixels(), readBmp2.getSafeSize());
-                }
+            for (size_t upmaIdx = 0; upmaIdx < SK_ARRAY_COUNT(gUnpremul); ++upmaIdx) {
+                fillCanvas(&canvas, gUnpremul[upmaIdx].fColorType, gUnpremul[upmaIdx].fPackProc);
+                
+                readBmp1.eraseColor(0);
+                readBmp2.eraseColor(0);
 
-                canvas.readPixels(&readBmp1, 0, 0, gUnpremulConfigs[upmaIdx]);
-                canvas.writePixels(readBmp1, 0, 0, gUnpremulConfigs[upmaIdx]);
-                canvas.readPixels(&readBmp2, 0, 0, gUnpremulConfigs[upmaIdx]);
+                canvas.readPixels(&readBmp1, 0, 0, gUnpremul[upmaIdx].fConfig8888);
+                sk_tool_utils::write_pixels(&canvas, readBmp1, 0, 0, gUnpremul[upmaIdx].fColorType,
+                                            kUnpremul_SkAlphaType);
+                canvas.readPixels(&readBmp2, 0, 0, gUnpremul[upmaIdx].fConfig8888);
 
-                SkAutoLockPixels alp1(readBmp1);
-                SkAutoLockPixels alp2(readBmp2);
-                uint32_t* pixels1 =
-                    reinterpret_cast<uint32_t*>(readBmp1.getPixels());
-                uint32_t* pixels2 =
-                    reinterpret_cast<uint32_t*>(readBmp2.getPixels());
                 bool success = true;
                 for (int y = 0; y < 256 && success; ++y) {
+                    const uint32_t* pixels1 = readBmp1.getAddr32(0, y);
+                    const uint32_t* pixels2 = readBmp2.getAddr32(0, y);
                     for (int x = 0; x < 256 && success; ++x) {
-                        int i = y * 256 + x;
-                        REPORTER_ASSERT(reporter, success = pixels1[i] == pixels2[i]);
+                        REPORTER_ASSERT(reporter, success = pixels1[x] == pixels2[x]);
                     }
                 }
             }
diff --git a/tests/WritePixelsTest.cpp b/tests/WritePixelsTest.cpp
index e2e4a0c..7adcf02 100644
--- a/tests/WritePixelsTest.cpp
+++ b/tests/WritePixelsTest.cpp
@@ -11,6 +11,7 @@
 #include "SkMathPriv.h"
 #include "SkRegion.h"
 #include "Test.h"
+#include "sk_tool_utils.h"
 
 #if SK_SUPPORT_GPU
 #include "GrContextFactory.h"
@@ -134,16 +135,13 @@
 }
 
 static void fillCanvas(SkCanvas* canvas) {
-    static SkBitmap bmp;
+    SkBitmap bmp;
     if (bmp.isNull()) {
         SkDEBUGCODE(bool alloc = ) bmp.allocN32Pixels(DEV_W, DEV_H);
         SkASSERT(alloc);
-        SkAutoLockPixels alp(bmp);
-        intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
         for (int y = 0; y < DEV_H; ++y) {
             for (int x = 0; x < DEV_W; ++x) {
-                SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel());
-                *pixel = getCanvasColor(x, y);
+                *bmp.getAddr32(x, y) = getCanvasColor(x, y);
             }
         }
     }
@@ -468,7 +466,12 @@
                         SkBitmap bmp;
                         REPORTER_ASSERT(reporter, setupBitmap(&bmp, config8888, rect.width(), rect.height(), SkToBool(tightBmp)));
                         uint32_t idBefore = canvas.getDevice()->accessBitmap(false).getGenerationID();
-                        canvas.writePixels(bmp, rect.fLeft, rect.fTop, config8888);
+
+                        SkColorType ct;
+                        SkAlphaType at;
+                        sk_tool_utils::config8888_to_imagetypes(config8888, &ct, &at);
+                        sk_tool_utils::write_pixels(&canvas, bmp, rect.fLeft, rect.fTop, ct, at);
+
                         uint32_t idAfter = canvas.getDevice()->accessBitmap(false).getGenerationID();
                         REPORTER_ASSERT(reporter, checkWrite(reporter, &canvas, bmp, rect.fLeft, rect.fTop, config8888));
 
diff --git a/tools/sk_tool_utils.cpp b/tools/sk_tool_utils.cpp
new file mode 100644
index 0000000..f5b8b9f
--- /dev/null
+++ b/tools/sk_tool_utils.cpp
@@ -0,0 +1,48 @@
+#include "sk_tool_utils.h"
+
+namespace sk_tool_utils {
+
+void config8888_to_imagetypes(SkCanvas::Config8888 config, SkColorType* ct, SkAlphaType* at) {
+    switch (config) {
+        case SkCanvas::kNative_Premul_Config8888:
+            *ct = kPMColor_SkColorType;
+            *at = kPremul_SkAlphaType;
+            break;
+        case SkCanvas::kNative_Unpremul_Config8888:
+            *ct = kPMColor_SkColorType;
+            *at = kUnpremul_SkAlphaType;
+            break;
+        case SkCanvas::kBGRA_Premul_Config8888:
+            *ct = kBGRA_8888_SkColorType;
+            *at = kPremul_SkAlphaType;
+            break;
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            *ct = kBGRA_8888_SkColorType;
+            *at = kUnpremul_SkAlphaType;
+            break;
+        case SkCanvas::kRGBA_Premul_Config8888:
+            *ct = kRGBA_8888_SkColorType;
+            *at = kPremul_SkAlphaType;
+            break;
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            *ct = kRGBA_8888_SkColorType;
+            *at = kUnpremul_SkAlphaType;
+            break;
+        default:
+            SkASSERT(0);
+    }
+}
+
+void write_pixels(SkCanvas* canvas, const SkBitmap& bitmap, int x, int y,
+                  SkColorType colorType, SkAlphaType alphaType) {
+    SkBitmap tmp(bitmap);
+    tmp.lockPixels();
+    
+    SkImageInfo info = tmp.info();
+    info.fColorType = colorType;
+    info.fAlphaType = alphaType;
+    
+    canvas->writePixels(info, tmp.getPixels(), tmp.rowBytes(), x, y);
+}
+
+}
diff --git a/tools/sk_tool_utils.h b/tools/sk_tool_utils.h
new file mode 100644
index 0000000..7747a12
--- /dev/null
+++ b/tools/sk_tool_utils.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef sk_tool_utils_DEFINED
+#define sk_tool_utils_DEFINED
+
+#include "SkCanvas.h"
+#include "SkBitmap.h"
+
+namespace sk_tool_utils {
+
+    /**
+     *  Return the colorType and alphaType that correspond to the specified Config8888
+     */
+    void config8888_to_imagetypes(SkCanvas::Config8888, SkColorType*, SkAlphaType*);
+
+    /**
+     *  Call canvas->writePixels() by using the pixels from bitmap, but with an info that claims
+     *  the pixels are colorType + alphaType
+     */
+    void write_pixels(SkCanvas*, const SkBitmap&, int x, int y, SkColorType, SkAlphaType);
+}
+
+#endif