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@12341 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/core.gypi b/gyp/core.gypi
index e31b8ff..54e23ef 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -73,6 +73,7 @@
         '<(skia_src_path)/core/SkDevice.cpp',
         '<(skia_src_path)/core/SkDeviceLooper.cpp',
         '<(skia_src_path)/core/SkDeviceProfile.cpp',
+        '<(skia_src_path)/lazy/SkDiscardablePixelRef.cpp',
         '<(skia_src_path)/core/SkDither.cpp',
         '<(skia_src_path)/core/SkDraw.cpp',
         '<(skia_src_path)/core/SkDrawLooper.cpp',
diff --git a/gyp/images.gyp b/gyp/images.gyp
index 7ead87e..eed2dba 100644
--- a/gyp/images.gyp
+++ b/gyp/images.gyp
@@ -18,6 +18,7 @@
       'include_dirs': [
         '../include/images',
         '../include/lazy',
+        '../src/lazy',
         # for access to SkErrorInternals.h
         '../src/core/',
         # for access to SkImagePriv.h
@@ -36,6 +37,7 @@
         '../src/images/bmpdecoderhelper.cpp',
         '../src/images/bmpdecoderhelper.h',
 
+        '../src/images/SkDecodingImageGenerator.cpp',
         '../src/images/SkForceLinking.cpp',
         '../src/images/SkImageDecoder.cpp',
         '../src/images/SkImageDecoder_FactoryDefault.cpp',
diff --git a/gyp/tests.gyp b/gyp/tests.gyp
index 890a146..841baf7 100644
--- a/gyp/tests.gyp
+++ b/gyp/tests.gyp
@@ -12,6 +12,7 @@
         '../src/effects',
         '../src/image',
         '../src/lazy',
+        '../src/images',
         '../src/pathops',
         '../src/pdf',
         '../src/pipe/utils',
diff --git a/include/core/SkImageGenerator.h b/include/core/SkImageGenerator.h
new file mode 100644
index 0000000..e43fbc1
--- /dev/null
+++ b/include/core/SkImageGenerator.h
@@ -0,0 +1,65 @@
+/*
+ * 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 SkImageGenerator_DEFINED
+#define SkImageGenerator_DEFINED
+
+#include "SkImageInfo.h"
+
+class SkData;
+
+/**
+ *  An interface that allows a purgeable PixelRef (such as a
+ *  SkDiscardablePixelRef) to decode and re-decode an image as needed.
+ */
+class SkImageGenerator {
+public:
+    /**
+     *  The PixelRef which takes ownership of this SkImageGenerator
+     *  will call the image generator's destructor.
+     */
+    virtual ~SkImageGenerator() { }
+
+    /**
+     *  Return a ref to the encoded (i.e. compressed) representation,
+     *  of this data.
+     *
+     *  If non-NULL is returned, the caller is responsible for calling
+     *  unref() on the data when it is finished.
+     */
+    virtual SkData* refEncodedData() { return NULL; }
+
+    /**
+     *  Return some information about the image, allowing the owner of
+     *  this object to allocate pixels.
+     *
+     *  @return false if anything goes wrong.
+     */
+    virtual bool getInfo(SkImageInfo* info) = 0;
+
+    /**
+     *  Decode into the given pixels, a block of memory of size at
+     *  least (info.fHeight - 1) * rowBytes + (info.fWidth *
+     *  bytesPerPixel)
+     *
+     *  @param info A description of the format (config, size)
+     *         expected by the caller.  This can simply be identical
+     *         to the info returned by getInfo().
+     *
+     *         This contract also allows the caller to specify
+     *         different output-configs, which the implementation can
+     *         decide to support or not.
+     *
+     *  @return false if anything goes wrong or if the image info is
+     *          unsupported.
+     */
+    virtual bool getPixels(const SkImageInfo& info,
+                           void* pixels,
+                           size_t rowBytes) = 0;
+};
+
+#endif  // SkImageGenerator_DEFINED
diff --git a/src/images/SkDecodingImageGenerator.cpp b/src/images/SkDecodingImageGenerator.cpp
new file mode 100644
index 0000000..65fa6fd
--- /dev/null
+++ b/src/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/src/images/SkDecodingImageGenerator.h b/src/images/SkDecodingImageGenerator.h
new file mode 100644
index 0000000..682aeb6
--- /dev/null
+++ b/src/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/src/lazy/SkDiscardablePixelRef.cpp b/src/lazy/SkDiscardablePixelRef.cpp
new file mode 100644
index 0000000..b6e1b10
--- /dev/null
+++ b/src/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/src/lazy/SkDiscardablePixelRef.h b/src/lazy/SkDiscardablePixelRef.h
new file mode 100644
index 0000000..bbe19b8
--- /dev/null
+++ b/src/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/src/ports/SkDiscardableMemory_none.cpp b/src/ports/SkDiscardableMemory_none.cpp
index 6559097..700713b 100644
--- a/src/ports/SkDiscardableMemory_none.cpp
+++ b/src/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;
 }
diff --git a/tests/CachedDecodingPixelRefTest.cpp b/tests/CachedDecodingPixelRefTest.cpp
index 9a00546..2d8a0e7 100644
--- a/tests/CachedDecodingPixelRefTest.cpp
+++ b/tests/CachedDecodingPixelRefTest.cpp
@@ -8,6 +8,7 @@
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkData.h"
+#include "SkDecodingImageGenerator.h"
 #include "SkForceLinking.h"
 #include "SkImageDecoder.h"
 #include "SkImagePriv.h"
@@ -244,3 +245,35 @@
     // onDecodePixels should fail, so getting pixels will fail.
     REPORTER_ASSERT(reporter, NULL == lazy.getPixels());
 }
+
+static void compare_with_SkDecodingImageGenerator(skiatest::Reporter* reporter,
+                                                  SkData* encoded,
+                                                  const SkBitmap& original,
+                                                  bool comparePixels) {
+
+    SkBitmap lazy;
+    bool success = SkDecodingImageGenerator::Install(encoded, &lazy);
+    REPORTER_ASSERT(reporter, success);
+    if (!success) {
+        return;
+    }
+
+    REPORTER_ASSERT(reporter, NULL == lazy.getPixels());
+    {
+        SkAutoLockPixels autoLockPixels(lazy);  // now pixels are good.
+        REPORTER_ASSERT(reporter, NULL != lazy.getPixels());
+        if (NULL == lazy.getPixels()) {
+            return;
+        }
+    }
+    // pixels should be gone!
+    REPORTER_ASSERT(reporter, NULL == lazy.getPixels());
+    {
+        SkAutoLockPixels autoLockPixels(lazy);  // now pixels are good.
+        REPORTER_ASSERT(reporter, NULL != lazy.getPixels());
+    }
+    compare_bitmaps(reporter, original, lazy, comparePixels);
+}
+DEF_TEST(DecodingImageGenerator, reporter) {
+    test_three_encodings(reporter, compare_with_SkDecodingImageGenerator);
+}