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/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;
}