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);
+}