Make all pixel ops go thru ctx so we can correctly flush. Unify two texture upload code paths. 

Review URL: http://codereview.appspot.com/5373108/



git-svn-id: http://skia.googlecode.com/svn/trunk@2701 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/texdata.cpp b/gm/texdata.cpp
index a55bdeb..d3cc83c 100644
--- a/gm/texdata.cpp
+++ b/gm/texdata.cpp
@@ -124,9 +124,9 @@
                             ((x + y) % 2) ? (i ? green : red) : blue;
                     }
                 }
-                // BUG: uploadTextureData doesn't force a flush
-                ctx->flush();
-                texture->uploadTextureData(S, i ? 0 : S, S, S, gTextureData, 4 * stride);
+                texture->writePixels(S, (i ? 0 : S), S, S,
+                                     texture->config(), gTextureData,
+                                     4 * stride);
                 ctx->drawRect(paint, GrRect::MakeWH(2*S, 2*S));
             }
         }
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 4edb187..6bdd37d 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -445,10 +445,10 @@
      *                          FlushBits.
      */
     void flush(int flagsBitfield = 0);
-    
+
     /**
      * Reads a rectangle of pixels from a render target.
-     * @param renderTarget  the render target to read from. NULL means the
+     * @param target        the render target to read from. NULL means the
      *                      current render target.
      * @param left          left edge of the rectangle to read (inclusive)
      * @param top           top edge of the rectangle to read (inclusive)
@@ -456,42 +456,88 @@
      * @param height        height of rectangle to read in pixels.
      * @param config        the pixel config of the destination buffer
      * @param buffer        memory to read the rectangle into.
-     * @param rowBytes      number of bytes bewtween consecueive rows. Zero
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
      *                      means rows are tightly packed.
      *
      * @return true if the read succeeded, false if not. The read can fail
-     *              because of a unsupported pixel config or because no render
+     *              because of an unsupported pixel config or because no render
      *              target is currently set.
      */
     bool readRenderTargetPixels(GrRenderTarget* target,
                                 int left, int top, int width, int height,
                                 GrPixelConfig config, void* buffer, 
-                                size_t rowBytes = 0);
+                                size_t rowBytes) {
+        return this->internalReadRenderTargetPixels(target, left, top,
+                                                    width, height,
+                                                    config, buffer,
+                                                    rowBytes, 0);
+    }
+
+    /**
+     * Copy the src pixels [buffer, rowbytes, pixelconfig] into a render target
+     * at the specified rectangle.
+     * @param target        the render target to write into. NULL means the
+     *                      current render target.
+     * @param left          left edge of the rectangle to write (inclusive)
+     * @param top           top edge of the rectangle to write (inclusive)
+     * @param width         width of rectangle to write in pixels.
+     * @param height        height of rectangle to write in pixels.
+     * @param config        the pixel config of the source buffer
+     * @param buffer        memory to read the rectangle from.
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
+     *                      means rows are tightly packed.
+     */
+    void writeRenderTargetPixels(GrRenderTarget* target,
+                                 int left, int top, int width, int height,
+                                 GrPixelConfig config, const void* buffer,
+                                 size_t rowBytes) {
+        this->internalWriteRenderTargetPixels(target, left, top, width, height,
+                                              config, buffer, rowBytes, 0);
+    }
 
     /**
      * Reads a rectangle of pixels from a texture.
-     * @param texture       the render target to read from.
+     * @param texture       the texture to read from.
      * @param left          left edge of the rectangle to read (inclusive)
      * @param top           top edge of the rectangle to read (inclusive)
      * @param width         width of rectangle to read in pixels.
      * @param height        height of rectangle to read in pixels.
      * @param config        the pixel config of the destination buffer
      * @param buffer        memory to read the rectangle into.
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
+     *                      means rows are tightly packed.
      *
      * @return true if the read succeeded, false if not. The read can fail
-     *              because of a unsupported pixel config.
+     *              because of an unsupported pixel config.
      */
-    bool readTexturePixels(GrTexture* target,
+    bool readTexturePixels(GrTexture* texture,
                            int left, int top, int width, int height,
-                           GrPixelConfig config, void* buffer);
+                           GrPixelConfig config, void* buffer,
+                           size_t rowBytes) {
+        return this->internalReadTexturePixels(texture, left, top,
+                                               width, height,
+                                               config, buffer, rowBytes, 0);
+    }
 
     /**
-     *  Copy the src pixels [buffer, stride, pixelconfig] into the current
-     *  render-target at the specified rectangle.
+     * Writes a rectangle of pixels to a texture.
+     * @param texture       the render target to read from.
+     * @param left          left edge of the rectangle to write (inclusive)
+     * @param top           top edge of the rectangle to write (inclusive)
+     * @param width         width of rectangle to write in pixels.
+     * @param height        height of rectangle to write in pixels.
+     * @param config        the pixel config of the source buffer
+     * @param buffer        memory to read pixels from
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
+     *                      means rows are tightly packed.
      */
-    void writePixels(int left, int top, int width, int height,
-                     GrPixelConfig, const void* buffer, size_t stride);
-
+    void writeTexturePixels(GrTexture* texture,
+                            int left, int top, int width, int height,
+                            GrPixelConfig config, const void* buffer,
+                            size_t rowBytes) {
+        this->internalWriteTexturePixels(texture, left, top, width, height, 
+                                         config, buffer, rowBytes, 0);
+    }
     /**
      * Applies a 1D convolution kernel in the X direction to a rectangle of
      * pixels from a given texture.
@@ -654,7 +700,43 @@
                   float imageIncrement[2],
                   const float* kernel,
                   int kernelWidth);
-    
+
+    /**
+     * Flags to the internal read/write pixels funcs
+     */
+    enum PixelOpsFlags {
+        kDontFlush_PixelOpsFlag = 0x1,
+    };
+
+    bool internalReadRenderTargetPixels(GrRenderTarget* target,
+                                        int left, int top,
+                                        int width, int height,
+                                        GrPixelConfig config, void* buffer, 
+                                        size_t rowBytes, uint32_t flags);
+
+    void internalWriteRenderTargetPixels(GrRenderTarget* target,
+                                        int left, int top,
+                                        int width, int height,
+                                        GrPixelConfig, const void* buffer,
+                                        size_t rowBytes, uint32_t flags);
+
+    bool internalReadTexturePixels(GrTexture* texture,
+                                   int left, int top,
+                                   int width, int height,
+                                   GrPixelConfig config, void* buffer,
+                                   size_t rowBytes, uint32_t flags);
+
+    void internalWriteTexturePixels(GrTexture* texture,
+                                    int left, int top,
+                                    int width, int height,
+                                    GrPixelConfig config, const void* buffer,
+                                    size_t rowBytes, uint32_t flags);
+    // needed for access to internalWriteTexturePixels. TODO: make GrContext
+    // be a facade for an internal class. Then functions that are public on the 
+    // internal class would have only be callable in src/gpu. The facade would
+    // only have to functions necessary for clients.
+    friend class GrAtlas;
+
     // computes vertex layout bits based on the paint. If paint expresses
     // a texture for a stage, the stage coords will be bound to postitions
     // unless hasTexCoords[s]==true in which case stage s's input coords
diff --git a/include/gpu/GrRenderTarget.h b/include/gpu/GrRenderTarget.h
index 1b11aee..13b2160 100644
--- a/include/gpu/GrRenderTarget.h
+++ b/include/gpu/GrRenderTarget.h
@@ -123,12 +123,29 @@
      * @param height        height of rectangle to read in pixels.
      * @param config        the pixel config of the destination buffer
      * @param buffer        memory to read the rectangle into.
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
+     *                      means rows are tightly packed.
      *
      * @return true if the read succeeded, false if not. The read can fail
-     *              because of a unsupported pixel config.
+     *              because of an unsupported pixel config.
      */
     bool readPixels(int left, int top, int width, int height,
-                    GrPixelConfig config, void* buffer);
+                    GrPixelConfig config, void* buffer, size_t rowBytes);
+
+    /**
+     * Copy the src pixels [buffer, rowbytes, pixelconfig] into the render
+     * target at the specified rectangle.
+     * @param left          left edge of the rectangle to write (inclusive)
+     * @param top           top edge of the rectangle to write (inclusive)
+     * @param width         width of rectangle to write in pixels.
+     * @param height        height of rectangle to write in pixels.
+     * @param config        the pixel config of the source buffer
+     * @param buffer        memory to read the rectangle from.
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
+     *                      means rows are tightly packed.
+     */
+    void writePixels(int left, int top, int width, int height,
+                     GrPixelConfig config, const void* buffer, size_t rowBytes);
 
     // a MSAA RT may require explicit resolving , it may auto-resolve (e.g. FBO
     // 0 in GL), or be unresolvable because the client didn't give us the 
diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h
index 0192688..8274140 100644
--- a/include/gpu/GrTexture.h
+++ b/include/gpu/GrTexture.h
@@ -54,38 +54,37 @@
     }
 
     /**
-     * Updates a subrectangle of texels in the texture.
-     *
-     * @param x         left edge of rectangle to update
-     * @param y         top edge of rectangle to update
-     * @param width     width of rectangle to update
-     * @param height    height of rectangle to update
-     * @param srcData   width*height texels of data in same format that was
-     *                  used at texture creation.
-     * @param rowBytes  number of bytes per row in srcData, 0 means rows are 
-     *                  packed
-     */
-    virtual void uploadTextureData(int x,
-                                   int y,
-                                   int width,
-                                   int height,
-                                   const void* srcData,
-                                   size_t rowBytes) = 0;
-
-    /**
-     * Reads a rectangle of pixels from the texture.
+     * Read a rectangle of pixels from the texture.
      * @param left          left edge of the rectangle to read (inclusive)
      * @param top           top edge of the rectangle to read (inclusive)
      * @param width         width of rectangle to read in pixels.
      * @param height        height of rectangle to read in pixels.
      * @param config        the pixel config of the destination buffer
      * @param buffer        memory to read the rectangle into.
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
+     *                      means rows are tightly packed.
      *
      * @return true if the read succeeded, false if not. The read can fail
      *              because of a unsupported pixel config.
      */
     bool readPixels(int left, int top, int width, int height,
-                    GrPixelConfig config, void* buffer);
+                    GrPixelConfig config, void* buffer,
+                    size_t rowBytes);
+
+    /**
+     * Writes a rectangle of pixels to the texture.
+     * @param left          left edge of the rectangle to write (inclusive)
+     * @param top           top edge of the rectangle to write (inclusive)
+     * @param width         width of rectangle to write in pixels.
+     * @param height        height of rectangle to write in pixels.
+     * @param config        the pixel config of the source buffer
+     * @param buffer        memory to read pixels from
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
+     *                      means rows are tightly packed.
+     */
+    void writePixels(int left, int top, int width, int height,
+                     GrPixelConfig config, const void* buffer,
+                     size_t rowBytes);
 
     /**
      * Retrieves the render target underlying this texture that can be passed to
diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp
index ffe56b1..cc69578 100644
--- a/samplecode/SampleApp.cpp
+++ b/samplecode/SampleApp.cpp
@@ -177,10 +177,10 @@
                 // need to send the raster bits to the (gpu) window
                 fGrContext->setRenderTarget(fGrRenderTarget);
                 const SkBitmap& bm = win->getBitmap();
-                fGrContext->writePixels(0, 0, bm.width(), bm.height(),
-                                        kSkia8888_PM_GrPixelConfig,
-                                        bm.getPixels(),
-                                        bm.rowBytes());
+                fGrRenderTarget->writePixels(0, 0, bm.width(), bm.height(),
+                                             kSkia8888_PM_GrPixelConfig,
+                                             bm.getPixels(),
+                                             bm.rowBytes());
             }
         }
         win->presentGL();
@@ -211,7 +211,7 @@
             desc.fStencilBits = 8;
             desc.fSampleCnt = 0;
             desc.fRenderTargetHandle = 0;
-            fGrRenderTarget = fNullGrContext->createPlatformRenderTarget(desc);
+            fNullGrRenderTarget = fNullGrContext->createPlatformRenderTarget(desc);
         }
     }
 
diff --git a/src/gpu/GrAtlas.cpp b/src/gpu/GrAtlas.cpp
index 7b88498..5c95153 100644
--- a/src/gpu/GrAtlas.cpp
+++ b/src/gpu/GrAtlas.cpp
@@ -9,6 +9,7 @@
 
 
 #include "GrAtlas.h"
+#include "GrContext.h"
 #include "GrGpu.h"
 #include "GrRectanizer.h"
 #include "GrPlotMgr.h"
@@ -109,7 +110,14 @@
         image = storage.get();
     }
     adjustForPlot(loc, fPlot);
-    fTexture->uploadTextureData(loc->fX, loc->fY, dstW, dstH, image, 0);
+    GrContext* context = fTexture->getContext();
+    // We call the internal version so that we don't force a flush. We assume
+    // our caller is smart and hasn't referenced the part of the texture we're
+    // about to update since the last flush.
+    context->internalWriteTexturePixels(fTexture, loc->fX, loc->fY,
+                                        dstW, dstH, fTexture->config(),
+                                        image, 0,
+                                        GrContext::kDontFlush_PixelOpsFlag);
 
     // now tell the caller to skip the top/left BORDER
     loc->fX += BORDER;
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 47a2a6b..fe85b97 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -1637,28 +1637,59 @@
 #endif
 }
 
-bool GrContext::readTexturePixels(GrTexture* texture,
-                                  int left, int top, int width, int height,
-                                  GrPixelConfig config, void* buffer) {
+void GrContext::internalWriteTexturePixels(GrTexture* texture,
+                                           int left, int top,
+                                           int width, int height,
+                                           GrPixelConfig config,
+                                           const void* buffer,
+                                           size_t rowBytes,
+                                           uint32_t flags) {
+    SK_TRACE_EVENT0("GrContext::writeTexturePixels");
+    if (!(kDontFlush_PixelOpsFlag & flags)) {
+        this->flush();
+    }
+    // TODO: use scratch texture to perform conversion
+    if (GrPixelConfigIsUnpremultiplied(texture->config()) !=
+        GrPixelConfigIsUnpremultiplied(config)) {
+        return;
+    }
+
+    fGpu->writeTexturePixels(texture, left, top, width, height, 
+                             config, buffer, rowBytes);
+}
+
+bool GrContext::internalReadTexturePixels(GrTexture* texture,
+                                          int left, int top,
+                                          int width, int height,
+                                          GrPixelConfig config,
+                                          void* buffer,
+                                          size_t rowBytes,
+                                          uint32_t flags) {
     SK_TRACE_EVENT0("GrContext::readTexturePixels");
 
     // TODO: code read pixels for textures that aren't rendertargets
 
-    this->flush();
+    if (!(kDontFlush_PixelOpsFlag & flags)) {
+        this->flush();
+    }
     GrRenderTarget* target = texture->asRenderTarget();
     if (NULL != target) {
-        return this->readRenderTargetPixels(target,
-                                            left, top, width, height, 
-                                            config, buffer, 0);
+        return this->internalReadRenderTargetPixels(target,
+                                                    left, top, width, height,
+                                                    config, buffer, rowBytes,
+                                                    flags);
     } else {
         return false;
     }
 }
 
-bool GrContext::readRenderTargetPixels(GrRenderTarget* target,
-                                       int left, int top, int width, int height,
-                                       GrPixelConfig config, void* buffer,
-                                       size_t rowBytes) {
+bool GrContext::internalReadRenderTargetPixels(GrRenderTarget* target,
+                                               int left, int top,
+                                               int width, int height,
+                                               GrPixelConfig config,
+                                               void* buffer,
+                                               size_t rowBytes,
+                                               uint32_t flags) {
     SK_TRACE_EVENT0("GrContext::readRenderTargetPixels");
     if (NULL == target) { 
         target = fGpu->getRenderTarget();
@@ -1675,7 +1706,9 @@
         return false;
     }
 
-    this->flush();
+    if (!(kDontFlush_PixelOpsFlag & flags)) {
+        this->flush();
+    }
 
     GrTexture* src = target->asTexture();
     bool swapRAndB = NULL != src &&
@@ -1751,16 +1784,25 @@
                             config, buffer, rowBytes, flipY);
 }
 
-void GrContext::writePixels(int left, int top, int width, int height,
-                            GrPixelConfig config, const void* buffer,
-                            size_t stride) {
-    SK_TRACE_EVENT0("GrContext::writePixels");
+void GrContext::internalWriteRenderTargetPixels(GrRenderTarget* target, 
+                                                int left, int top,
+                                                int width, int height,
+                                                GrPixelConfig config,
+                                                const void* buffer,
+                                                size_t rowBytes,
+                                                uint32_t flags) {
+    SK_TRACE_EVENT0("GrContext::writeRenderTargetPixels");
+
+    if (NULL == target) { 
+        target = fGpu->getRenderTarget();
+        if (NULL == target) {
+            return;
+        }
+    }
 
     // TODO: when underlying api has a direct way to do this we should use it
     // (e.g. glDrawPixels on desktop GL).
 
-    this->flush(kForceCurrentRenderTarget_FlushBit);
-
     const GrTextureDesc desc = {
         kNone_GrTextureFlags, kNone_GrAALevel, width, height, { config }
     };
@@ -1769,7 +1811,8 @@
     if (NULL == texture) {
         return;
     }
-    texture->uploadTextureData(0, 0, width, height, buffer, stride);
+    this->internalWriteTexturePixels(texture, 0, 0, width, height,
+                                     config, buffer, rowBytes, flags);
 
     GrDrawTarget::AutoStateRestore  asr(fGpu);
     reset_target_state(fGpu);
@@ -1777,7 +1820,7 @@
     GrMatrix matrix;
     matrix.setTranslate(GrIntToScalar(left), GrIntToScalar(top));
     fGpu->setViewMatrix(matrix);
-
+    fGpu->setRenderTarget(target);
     fGpu->setTexture(0, texture);
 
     GrSamplerState sampler;
diff --git a/src/gpu/GrGLTexture.cpp b/src/gpu/GrGLTexture.cpp
index 6e93b6c..327f7cf 100644
--- a/src/gpu/GrGLTexture.cpp
+++ b/src/gpu/GrGLTexture.cpp
@@ -35,8 +35,7 @@
     fTexIDObj           = new GrGLTexID(GPUGL->glInterface(),
                                         textureDesc.fTextureID,
                                         textureDesc.fOwnsID);
-    fUploadFormat       = textureDesc.fUploadFormat;
-    fUploadType         = textureDesc.fUploadType;
+    fInternalFormat     = textureDesc.fInternalFormat;
     fOrientation        = textureDesc.fOrientation;
 
     if (NULL != rtDesc) {
@@ -86,83 +85,6 @@
     }
 }
 
-void GrGLTexture::uploadTextureData(int x,
-                                    int y,
-                                    int width,
-                                    int height,
-                                    const void* srcData,
-                                    size_t rowBytes) {
-    GrIRect bounds = GrIRect::MakeWH(this->width(), this->height());
-    GrIRect subrect = GrIRect::MakeXYWH(x,y,width, height);
-    if (!bounds.contains(subrect)) {
-        return;
-    }
-    GPUGL->setSpareTextureUnit();
-
-    // ES2 glCompressedTexSubImage2D doesn't support any formats
-    // (at least without extensions)
-    GrAssert(fUploadFormat != GR_GL_PALETTE8_RGBA8);
-
-    // in case we need a temporary, trimmed copy of the src pixels
-    SkAutoSMalloc<128 * 128> tempStorage;
-
-    size_t bpp = GrBytesPerPixel(this->config());
-    size_t trimRowBytes = width * bpp;
-    if (!rowBytes) {
-        rowBytes = trimRowBytes;
-    }
-    /*
-     *  check whether to allocate a temporary buffer for flipping y or
-     *  because our srcData has extra bytes past each row. If so, we need
-     *  to trim those off here, since GL ES may not let us specify
-     *  GL_UNPACK_ROW_LENGTH.
-     */
-    bool restoreGLRowLength = false;
-    bool flipY = kBottomUp_Orientation == fOrientation;
-    if (GPUGL->glCaps().fUnpackRowLengthSupport && !flipY) {
-        // can't use this for flipping, only non-neg values allowed. :(
-        if (srcData && rowBytes != trimRowBytes) {
-            GrGLint rowLength = static_cast<GrGLint>(rowBytes / bpp);
-            GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength));
-            restoreGLRowLength = true;
-        }
-    } else {
-        if (srcData && (trimRowBytes != rowBytes || flipY)) {
-            // copy the data into our new storage, skipping the trailing bytes
-            size_t trimSize = height * trimRowBytes;
-            const char* src = (const char*)srcData;
-            if (flipY) {
-                src += (height - 1) * rowBytes;
-            }
-            char* dst = (char*)tempStorage.reset(trimSize);
-            for (int y = 0; y < height; y++) {
-                memcpy(dst, src, trimRowBytes);
-                if (flipY) {
-                    src -= rowBytes;
-                } else {
-                    src += rowBytes;
-                }
-                dst += trimRowBytes;
-            }
-            // now point srcData to our copied version
-            srcData = tempStorage.get();
-        }
-    }
-
-    if (flipY) {
-        y = this->height() - (y + height);
-    }
-    GL_CALL(BindTexture(GR_GL_TEXTURE_2D, fTexIDObj->id()));
-    GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, static_cast<GrGLint>(bpp)));
-    GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D, 0, x, y, width, height,
-                          fUploadFormat, fUploadType, srcData));
-
-    if (restoreGLRowLength) {
-        GrAssert(GPUGL->glCaps().fUnpackRowLengthSupport);
-        GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
-    }
-}
-
 intptr_t GrGLTexture::getTextureHandle() const {
     return fTexIDObj->id();
 }
diff --git a/src/gpu/GrGLTexture.h b/src/gpu/GrGLTexture.h
index cedac5a..b023bc6 100644
--- a/src/gpu/GrGLTexture.h
+++ b/src/gpu/GrGLTexture.h
@@ -64,9 +64,8 @@
         int             fHeight;
         GrPixelConfig   fConfig;
         GrGLuint        fTextureID;
+        GrGLenum        fInternalFormat;
         bool            fOwnsID;
-        GrGLenum        fUploadFormat;
-        GrGLenum        fUploadType;
         Orientation     fOrientation;
     };
 
@@ -82,13 +81,6 @@
 
     virtual ~GrGLTexture() { this->release(); }
 
-    // overrides of GrTexture
-    virtual void uploadTextureData(int x,
-                                   int y,
-                                   int width,
-                                   int height,
-                                   const void* srcData,
-                                   size_t rowBytes);
     virtual intptr_t getTextureHandle() const;
 
     // these functions 
@@ -103,8 +95,7 @@
     }
     GrGLuint textureID() const { return fTexIDObj->id(); }
 
-    GrGLenum uploadFormat() const { return fUploadFormat; }
-    GrGLenum uploadType() const { return fUploadType; }
+    GrGLenum internalFormat() const { return fInternalFormat; }
 
     // Ganesh assumes texture coordinates have their origin
     // in the top-left corner of the image. OpenGL, however,
@@ -128,8 +119,7 @@
     TexParams                       fTexParams;
     GrGpu::ResetTimestamp           fTexParamsTimestamp;
     GrGLTexID*                      fTexIDObj;
-    GrGLenum                        fUploadFormat;
-    GrGLenum                        fUploadType;
+    GrGLenum                        fInternalFormat;
     Orientation                     fOrientation;
 
     void init(GrGpuGL* gpu,
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 1e6b420..764ccd3 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -253,6 +253,17 @@
                               config, buffer, rowBytes, invertY);
 }
 
+void GrGpu::writeTexturePixels(GrTexture* texture,
+                               int left, int top, int width, int height,
+                               GrPixelConfig config, const void* buffer,
+                               size_t rowBytes) {
+    GrAssert(GrPixelConfigIsUnpremultiplied(config) ==
+             GrPixelConfigIsUnpremultiplied(texture->config()));
+    this->handleDirtyContext();
+    this->onWriteTexturePixels(texture, left, top, width, height,
+                               config, buffer, rowBytes);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 static const int MAX_QUADS = 1 << 12; // max possible: (1 << 14) - 1;
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index cec03bc..9b39319 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -245,6 +245,22 @@
                     GrPixelConfig config, void* buffer, size_t rowBytes,
                     bool invertY);
 
+    /**
+     * Updates the pixels in a rectangle of a texture.
+     * @param left          left edge of the rectangle to write (inclusive)
+     * @param top           top edge of the rectangle to write (inclusive)
+     * @param width         width of rectangle to write in pixels.
+     * @param height        height of rectangle to write in pixels.
+     * @param config        the pixel config of the source buffer
+     * @param buffer        memory to read pixels from
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
+     *                      means rows are tightly packed.
+     */
+    void writeTexturePixels(GrTexture* texture,
+                            int left, int top, int width, int height,
+                            GrPixelConfig config, const void* buffer,
+                            size_t rowBytes);
+
     const GrGpuStats& getStats() const;
     void resetStats();
     void printStats() const;
@@ -402,6 +418,12 @@
                               size_t rowBytes,
                               bool invertY) = 0;
 
+    // overridden by API-specific derived class to perform the texture update
+    virtual void onWriteTexturePixels(GrTexture* texture,
+                                      int left, int top, int width, int height,
+                                      GrPixelConfig config, const void* buffer,
+                                      size_t rowBytes) = 0;
+
     // called to program the vertex data, indexCount will be 0 if drawing non-
     // indexed geometry. The subclass may adjust the startVertex and/or
     // startIndex since it may have already accounted for these in the setup.
diff --git a/src/gpu/GrGpuGL.cpp b/src/gpu/GrGpuGL.cpp
index 86a95a9..facfac9 100644
--- a/src/gpu/GrGpuGL.cpp
+++ b/src/gpu/GrGpuGL.cpp
@@ -523,10 +523,10 @@
 }
 
 GrTexture* GrGpuGL::onCreatePlatformTexture(const GrPlatformTextureDesc& desc) {
-    GrGLenum internalFormat; // we don't need this value
+    GrGLenum dontCare;
     GrGLTexture::Desc glTexDesc;
-    if (!this->canBeTexture(desc.fConfig, &internalFormat, 
-                            &glTexDesc.fUploadFormat, &glTexDesc.fUploadType)) {
+    if (!this->canBeTexture(desc.fConfig, &glTexDesc.fInternalFormat,
+                            &dontCare, &dontCare)) {
         return NULL;
     }
     
@@ -639,9 +639,8 @@
     if (isTexture) {
         GrGLTexture::Desc texDesc;
         GrGLenum dontCare;
-        if (!canBeTexture(desc.fConfig, &dontCare,
-                         &texDesc.fUploadFormat,
-                         &texDesc.fUploadType)) {
+        if (!canBeTexture(desc.fConfig, &texDesc.fInternalFormat,
+                         &dontCare, &dontCare)) {
             return NULL;
         }
         texDesc.fWidth  = desc.fWidth;
@@ -675,44 +674,82 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void GrGpuGL::allocateAndUploadTexData(const GrGLTexture::Desc& desc,
-                                       GrGLenum internalFormat,
-                                       const void* data,
-                                       size_t rowBytes) {
-    // we assume the texture is bound
+void GrGpuGL::onWriteTexturePixels(GrTexture* texture,
+                                   int left, int top, int width, int height,
+                                   GrPixelConfig config, const void* buffer,
+                                   size_t rowBytes) {
+    GrGLTexture* glTex = static_cast<GrGLTexture*>(texture);
 
-    size_t bpp = GrBytesPerPixel(desc.fConfig);
-    size_t trimRowBytes = desc.fWidth * bpp;
+    this->setSpareTextureUnit();
+    GL_CALL(BindTexture(GR_GL_TEXTURE_2D, glTex->textureID()));
+    GrGLTexture::Desc desc;
+    desc.fConfig = glTex->config();
+    desc.fWidth = glTex->width();
+    desc.fHeight = glTex->height();
+    desc.fOrientation = glTex->orientation();
+    desc.fTextureID = glTex->textureID();
+    desc.fInternalFormat = glTex->internalFormat();
 
-    if (!rowBytes) {
-        rowBytes = trimRowBytes;
+    this->uploadTexData(desc, left, top, width, height, config, buffer, rowBytes);
+}
+
+void GrGpuGL::uploadTexData(const GrGLTexture::Desc& desc,
+                            int left, int top, int width, int height,
+                            GrPixelConfig dataConfig,
+                            const void* data,
+                            size_t rowBytes) {
+    GrIRect bounds = GrIRect::MakeWH(desc.fWidth, desc.fHeight);
+    GrIRect subrect = GrIRect::MakeXYWH(left, top, width, height);
+    if (!bounds.contains(subrect)) {
+        return;
     }
 
+    // ES2 glCompressedTexSubImage2D doesn't support any formats
+    // (at least without extensions)
+    GrAssert(desc.fInternalFormat != GR_GL_PALETTE8_RGBA8 ||
+             bounds == subrect);
+
     // in case we need a temporary, trimmed copy of the src pixels
     SkAutoSMalloc<128 * 128> tempStorage;
 
+    GrGLenum dontCare;
+    GrGLenum externalFormat;
+    GrGLenum externalType;
+    if (!this->canBeTexture(dataConfig, &dontCare,
+                            &externalFormat, &externalType)) {
+        return;
+    }
+
+    size_t bpp = GrBytesPerPixel(dataConfig);
+    size_t trimRowBytes = width * bpp;
+    if (!rowBytes) {
+        rowBytes = trimRowBytes;
+    }
     /*
-     * check whether to allocate a temporary buffer for flipping y or
-     * because our data has extra bytes past each row. If so, we need
-     * to trim those off here, since GL ES doesn't let us specify
-     * GL_UNPACK_ROW_LENGTH.
+     *  check whether to allocate a temporary buffer for flipping y or
+     *  because our srcData has extra bytes past each row. If so, we need
+     *  to trim those off here, since GL ES may not let us specify
+     *  GL_UNPACK_ROW_LENGTH.
      */
+    bool restoreGLRowLength = false;
     bool flipY = GrGLTexture::kBottomUp_Orientation == desc.fOrientation;
     if (this->glCaps().fUnpackRowLengthSupport && !flipY) {
-        if (data && rowBytes != trimRowBytes) {
+        // can't use this for flipping, only non-neg values allowed. :(
+        if (rowBytes != trimRowBytes) {
             GrGLint rowLength = static_cast<GrGLint>(rowBytes / bpp);
             GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength));
+            restoreGLRowLength = true;
         }
     } else {
-        if (data && (trimRowBytes != rowBytes || flipY)) {
+        if (trimRowBytes != rowBytes || flipY) {
             // copy the data into our new storage, skipping the trailing bytes
-            size_t trimSize = desc.fHeight * trimRowBytes;
+            size_t trimSize = height * trimRowBytes;
             const char* src = (const char*)data;
             if (flipY) {
-                src += (desc.fHeight - 1) * rowBytes;
+                src += (height - 1) * rowBytes;
             }
             char* dst = (char*)tempStorage.reset(trimSize);
-            for (int y = 0; y < desc.fHeight; y++) {
+            for (int y = 0; y < height; y++) {
                 memcpy(dst, src, trimRowBytes);
                 if (flipY) {
                     src -= rowBytes;
@@ -721,31 +758,27 @@
                 }
                 dst += trimRowBytes;
             }
-            // now point data to our trimmed version
+            // now point dat to our copied version
             data = tempStorage.get();
-            rowBytes = trimRowBytes;
         }
     }
 
     GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, static_cast<GrGLint>(bpp)));
-    if (kIndex_8_GrPixelConfig == desc.fConfig &&
-        this->getCaps().f8BitPaletteSupport) {
-        // ES only supports CompressedTexImage2D, not CompressedTexSubimage2D
-        GrGLsizei imageSize = desc.fWidth * desc.fHeight +
-                              kGrColorTableSize;
-        GL_CALL(CompressedTexImage2D(GR_GL_TEXTURE_2D, 0, desc.fUploadFormat,
-                                     desc.fWidth, desc.fHeight,
-                                     0, imageSize, data));
-        if (this->glCaps().fUnpackRowLengthSupport) {
-            GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
-        }
+    if (bounds == subrect) {
+        GL_CALL(TexImage2D(GR_GL_TEXTURE_2D, 0, desc.fInternalFormat,
+                           desc.fWidth, desc.fHeight, 0,
+                           externalFormat, externalType, data));
     } else {
-        GL_CALL(TexImage2D(GR_GL_TEXTURE_2D, 0, internalFormat,
-                            desc.fWidth, desc.fHeight, 0,
-                            desc.fUploadFormat, desc.fUploadType, data));
-        if (this->glCaps().fUnpackRowLengthSupport) {
-            GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
+        if (flipY) {
+            top = desc.fHeight - (top + height);
         }
+        GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D, 0, left, top, width, height,
+                              externalFormat, externalType, data));
+    }
+
+    if (restoreGLRowLength) {
+        GrAssert(this->glCaps().fUnpackRowLengthSupport);
+        GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
     }
 }
 
@@ -856,7 +889,8 @@
 
     GrGLTexture::Desc glTexDesc;
     GrGLRenderTarget::Desc  glRTDesc;
-    GrGLenum internalFormat;
+    GrGLenum externalFormat;
+    GrGLenum externalType;
 
     glTexDesc.fWidth  = desc.fWidth;
     glTexDesc.fHeight = desc.fHeight;
@@ -871,9 +905,9 @@
 
     bool renderTarget = 0 != (desc.fFlags & kRenderTarget_GrTextureFlagBit);
     if (!canBeTexture(desc.fConfig,
-                      &internalFormat,
-                      &glTexDesc.fUploadFormat,
-                      &glTexDesc.fUploadType)) {
+                      &glTexDesc.fInternalFormat,
+                      &externalFormat,
+                      &externalType)) {
         return return_null_texture();
     }
 
@@ -928,7 +962,14 @@
     GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
                           GR_GL_TEXTURE_WRAP_T,
                           initialTexParams.fWrapT));
-    this->allocateAndUploadTexData(glTexDesc, internalFormat,srcData, rowBytes);
+    if (NULL == srcData) {
+        GL_CALL(TexImage2D(GR_GL_TEXTURE_2D, 0, glTexDesc.fInternalFormat,
+                           glTexDesc.fWidth, glTexDesc.fHeight, 0,
+                           externalFormat, externalType, NULL));
+    } else {
+        this->uploadTexData(glTexDesc, 0, 0, glTexDesc.fWidth, glTexDesc.fHeight,
+                            desc.fConfig, srcData, rowBytes);
+    }
 
     GrGLTexture* tex;
     if (renderTarget) {
@@ -2075,51 +2116,51 @@
 
 bool GrGpuGL::canBeTexture(GrPixelConfig config,
                            GrGLenum* internalFormat,
-                           GrGLenum* format,
-                           GrGLenum* type) {
+                           GrGLenum* externalFormat,
+                           GrGLenum* externalType) {
     switch (config) {
         case kRGBA_8888_PM_GrPixelConfig:
         case kRGBA_8888_UPM_GrPixelConfig:
-            *format = GR_GL_RGBA;
+            *externalFormat = GR_GL_RGBA;
             *internalFormat = GR_GL_RGBA;
-            *type = GR_GL_UNSIGNED_BYTE;
+            *externalType = GR_GL_UNSIGNED_BYTE;
             break;
         case kBGRA_8888_PM_GrPixelConfig:
         case kBGRA_8888_UPM_GrPixelConfig:
             if (!fGLCaps.fBGRAFormatSupport) {
                 return false;
             }
-            *format = GR_GL_BGRA;
+            *externalFormat = GR_GL_BGRA;
             if (fGLCaps.fBGRAIsInternalFormat) {
                 *internalFormat = GR_GL_BGRA;
             } else {
                 *internalFormat = GR_GL_RGBA;
             }
-            *type = GR_GL_UNSIGNED_BYTE;
+            *externalType = GR_GL_UNSIGNED_BYTE;
             break;
         case kRGB_565_GrPixelConfig:
-            *format = GR_GL_RGB;
+            *externalFormat = GR_GL_RGB;
             *internalFormat = GR_GL_RGB;
-            *type = GR_GL_UNSIGNED_SHORT_5_6_5;
+            *externalType = GR_GL_UNSIGNED_SHORT_5_6_5;
             break;
         case kRGBA_4444_GrPixelConfig:
-            *format = GR_GL_RGBA;
+            *externalFormat = GR_GL_RGBA;
             *internalFormat = GR_GL_RGBA;
-            *type = GR_GL_UNSIGNED_SHORT_4_4_4_4;
+            *externalType = GR_GL_UNSIGNED_SHORT_4_4_4_4;
             break;
         case kIndex_8_GrPixelConfig:
             if (this->getCaps().f8BitPaletteSupport) {
-                *format = GR_GL_PALETTE8_RGBA8;
+                *externalFormat = GR_GL_PALETTE8_RGBA8;
                 *internalFormat = GR_GL_PALETTE8_RGBA8;
-                *type = GR_GL_UNSIGNED_BYTE;   // unused I think
+                *externalType = GR_GL_UNSIGNED_BYTE;   // unused I think
             } else {
                 return false;
             }
             break;
         case kAlpha_8_GrPixelConfig:
-            *format = GR_GL_ALPHA;
+            *externalFormat = GR_GL_ALPHA;
             *internalFormat = GR_GL_ALPHA;
-            *type = GR_GL_UNSIGNED_BYTE;
+            *externalType = GR_GL_UNSIGNED_BYTE;
             break;
         default:
             return false;
diff --git a/src/gpu/GrGpuGL.h b/src/gpu/GrGpuGL.h
index c1b17ee..18baedf 100644
--- a/src/gpu/GrGpuGL.h
+++ b/src/gpu/GrGpuGL.h
@@ -175,6 +175,11 @@
                               size_t rowBytes,
                               bool invertY) SK_OVERRIDE;
 
+    virtual void onWriteTexturePixels(GrTexture* texture,
+                                      int left, int top, int width, int height,
+                                      GrPixelConfig config, const void* buffer,
+                                      size_t rowBytes) SK_OVERRIDE;
+
     virtual void onGpuDrawIndexed(GrPrimitiveType type,
                                   uint32_t startVertex,
                                   uint32_t startIndex,
@@ -262,13 +267,14 @@
 
     bool canBeTexture(GrPixelConfig config,
                       GrGLenum* internalFormat,
-                      GrGLenum* format,
-                      GrGLenum* type);
-    // helpers for onCreateTexture
-    void allocateAndUploadTexData(const GrGLTexture::Desc& desc,
-                                  GrGLenum internalFormat,
-                                  const void* data,
-                                  size_t rowBytes);
+                      GrGLenum* externalFormat,
+                      GrGLenum* externalType);
+    // helper for onCreateTexture and writeTexturePixels
+    void uploadTexData(const GrGLTexture::Desc& desc,
+                       int left, int top, int width, int height,
+                       GrPixelConfig dataConfig,
+                       const void* data,
+                       size_t rowBytes);
 
     bool createRenderTargetObjects(int width, int height,
                                    GrGLuint texID,
diff --git a/src/gpu/GrRenderTarget.cpp b/src/gpu/GrRenderTarget.cpp
index 7a53c0b..7901648 100644
--- a/src/gpu/GrRenderTarget.cpp
+++ b/src/gpu/GrRenderTarget.cpp
@@ -14,14 +14,31 @@
 #include "GrStencilBuffer.h"
 
 bool GrRenderTarget::readPixels(int left, int top, int width, int height,
-                                GrPixelConfig config, void* buffer) {
+                                GrPixelConfig config, void* buffer,
+                                size_t rowBytes) {
     // go through context so that all necessary flushing occurs
-    GrContext* context = this->getGpu()->getContext();
-    GrAssert(NULL != context);
+    GrContext* context = this->getContext();
+    if (NULL == context) {
+        return false;
+    }
     return context->readRenderTargetPixels(this,
-                                           left, top, 
+                                           left, top,
                                            width, height,
-                                           config, buffer);
+                                           config, buffer, rowBytes);
+}
+
+void GrRenderTarget::writePixels(int left, int top, int width, int height,
+                                 GrPixelConfig config, const void* buffer,
+                                 size_t rowBytes) {
+    // go through context so that all necessary flushing occurs
+    GrContext* context = this->getContext();
+    if (NULL == context) {
+        return;
+    }
+    context->writeRenderTargetPixels(this,
+                                     left, top,
+                                     width, height,
+                                     config, buffer, rowBytes);
 }
 
 size_t GrRenderTarget::sizeInBytes() const {
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index 77868df..c145c71 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -14,14 +14,31 @@
 #include "GrRenderTarget.h"
 
 bool GrTexture::readPixels(int left, int top, int width, int height,
-                           GrPixelConfig config, void* buffer) {
+                           GrPixelConfig config, void* buffer,
+                           size_t rowBytes) {
     // go through context so that all necessary flushing occurs
-    GrContext* context = this->getGpu()->getContext();
-    GrAssert(NULL != context);
+    GrContext* context = this->getContext();
+    if (NULL == context) {
+        return false;
+    }
     return context->readTexturePixels(this,
-                                        left, top, 
-                                        width, height,
-                                        config, buffer);
+                                      left, top,
+                                      width, height,
+                                      config, buffer, rowBytes);
+}
+
+void GrTexture::writePixels(int left, int top, int width, int height,
+                            GrPixelConfig config, const void* buffer,
+                            size_t rowBytes) {
+    // go through context so that all necessary flushing occurs
+    GrContext* context = this->getContext();
+    if (NULL == context) {
+        return;
+    }
+    context->writeTexturePixels(this,
+                                left, top,
+                                width, height,
+                                config, buffer, rowBytes);
 }
 
 void GrTexture::releaseRenderTarget() {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 91184b7..69973be 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -314,13 +314,8 @@
                                                bitmap.isOpaque());
     }
 
-    fContext->setRenderTarget(fRenderTarget);
-    // we aren't setting the clip or matrix, so mark as dirty
-    // we don't need to set them for this call and don't have them anyway
-    fNeedPrepareRenderTarget = true;
-
-    fContext->writePixels(x, y, bitmap.width(), bitmap.height(),
-                          config, bitmap.getPixels(), bitmap.rowBytes());
+    fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(),
+                               config, bitmap.getPixels(), bitmap.rowBytes());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1031,7 +1026,7 @@
     if (NULL == texture) {
         return false;
     }
-    texture->uploadTextureData(0, 0, desc.fWidth, desc.fHeight, 
+    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
                                dstM.fImage, dstM.fRowBytes);
 
     if (grp->hasTextureOrMask() && ivm.invert(&ivm)) {
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 6a39410..e76ec5d 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -99,8 +99,9 @@
             } else {
                 entry = ctx->lockScratchTexture(desc,
                                         GrContext::kExact_ScratchTexMatch);
-                entry.texture()->uploadTextureData(0, 0, bitmap->width(), 
-                    bitmap->height(), storage.get(), 0);
+                entry.texture()->writePixels(0, 0, bitmap->width(), 
+                                             bitmap->height(), desc.fConfig,
+                                             storage.get(), 0);
                 return entry;
             }
 
@@ -119,8 +120,11 @@
     } else {
         entry = ctx->lockScratchTexture(desc,
                                         GrContext::kExact_ScratchTexMatch);
-        entry.texture()->uploadTextureData(0, 0, bitmap->width(), 
-            bitmap->height(), bitmap->getPixels(), bitmap->rowBytes());
+        entry.texture()->writePixels(0, 0,
+                                     bitmap->width(), bitmap->height(),
+                                     desc.fConfig,
+                                     bitmap->getPixels(),
+                                     bitmap->rowBytes());
         return entry;
     }
 }
diff --git a/src/gpu/SkGrTexturePixelRef.cpp b/src/gpu/SkGrTexturePixelRef.cpp
index 140993a..8d0eb89 100644
--- a/src/gpu/SkGrTexturePixelRef.cpp
+++ b/src/gpu/SkGrTexturePixelRef.cpp
@@ -79,7 +79,7 @@
         void* buffer = dst->getPixels();
         return fTexture->readPixels(left, top, width, height,
                                     kSkia8888_PM_GrPixelConfig,
-                                    buffer);
+                                    buffer, dst->rowBytes());
     } else {
         return false;
     }
@@ -123,7 +123,7 @@
         void* buffer = dst->getPixels();
         return fRenderTarget->readPixels(left, top, width, height,
                                          kSkia8888_PM_GrPixelConfig,
-                                         buffer);
+                                         buffer, dst->rowBytes());
     } else {
         return false;
     }