change old picture serialization to really handle images

BUG=skia:3965

Review URL: https://codereview.chromium.org/1199473002
diff --git a/src/c/sk_surface.cpp b/src/c/sk_surface.cpp
index 15f68db..280897b 100644
--- a/src/c/sk_surface.cpp
+++ b/src/c/sk_surface.cpp
@@ -207,8 +207,9 @@
     return (sk_image_t*)SkImage::NewRasterCopy(info, pixels, rowBytes);
 }
 
-sk_image_t* sk_image_new_from_data(const sk_data_t* cdata) {
-    return ToImage(SkImage::NewFromData(AsData(cdata)));
+sk_image_t* sk_image_new_from_encoded(const sk_data_t* cdata, const sk_irect_t* subset) {
+    return ToImage(SkImage::NewFromEncoded(AsData(cdata),
+                                           reinterpret_cast<const SkIRect*>(subset)));
 }
 
 sk_data_t* sk_image_encode(const sk_image_t* cimage) {
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 49e2c04..8f79912 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -1270,7 +1270,9 @@
         return false;
     }
 
-    SkPixelRef::LockRequest req = { fInfo.dimensions(), kNone_SkFilterQuality };
+    // We have to lock the whole thing (using the pixelref's dimensions) until the api supports
+    // a partial lock (with offset/origin). Hence we can't use our fInfo.
+    SkPixelRef::LockRequest req = { pr->info().dimensions(), kNone_SkFilterQuality };
     SkPixelRef::LockResult res;
     if (pr->requestLock(req, &res)) {
         SkASSERT(res.fPixels);
diff --git a/src/core/SkImageGeneratorPriv.h b/src/core/SkImageGeneratorPriv.h
index e03294d..e55f43d 100644
--- a/src/core/SkImageGeneratorPriv.h
+++ b/src/core/SkImageGeneratorPriv.h
@@ -33,7 +33,7 @@
  *
  *  @return true iff successful.
  */
-bool SkInstallDiscardablePixelRef(SkImageGenerator*, SkBitmap* destination,
+bool SkInstallDiscardablePixelRef(SkImageGenerator*, const SkIRect* subset, SkBitmap* destination,
                                   SkDiscardableMemory::Factory* factory);
 
 #endif
diff --git a/src/core/SkPictureData.cpp b/src/core/SkPictureData.cpp
index ddd074e..87517e0 100644
--- a/src/core/SkPictureData.cpp
+++ b/src/core/SkPictureData.cpp
@@ -68,6 +68,15 @@
             fTextBlobRefs[i] = SkRef(blobs[i]);
         }
     }
+        
+    const SkTDArray<const SkImage*>& imgs = record.getImageRefs();
+    fImageCount = imgs.count();
+    if (fImageCount > 0) {
+        fImageRefs = SkNEW_ARRAY(const SkImage*, fImageCount);
+        for (int i = 0; i < fImageCount; ++i) {
+            fImageRefs[i] = SkRef(imgs[i]);
+        }
+    }
 }
 
 void SkPictureData::init() {
@@ -75,6 +84,8 @@
     fPictureCount = 0;
     fTextBlobRefs = NULL;
     fTextBlobCount = 0;
+    fImageRefs = NULL;
+    fImageCount = 0;
     fOpData = NULL;
     fFactoryPlayback = NULL;
 }
@@ -91,12 +102,17 @@
         fTextBlobRefs[i]->unref();
     }
     SkDELETE_ARRAY(fTextBlobRefs);
-
+    
+    for (int i = 0; i < fImageCount; i++) {
+        fImageRefs[i]->unref();
+    }
+    SkDELETE_ARRAY(fImageRefs);
+    
     SkDELETE(fFactoryPlayback);
 }
 
 bool SkPictureData::containsBitmaps() const {
-    if (fBitmaps.count() > 0) {
+    if (fBitmaps.count() > 0 || fImageCount > 0) {
         return true;
     }
     for (int i = 0; i < fPictureCount; ++i) {
@@ -217,6 +233,13 @@
             fTextBlobRefs[i]->flatten(buffer);
         }
     }
+    
+    if (fImageCount > 0) {
+        write_tag_size(buffer, SK_PICT_IMAGE_BUFFER_TAG, fImageCount);
+        for (i = 0; i  < fImageCount; ++i) {
+            buffer.writeImage(fImageRefs[i]);
+        }
+    }
 }
 
 void SkPictureData::serialize(SkWStream* stream,
@@ -403,8 +426,67 @@
     return true;    // success
 }
 
-bool SkPictureData::parseBufferTag(SkReadBuffer& buffer,
-                                   uint32_t tag, uint32_t size) {
+static const SkImage* create_image_from_buffer(SkReadBuffer& buffer) {
+    int width = buffer.read32();
+    int height = buffer.read32();
+    if (width <= 0 || height <= 0) {    // SkImage never has a zero dimension
+        buffer.validate(false);
+        return NULL;
+    }
+
+    SkAutoTUnref<SkData> encoded(buffer.readByteArrayAsData());
+    int originX = buffer.read32();
+    int originY = buffer.read32();
+    if (0 == encoded->size() || originX < 0 || originY < 0) {
+        buffer.validate(false);
+        return NULL;
+    }
+
+    const SkIRect subset = SkIRect::MakeXYWH(originX, originY, width, height);
+    return SkImage::NewFromEncoded(encoded, &subset);
+}
+
+// Need a shallow wrapper to return const SkPicture* to match the other factories,
+// as SkPicture::CreateFromBuffer() returns SkPicture*
+static const SkPicture* create_picture_from_buffer(SkReadBuffer& buffer) {
+    return SkPicture::CreateFromBuffer(buffer);
+}
+
+template <typename T>
+bool new_array_from_buffer(SkReadBuffer& buffer, uint32_t inCount,
+                           const T*** array, int* outCount, const T* (*factory)(SkReadBuffer&)) {
+    if (!buffer.validate((0 == *outCount) && (NULL == *array))) {
+        return false;
+    }
+    if (0 == inCount) {
+        return true;
+    }
+    *outCount = inCount;
+    *array = SkNEW_ARRAY(const T*, *outCount);
+    bool success = true;
+    int i = 0;
+    for (; i < *outCount; i++) {
+        (*array)[i] = factory(buffer);
+        if (NULL == (*array)[i]) {
+            success = false;
+            break;
+        }
+    }
+    if (!success) {
+        // Delete all of the blobs that were already created (up to but excluding i):
+        for (int j = 0; j < i; j++) {
+            (*array)[j]->unref();
+        }
+        // Delete the array
+        SkDELETE_ARRAY(*array);
+        *array = NULL;
+        *outCount = 0;
+        return false;
+    }
+    return true;
+}
+
+bool SkPictureData::parseBufferTag(SkReadBuffer& buffer, uint32_t tag, uint32_t size) {
     switch (tag) {
         case SK_PICT_BITMAP_BUFFER_TAG: {
             const int count = SkToInt(size);
@@ -433,33 +515,18 @@
                     buffer.readPath(&fPaths[i]);
                 }
             } break;
-        case SK_PICT_TEXTBLOB_BUFFER_TAG: {
-            if (!buffer.validate((0 == fTextBlobCount) && (NULL == fTextBlobRefs))) {
+        case SK_PICT_TEXTBLOB_BUFFER_TAG:
+            if (!new_array_from_buffer(buffer, size, &fTextBlobRefs, &fTextBlobCount,
+                                       SkTextBlob::CreateFromBuffer)) {
                 return false;
             }
-            fTextBlobCount = size;
-            fTextBlobRefs = SkNEW_ARRAY(const SkTextBlob*, fTextBlobCount);
-            bool success = true;
-            int i = 0;
-            for ( ; i < fTextBlobCount; i++) {
-                fTextBlobRefs[i] = SkTextBlob::CreateFromBuffer(buffer);
-                if (NULL == fTextBlobRefs[i]) {
-                    success = false;
-                    break;
-                }
-            }
-            if (!success) {
-                // Delete all of the blobs that were already created (up to but excluding i):
-                for (int j = 0; j < i; j++) {
-                    fTextBlobRefs[j]->unref();
-                }
-                // Delete the array
-                SkDELETE_ARRAY(fTextBlobRefs);
-                fTextBlobRefs = NULL;
-                fTextBlobCount = 0;
+            break;
+        case SK_PICT_IMAGE_BUFFER_TAG:
+            if (!new_array_from_buffer(buffer, size, &fImageRefs, &fImageCount,
+                                       create_image_from_buffer)) {
                 return false;
             }
-        } break;
+            break;
         case SK_PICT_READER_TAG: {
             SkAutoDataUnref data(SkData::NewUninitialized(size));
             if (!buffer.readByteArray(data->writable_data(), size) ||
@@ -469,32 +536,11 @@
             SkASSERT(NULL == fOpData);
             fOpData = data.detach();
         } break;
-        case SK_PICT_PICTURE_TAG: {
-            if (!buffer.validate((0 == fPictureCount) && (NULL == fPictureRefs))) {
+        case SK_PICT_PICTURE_TAG:
+            if (!new_array_from_buffer(buffer, size, &fPictureRefs, &fPictureCount,
+                                       create_picture_from_buffer)) {
                 return false;
             }
-            fPictureCount = size;
-            fPictureRefs = SkNEW_ARRAY(const SkPicture*, fPictureCount);
-            bool success = true;
-            int i = 0;
-            for ( ; i < fPictureCount; i++) {
-                fPictureRefs[i] = SkPicture::CreateFromBuffer(buffer);
-                if (NULL == fPictureRefs[i]) {
-                    success = false;
-                    break;
-                }
-            }
-            if (!success) {
-                // Delete all of the pictures that were already created (up to but excluding i):
-                for (int j = 0; j < i; j++) {
-                    fPictureRefs[j]->unref();
-                }
-                // Delete the array
-                SkDELETE_ARRAY(fPictureRefs);
-                fPictureCount = 0;
-                return false;
-            }
-        } break;
         default:
             // The tag was invalid.
             return false;
diff --git a/src/core/SkPictureData.h b/src/core/SkPictureData.h
index cada8d1..1a490ce 100644
--- a/src/core/SkPictureData.h
+++ b/src/core/SkPictureData.h
@@ -51,6 +51,7 @@
 #define SK_PICT_PAINT_BUFFER_TAG    SkSetFourByteTag('p', 'n', 't', ' ')
 #define SK_PICT_PATH_BUFFER_TAG     SkSetFourByteTag('p', 't', 'h', ' ')
 #define SK_PICT_TEXTBLOB_BUFFER_TAG SkSetFourByteTag('b', 'l', 'o', 'b')
+#define SK_PICT_IMAGE_BUFFER_TAG    SkSetFourByteTag('i', 'm', 'a', 'g')
 
 // Always write this guy last (with no length field afterwards)
 #define SK_PICT_EOF_TAG     SkSetFourByteTag('e', 'o', 'f', ' ')
@@ -90,6 +91,11 @@
         return fBitmaps[index];
     }
 
+    const SkImage* getImage(SkReader32* reader) const {
+        const int index = reader->readInt();
+        return fImageRefs[index];
+    }
+    
     const SkPath& getPath(SkReader32* reader) const {
         int index = reader->readInt() - 1;
         return fPaths[index];
@@ -156,6 +162,8 @@
     int fPictureCount;
     const SkTextBlob** fTextBlobRefs;
     int fTextBlobCount;
+    const SkImage** fImageRefs;
+    int fImageCount;
 
     SkPictureContentInfo fContentInfo;
 
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index 2511615..8720e19 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -69,8 +69,10 @@
     DRAW_PATCH, // could not add in aphabetical order
     DRAW_PICTURE_MATRIX_PAINT,
     DRAW_TEXT_BLOB,
+    DRAW_IMAGE,
+    DRAW_IMAGE_RECT,
 
-    LAST_DRAWTYPE_ENUM = DRAW_TEXT_BLOB
+    LAST_DRAWTYPE_ENUM = DRAW_IMAGE_RECT
 };
 
 // In the 'match' method, this constant will match any flavor of DRAW_BITMAP*
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index f4fdc8b..ac71c27 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -216,6 +216,19 @@
         case END_COMMENT_GROUP:
             // deprecated (M44)
             break;
+        case DRAW_IMAGE: {
+            const SkPaint* paint = fPictureData->getPaint(reader);
+            const SkImage* image = fPictureData->getImage(reader);
+            const SkPoint& loc = reader->skipT<SkPoint>();
+            canvas->drawImage(image, loc.fX, loc.fY, paint);
+        } break;
+        case DRAW_IMAGE_RECT: {
+            const SkPaint* paint = fPictureData->getPaint(reader);
+            const SkImage* image = fPictureData->getImage(reader);
+            const SkRect* src = get_rect_ptr(reader);   // may be null
+            const SkRect& dst = reader->skipT<SkRect>();     // required
+            canvas->drawImageRect(image, src, dst, paint);
+        } break;
         case DRAW_OVAL: {
             const SkPaint& paint = *fPictureData->getPaint(reader);
             canvas->drawOval(reader->skipT<SkRect>(), paint);
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index c85af90..018da0b 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -96,6 +96,8 @@
         1,  // DRAW_PATCH - right after op code
         1,  // DRAW_PICTURE_MATRIX_PAINT - right after op code
         1,  // DRAW_TEXT_BLOB- right after op code
+        1,  // DRAW_IMAGE - right after op code
+        1,  // DRAW_IMAGE_RECT - right after op code
     };
 
     SK_COMPILE_ASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1,
@@ -566,18 +568,34 @@
 
 void SkPictureRecord::onDrawImage(const SkImage* image, SkScalar x, SkScalar y,
                                   const SkPaint* paint) {
-    SkBitmap bm;
-    if (as_IB(image)->getROPixels(&bm)) {
-        this->SkPictureRecord::onDrawBitmap(bm, x, y, paint);
-    }
+    // op + paint_index + image_index + x + y
+    size_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar);
+    size_t initialOffset = this->addDraw(DRAW_IMAGE, &size);
+    SkASSERT(initialOffset+get_paint_offset(DRAW_IMAGE, size) == fWriter.bytesWritten());
+    this->addPaintPtr(paint);
+    this->addImage(image);
+    this->addScalar(x);
+    this->addScalar(y);
+    this->validate(initialOffset, size);
 }
 
 void SkPictureRecord::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
                                       const SkPaint* paint) {
-    SkBitmap bm;
-    if (as_IB(image)->getROPixels(&bm)) {
-        this->SkPictureRecord::onDrawBitmapRect(bm, src, dst, paint, kNone_DrawBitmapRectFlag);
+    // id + paint_index + bitmap_index + bool_for_src
+    size_t size = 4 * kUInt32Size;
+    if (src) {
+        size += sizeof(*src);   // + rect
     }
+    size += sizeof(dst);        // + rect
+    
+    size_t initialOffset = this->addDraw(DRAW_IMAGE_RECT, &size);
+    SkASSERT(initialOffset+get_paint_offset(DRAW_IMAGE_RECT, size)
+             == fWriter.bytesWritten());
+    this->addPaintPtr(paint);
+    this->addImage(image);
+    this->addRectPtr(src);  // may be null
+    this->addRect(dst);
+    this->validate(initialOffset, size);
 }
 
 void SkPictureRecord::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
@@ -892,6 +910,16 @@
     this->addInt(fBitmaps.count()-1);  // Remember, 0-based.
 }
 
+void SkPictureRecord::addImage(const SkImage* image) {
+    int index = fImageRefs.find(image);
+    if (index >= 0) {
+        this->addInt(index);
+    } else {
+        *fImageRefs.append() = SkRef(image);
+        this->addInt(fImageRefs.count()-1);
+    }
+}
+
 void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
     fWriter.writeMatrix(matrix);
 }
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index 2aeb69d..0b6ef8b 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -37,6 +37,10 @@
         return fTextBlobRefs;
     }
 
+    const SkTDArray<const SkImage* >& getImageRefs() const {
+        return fImageRefs;
+    }
+    
     SkData* opData(bool deepCopy) const {
         this->validate(fWriter.bytesWritten(), 0);
 
@@ -118,6 +122,7 @@
     }
 
     void addBitmap(const SkBitmap& bitmap);
+    void addImage(const SkImage*);
     void addMatrix(const SkMatrix& matrix);
     void addPaint(const SkPaint& paint) { this->addPaintPtr(&paint); }
     void addPaintPtr(const SkPaint* paint);
@@ -223,6 +228,7 @@
     SkWriter32 fWriter;
 
     // we ref each item in these arrays
+    SkTDArray<const SkImage*>    fImageRefs;
     SkTDArray<const SkPicture*>  fPictureRefs;
     SkTDArray<const SkTextBlob*> fTextBlobRefs;
 
diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp
index cfe93fd..6e1f5c6 100644
--- a/src/core/SkPixelRef.cpp
+++ b/src/core/SkPixelRef.cpp
@@ -259,6 +259,10 @@
     if (request.fSize.isEmpty()) {
         return false;
     }
+    // until we support subsets, we have to check this...
+    if (request.fSize.width() != fInfo.width() || request.fSize.height() != fInfo.height()) {
+        return false;
+    }
 
     if (fPreLocked) {
         result->fUnlockProc = NULL;
diff --git a/src/core/SkReadBuffer.h b/src/core/SkReadBuffer.h
index ba47835..2564051 100644
--- a/src/core/SkReadBuffer.h
+++ b/src/core/SkReadBuffer.h
@@ -58,6 +58,7 @@
         kImageFilterNoUniqueID_Version     = 40,
         kBitmapSourceFilterQuality_Version = 41,
         kPictureShaderHasPictureBool_Version = 42,
+        kHasDrawImageOpCodes_Version       = 43,
     };
 
     /**
diff --git a/src/core/SkWriteBuffer.cpp b/src/core/SkWriteBuffer.cpp
index d2eb8c5..faa7f00 100644
--- a/src/core/SkWriteBuffer.cpp
+++ b/src/core/SkWriteBuffer.cpp
@@ -218,6 +218,34 @@
     SkBitmap::WriteRawPixels(this, bitmap);
 }
 
+static bool try_write_encoded(SkWriteBuffer* buffer, SkData* encoded) {
+    SkPixelSerializer* ps = buffer->getPixelSerializer();
+    // Assumes that if the client did not set a serializer, they are
+    // happy to get the encoded data.
+    if (!ps || ps->useEncodedData(encoded->data(), encoded->size())) {
+        write_encoded_bitmap(buffer, encoded, SkIPoint::Make(0, 0));
+        return true;
+    }
+    return false;
+}
+
+void SkWriteBuffer::writeImage(const SkImage* image) {
+    this->writeInt(image->width());
+    this->writeInt(image->height());
+
+    SkAutoTUnref<SkData> encoded(image->refEncoded());
+    if (encoded && try_write_encoded(this, encoded)) {
+        return;
+    }
+
+    encoded.reset(image->encode(SkImageEncoder::kPNG_Type, 100));
+    if (encoded && try_write_encoded(this, encoded)) {
+        return;
+    }
+    
+    this->writeUInt(0); // signal no pixels (in place of the size of the encoded data)
+}
+
 void SkWriteBuffer::writeTypeface(SkTypeface* obj) {
     if (NULL == obj || NULL == fTFSet) {
         fWriter.write32(0);
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 66756cb..5e7d1b9 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -7,6 +7,7 @@
 
 #include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "SkData.h"
 #include "SkImageGenerator.h"
 #include "SkImagePriv.h"
 #include "SkImage_Base.h"
@@ -64,12 +65,16 @@
     return NULL;
 }
 
-SkImage* SkImage::NewFromData(SkData* data) {
-    if (NULL == data) {
+SkData* SkImage::refEncoded() const {
+    return as_IB(this)->onRefEncoded();
+}
+
+SkImage* SkImage::NewFromEncoded(SkData* encoded, const SkIRect* subset) {
+    if (NULL == encoded || 0 == encoded->size()) {
         return NULL;
     }
-    SkImageGenerator* generator = SkImageGenerator::NewFromData(data);
-    return generator ? SkImage::NewFromGenerator(generator) : NULL;
+    SkImageGenerator* generator = SkImageGenerator::NewFromData(encoded);
+    return generator ? SkImage::NewFromGenerator(generator, subset) : NULL;
 }
 
 SkSurface* SkImage::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) const {
@@ -201,7 +206,26 @@
     return surface->newImageSnapshot();
 }
 
-//////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImage::peekPixels(SkPixmap* pmap) const {
+    SkImageInfo info;
+    size_t rowBytes;
+    const void* pixels = this->peekPixels(&info, &rowBytes);
+    if (pixels) {
+        if (pmap) {
+            pmap->reset(info, pixels, rowBytes);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY) const {
+    return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #if !SK_SUPPORT_GPU
 
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 3bbff58..0793abb 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -59,6 +59,7 @@
     // newWidth > 0, newHeight > 0, subset either NULL or a proper subset of this bounds
     virtual SkImage* onNewImage(int newWidth, int newHeight, const SkIRect* subset,
                                 SkFilterQuality) const;
+    virtual SkData* onRefEncoded() const { return NULL; }
 
 private:
     const SkSurfaceProps fProps;
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index 2b64839..ab37659 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -9,7 +9,7 @@
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkData.h"
-#include "SkImageGenerator.h"
+#include "SkImageGeneratorPriv.h"
 #include "SkImagePriv.h"
 #include "SkPixelRef.h"
 #include "SkSurface.h"
@@ -59,6 +59,7 @@
     SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const override;
     bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY) const override;
     const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const override;
+    SkData* onRefEncoded() const override;
     bool getROPixels(SkBitmap*) const override;
 
     // exposed for SkSurface_Raster via SkNewImageFromPixelRef
@@ -141,6 +142,18 @@
     return fBitmap.getPixels();
 }
 
+SkData* SkImage_Raster::onRefEncoded() const {
+    SkPixelRef* pr = fBitmap.pixelRef();
+    const SkImageInfo prInfo = pr->info();
+    const SkImageInfo bmInfo = fBitmap.info();
+
+    // we only try if we (the image) cover the entire area of the pixelRef
+    if (prInfo.width() == bmInfo.width() && prInfo.height() == bmInfo.height()) {
+        return pr->refEncodedData();
+    }
+    return NULL;
+}
+
 bool SkImage_Raster::getROPixels(SkBitmap* dst) const {
     *dst = fBitmap;
     return true;
@@ -185,9 +198,9 @@
     return SkNEW_ARGS(SkImage_Raster, (info, data, rowBytes, NULL));
 }
 
-SkImage* SkImage::NewFromGenerator(SkImageGenerator* generator) {
+SkImage* SkImage::NewFromGenerator(SkImageGenerator* generator, const SkIRect* subset) {
     SkBitmap bitmap;
-    if (!SkInstallDiscardablePixelRef(generator, &bitmap)) {
+    if (!SkInstallDiscardablePixelRef(generator, subset, &bitmap, NULL)) {
         return NULL;
     }
     if (0 == bitmap.width() || 0 == bitmap.height()) {
diff --git a/src/lazy/SkDiscardablePixelRef.cpp b/src/lazy/SkDiscardablePixelRef.cpp
index 1e42042..73fa4ce 100644
--- a/src/lazy/SkDiscardablePixelRef.cpp
+++ b/src/lazy/SkDiscardablePixelRef.cpp
@@ -107,37 +107,55 @@
     fDiscardableMemoryIsLocked = false;
 }
 
-bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, SkBitmap* dst,
+bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, const SkIRect* subset, SkBitmap* dst,
                                   SkDiscardableMemory::Factory* factory) {
     SkAutoTDelete<SkImageGenerator> autoGenerator(generator);
     if (NULL == autoGenerator.get()) {
         return false;
     }
-    SkImageInfo info = autoGenerator->getInfo();
-    if (info.isEmpty() || !dst->setInfo(info)) {
+
+    SkImageInfo prInfo = autoGenerator->getInfo();
+    if (prInfo.isEmpty()) {
         return false;
     }
-    // Since dst->setInfo() may have changed/fixed-up info, we copy it back from that bitmap
-    info = dst->info();
 
-    SkASSERT(info.colorType() != kUnknown_SkColorType);
+    SkIPoint origin = SkIPoint::Make(0, 0);
+    SkImageInfo bmInfo = prInfo;
+    if (subset) {
+        const SkIRect prBounds = SkIRect::MakeWH(prInfo.width(), prInfo.height());
+        if (subset->isEmpty() || !prBounds.contains(*subset)) {
+            return false;
+        }
+        bmInfo = prInfo.makeWH(subset->width(), subset->height());
+        origin.set(subset->x(), subset->y());
+    }
+
+    // must compute our desired rowBytes w.r.t. the pixelRef's dimensions, not ours, which may be
+    // smaller.
+    if (!dst->setInfo(bmInfo, prInfo.minRowBytes())) {
+        return false;
+    }
+
+    // Since dst->setInfo() may have changed/fixed-up info, we check from the bitmap
+    SkASSERT(dst->info().colorType() != kUnknown_SkColorType);
+
     if (dst->empty()) {  // Use a normal pixelref.
         return dst->tryAllocPixels();
     }
     SkAutoTUnref<SkDiscardablePixelRef> ref(
         SkNEW_ARGS(SkDiscardablePixelRef,
-                   (info, autoGenerator.detach(), dst->rowBytes(), factory)));
-    dst->setPixelRef(ref);
+                   (prInfo, autoGenerator.detach(), dst->rowBytes(), factory)));
+    dst->setPixelRef(ref, origin.x(), origin.y());
     return true;
 }
 
 // These are the public API
 
 bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, SkBitmap* dst) {
-    return SkInstallDiscardablePixelRef(generator, dst, NULL);
+    return SkInstallDiscardablePixelRef(generator, NULL, dst, NULL);
 }
 
 bool SkInstallDiscardablePixelRef(SkData* encoded, SkBitmap* dst) {
     SkImageGenerator* generator = SkImageGenerator::NewFromData(encoded);
-    return generator ? SkInstallDiscardablePixelRef(generator, dst, NULL) : false;
+    return generator ? SkInstallDiscardablePixelRef(generator, NULL, dst, NULL) : false;
 }
diff --git a/src/lazy/SkDiscardablePixelRef.h b/src/lazy/SkDiscardablePixelRef.h
index 4515fd2..5d248a2 100644
--- a/src/lazy/SkDiscardablePixelRef.h
+++ b/src/lazy/SkDiscardablePixelRef.h
@@ -9,7 +9,7 @@
 #define SkDiscardablePixelRef_DEFINED
 
 #include "SkDiscardableMemory.h"
-#include "SkImageGenerator.h"
+#include "SkImageGeneratorPriv.h"
 #include "SkImageInfo.h"
 #include "SkPixelRef.h"
 
@@ -61,7 +61,7 @@
         return fGenerator->getYUV8Planes(sizes, planes, rowBytes, colorSpace);
     }
 
-    friend bool SkInstallDiscardablePixelRef(SkImageGenerator*, SkBitmap*,
+    friend bool SkInstallDiscardablePixelRef(SkImageGenerator*, const SkIRect*, SkBitmap*,
                                              SkDiscardableMemory::Factory*);
 
     typedef SkPixelRef INHERITED;
diff --git a/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp
index eb2b46a..ba16e4b 100644
--- a/src/ports/SkImageDecoder_CG.cpp
+++ b/src/ports/SkImageDecoder_CG.cpp
@@ -157,7 +157,18 @@
         }
     }
 
-    bm->setInfo(SkImageInfo::MakeN32Premul(width, height, cpType));
+    SkAlphaType at = kPremul_SkAlphaType;
+    switch (CGImageGetAlphaInfo(image)) {
+        case kCGImageAlphaNone:
+        case kCGImageAlphaNoneSkipLast:
+        case kCGImageAlphaNoneSkipFirst:
+            at = kOpaque_SkAlphaType;
+            break;
+        default:
+            break;
+    }
+
+    bm->setInfo(SkImageInfo::Make(width, height, kN32_SkColorType, at, cpType));
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
         return kSuccess;
     }
diff --git a/src/utils/SkLua.cpp b/src/utils/SkLua.cpp
index 11d9b29..cee4d4d 100644
--- a/src/utils/SkLua.cpp
+++ b/src/utils/SkLua.cpp
@@ -2007,7 +2007,7 @@
         const char* name = lua_tolstring(L, 1, NULL);
         SkAutoDataUnref data(SkData::NewFromFileName(name));
         if (data.get()) {
-            SkImage* image = SkImage::NewFromData(data);
+            SkImage* image = SkImage::NewFromEncoded(data);
             if (image) {
                 push_ref(L, image)->unref();
                 return 1;