Add SkImageGenerator Interface

-   Add SkDiscardablePixelRef class that uses SkDiscardableMemory and
    a SkImageGenerator.

-   Add SkDecodingImageGenerator class as an example of a
    SkImageGenerator.

-   Add DecodingImageGenerator unit test.

-   Add SkBasicDiscardableMemory implmentation for unit tests only.

R=reed@google.com, scroggo@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk/src@12341 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/images/SkDecodingImageGenerator.cpp b/images/SkDecodingImageGenerator.cpp
new file mode 100644
index 0000000..65fa6fd
--- /dev/null
+++ b/images/SkDecodingImageGenerator.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDecodingImageGenerator.h"
+
+#include "SkBitmapFactory.h"
+#include "SkData.h"
+#include "SkDiscardablePixelRef.h"
+#include "SkImageDecoder.h"
+
+SkDecodingImageGenerator::SkDecodingImageGenerator(SkData* data)
+    : fData(data) {
+    SkASSERT(fData != NULL);
+    fData->ref();
+}
+
+SkDecodingImageGenerator::~SkDecodingImageGenerator() {
+    fData->unref();
+}
+
+SkData* SkDecodingImageGenerator::refEncodedData() {
+    fData->ref();
+    return fData;
+}
+
+bool SkDecodingImageGenerator::getInfo(SkImageInfo* info) {
+    SkASSERT(info != NULL);
+    return SkImageDecoder::DecodeMemoryToTarget(fData->data(),
+                                                fData->size(),
+                                                info, NULL);
+}
+
+bool SkDecodingImageGenerator::getPixels(const SkImageInfo& info,
+                                         void* pixels,
+                                         size_t rowBytes) {
+    SkASSERT(pixels != NULL);
+    SkBitmapFactory::Target target = {pixels, rowBytes};
+    SkImageInfo tmpInfo = info;
+    return SkImageDecoder::DecodeMemoryToTarget(fData->data(),
+                                                fData->size(),
+                                                &tmpInfo, &target);
+}
+bool SkDecodingImageGenerator::Install(SkData* data, SkBitmap* dst) {
+    SkASSERT(data != NULL);
+    SkASSERT(dst != NULL);
+    SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (data)));
+    return SkDiscardablePixelRef::Install(gen, dst);
+}
diff --git a/images/SkDecodingImageGenerator.h b/images/SkDecodingImageGenerator.h
new file mode 100644
index 0000000..682aeb6
--- /dev/null
+++ b/images/SkDecodingImageGenerator.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDecodingImageGenerator_DEFINED
+#define SkDecodingImageGenerator_DEFINED
+
+#include "SkImageGenerator.h"
+
+class SkBitmap;
+
+/**
+ * Calls into SkImageDecoder::DecodeMemoryToTarget to implement a
+ * SkImageGenerator
+ */
+class SkDecodingImageGenerator : public SkImageGenerator {
+public:
+    /*
+     *  The constructor will take a reference to the SkData.  The
+     *  destructor will unref() it.
+     */
+    SkDecodingImageGenerator(SkData* data);
+    virtual ~SkDecodingImageGenerator();
+
+    virtual SkData* refEncodedData() SK_OVERRIDE;
+
+    virtual bool getInfo(SkImageInfo* info) SK_OVERRIDE;
+
+    virtual bool getPixels(const SkImageInfo& info,
+                           void* pixels,
+                           size_t rowBytes) SK_OVERRIDE;
+
+    /**
+     *  Install the SkData into the destination bitmap, using a new
+     *  SkDiscardablePixelRef and a new SkDecodingImageGenerator.
+     */
+    static bool Install(SkData* data, SkBitmap* destination);
+
+private:
+    SkData* fData;
+};
+#endif  // SkDecodingImageGenerator_DEFINED
diff --git a/lazy/SkDiscardablePixelRef.cpp b/lazy/SkDiscardablePixelRef.cpp
new file mode 100644
index 0000000..b6e1b10
--- /dev/null
+++ b/lazy/SkDiscardablePixelRef.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDiscardablePixelRef.h"
+#include "SkDiscardableMemory.h"
+
+SkDiscardablePixelRef::SkDiscardablePixelRef(SkImageGenerator* generator,
+                                             const SkImageInfo& info,
+                                             size_t size,
+                                             size_t rowBytes)
+    : fGenerator(generator)
+    , fInfo(info)
+    , fSize(size)
+    , fRowBytes(rowBytes)
+    , fDiscardableMemory(NULL) {
+    SkASSERT(fGenerator != NULL);
+    SkASSERT(fSize > 0);
+    SkASSERT(fRowBytes > 0);
+}
+
+SkDiscardablePixelRef::~SkDiscardablePixelRef() {
+    SkDELETE(fGenerator);
+}
+
+void* SkDiscardablePixelRef::onLockPixels(SkColorTable**) {
+    if (fDiscardableMemory != NULL) {
+        if (fDiscardableMemory->lock()) {
+            return fDiscardableMemory->data();
+        }
+        fDiscardableMemory = NULL;
+    }
+    fDiscardableMemory = SkDiscardableMemory::Create(fSize);
+    if (NULL == fDiscardableMemory) {
+        return NULL;  // Memory allocation failed.
+    }
+    void* pixels = fDiscardableMemory->data();
+    if (!fGenerator->getPixels(fInfo, pixels, fRowBytes)) {
+        return NULL;  // TODO(halcanary) Find out correct thing to do.
+    }
+    return pixels;
+}
+void SkDiscardablePixelRef::onUnlockPixels() {
+    if (fDiscardableMemory != NULL) {
+        fDiscardableMemory->unlock();
+    }
+}
+
+bool SkDiscardablePixelRef::Install(SkImageGenerator* generator,
+                                    SkBitmap* dst) {
+    SkImageInfo info;
+    SkASSERT(generator != NULL);
+    if ((NULL == generator)
+        || (!generator->getInfo(&info))
+        || (!dst->setConfig(info, 0))) {
+        SkDELETE(generator);
+        return false;
+    }
+    SkAutoTUnref<SkDiscardablePixelRef> ref(SkNEW_ARGS(SkDiscardablePixelRef,
+                                                   (generator, info,
+                                                    dst->getSize(),
+                                                    dst->rowBytes())));
+    dst->setPixelRef(ref);
+    return true;
+}
diff --git a/lazy/SkDiscardablePixelRef.h b/lazy/SkDiscardablePixelRef.h
new file mode 100644
index 0000000..bbe19b8
--- /dev/null
+++ b/lazy/SkDiscardablePixelRef.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDiscardablePixelRef_DEFINED
+#define SkDiscardablePixelRef_DEFINED
+
+#include "SkPixelRef.h"
+#include "SkImageGenerator.h"
+#include "SkImageInfo.h"
+
+class SkDiscardableMemory;
+
+/**
+ * An interface that allows a purgable PixelRef to re-decode an image.
+ */
+
+class SkDiscardablePixelRef : public SkPixelRef {
+public:
+    /**
+     *  Takes ownership of SkImageGenerator.  If this method fails for
+     *  whatever reason, it will return false and immediatetely delete
+     *  the generator.  If it succeeds, it will modify destination
+     *  bitmap.
+     *
+     *  If Install fails or when the SkDiscardablePixelRef that is
+     *  installed into destination is destroyed, it will call
+     *  SkDELETE() on the generator.  Therefore, generator should be
+     *  allocated with SkNEW() or SkNEW_ARGS().
+     */
+    static bool Install(SkImageGenerator* generator, SkBitmap* destination);
+
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
+
+protected:
+    ~SkDiscardablePixelRef();
+    virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE;
+    virtual void onUnlockPixels() SK_OVERRIDE;
+    virtual bool onLockPixelsAreWritable() const SK_OVERRIDE { return false; }
+
+    virtual SkData* onRefEncodedData() SK_OVERRIDE {
+        return fGenerator->refEncodedData();
+    }
+
+private:
+    SkImageGenerator* const fGenerator;
+    const SkImageInfo fInfo;
+    const size_t fSize;  // size of memory to be allocated
+    const size_t fRowBytes;
+    // These const members should not change over the life of the
+    // PixelRef, since the SkBitmap doesn't expect them to change.
+
+    SkDiscardableMemory* fDiscardableMemory;
+
+    /* Takes ownership of SkImageGenerator. */
+    SkDiscardablePixelRef(SkImageGenerator* generator,
+                          const SkImageInfo& info,
+                          size_t size,
+                          size_t rowBytes);
+};
+#endif  // SkDiscardablePixelRef_DEFINED
diff --git a/ports/SkDiscardableMemory_none.cpp b/ports/SkDiscardableMemory_none.cpp
index 6559097..700713b 100644
--- a/ports/SkDiscardableMemory_none.cpp
+++ b/ports/SkDiscardableMemory_none.cpp
@@ -6,7 +6,56 @@
  */
 
 #include "SkDiscardableMemory.h"
+#include "SkTypes.h"
+
+namespace {
+////////////////////////////////////////////////////////////////////////////////
+/**
+ *  Always successful, never purges.  Useful for testing.
+ */
+class SkMockDiscardableMemory : public SkDiscardableMemory {
+public:
+    SkMockDiscardableMemory(void*);
+    virtual ~SkMockDiscardableMemory();
+    virtual bool lock() SK_OVERRIDE;
+    virtual void* data() SK_OVERRIDE;
+    virtual void unlock() SK_OVERRIDE;
+private:
+    bool fLocked;
+    void* fPointer;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+SkMockDiscardableMemory::SkMockDiscardableMemory(void* ptr)
+    : fLocked(true)
+    , fPointer(ptr) {  // Takes ownership of ptr.
+    SkASSERT(fPointer != NULL);
+}
+
+SkMockDiscardableMemory::~SkMockDiscardableMemory() {
+    SkASSERT(!fLocked);
+    sk_free(fPointer);
+}
+
+bool SkMockDiscardableMemory::lock() {
+    SkASSERT(!fLocked);
+    return fLocked = true;
+}
+
+void* SkMockDiscardableMemory::data() {
+    SkASSERT(fLocked);
+    return fLocked ? fPointer : NULL;
+}
+
+void SkMockDiscardableMemory::unlock() {
+    SkASSERT(fLocked);
+    fLocked = false;
+}
+////////////////////////////////////////////////////////////////////////////////
+}  // namespace
 
 SkDiscardableMemory* SkDiscardableMemory::Create(size_t bytes) {
-    return NULL;
+    void* ptr = sk_malloc_throw(bytes);
+    return (ptr != NULL) ? SkNEW_ARGS(SkMockDiscardableMemory, (ptr)) : NULL;
 }