aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/images
diff options
context:
space:
mode:
authorGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2008-12-17 15:59:43 +0000
committerGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2008-12-17 15:59:43 +0000
commit8a1c16ff38322f0210116fa7293eb8817c7e477e (patch)
treefe40e07f6c8983318a2f79032b9a706ede1090c1 /src/images
parent2559c629078f738ac37095d896d580b681ac6a30 (diff)
grab from latest android
git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/images')
-rw-r--r--src/images/SkBitmap_RLEPixels.h19
-rw-r--r--src/images/SkCreateRLEPixelRef.cpp120
-rw-r--r--src/images/SkFDStream.cpp85
-rw-r--r--src/images/SkFlipPixelRef.cpp127
-rw-r--r--src/images/SkImageDecoder.cpp190
-rw-r--r--src/images/SkImageDecoder_fpdfemb.cpp236
-rw-r--r--src/images/SkImageDecoder_libbmp.cpp147
-rw-r--r--src/images/SkImageDecoder_libgif.cpp340
-rw-r--r--src/images/SkImageDecoder_libico.cpp388
-rw-r--r--src/images/SkImageDecoder_libjpeg.cpp811
-rw-r--r--src/images/SkImageDecoder_libpng.cpp795
-rw-r--r--src/images/SkImageDecoder_libpvjpeg.cpp206
-rw-r--r--src/images/SkImageDecoder_wbmp.cpp167
-rw-r--r--src/images/SkImageRef.cpp165
-rw-r--r--src/images/SkImageRefPool.cpp186
-rw-r--r--src/images/SkImageRefPool.h43
-rw-r--r--src/images/SkImageRef_GlobalPool.cpp83
-rw-r--r--src/images/SkMMapStream.cpp63
-rw-r--r--src/images/SkMovie.cpp101
-rw-r--r--src/images/SkMovie_gif.cpp224
-rw-r--r--src/images/SkPageFlipper.cpp85
-rw-r--r--src/images/SkScaledBitmapSampler.cpp338
-rw-r--r--src/images/SkScaledBitmapSampler.h55
-rw-r--r--src/images/SkStream.cpp856
-rw-r--r--src/images/bmpdecoderhelper.cpp376
-rw-r--r--src/images/bmpdecoderhelper.h123
-rw-r--r--src/images/fpdfemb_ext.h81
27 files changed, 6410 insertions, 0 deletions
diff --git a/src/images/SkBitmap_RLEPixels.h b/src/images/SkBitmap_RLEPixels.h
new file mode 100644
index 0000000000..c83bc69e7e
--- /dev/null
+++ b/src/images/SkBitmap_RLEPixels.h
@@ -0,0 +1,19 @@
+#ifndef SkBitmap_RLEPixels_DEFINED
+#define SkBitmap_RLEPixels_DEFINED
+
+#include "SkChunkAlloc.h"
+
+class SkBitmap_RLEPixels {
+public:
+ SkBitmap_RLEPixels(int width, int height);
+ ~SkBitmap_RLEPixels();
+
+ uint8_t* yptrs() const { return fYPtrs; }
+ uint8_t* allocChunk(size_t chunk);
+
+private:
+ SkChunkAlloc fChunk;
+ uint8_t** fYPtrs;
+};
+
+#endif
diff --git a/src/images/SkCreateRLEPixelRef.cpp b/src/images/SkCreateRLEPixelRef.cpp
new file mode 100644
index 0000000000..575623749b
--- /dev/null
+++ b/src/images/SkCreateRLEPixelRef.cpp
@@ -0,0 +1,120 @@
+#include "SkChunkAlloc.h"
+#include "SkPackBits.h"
+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+
+class RLEPixelRef : public SkPixelRef {
+public:
+ RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable);
+ virtual ~RLEPixelRef();
+
+protected:
+ // overrides from SkPixelRef
+ virtual void* onLockPixels(SkColorTable**);
+ virtual void onUnlockPixels();
+
+private:
+ SkBitmap::RLEPixels* fRLEPixels;
+ SkColorTable* fCTable;
+};
+
+RLEPixelRef::RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable)
+ : SkPixelRef(NULL) {
+ fRLEPixels = rlep; // we now own this ptr
+ fCTable = ctable;
+ ctable->safeRef();
+}
+
+RLEPixelRef::~RLEPixelRef() {
+ SkDELETE(fRLEPixels);
+ fCTable->safeUnref();
+}
+
+void* RLEPixelRef::onLockPixels(SkColorTable** ct) {
+ *ct = fCTable;
+ return fRLEPixels;
+}
+
+void RLEPixelRef::onUnlockPixels() {
+ // nothing to do
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class ChunkRLEPixels : public SkBitmap::RLEPixels {
+public:
+ ChunkRLEPixels(int width, int height, size_t chunkSize)
+ : SkBitmap::RLEPixels(width, height), fStorage(chunkSize) {
+ }
+
+ SkChunkAlloc fStorage;
+};
+
+SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src);
+SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src) {
+
+ if (SkBitmap::kIndex8_Config != src.config() &&
+ SkBitmap::kA8_Config != src.config()) {
+ return NULL;
+ }
+
+ size_t maxPacked = SkPackBits::ComputeMaxSize8(src.width());
+
+ // estimate the rle size based on the original size
+ size_t size = src.getSize() >> 3;
+ if (size < maxPacked) {
+ size = maxPacked;
+ }
+
+ ChunkRLEPixels* rlePixels = SkNEW_ARGS(ChunkRLEPixels,
+ (src.width(), src.height(), size));
+
+ uint8_t* dstRow = NULL;
+ size_t free = 0;
+ size_t totalPacked = 0;
+
+ for (int y = 0; y < src.height(); y++) {
+ const uint8_t* srcRow = src.getAddr8(0, y);
+
+ if (free < maxPacked) {
+ dstRow = (uint8_t*)rlePixels->fStorage.allocThrow(size);
+ free = size;
+ }
+ size_t packedSize = SkPackBits::Pack8(srcRow, src.width(), dstRow);
+ SkASSERT(packedSize <= free);
+ rlePixels->setPackedAtY(y, dstRow);
+
+ dstRow += packedSize;
+ free -= packedSize;
+
+ totalPacked += packedSize;
+ }
+
+//#ifdef SK_DEBUG
+#if 0
+ // test
+ uint8_t* buffer = new uint8_t[src.width()];
+ for (int y = 0; y < src.height(); y++) {
+ const uint8_t* srcRow = src.getAddr8(0, y);
+ SkPackBits::Unpack8(buffer, 0, src.width(), rlePixels->packedAtY(y));
+ int n = memcmp(buffer, srcRow, src.width());
+ if (n) {
+ SkDebugf("----- memcmp returned %d on line %d\n", n, y);
+ }
+ SkASSERT(n == 0);
+ }
+ delete[] buffer;
+
+ size_t totalAlloc = src.height() * sizeof(uint8_t*) + totalPacked;
+
+ SkDebugf("--- RLE: orig [%d %d] %d, rle %d %d savings %g\n",
+ src.width(), src.height(), src.getSize(),
+ src.height() * sizeof(uint8_t*), totalPacked,
+ (float)totalAlloc / src.getSize());
+
+#endif
+
+ // transfer ownership of rlePixels to our pixelref
+ return SkNEW_ARGS(RLEPixelRef, (rlePixels, src.getColorTable()));
+}
+
diff --git a/src/images/SkFDStream.cpp b/src/images/SkFDStream.cpp
new file mode 100644
index 0000000000..db4a51a60a
--- /dev/null
+++ b/src/images/SkFDStream.cpp
@@ -0,0 +1,85 @@
+#include "SkStream.h"
+#include <unistd.h>
+
+//#define TRACE_FDSTREAM
+
+SkFDStream::SkFDStream(int fileDesc, bool closeWhenDone)
+ : fFD(fileDesc), fCloseWhenDone(closeWhenDone) {
+}
+
+SkFDStream::~SkFDStream() {
+ if (fFD >= 0 && fCloseWhenDone) {
+ ::close(fFD);
+ }
+}
+
+bool SkFDStream::rewind() {
+ if (fFD >= 0) {
+ off_t value = ::lseek(fFD, 0, SEEK_SET);
+#ifdef TRACE_FDSTREAM
+ if (value) {
+ SkDebugf("xxxxxxxxxxxxxx rewind failed %d\n", value);
+ }
+#endif
+ return value == 0;
+ }
+ return false;
+}
+
+size_t SkFDStream::read(void* buffer, size_t size) {
+ if (fFD >= 0) {
+ if (buffer == NULL && size == 0) { // request total size
+ off_t curr = ::lseek(fFD, 0, SEEK_CUR);
+ if (curr < 0) {
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx lseek failed 0 CURR\n");
+#endif
+ return 0; // error
+ }
+ off_t size = ::lseek(fFD, 0, SEEK_END);
+ if (size < 0) {
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx lseek failed 0 END\n");
+#endif
+ size = 0; // error
+ }
+ if (::lseek(fFD, curr, SEEK_SET) != curr) {
+ // can't restore, error
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx lseek failed %d SET\n", curr);
+#endif
+ return 0;
+ }
+ return size;
+ } else if (NULL == buffer) { // skip
+ off_t oldCurr = ::lseek(fFD, 0, SEEK_CUR);
+ if (oldCurr < 0) {
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx lseek1 failed %d CUR\n", oldCurr);
+#endif
+ return 0; // error;
+ }
+ off_t newCurr = ::lseek(fFD, size, SEEK_CUR);
+ if (newCurr < 0) {
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx lseek2 failed %d CUR\n", newCurr);
+#endif
+ return 0; // error;
+ }
+ // return the actual amount we skipped
+ return newCurr - oldCurr;
+ } else { // read
+ ssize_t actual = ::read(fFD, buffer, size);
+ // our API can't return an error, so we return 0
+ if (actual < 0) {
+#ifdef TRACE_FDSTREAM
+ SkDebugf("xxxxxxxxxxxxx read failed %d actual %d\n", size, actual);
+#endif
+ actual = 0;
+ }
+ return actual;
+ }
+ }
+ return 0;
+}
+
diff --git a/src/images/SkFlipPixelRef.cpp b/src/images/SkFlipPixelRef.cpp
new file mode 100644
index 0000000000..95403cc3f9
--- /dev/null
+++ b/src/images/SkFlipPixelRef.cpp
@@ -0,0 +1,127 @@
+#include "SkFlipPixelRef.h"
+#include "SkFlattenable.h"
+#include "SkRegion.h"
+
+SkFlipPixelRef::SkFlipPixelRef(SkBitmap::Config config, int width, int height)
+: fFlipper(width, height) {
+ fConfig = config;
+ fSize = SkBitmap::ComputeSize(config, width, height);
+ fStorage = sk_malloc_throw(fSize << 1);
+ fPage0 = fStorage;
+ fPage1 = (char*)fStorage + fSize;
+}
+
+SkFlipPixelRef::~SkFlipPixelRef() {
+ sk_free(fStorage);
+}
+
+const SkRegion& SkFlipPixelRef::beginUpdate(SkBitmap* device) {
+ void* writeAddr;
+ const void* readAddr;
+ this->getFrontBack(&readAddr, &writeAddr);
+
+ device->setConfig(fConfig, fFlipper.width(), fFlipper.height());
+ device->setPixels(writeAddr);
+
+ SkRegion copyBits;
+ const SkRegion& dirty = fFlipper.update(&copyBits);
+
+ SkFlipPixelRef::CopyBitsFromAddr(*device, copyBits, readAddr);
+ return dirty;
+}
+
+void SkFlipPixelRef::endUpdate() {
+ this->swapPages();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void* SkFlipPixelRef::onLockPixels(SkColorTable** ct) {
+ fMutex.acquire();
+ *ct = NULL;
+ return fPage0;
+}
+
+void SkFlipPixelRef::onUnlockPixels() {
+ fMutex.release();
+}
+
+void SkFlipPixelRef::swapPages() {
+ fMutex.acquire();
+ SkTSwap<void*>(fPage0, fPage1);
+ fMutex.release();
+}
+
+void SkFlipPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+ this->INHERITED::flatten(buffer);
+
+ buffer.write32(fSize);
+ // only need to write page0
+ buffer.writePad(fPage0, fSize);
+}
+
+SkFlipPixelRef::SkFlipPixelRef(SkFlattenableReadBuffer& buffer)
+ : INHERITED(buffer, NULL) {
+ fSize = buffer.readU32();
+ fStorage = sk_malloc_throw(fSize << 1);
+ fPage0 = fStorage;
+ fPage1 = (char*)fStorage + fSize;
+ buffer.read(fPage0, fSize);
+}
+
+SkPixelRef* SkFlipPixelRef::Create(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkFlipPixelRef, (buffer));
+}
+
+static SkPixelRef::Registrar::Registrar reg("SkFlipPixelRef",
+ SkFlipPixelRef::Create);
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void copyRect(const SkBitmap& dst, const SkIRect& rect,
+ const void* srcAddr, int shift) {
+ const size_t offset = rect.fTop * dst.rowBytes() + (rect.fLeft << shift);
+ char* dstP = static_cast<char*>(dst.getPixels()) + offset;
+ const char* srcP = static_cast<const char*>(srcAddr) + offset;
+ const size_t rb = dst.rowBytes();
+ const size_t bytes = rect.width() << shift;
+
+ int height = rect.height();
+ while (--height >= 0) {
+ memcpy(dstP, srcP, bytes);
+ dstP += rb;
+ srcP += rb;
+ }
+}
+
+static int getShift(SkBitmap::Config config) {
+ switch (config) {
+ case SkBitmap::kARGB_8888_Config:
+ return 2;
+ case SkBitmap::kRGB_565_Config:
+ case SkBitmap::kARGB_4444_Config:
+ return 1;
+ case SkBitmap::kIndex8_Config:
+ case SkBitmap::kA8_Config:
+ return 0;
+ default:
+ return -1; // signal not supported
+ }
+}
+
+void SkFlipPixelRef::CopyBitsFromAddr(const SkBitmap& dst, const SkRegion& clip,
+ const void* srcAddr) {
+ const int shift = getShift(dst.config());
+ if (shift < 0) {
+ return;
+ }
+
+ const SkIRect bounds = {0, 0, dst.width(), dst.height()};
+ SkRegion::Cliperator iter(clip, bounds);
+
+ while (!iter.done()) {
+ copyRect(dst, iter.rect(), srcAddr, shift);
+ iter.next();
+ }
+}
+
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
new file mode 100644
index 0000000000..18b52d689c
--- /dev/null
+++ b/src/images/SkImageDecoder.cpp
@@ -0,0 +1,190 @@
+/* libs/graphics/images/SkImageDecoder.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkImageDecoder.h"
+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;
+
+SkBitmap::Config SkImageDecoder::GetDeviceConfig()
+{
+ return gDeviceConfig;
+}
+
+void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
+{
+ gDeviceConfig = config;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder::SkImageDecoder()
+ : fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1),
+ fDitherImage(true) {
+}
+
+SkImageDecoder::~SkImageDecoder() {
+ fPeeker->safeUnref();
+ fChooser->safeUnref();
+ fAllocator->safeUnref();
+}
+
+SkImageDecoder::Format SkImageDecoder::getFormat() const {
+ return kUnknown_Format;
+}
+
+SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
+ SkRefCnt_SafeAssign(fPeeker, peeker);
+ return peeker;
+}
+
+SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
+ SkRefCnt_SafeAssign(fChooser, chooser);
+ return chooser;
+}
+
+SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
+ SkRefCnt_SafeAssign(fAllocator, alloc);
+ return alloc;
+}
+
+void SkImageDecoder::setSampleSize(int size) {
+ if (size < 1) {
+ size = 1;
+ }
+ fSampleSize = size;
+}
+
+bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
+ int height) const {
+ Chooser* chooser = fChooser;
+
+ if (NULL == chooser) { // no chooser, we just say YES to decoding :)
+ return true;
+ }
+ chooser->begin(1);
+ chooser->inspect(0, config, width, height);
+ return chooser->choose() == 0;
+}
+
+bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
+ SkColorTable* ctable) const {
+ return bitmap->allocPixels(fAllocator, ctable);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode) {
+ SkBitmap tmp;
+
+ // we reset this to false before calling onDecode
+ fShouldCancelDecode = false;
+
+ // pass a temporary bitmap, so that if we return false, we are assured of
+ // leaving the caller's bitmap untouched.
+ if (this->onDecode(stream, &tmp, pref, mode)) {
+ /* We operate on a tmp bitmap until we know we succeed. This way
+ we're sure we don't change the caller's bitmap and then later
+ return false. Returning false must mean that their parameter
+ is unchanged.
+ */
+ bm->swap(tmp);
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode) {
+ SkASSERT(file);
+ SkASSERT(bm);
+
+ SkFILEStream stream(file);
+ if (stream.isValid()) {
+ if (SkImageDecoder::DecodeStream(&stream, bm, pref, mode)) {
+ bm->pixelRef()->setURI(file);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode) {
+ if (0 == size) {
+ return false;
+ }
+ SkASSERT(buffer);
+
+ SkMemoryStream stream(buffer, size);
+ return SkImageDecoder::DecodeStream(&stream, bm, pref, mode);
+}
+
+bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode) {
+ SkASSERT(stream);
+ SkASSERT(bm);
+
+ bool success = false;
+ SkImageDecoder* codec = SkImageDecoder::Factory(stream);
+
+ if (NULL != codec) {
+ success = codec->decode(stream, bm, pref, mode);
+ delete codec;
+ }
+ return success;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+
+SkImageEncoder::~SkImageEncoder() {}
+
+bool SkImageEncoder::encodeStream(SkWStream* stream, const SkBitmap& bm,
+ int quality) {
+ quality = SkMin32(100, SkMax32(0, quality));
+ return this->onEncode(stream, bm, quality);
+}
+
+bool SkImageEncoder::encodeFile(const char file[], const SkBitmap& bm,
+ int quality) {
+ quality = SkMin32(100, SkMax32(0, quality));
+ SkFILEWStream stream(file);
+ return this->onEncode(&stream, bm, quality);
+}
+
+bool SkImageEncoder::EncodeFile(const char file[], const SkBitmap& bm, Type t,
+ int quality) {
+ SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
+ return enc.get() && enc.get()->encodeFile(file, bm, quality);
+}
+
+bool SkImageEncoder::EncodeStream(SkWStream* stream, const SkBitmap& bm, Type t,
+ int quality) {
+ SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
+ return enc.get() && enc.get()->encodeStream(stream, bm, quality);
+}
+
+#endif
+
diff --git a/src/images/SkImageDecoder_fpdfemb.cpp b/src/images/SkImageDecoder_fpdfemb.cpp
new file mode 100644
index 0000000000..7f37e3db02
--- /dev/null
+++ b/src/images/SkImageDecoder_fpdfemb.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkImageDecoder.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkColorPriv.h"
+#include "SkTDArray.h"
+
+#include "fpdfemb.h"
+
+class SkFPDFEMBImageDecoder : public SkImageDecoder {
+public:
+ SkFPDFEMBImageDecoder() {}
+
+ virtual Format getFormat() const {
+ return kBMP_Format;
+ }
+
+protected:
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode);
+
+private:
+ bool render(FPDFEMB_PAGE page, const FPDFEMB_RECT& bounds, SkBitmap* bm,
+ SkBitmap::Config prefConfig, SkImageDecoder::Mode mode);
+};
+
+SkImageDecoder* SkImageDecoder_FPDFEMB_Factory(SkStream*);
+SkImageDecoder* SkImageDecoder_FPDFEMB_Factory(SkStream* stream) {
+ static const char kPDFSig[] = { '%', 'P', 'D', 'F' };
+
+ size_t len = stream->getLength();
+ char buffer[sizeof(kPDFSig)];
+
+ SkDebugf("---- SkImageDecoder_FPDFEMB_Factory len=%d\n", len);
+
+ if (len != 12683) { return NULL; }
+
+ if (len > sizeof(kPDFSig) &&
+ stream->read(buffer, sizeof(kPDFSig)) == sizeof(kPDFSig) &&
+ !memcmp(buffer, kPDFSig, sizeof(kPDFSig))) {
+ return SkNEW(SkFPDFEMBImageDecoder);
+ }
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern "C" {
+ static void* pdf_alloc(FPDFEMB_MEMMGR* pMgr, unsigned int size) {
+ void* addr = sk_malloc_throw(size);
+ // SkDebugf("---- pdf_alloc %d %p\n", size, addr);
+ return addr;
+ }
+
+ static void* pdf_alloc_nl(FPDFEMB_MEMMGR* pMgr, unsigned int size) {
+ void* addr = sk_malloc_flags(size, 0);
+ // SkDebugf("---- pdf_alloc_nl %d %p\n", size, addr);
+ return addr;
+ }
+
+ static void* pdf_realloc(FPDFEMB_MEMMGR*, void* addr, unsigned int size) {
+ void* newaddr = sk_realloc_throw(addr, size);
+ // SkDebugf("---- pdf_realloc %p %d %p\n", addr, size, newaddr);
+ return newaddr;
+ }
+
+ static void pdf_free(FPDFEMB_MEMMGR* pMgr, void* pointer) {
+ // SkDebugf("---- pdf_free %p\n", pointer);
+ sk_free(pointer);
+ }
+
+ void FX_OUTPUT_LOG_FUNC(const char* format, ...) {
+ SkDebugf("---- LOG_FUNC %s\n", format);
+ }
+
+ static unsigned int file_getsize(FPDFEMB_FILE_ACCESS* file) {
+ SkStream* stream = (SkStream*)file->user;
+ return stream->getLength();
+ }
+
+ static FPDFEMB_RESULT file_readblock(FPDFEMB_FILE_ACCESS* file, void* dst,
+ unsigned int offset, unsigned int size) {
+ SkStream* stream = (SkStream*)file->user;
+// SkDebugf("---- readblock %p %p %d %d\n", stream, dst, offset, size);
+ if (!stream->rewind()) {
+ SkDebugf("---- rewind failed\n");
+ return FPDFERR_ERROR;
+ }
+ if (stream->skip(offset) != offset) {
+ SkDebugf("---- skip failed\n");
+ return FPDFERR_ERROR;
+ }
+ if (stream->read(dst, size) != size) {
+ SkDebugf("---- read failed\n");
+ return FPDFERR_ERROR;
+ }
+ return FPDFERR_SUCCESS;
+ }
+
+ static void pdf_oom_handler(void* memory, int size) {
+ SkDebugf("======== pdf OOM %p %d\n", memory, size);
+ }
+}
+
+static inline int PDF2Pixels(int x) { return x / 100; }
+static inline SkScalar PDF2Scalar(int x) {
+ return SkScalarMulDiv(SK_Scalar1, x, 100);
+}
+
+bool SkFPDFEMBImageDecoder::render(FPDFEMB_PAGE page, const FPDFEMB_RECT& bounds, SkBitmap* bm,
+ SkBitmap::Config prefConfig, SkImageDecoder::Mode mode) {
+ int width = PDF2Pixels(bounds.right - bounds.left);
+ int height = PDF2Pixels(bounds.top - bounds.bottom);
+
+ SkDebugf("----- bitmap size [%d %d], mode=%d\n", width, height, mode);
+ bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ return true;
+ }
+
+ // USE THE CODEC TO ALLOCATE THE PIXELS!!!!
+ if (!this->allocPixelRef(bm, NULL)) {
+ SkDebugf("----- failed to alloc pixels\n");
+ return false;
+ }
+
+ bm->eraseColor(0);
+
+ FPDFEMB_RESULT result;
+ FPDFEMB_BITMAP dib;
+
+ result = FPDFEMB_CreateDIB(width, height, FPDFDIB_BGRA, bm->getPixels(),
+ bm->rowBytes(), &dib);
+ SkDebugf("---- createdib %d\n", result);
+
+ result = FPDFEMB_StartRender(dib, page, 0, 0, width, height, 0, 0, NULL, NULL);
+ SkDebugf("---- render %d\n", result);
+
+ result = FPDFEMB_DestroyDIB(dib);
+ SkDebugf("---- destroydib %d\n", result);
+
+ SkPMColor* dst = bm->getAddr32(0, 0);
+ const uint8_t* src = (uint8_t*)dst;
+ int n = bm->getSize() >> 2;
+ for (int i = 0; i < n; i++) {
+ int b = *src++;
+ int g = *src++;
+ int r = *src++;
+ int a = *src++;
+ *dst++ = SkPackARGB32(a, r, g, b);
+ }
+
+ return true;
+}
+
+#define USE_FIXED_MEM (4 * 1024 * 1024)
+
+bool SkFPDFEMBImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config prefConfig, Mode mode) {
+
+ FPDFEMB_RESULT result;
+#ifdef USE_FIXED_MEM
+ SkAutoMalloc storage(USE_FIXED_MEM);
+ result = FPDFEMB_InitFixedMemory(storage.get(), USE_FIXED_MEM,
+ pdf_oom_handler);
+#else
+ FPDFEMB_MEMMGR memmgr;
+ memmgr.Alloc = pdf_alloc;
+ memmgr.AllocNL = pdf_alloc_nl;
+ memmgr.Realloc = pdf_realloc;
+ memmgr.Free = pdf_free;
+
+ result = FPDFEMB_Init(&memmgr);
+#endif
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory init %d, streamLen = %d\n", result, stream->getLength());
+
+ FPDFEMB_FILE_ACCESS file;
+ file.GetSize = file_getsize;
+ file.ReadBlock = file_readblock;
+ file.user = stream;
+
+ FPDFEMB_DOCUMENT document;
+ result = FPDFEMB_StartLoadDocument(&file, NULL, &document, NULL);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory open %d %p\n", result, document);
+
+ int pageCount = FPDFEMB_GetPageCount(document);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory pageCount %d\n", pageCount);
+
+ if (pageCount > 0) {
+ FPDFEMB_PAGE page;
+ result = FPDFEMB_LoadPage(document, 0, &page);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory load page %d\n", result);
+
+ int width, height;
+ result = FPDFEMB_GetPageSize(page, &width, &height);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page size %d [%d %d]\n", result, width, height);
+
+ FPDFEMB_RECT rect;
+ result = FPDFEMB_GetPageBBox(page, &rect);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page rect %d [%d %d %d %d]\n", result,
+ rect.left, rect.top, rect.right, rect.bottom);
+
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory begin page parse...\n");
+ result = FPDFEMB_StartParse(page, false, NULL);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page parse %d\n", result);
+
+ if (0 == result) {
+ this->render(page, rect, bm, prefConfig, mode);
+ }
+
+ result = FPDFEMB_ClosePage(page);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory close page %d\n", result);
+ }
+
+ result = FPDFEMB_CloseDocument(document);
+ SkDebugf("----- SkImageDecoder_FPDFEMB_Factory close %d\n", result);
+
+ // FPDFEMB_Exit();
+
+ return true;
+}
diff --git a/src/images/SkImageDecoder_libbmp.cpp b/src/images/SkImageDecoder_libbmp.cpp
new file mode 100644
index 0000000000..32a7a6de42
--- /dev/null
+++ b/src/images/SkImageDecoder_libbmp.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bmpdecoderhelper.h"
+#include "SkImageDecoder.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkColorPriv.h"
+#include "SkTDArray.h"
+
+class SkBMPImageDecoder : public SkImageDecoder {
+public:
+ SkBMPImageDecoder() {}
+
+ virtual Format getFormat() const {
+ return kBMP_Format;
+ }
+
+protected:
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode);
+};
+
+SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream*);
+SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream* stream) {
+ static const char kBmpMagic[] = { 'B', 'M' };
+
+ size_t len = stream->getLength();
+ char buffer[sizeof(kBmpMagic)];
+
+ if (len > sizeof(kBmpMagic) &&
+ stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) &&
+ !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) {
+ return SkNEW(SkBMPImageDecoder);
+ }
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
+public:
+ // we don't copy the bitmap, just remember the pointer
+ SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {}
+
+ // override from BmpDecoderCallback
+ virtual uint8* SetSize(int width, int height) {
+ fWidth = width;
+ fHeight = height;
+ if (fJustBounds) {
+ return NULL;
+ }
+
+ fRGB.setCount(width * height * 3); // 3 == r, g, b
+ return fRGB.begin();
+ }
+
+ int width() const { return fWidth; }
+ int height() const { return fHeight; }
+ uint8_t* rgb() const { return fRGB.begin(); }
+
+private:
+ SkTDArray<uint8_t> fRGB;
+ int fWidth;
+ int fHeight;
+ bool fJustBounds;
+};
+
+bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config prefConfig, Mode mode) {
+
+ size_t length = stream->getLength();
+ SkAutoMalloc storage(length);
+
+ if (stream->read(storage.get(), length) != length) {
+ return false;
+ }
+
+ const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;
+ SkBmpDecoderCallback callback(justBounds);
+
+ // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...]
+ {
+ image_codec::BmpDecoderHelper helper;
+ const int max_pixels = 16383*16383; // max width*height
+ if (!helper.DecodeImage((const char*)storage.get(), length,
+ max_pixels, &callback)) {
+ return false;
+ }
+ }
+
+ // we don't need this anymore, so free it now (before we try to allocate
+ // the bitmap's pixels) rather than waiting for its destructor
+ storage.free();
+
+ int width = callback.width();
+ int height = callback.height();
+ SkBitmap::Config config = SkBitmap::kARGB_8888_Config;
+
+ // only accept prefConfig if it makes sense for us
+ if (SkBitmap::kARGB_4444_Config == prefConfig ||
+ SkBitmap::kRGB_565_Config == config) {
+ config = prefConfig;
+ }
+
+ SkScaledBitmapSampler sampler(width, height, getSampleSize());
+
+ bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+ bm->setIsOpaque(true);
+ if (justBounds) {
+ return true;
+ }
+
+ if (!this->allocPixelRef(bm, NULL)) {
+ return false;
+ }
+
+ SkAutoLockPixels alp(*bm);
+
+ if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) {
+ return false;
+ }
+
+ const int srcRowBytes = width * 3;
+ const int dstHeight = sampler.scaledHeight();
+ const uint8_t* srcRow = callback.rgb();
+
+ srcRow += sampler.srcY0() * srcRowBytes;
+ for (int y = 0; y < dstHeight; y++) {
+ sampler.next(srcRow);
+ srcRow += sampler.srcDY() * srcRowBytes;
+ }
+ return true;
+}
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
new file mode 100644
index 0000000000..519366a18e
--- /dev/null
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -0,0 +1,340 @@
+/* libs/graphics/images/SkImageDecoder_libgif.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkImageDecoder.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkPackBits.h"
+
+#include "gif_lib.h"
+
+class SkGIFImageDecoder : public SkImageDecoder {
+public:
+ virtual Format getFormat() const {
+ return kGIF_Format;
+ }
+
+protected:
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode);
+};
+
+static const uint8_t gStartingIterlaceYValue[] = {
+ 0, 4, 2, 1
+};
+static const uint8_t gDeltaIterlaceYValue[] = {
+ 8, 8, 4, 2
+};
+
+/* Implement the GIF interlace algorithm in an iterator.
+ 1) grab every 8th line beginning at 0
+ 2) grab every 8th line beginning at 4
+ 3) grab every 4th line beginning at 2
+ 4) grab every 2nd line beginning at 1
+*/
+class GifInterlaceIter {
+public:
+ GifInterlaceIter(int height) : fHeight(height) {
+ fStartYPtr = gStartingIterlaceYValue;
+ fDeltaYPtr = gDeltaIterlaceYValue;
+
+ fCurrY = *fStartYPtr++;
+ fDeltaY = *fDeltaYPtr++;
+ }
+
+ int currY() const {
+ SkASSERT(fStartYPtr);
+ SkASSERT(fDeltaYPtr);
+ return fCurrY;
+ }
+
+ void next() {
+ SkASSERT(fStartYPtr);
+ SkASSERT(fDeltaYPtr);
+
+ int y = fCurrY + fDeltaY;
+ // We went from an if statement to a while loop so that we iterate
+ // through fStartYPtr until a valid row is found. This is so that images
+ // that are smaller than 5x5 will not trash memory.
+ while (y >= fHeight) {
+ if (gStartingIterlaceYValue +
+ SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
+ // we done
+ SkDEBUGCODE(fStartYPtr = NULL;)
+ SkDEBUGCODE(fDeltaYPtr = NULL;)
+ y = 0;
+ } else {
+ y = *fStartYPtr++;
+ fDeltaY = *fDeltaYPtr++;
+ }
+ }
+ fCurrY = y;
+ }
+
+private:
+ const int fHeight;
+ int fCurrY;
+ int fDeltaY;
+ const uint8_t* fStartYPtr;
+ const uint8_t* fDeltaYPtr;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+//#define GIF_STAMP "GIF" /* First chars in file - GIF stamp. */
+//#define GIF_STAMP_LEN (sizeof(GIF_STAMP) - 1)
+
+static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
+ int size) {
+ SkStream* stream = (SkStream*) fileType->UserData;
+ return (int) stream->read(out, size);
+}
+
+void CheckFreeExtension(SavedImage* Image) {
+ if (Image->ExtensionBlocks) {
+ FreeExtension(Image);
+ }
+}
+
+// return NULL on failure
+static const ColorMapObject* find_colormap(const GifFileType* gif) {
+ const ColorMapObject* cmap = gif->Image.ColorMap;
+ if (NULL == cmap) {
+ cmap = gif->SColorMap;
+ }
+ // some sanity checks
+ if ((unsigned)cmap->ColorCount > 256 ||
+ cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
+ cmap = NULL;
+ }
+ return cmap;
+}
+
+// return -1 if not found (i.e. we're completely opaque)
+static int find_transpIndex(const SavedImage& image, int colorCount) {
+ int transpIndex = -1;
+ for (int i = 0; i < image.ExtensionBlockCount; ++i) {
+ const ExtensionBlock* eb = image.ExtensionBlocks + i;
+ if (eb->Function == 0xF9 && eb->ByteCount == 4) {
+ if (eb->Bytes[0] & 1) {
+ transpIndex = (unsigned char)eb->Bytes[3];
+ // check for valid transpIndex
+ if (transpIndex >= colorCount) {
+ transpIndex = -1;
+ }
+ break;
+ }
+ }
+ }
+ return transpIndex;
+}
+
+static bool error_return(GifFileType* gif, const SkBitmap& bm,
+ const char msg[]) {
+#if 0
+ SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n",
+ msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable());
+#endif
+ return false;
+}
+
+bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm,
+ SkBitmap::Config prefConfig, Mode mode) {
+ GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
+ if (NULL == gif) {
+ return error_return(gif, *bm, "DGifOpen");
+ }
+
+ SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);
+
+ SavedImage temp_save;
+ temp_save.ExtensionBlocks=NULL;
+ temp_save.ExtensionBlockCount=0;
+ SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);
+
+ int width, height;
+ GifRecordType recType;
+ GifByteType *extData;
+
+ do {
+ if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
+ return error_return(gif, *bm, "DGifGetRecordType");
+ }
+
+ switch (recType) {
+ case IMAGE_DESC_RECORD_TYPE: {
+ if (DGifGetImageDesc(gif) == GIF_ERROR) {
+ return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
+ }
+
+ if (gif->ImageCount < 1) { // sanity check
+ return error_return(gif, *bm, "ImageCount < 1");
+ }
+
+ width = gif->SWidth;
+ height = gif->SHeight;
+ if (width <= 0 || height <= 0 ||
+ !this->chooseFromOneChoice(SkBitmap::kIndex8_Config,
+ width, height)) {
+ return error_return(gif, *bm, "chooseFromOneChoice");
+ }
+
+ bm->setConfig(SkBitmap::kIndex8_Config, width, height);
+ if (SkImageDecoder::kDecodeBounds_Mode == mode)
+ return true;
+
+ SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
+ const GifImageDesc& desc = image->ImageDesc;
+
+ // check for valid descriptor
+ if ( (desc.Top | desc.Left) < 0 ||
+ desc.Left + desc.Width > width ||
+ desc.Top + desc.Height > height) {
+ return error_return(gif, *bm, "TopLeft");
+ }
+
+ // now we decode the colortable
+ int colorCount = 0;
+ {
+ const ColorMapObject* cmap = find_colormap(gif);
+ if (NULL == cmap) {
+ return error_return(gif, *bm, "null cmap");
+ }
+
+ colorCount = cmap->ColorCount;
+ SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount));
+ SkPMColor* colorPtr = ctable->lockColors();
+ for (int index = 0; index < colorCount; index++)
+ colorPtr[index] = SkPackARGB32(0xFF,
+ cmap->Colors[index].Red,
+ cmap->Colors[index].Green,
+ cmap->Colors[index].Blue);
+
+ int transpIndex = find_transpIndex(temp_save, colorCount);
+ if (transpIndex < 0)
+ ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+ else
+ colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor
+ ctable->unlockColors(true);
+
+ SkAutoUnref aurts(ctable);
+ if (!this->allocPixelRef(bm, ctable)) {
+ return error_return(gif, *bm, "allocPixelRef");
+ }
+ }
+
+ SkAutoLockPixels alp(*bm);
+
+ // time to decode the scanlines
+ //
+ uint8_t* scanline = bm->getAddr8(0, 0);
+ const int rowBytes = bm->rowBytes();
+ const int innerWidth = desc.Width;
+ const int innerHeight = desc.Height;
+
+ // abort if either inner dimension is <= 0
+ if (innerWidth <= 0 || innerHeight <= 0) {
+ return error_return(gif, *bm, "non-pos inner width/height");
+ }
+
+ // are we only a subset of the total bounds?
+ if ((desc.Top | desc.Left) > 0 ||
+ innerWidth < width || innerHeight < height)
+ {
+ uint8_t fill = (uint8_t)gif->SBackGroundColor;
+ // check for valid fill index/color
+ if (fill >= (unsigned)colorCount) {
+ fill = 0;
+ }
+ memset(scanline, gif->SBackGroundColor, bm->getSize());
+ // bump our starting address
+ scanline += desc.Top * rowBytes + desc.Left;
+ }
+
+ // now decode each scanline
+ if (gif->Image.Interlace)
+ {
+ GifInterlaceIter iter(innerHeight);
+ for (int y = 0; y < innerHeight; y++)
+ {
+ uint8_t* row = scanline + iter.currY() * rowBytes;
+ if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) {
+ return error_return(gif, *bm, "interlace DGifGetLine");
+ }
+ iter.next();
+ }
+ }
+ else
+ {
+ // easy, non-interlace case
+ for (int y = 0; y < innerHeight; y++) {
+ if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
+ return error_return(gif, *bm, "DGifGetLine");
+ }
+ scanline += rowBytes;
+ }
+ }
+ goto DONE;
+ } break;
+
+ case EXTENSION_RECORD_TYPE:
+ if (DGifGetExtension(gif, &temp_save.Function,
+ &extData) == GIF_ERROR) {
+ return error_return(gif, *bm, "DGifGetExtension");
+ }
+
+ while (extData != NULL) {
+ /* Create an extension block with our data */
+ if (AddExtensionBlock(&temp_save, extData[0],
+ &extData[1]) == GIF_ERROR) {
+ return error_return(gif, *bm, "AddExtensionBlock");
+ }
+ if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
+ return error_return(gif, *bm, "DGifGetExtensionNext");
+ }
+ temp_save.Function = 0;
+ }
+ break;
+
+ case TERMINATE_RECORD_TYPE:
+ break;
+
+ default: /* Should be trapped by DGifGetRecordType */
+ break;
+ }
+ } while (recType != TERMINATE_RECORD_TYPE);
+
+DONE:
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream* stream) {
+ char buf[GIF_STAMP_LEN];
+ if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
+ if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+ memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+ memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
+ return SkNEW(SkGIFImageDecoder);
+ }
+ }
+ return NULL;
+}
+
diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
new file mode 100644
index 0000000000..b179a6b61a
--- /dev/null
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -0,0 +1,388 @@
+/* libs/graphics/images/SkImageDecoder_libico.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+#include "SkColorPriv.h"
+#include "SkTypes.h"
+
+class SkICOImageDecoder : public SkImageDecoder {
+public:
+ SkICOImageDecoder();
+
+ virtual Format getFormat() const {
+ return kICO_Format;
+ }
+
+protected:
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode);
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+//read bytes starting from the begin-th index in the buffer
+//read in Intel order, and return an integer
+
+#define readByte(buffer,begin) buffer[begin]
+#define read2Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8)
+#define read4Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8)+(buffer[begin+2]<<16)+(buffer[begin+3]<<24)
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream*);
+SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream* stream)
+{
+ //i'm going to check if we basically have 0,0,1,0 (reserved = 0, type = 1)
+ //is that required and sufficient?
+ SkAutoMalloc autoMal(4);
+ unsigned char* buf = (unsigned char*)autoMal.get();
+ stream->read((void*)buf, 4);
+ int reserved = read2Bytes(buf, 0);
+ int type = read2Bytes(buf, 2);
+ if (reserved != 0 || type != 1) //it's not an ico
+ return NULL;
+ return SkNEW(SkICOImageDecoder);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+SkICOImageDecoder::SkICOImageDecoder()
+{
+}
+
+//helpers - my function pointer will call one of these, depending on the bitCount, each time through the inner loop
+static void editPixelBit1(const int pixelNo, const unsigned char* buf,
+ const int xorOffset, int& x, int y, const int w,
+ SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit4(const int pixelNo, const unsigned char* buf,
+ const int xorOffset, int& x, int y, const int w,
+ SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit8(const int pixelNo, const unsigned char* buf,
+ const int xorOffset, int& x, int y, const int w,
+ SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit24(const int pixelNo, const unsigned char* buf,
+ const int xorOffset, int& x, int y, const int w,
+ SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+static void editPixelBit32(const int pixelNo, const unsigned char* buf,
+ const int xorOffset, int& x, int y, const int w,
+ SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors);
+
+
+static int calculateRowBytesFor8888(int w, int bitCount)
+{
+ // Default rowBytes is w << 2 for kARGB_8888
+ // In the case of a 4 bit image with an odd width, we need to add some
+ // so we can go off the end of the drawn bitmap.
+ // Add 4 to ensure that it is still a multiple of 4.
+ if (4 == bitCount && (w & 0x1)) {
+ return (w + 1) << 2;
+ }
+ // Otherwise return 0, which will allow it to be calculated automatically.
+ return 0;
+}
+
+bool SkICOImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode)
+{
+ size_t length = stream->read(NULL, 0);
+ SkAutoMalloc autoMal(length);
+ unsigned char* buf = (unsigned char*)autoMal.get();
+ if (stream->read((void*)buf, length) != length) {
+ return false;
+ }
+
+ //these should always be the same - should i use for error checking? - what about files that have some
+ //incorrect values, but still decode properly?
+ int reserved = read2Bytes(buf, 0); // 0
+ int type = read2Bytes(buf, 2); // 1
+ if (reserved != 0 || type != 1)
+ return false;
+ int count = read2Bytes(buf, 4);
+
+ //need to at least have enough space to hold the initial table of info
+ if (length < (size_t)(6 + count*16))
+ return false;
+
+ int choice;
+ Chooser* chooser = this->getChooser();
+ //FIXME:if no chooser, consider providing the largest color image
+ //what are the odds that the largest image would be monochrome?
+ if (NULL == chooser) {
+ choice = 0;
+ } else {
+ chooser->begin(count);
+ for (int i = 0; i < count; i++)
+ {
+ //need to find out the config, width, and height from the stream
+ int width = readByte(buf, 6 + i*16);
+ int height = readByte(buf, 7 + i*16);
+ int offset = read4Bytes(buf, 18 + i*16);
+ int bitCount = read2Bytes(buf, offset+14);
+ SkBitmap::Config c;
+ //currently only provide ARGB_8888_, but maybe we want kIndex8_Config for 1 and 4, and possibly 8?
+ //or maybe we'll determine this based on the provided config
+ switch (bitCount)
+ {
+ case 1:
+ case 4:
+ // In reality, at least for the moment, these will be decoded into kARGB_8888 bitmaps.
+ // However, this will be used to distinguish between the lower quality 1bpp and 4 bpp
+ // images and the higher quality images.
+ c = SkBitmap::kIndex8_Config;
+ break;
+ case 8:
+ case 24:
+ case 32:
+ c = SkBitmap::kARGB_8888_Config;
+ break;
+ default:
+ SkDEBUGF(("Image with %ibpp not supported\n", bitCount));
+ continue;
+ }
+ chooser->inspect(i, c, width, height);
+ }
+ choice = chooser->choose();
+ }
+
+ //you never know what the chooser is going to supply
+ if (choice >= count || choice < 0)
+ return false;
+
+ //skip ahead to the correct header
+ //commented out lines are not used, but if i switch to other read method, need to know how many to skip
+ //otherwise, they could be used for error checking
+ int w = readByte(buf, 6 + choice*16);
+ int h = readByte(buf, 7 + choice*16);
+ int colorCount = readByte(buf, 8 + choice*16);
+ //int reservedToo = readByte(buf, 9 + choice*16); //0
+ //int planes = read2Bytes(buf, 10 + choice*16); //1 - but often 0
+ //int fakeBitCount = read2Bytes(buf, 12 + choice*16); //should be real - usually 0
+ int size = read4Bytes(buf, 14 + choice*16); //matters?
+ int offset = read4Bytes(buf, 18 + choice*16);
+ if ((size_t)(offset + size) > length)
+ return false;
+ //int infoSize = read4Bytes(buf, offset); //40
+ //int width = read4Bytes(buf, offset+4); //should == w
+ //int height = read4Bytes(buf, offset+8); //should == 2*h
+ //int planesToo = read2Bytes(buf, offset+12); //should == 1 (does it?)
+ int bitCount = read2Bytes(buf, offset+14);
+
+ void (*placePixel)(const int pixelNo, const unsigned char* buf,
+ const int xorOffset, int& x, int y, const int w,
+ SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) = NULL;
+ switch (bitCount)
+ {
+ case 1:
+ placePixel = &editPixelBit1;
+ colorCount = 2;
+ break;
+ case 4:
+ placePixel = &editPixelBit4;
+ colorCount = 16;
+ break;
+ case 8:
+ placePixel = &editPixelBit8;
+ colorCount = 256;
+ break;
+ case 24:
+ placePixel = &editPixelBit24;
+ colorCount = 0;
+ break;
+ case 32:
+ placePixel = &editPixelBit32;
+ colorCount = 0;
+ break;
+ default:
+ SkDEBUGF(("Decoding %ibpp is unimplemented\n", bitCount));
+ return false;
+ }
+
+ //these should all be zero, but perhaps are not - need to check
+ //int compression = read4Bytes(buf, offset+16); //0
+ //int imageSize = read4Bytes(buf, offset+20); //0 - sometimes has a value
+ //int xPixels = read4Bytes(buf, offset+24); //0
+ //int yPixels = read4Bytes(buf, offset+28); //0
+ //int colorsUsed = read4Bytes(buf, offset+32) //0 - might have an actual value though
+ //int colorsImportant = read4Bytes(buf, offset+36); //0
+
+ int begin = offset + 40;
+ //this array represents the colortable
+ //if i allow other types of bitmaps, it may actually be used as a part of the bitmap
+ SkPMColor* colors = NULL;
+ int blue, green, red;
+ if (colorCount)
+ {
+ colors = new SkPMColor[colorCount];
+ for (int j = 0; j < colorCount; j++)
+ {
+ //should this be a function - maybe a #define?
+ blue = readByte(buf, begin + 4*j);
+ green = readByte(buf, begin + 4*j + 1);
+ red = readByte(buf, begin + 4*j + 2);
+ colors[j] = SkPackARGB32(0xFF, red & 0xFF, green & 0xFF, blue & 0xFF);
+ }
+ }
+ int bitWidth = w*bitCount;
+ int test = bitWidth & 0x1F;
+ int mask = -(((test >> 4) | (test >> 3) | (test >> 2) | (test >> 1) | test) & 0x1); //either 0xFFFFFFFF or 0
+ int lineBitWidth = (bitWidth & 0xFFFFFFE0) + (0x20 & mask);
+ int lineWidth = lineBitWidth/bitCount;
+
+ int xorOffset = begin + colorCount*4; //beginning of the color bitmap
+ //other read method means we will just be here already
+ int andOffset = xorOffset + ((lineWidth*h*bitCount) >> 3);
+
+ /*int */test = w & 0x1F; //the low 5 bits - we are rounding up to the next 32 (2^5)
+ /*int */mask = -(((test >> 4) | (test >> 3) | (test >> 2) | (test >> 1) | test) & 0x1); //either 0xFFFFFFFF or 0
+ int andLineWidth = (w & 0xFFFFFFE0) + (0x20 & mask);
+ //if we allow different Configs, everything is the same til here
+ //change the config, and use different address getter, and place index vs color, and add the color table
+ //FIXME: what is the tradeoff in size?
+ //if the andbitmap (mask) is all zeroes, then we can easily do an index bitmap
+ //however, with small images with large colortables, maybe it's better to still do argb_8888
+
+ bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
+
+ if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ delete[] colors;
+ return true;
+ }
+
+ if (!this->allocPixelRef(bm, NULL))
+ {
+ delete[] colors;
+ return false;
+ }
+
+ SkAutoLockPixels alp(*bm);
+
+ for (int y = 0; y < h; y++)
+ {
+ for (int x = 0; x < w; x++)
+ {
+ //U32* address = bm->getAddr32(x, y);
+
+ //check the alpha bit first, but pass it along to the function to figure out how to deal with it
+ int andPixelNo = andLineWidth*(h-y-1)+x;
+ //only need to get a new alphaByte when x %8 == 0
+ //but that introduces an if and a mod - probably much slower
+ //that's ok, it's just a read of an array, not a stream
+ int alphaByte = readByte(buf, andOffset + (andPixelNo >> 3));
+ int shift = 7 - (andPixelNo & 0x7);
+ int m = 1 << shift;
+
+ int pixelNo = lineWidth*(h-y-1)+x;
+ placePixel(pixelNo, buf, xorOffset, x, y, w, bm, alphaByte, m, shift, colors);
+
+ }
+ }
+
+ delete [] colors;
+ //ensure we haven't read off the end?
+ //of course this doesn't help us if the andOffset was a lie...
+ //return andOffset + (andLineWidth >> 3) <= length;
+ return true;
+} //onDecode
+
+//function to place the pixel, determined by the bitCount
+static void editPixelBit1(const int pixelNo, const unsigned char* buf,
+ const int xorOffset, int& x, int y, const int w,
+ SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+ // note that this should be the same as/similar to the AND bitmap
+ SkPMColor* address = bm->getAddr32(x,y);
+ int byte = readByte(buf, xorOffset + (pixelNo >> 3));
+ int colorBit;
+ int alphaBit;
+ // Read all of the bits in this byte.
+ int i = x + 8;
+ // Pin to the width so we do not write outside the bounds of
+ // our color table.
+ i = i > w ? w : i;
+ // While loop to check all 8 bits individually.
+ while (x < i)
+ {
+
+ colorBit = (byte & m) >> shift;
+ alphaBit = (alphaByte & m) >> shift;
+ *address = (alphaBit-1)&(colors[colorBit]);
+ x++;
+ // setup for the next pixel
+ address = address + 1;
+ m = m >> 1;
+ shift -= 1;
+ }
+ x--;
+}
+static void editPixelBit4(const int pixelNo, const unsigned char* buf,
+ const int xorOffset, int& x, int y, const int w,
+ SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+ SkPMColor* address = bm->getAddr32(x, y);
+ int byte = readByte(buf, xorOffset + (pixelNo >> 1));
+ int pixel = (byte >> 4) & 0xF;
+ int alphaBit = (alphaByte & m) >> shift;
+ *address = (alphaBit-1)&(colors[pixel]);
+ x++;
+ //if w is odd, x may be the same as w, which means we are writing to an unused portion of the bitmap
+ //but that's okay, since i've added an extra rowByte for just this purpose
+ address = address + 1;
+ pixel = byte & 0xF;
+ m = m >> 1;
+ alphaBit = (alphaByte & m) >> (shift-1);
+ //speed up trick here
+ *address = (alphaBit-1)&(colors[pixel]);
+}
+
+static void editPixelBit8(const int pixelNo, const unsigned char* buf,
+ const int xorOffset, int& x, int y, const int w,
+ SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+ SkPMColor* address = bm->getAddr32(x, y);
+ int pixel = readByte(buf, xorOffset + pixelNo);
+ int alphaBit = (alphaByte & m) >> shift;
+ *address = (alphaBit-1)&(colors[pixel]);
+}
+
+static void editPixelBit24(const int pixelNo, const unsigned char* buf,
+ const int xorOffset, int& x, int y, const int w,
+ SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+ SkPMColor* address = bm->getAddr32(x, y);
+ int blue = readByte(buf, xorOffset + 3*pixelNo);
+ int green = readByte(buf, xorOffset + 3*pixelNo + 1);
+ int red = readByte(buf, xorOffset + 3*pixelNo + 2);
+ int alphaBit = (alphaByte & m) >> shift;
+ //alphaBit == 1 => alpha = 0
+ int alpha = (alphaBit-1) & 0xFF;
+ *address = SkPackARGB32(alpha, red & alpha, green & alpha, blue & alpha);
+}
+
+static void editPixelBit32(const int pixelNo, const unsigned char* buf,
+ const int xorOffset, int& x, int y, const int w,
+ SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors)
+{
+ SkPMColor* address = bm->getAddr32(x, y);
+ int blue = readByte(buf, xorOffset + 4*pixelNo);
+ int green = readByte(buf, xorOffset + 4*pixelNo + 1);
+ int red = readByte(buf, xorOffset + 4*pixelNo + 2);
+ int alphaBit = (alphaByte & m) >> shift;
+ int alpha = readByte(buf, xorOffset + 4*pixelNo + 3) & ((alphaBit-1)&0xFF);
+ *address = SkPackARGB32(alpha, red & alpha, green & alpha, blue & alpha);
+}
+
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
new file mode 100644
index 0000000000..492de2374f
--- /dev/null
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -0,0 +1,811 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkImageDecoder.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+#include <stdio.h>
+extern "C" {
+ #include "jpeglib.h"
+ #include "jerror.h"
+}
+
+// this enables timing code to report milliseconds for an encode
+//#define TIME_ENCODE
+//#define TIME_DECODE
+
+// this enables our rgb->yuv code, which is faster than libjpeg on ARM
+// disable for the moment, as we have some glitches when width != multiple of 4
+#define WE_CONVERT_TO_YUV
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+class SkJPEGImageDecoder : public SkImageDecoder {
+public:
+ virtual Format getFormat() const {
+ return kJPEG_Format;
+ }
+
+protected:
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode);
+};
+
+SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream* stream) {
+ static const char gHeader[] = { 0xFF, 0xD8, 0xFF };
+ static const size_t HEADER_SIZE = sizeof(gHeader);
+
+ char buffer[HEADER_SIZE];
+ size_t len = stream->read(buffer, HEADER_SIZE);
+
+ if (len != HEADER_SIZE) {
+ return NULL; // can't read enough
+ }
+
+ if (memcmp(buffer, gHeader, HEADER_SIZE)) {
+ return NULL;
+ }
+
+ return SkNEW(SkJPEGImageDecoder);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+#include "SkTime.h"
+
+class AutoTimeMillis {
+public:
+ AutoTimeMillis(const char label[]) : fLabel(label) {
+ if (!fLabel) {
+ fLabel = "";
+ }
+ fNow = SkTime::GetMSecs();
+ }
+ ~AutoTimeMillis() {
+ SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
+ }
+private:
+ const char* fLabel;
+ SkMSec fNow;
+};
+
+/* our source struct for directing jpeg to our stream object
+*/
+struct sk_source_mgr : jpeg_source_mgr {
+ sk_source_mgr(SkStream* stream, SkImageDecoder* decoder);
+
+ SkStream* fStream;
+ const void* fMemoryBase;
+ size_t fMemoryBaseSize;
+ SkImageDecoder* fDecoder;
+ enum {
+ kBufferSize = 1024
+ };
+ char fBuffer[kBufferSize];
+};
+
+/* Automatically clean up after throwing an exception */
+class JPEGAutoClean {
+public:
+ JPEGAutoClean(): cinfo_ptr(NULL) {}
+ ~JPEGAutoClean() {
+ if (cinfo_ptr) {
+ jpeg_destroy_decompress(cinfo_ptr);
+ }
+ }
+ void set(jpeg_decompress_struct* info) {
+ cinfo_ptr = info;
+ }
+private:
+ jpeg_decompress_struct* cinfo_ptr;
+};
+
+static void sk_init_source(j_decompress_ptr cinfo) {
+ sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
+ src->next_input_byte = (const JOCTET*)src->fBuffer;
+ src->bytes_in_buffer = 0;
+}
+
+static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) {
+ sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
+ if (src->fDecoder != NULL && src->fDecoder->shouldCancelDecode()) {
+ return FALSE;
+ }
+ size_t bytes = src->fStream->read(src->fBuffer, sk_source_mgr::kBufferSize);
+ // note that JPEG is happy with less than the full read,
+ // as long as the result is non-zero
+ if (bytes == 0) {
+ return FALSE;
+ }
+
+ src->next_input_byte = (const JOCTET*)src->fBuffer;
+ src->bytes_in_buffer = bytes;
+ return TRUE;
+}
+
+static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+ SkASSERT(num_bytes > 0);
+
+ sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
+
+ long bytesToSkip = num_bytes - src->bytes_in_buffer;
+
+ // check if the skip amount exceeds the current buffer
+ if (bytesToSkip > 0) {
+ size_t bytes = src->fStream->skip(bytesToSkip);
+ if (bytes != (size_t)bytesToSkip) {
+// SkDebugf("xxxxxxxxxxxxxx failure to skip request %d actual %d\n", bytesToSkip, bytes);
+ cinfo->err->error_exit((j_common_ptr)cinfo);
+ }
+ src->next_input_byte = (const JOCTET*)src->fBuffer;
+ src->bytes_in_buffer = 0;
+ } else {
+ src->next_input_byte += num_bytes;
+ src->bytes_in_buffer -= num_bytes;
+ }
+}
+
+static boolean sk_resync_to_restart(j_decompress_ptr cinfo, int desired) {
+ sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
+
+ // what is the desired param for???
+
+ if (!src->fStream->rewind()) {
+ SkDebugf("xxxxxxxxxxxxxx failure to rewind\n");
+ cinfo->err->error_exit((j_common_ptr)cinfo);
+ return FALSE;
+ }
+ src->next_input_byte = (const JOCTET*)src->fBuffer;
+ src->bytes_in_buffer = 0;
+ return TRUE;
+}
+
+static void sk_term_source(j_decompress_ptr /*cinfo*/) {}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void skmem_init_source(j_decompress_ptr cinfo) {
+ sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
+ src->next_input_byte = (const JOCTET*)src->fMemoryBase;
+ src->bytes_in_buffer = src->fMemoryBaseSize;
+}
+
+static boolean skmem_fill_input_buffer(j_decompress_ptr cinfo) {
+ SkDebugf("xxxxxxxxxxxxxx skmem_fill_input_buffer called\n");
+ return FALSE;
+}
+
+static void skmem_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+ sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
+// SkDebugf("xxxxxxxxxxxxxx skmem_skip_input_data called %d\n", num_bytes);
+ src->next_input_byte = (const JOCTET*)((const char*)src->next_input_byte + num_bytes);
+ src->bytes_in_buffer -= num_bytes;
+}
+
+static boolean skmem_resync_to_restart(j_decompress_ptr cinfo, int desired) {
+ SkDebugf("xxxxxxxxxxxxxx skmem_resync_to_restart called\n");
+ return TRUE;
+}
+
+static void skmem_term_source(j_decompress_ptr /*cinfo*/) {}
+
+///////////////////////////////////////////////////////////////////////////////
+
+sk_source_mgr::sk_source_mgr(SkStream* stream, SkImageDecoder* decoder) : fStream(stream) {
+ fDecoder = decoder;
+ const void* baseAddr = stream->getMemoryBase();
+ if (baseAddr && false) {
+ fMemoryBase = baseAddr;
+ fMemoryBaseSize = stream->getLength();
+
+ init_source = skmem_init_source;
+ fill_input_buffer = skmem_fill_input_buffer;
+ skip_input_data = skmem_skip_input_data;
+ resync_to_restart = skmem_resync_to_restart;
+ term_source = skmem_term_source;
+ } else {
+ fMemoryBase = NULL;
+ fMemoryBaseSize = 0;
+
+ init_source = sk_init_source;
+ fill_input_buffer = sk_fill_input_buffer;
+ skip_input_data = sk_skip_input_data;
+ resync_to_restart = sk_resync_to_restart;
+ term_source = sk_term_source;
+ }
+// SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize);
+}
+
+#include <setjmp.h>
+
+struct sk_error_mgr : jpeg_error_mgr {
+ jmp_buf fJmpBuf;
+};
+
+static void sk_error_exit(j_common_ptr cinfo) {
+ sk_error_mgr* error = (sk_error_mgr*)cinfo->err;
+
+ (*error->output_message) (cinfo);
+
+ /* Let the memory manager delete any temp files before we die */
+ jpeg_destroy(cinfo);
+
+ longjmp(error->fJmpBuf, -1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer,
+ int count) {
+ for (int i = 0; i < count; i++) {
+ JSAMPLE* rowptr = (JSAMPLE*)buffer;
+ int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
+ if (row_count != 1) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// This guy exists just to aid in debugging, as it allows debuggers to just
+// set a break-point in one place to see all error exists.
+static bool return_false(const jpeg_decompress_struct& cinfo,
+ const SkBitmap& bm, const char msg[]) {
+#if 0
+ SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
+ cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
+ bm.width(), bm.height());
+#endif
+ return false; // must always return false
+}
+
+bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config prefConfig, Mode mode) {
+#ifdef TIME_DECODE
+ AutoTimeMillis atm("JPEG Decode");
+#endif
+
+ SkAutoMalloc srcStorage;
+ JPEGAutoClean autoClean;
+
+ jpeg_decompress_struct cinfo;
+ sk_error_mgr sk_err;
+ sk_source_mgr sk_stream(stream, this);
+
+ cinfo.err = jpeg_std_error(&sk_err);
+ sk_err.error_exit = sk_error_exit;
+
+ // All objects need to be instantiated before this setjmp call so that
+ // they will be cleaned up properly if an error occurs.
+ if (setjmp(sk_err.fJmpBuf)) {
+ return return_false(cinfo, *bm, "setjmp");
+ }
+
+ jpeg_create_decompress(&cinfo);
+ autoClean.set(&cinfo);
+
+ //jpeg_stdio_src(&cinfo, file);
+ cinfo.src = &sk_stream;
+
+ int status = jpeg_read_header(&cinfo, true);
+ if (status != JPEG_HEADER_OK) {
+ return return_false(cinfo, *bm, "read_header");
+ }
+
+ /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
+ can) much faster that we, just use their num/denom api to approximate
+ the size.
+ */
+ int sampleSize = this->getSampleSize();
+
+ cinfo.dct_method = JDCT_IFAST;
+ cinfo.scale_num = 1;
+ cinfo.scale_denom = sampleSize;
+
+ /* this gives about 30% performance improvement. In theory it may
+ reduce the visual quality, in practice I'm not seeing a difference
+ */
+ cinfo.do_fancy_upsampling = 0;
+
+ /* this gives another few percents */
+ cinfo.do_block_smoothing = 0;
+
+ /* default format is RGB */
+ cinfo.out_color_space = JCS_RGB;
+
+ SkBitmap::Config config = prefConfig;
+ // if no user preference, see what the device recommends
+ if (config == SkBitmap::kNo_Config)
+ config = SkImageDecoder::GetDeviceConfig();
+
+ // only these make sense for jpegs
+ if (config != SkBitmap::kARGB_8888_Config &&
+ config != SkBitmap::kARGB_4444_Config &&
+ config != SkBitmap::kRGB_565_Config) {
+ config = SkBitmap::kARGB_8888_Config;
+ }
+
+#ifdef ANDROID_RGB
+ cinfo.dither_mode = JDITHER_NONE;
+ if (config == SkBitmap::kARGB_8888_Config) {
+ cinfo.out_color_space = JCS_RGBA_8888;
+ } else if (config == SkBitmap::kRGB_565_Config) {
+ if (sampleSize == 1) {
+ // SkScaledBitmapSampler can't handle RGB_565 yet,
+ // so don't even try.
+ cinfo.out_color_space = JCS_RGB_565;
+ if (this->getDitherImage()) {
+ cinfo.dither_mode = JDITHER_ORDERED;
+ }
+ }
+ }
+#endif
+
+ /* image_width and image_height are the original dimensions, available
+ after jpeg_read_header(). To see the scaled dimensions, we have to call
+ jpeg_start_decompress(), and then read output_width and output_height.
+ */
+ if (!jpeg_start_decompress(&cinfo)) {
+ return return_false(cinfo, *bm, "start_decompress");
+ }
+
+ /* If we need to better match the request, we might examine the image and
+ output dimensions, and determine if the downsampling jpeg provided is
+ not sufficient. If so, we can recompute a modified sampleSize value to
+ make up the difference.
+
+ To skip this additional scaling, just set sampleSize = 1; below.
+ */
+ sampleSize = sampleSize * cinfo.output_width / cinfo.image_width;
+
+
+ // should we allow the Chooser (if present) to pick a config for us???
+ if (!this->chooseFromOneChoice(config, cinfo.output_width,
+ cinfo.output_height)) {
+ return return_false(cinfo, *bm, "chooseFromOneChoice");
+ }
+
+#ifdef ANDROID_RGB
+ /* short-circuit the SkScaledBitmapSampler when possible, as this gives
+ a significant performance boost.
+ */
+ if (sampleSize == 1 &&
+ ((config == SkBitmap::kARGB_8888_Config &&
+ cinfo.out_color_space == JCS_RGBA_8888) ||
+ (config == SkBitmap::kRGB_565_Config &&
+ cinfo.out_color_space == JCS_RGB_565)))
+ {
+ bm->setConfig(config, cinfo.output_width, cinfo.output_height);
+ bm->setIsOpaque(true);
+ if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ return true;
+ }
+ if (!this->allocPixelRef(bm, NULL)) {
+ return return_false(cinfo, *bm, "allocPixelRef");
+ }
+ SkAutoLockPixels alp(*bm);
+ JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
+ INT32 const bpr = bm->rowBytes();
+
+ while (cinfo.output_scanline < cinfo.output_height) {
+ int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
+ // if row_count == 0, then we didn't get a scanline, so abort.
+ // if we supported partial images, we might return true in this case
+ if (0 == row_count) {
+ return return_false(cinfo, *bm, "read_scanlines");
+ }
+ if (this->shouldCancelDecode()) {
+ return return_false(cinfo, *bm, "shouldCancelDecode");
+ }
+ rowptr += bpr;
+ }
+ jpeg_finish_decompress(&cinfo);
+ return true;
+ }
+#endif
+
+ // check for supported formats
+ SkScaledBitmapSampler::SrcConfig sc;
+ if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
+ sc = SkScaledBitmapSampler::kRGB;
+#ifdef ANDROID_RGB
+ } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
+ sc = SkScaledBitmapSampler::kRGBX;
+ //} else if (JCS_RGB_565 == cinfo.out_color_space) {
+ // sc = SkScaledBitmapSampler::kRGB_565;
+#endif
+ } else if (1 == cinfo.out_color_components &&
+ JCS_GRAYSCALE == cinfo.out_color_space) {
+ sc = SkScaledBitmapSampler::kGray;
+ } else {
+ return return_false(cinfo, *bm, "jpeg colorspace");
+ }
+
+ SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height,
+ sampleSize);
+
+ bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+ // jpegs are always opauqe (i.e. have no per-pixel alpha)
+ bm->setIsOpaque(true);
+
+ if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ return true;
+ }
+ if (!this->allocPixelRef(bm, NULL)) {
+ return return_false(cinfo, *bm, "allocPixelRef");
+ }
+
+ SkAutoLockPixels alp(*bm);
+ if (!sampler.begin(bm, sc, this->getDitherImage())) {
+ return return_false(cinfo, *bm, "sampler.begin");
+ }
+
+ uint8_t* srcRow = (uint8_t*)srcStorage.alloc(cinfo.output_width * 4);
+
+ // Possibly skip initial rows [sampler.srcY0]
+ if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
+ return return_false(cinfo, *bm, "skip rows");
+ }
+
+ // now loop through scanlines until y == bm->height() - 1
+ for (int y = 0;; y++) {
+ JSAMPLE* rowptr = (JSAMPLE*)srcRow;
+ int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
+ if (0 == row_count) {
+ return return_false(cinfo, *bm, "read_scanlines");
+ }
+ if (this->shouldCancelDecode()) {
+ return return_false(cinfo, *bm, "shouldCancelDecode");
+ }
+
+ sampler.next(srcRow);
+ if (bm->height() - 1 == y) {
+ // we're done
+ break;
+ }
+
+ if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
+ return return_false(cinfo, *bm, "skip rows");
+ }
+ }
+
+ // we formally skip the rest, so we don't get a complaint from libjpeg
+ if (!skip_src_rows(&cinfo, srcRow,
+ cinfo.output_height - cinfo.output_scanline)) {
+ return return_false(cinfo, *bm, "skip rows");
+ }
+ jpeg_finish_decompress(&cinfo);
+
+// SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+
+#include "SkColorPriv.h"
+
+// taken from jcolor.c in libjpeg
+#if 0 // 16bit - precise but slow
+ #define CYR 19595 // 0.299
+ #define CYG 38470 // 0.587
+ #define CYB 7471 // 0.114
+
+ #define CUR -11059 // -0.16874
+ #define CUG -21709 // -0.33126
+ #define CUB 32768 // 0.5
+
+ #define CVR 32768 // 0.5
+ #define CVG -27439 // -0.41869
+ #define CVB -5329 // -0.08131
+
+ #define CSHIFT 16
+#else // 8bit - fast, slightly less precise
+ #define CYR 77 // 0.299
+ #define CYG 150 // 0.587
+ #define CYB 29 // 0.114
+
+ #define CUR -43 // -0.16874
+ #define CUG -85 // -0.33126
+ #define CUB 128 // 0.5
+
+ #define CVR 128 // 0.5
+ #define CVG -107 // -0.41869
+ #define CVB -21 // -0.08131
+
+ #define CSHIFT 8
+#endif
+
+static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
+ int r = SkGetPackedR32(c);
+ int g = SkGetPackedG32(c);
+ int b = SkGetPackedB32(c);
+
+ int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
+ int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
+ int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
+
+ dst[0] = SkToU8(y);
+ dst[1] = SkToU8(u + 128);
+ dst[2] = SkToU8(v + 128);
+}
+
+static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
+ int r = SkGetPackedR4444(c);
+ int g = SkGetPackedG4444(c);
+ int b = SkGetPackedB4444(c);
+
+ int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
+ int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
+ int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
+
+ dst[0] = SkToU8(y);
+ dst[1] = SkToU8(u + 128);
+ dst[2] = SkToU8(v + 128);
+}
+
+static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
+ int r = SkGetPackedR16(c);
+ int g = SkGetPackedG16(c);
+ int b = SkGetPackedB16(c);
+
+ int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
+ int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
+ int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
+
+ dst[0] = SkToU8(y);
+ dst[1] = SkToU8(u + 128);
+ dst[2] = SkToU8(v + 128);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
+ const void* SK_RESTRICT src, int width,
+ const SkPMColor* SK_RESTRICT ctable);
+
+static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
+ const void* SK_RESTRICT srcRow, int width,
+ const SkPMColor*) {
+ const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
+ while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+ rgb2yuv_32(dst, *src++);
+#else
+ uint32_t c = *src++;
+ dst[0] = SkGetPackedR32(c);
+ dst[1] = SkGetPackedG32(c);
+ dst[2] = SkGetPackedB32(c);
+#endif
+ dst += 3;
+ }
+}
+
+static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
+ const void* SK_RESTRICT srcRow, int width,
+ const SkPMColor*) {
+ const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
+ while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+ rgb2yuv_4444(dst, *src++);
+#else
+ SkPMColor16 c = *src++;
+ dst[0] = SkPacked4444ToR32(c);
+ dst[1] = SkPacked4444ToG32(c);
+ dst[2] = SkPacked4444ToB32(c);
+#endif
+ dst += 3;
+ }
+}
+
+static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
+ const void* SK_RESTRICT srcRow, int width,
+ const SkPMColor*) {
+ const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
+ while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+ rgb2yuv_16(dst, *src++);
+#else
+ uint16_t c = *src++;
+ dst[0] = SkPacked16ToR32(c);
+ dst[1] = SkPacked16ToG32(c);
+ dst[2] = SkPacked16ToB32(c);
+#endif
+ dst += 3;
+ }
+}
+
+static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
+ const void* SK_RESTRICT srcRow, int width,
+ const SkPMColor* SK_RESTRICT ctable) {
+ const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
+ while (--width >= 0) {
+#ifdef WE_CONVERT_TO_YUV
+ rgb2yuv_32(dst, ctable[*src++]);
+#else
+ uint32_t c = ctable[*src++];
+ dst[0] = SkGetPackedR32(c);
+ dst[1] = SkGetPackedG32(c);
+ dst[2] = SkGetPackedB32(c);
+#endif
+ dst += 3;
+ }
+}
+
+static WriteScanline ChooseWriter(const SkBitmap& bm) {
+ switch (bm.config()) {
+ case SkBitmap::kARGB_8888_Config:
+ return Write_32_YUV;
+ case SkBitmap::kRGB_565_Config:
+ return Write_16_YUV;
+ case SkBitmap::kARGB_4444_Config:
+ return Write_4444_YUV;
+ case SkBitmap::kIndex8_Config:
+ return Write_Index_YUV;
+ default:
+ return NULL;
+ }
+}
+
+struct sk_destination_mgr : jpeg_destination_mgr {
+ sk_destination_mgr(SkWStream* stream);
+
+ SkWStream* fStream;
+
+ enum {
+ kBufferSize = 1024
+ };
+ uint8_t fBuffer[kBufferSize];
+};
+
+static void sk_init_destination(j_compress_ptr cinfo) {
+ sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
+
+ dest->next_output_byte = dest->fBuffer;
+ dest->free_in_buffer = sk_destination_mgr::kBufferSize;
+}
+
+static boolean sk_empty_output_buffer(j_compress_ptr cinfo) {
+ sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
+
+// if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize - dest->free_in_buffer))
+ if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize)) {
+ ERREXIT(cinfo, JERR_FILE_WRITE);
+ return false;
+ }
+
+ dest->next_output_byte = dest->fBuffer;
+ dest->free_in_buffer = sk_destination_mgr::kBufferSize;
+ return TRUE;
+}
+
+static void sk_term_destination (j_compress_ptr cinfo) {
+ sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
+
+ size_t size = sk_destination_mgr::kBufferSize - dest->free_in_buffer;
+ if (size > 0) {
+ if (!dest->fStream->write(dest->fBuffer, size)) {
+ ERREXIT(cinfo, JERR_FILE_WRITE);
+ return;
+ }
+ }
+ dest->fStream->flush();
+}
+
+sk_destination_mgr::sk_destination_mgr(SkWStream* stream)
+ : fStream(stream) {
+ this->init_destination = sk_init_destination;
+ this->empty_output_buffer = sk_empty_output_buffer;
+ this->term_destination = sk_term_destination;
+}
+
+class SkJPEGImageEncoder : public SkImageEncoder {
+protected:
+ virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
+#ifdef TIME_ENCODE
+ AutoTimeMillis atm("JPEG Encode");
+#endif
+
+ const WriteScanline writer = ChooseWriter(bm);
+ if (NULL == writer) {
+ return false;
+ }
+
+ SkAutoLockPixels alp(bm);
+ if (NULL == bm.getPixels()) {
+ return false;
+ }
+
+ jpeg_compress_struct cinfo;
+ sk_error_mgr sk_err;
+ sk_destination_mgr sk_wstream(stream);
+
+ // allocate these before set call setjmp
+ SkAutoMalloc oneRow;
+ SkAutoLockColors ctLocker;
+
+ cinfo.err = jpeg_std_error(&sk_err);
+ sk_err.error_exit = sk_error_exit;
+ if (setjmp(sk_err.fJmpBuf)) {
+ return false;
+ }
+ jpeg_create_compress(&cinfo);
+
+ cinfo.dest = &sk_wstream;
+ cinfo.image_width = bm.width();
+ cinfo.image_height = bm.height();
+ cinfo.input_components = 3;
+#ifdef WE_CONVERT_TO_YUV
+ cinfo.in_color_space = JCS_YCbCr;
+#else
+ cinfo.in_color_space = JCS_RGB;
+#endif
+ cinfo.input_gamma = 1;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
+ cinfo.dct_method = JDCT_IFAST;
+
+ jpeg_start_compress(&cinfo, TRUE);
+
+ const int width = bm.width();
+ uint8_t* oneRowP = (uint8_t*)oneRow.alloc(width * 3);
+
+ const SkPMColor* colors = ctLocker.lockColors(bm);
+ const void* srcRow = bm.getPixels();
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
+
+ writer(oneRowP, srcRow, width, colors);
+ row_pointer[0] = oneRowP;
+ (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
+ }
+
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
+ return true;
+ }
+};
+
+SkImageEncoder* SkImageEncoder_JPEG_Factory();
+SkImageEncoder* SkImageEncoder_JPEG_Factory() {
+ return SkNEW(SkJPEGImageEncoder);
+}
+
+#endif /* SK_SUPPORT_IMAGE_ENCODE */
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkImageDecoder::UnitTest() {
+ SkBitmap bm;
+
+ (void)SkImageDecoder::DecodeFile("logo.jpg", &bm);
+}
+
+#endif
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
new file mode 100644
index 0000000000..862ebf1d98
--- /dev/null
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -0,0 +1,795 @@
+/* libs/graphics/images/SkImageDecoder_libpng.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkImageDecoder.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkMath.h"
+#include "SkScaledBitmapSampler.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+extern "C" {
+#include "png.h"
+}
+
+class SkPNGImageDecoder : public SkImageDecoder {
+public:
+ virtual Format getFormat() const {
+ return kPNG_Format;
+ }
+
+protected:
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode);
+};
+
+#ifndef png_jmpbuf
+# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
+#endif
+
+#define PNG_BYTES_TO_CHECK 4
+
+/* Automatically clean up after throwing an exception */
+struct PNGAutoClean {
+ PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
+ ~PNGAutoClean() {
+ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+ }
+private:
+ png_structp png_ptr;
+ png_infop info_ptr;
+};
+
+SkImageDecoder* SkImageDecoder_PNG_Factory(SkStream* stream) {
+ char buf[PNG_BYTES_TO_CHECK];
+ if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
+ !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
+ return SkNEW(SkPNGImageDecoder);
+ }
+ return NULL;
+}
+
+static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
+ SkStream* sk_stream = (SkStream*) png_ptr->io_ptr;
+ size_t bytes = sk_stream->read(data, length);
+ if (bytes != length) {
+ png_error(png_ptr, "Read Error!");
+ }
+}
+
+static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
+ SkImageDecoder::Peeker* peeker =
+ (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
+ // peek() returning true means continue decoding
+ return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ?
+ 1 : -1;
+}
+
+static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
+#if 0
+ SkDebugf("------ png error %s\n", msg);
+#endif
+ longjmp(png_jmpbuf(png_ptr), 1);
+}
+
+static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
+ for (int i = 0; i < count; i++) {
+ uint8_t* tmp = storage;
+ png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
+ }
+}
+
+static bool pos_le(int value, int max) {
+ return value > 0 && value <= max;
+}
+
+static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
+ SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
+
+ bool reallyHasAlpha = false;
+
+ for (int y = bm->height() - 1; y >= 0; --y) {
+ SkPMColor* p = bm->getAddr32(0, y);
+ for (int x = bm->width() - 1; x >= 0; --x) {
+ if (match == *p) {
+ *p = 0;
+ reallyHasAlpha = true;
+ }
+ p += 1;
+ }
+ }
+ return reallyHasAlpha;
+}
+
+bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
+ SkBitmap::Config prefConfig, Mode mode) {
+// SkAutoTrace apr("SkPNGImageDecoder::onDecode");
+
+ /* Create and initialize the png_struct with the desired error handler
+ * functions. If you want to use the default stderr and longjump method,
+ * you can supply NULL for the last three parameters. We also supply the
+ * the compiler header file version, so that we know if the application
+ * was compiled with a compatible version of the library. */
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+ NULL, sk_error_fn, NULL);
+ // png_voidp user_error_ptr, user_error_fn, user_warning_fn);
+ if (png_ptr == NULL) {
+ return false;
+ }
+
+ /* Allocate/initialize the memory for image information. */
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL) {
+ png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
+ return false;
+ }
+
+ PNGAutoClean autoClean(png_ptr, info_ptr);
+
+ /* Set error handling if you are using the setjmp/longjmp method (this is
+ * the normal method of doing things with libpng). REQUIRED unless you
+ * set up your own error handlers in the png_create_read_struct() earlier.
+ */
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ return false;
+ }
+
+ /* If you are using replacement read functions, instead of calling
+ * png_init_io() here you would call:
+ */
+ png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
+ /* where user_io_ptr is a structure you want available to the callbacks */
+ /* If we have already read some of the signature */
+// png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
+
+ // hookup our peeker so we can see any user-chunks the caller may be interested in
+ png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
+ if (this->getPeeker()) {
+ png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);
+ }
+
+ /* The call to png_read_info() gives us all of the information from the
+ * PNG file before the first IDAT (image data chunk). */
+ png_read_info(png_ptr, info_ptr);
+ png_uint_32 origWidth, origHeight;
+ int bit_depth, color_type, interlace_type;
+ png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type,
+ &interlace_type, int_p_NULL, int_p_NULL);
+
+ /* tell libpng to strip 16 bit/color files down to 8 bits/color */
+ if (bit_depth == 16) {
+ png_set_strip_16(png_ptr);
+ }
+ /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
+ * byte into separate bytes (useful for paletted and grayscale images). */
+ if (bit_depth < 8) {
+ png_set_packing(png_ptr);
+ }
+ /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+ png_set_gray_1_2_4_to_8(png_ptr);
+ }
+
+ /* Make a grayscale image into RGB. */
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(png_ptr);
+ }
+
+ SkBitmap::Config config;
+ bool hasAlpha = false;
+ bool doDither = this->getDitherImage();
+ SkPMColor theTranspColor = 0; // 0 tells us not to try to match
+
+ // check for sBIT chunk data, in case we should disable dithering because
+ // our data is not truely 8bits per component
+ if (doDither) {
+#if 0
+ SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red,
+ info_ptr->sig_bit.green, info_ptr->sig_bit.blue,
+ info_ptr->sig_bit.alpha);
+#endif
+ // 0 seems to indicate no information available
+ if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) &&
+ pos_le(info_ptr->sig_bit.green, SK_G16_BITS) &&
+ pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) {
+ doDither = false;
+ }
+ }
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ config = SkBitmap::kIndex8_Config; // defer sniffing for hasAlpha
+ } else {
+ png_color_16p transpColor = NULL;
+ int numTransp = 0;
+
+ png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
+
+ bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
+
+ if (valid && numTransp == 1 && transpColor != NULL) {
+ /* Compute our transparent color, which we'll match against later.
+ We don't really handle 16bit components properly here, since we
+ do our compare *after* the values have been knocked down to 8bit
+ which means we will find more matches than we should. The real
+ fix seems to be to see the actual 16bit components, do the
+ compare, and then knock it down to 8bits ourselves.
+ */
+ if (color_type & PNG_COLOR_MASK_COLOR) {
+ if (16 == bit_depth) {
+ theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8,
+ transpColor->green >> 8, transpColor->blue >> 8);
+ } else {
+ theTranspColor = SkPackARGB32(0xFF, transpColor->red,
+ transpColor->green, transpColor->blue);
+ }
+ } else { // gray
+ if (16 == bit_depth) {
+ theTranspColor = SkPackARGB32(0xFF, transpColor->gray >> 8,
+ transpColor->gray >> 8, transpColor->gray >> 8);
+ } else {
+ theTranspColor = SkPackARGB32(0xFF, transpColor->gray,
+ transpColor->gray, transpColor->gray);
+ }
+ }
+ }
+
+ if (valid ||
+ PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
+ PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
+ hasAlpha = true;
+ config = SkBitmap::kARGB_8888_Config;
+ } else { // we get to choose the config
+ config = prefConfig;
+ if (config == SkBitmap::kNo_Config) {
+ config = SkImageDecoder::GetDeviceConfig();
+ }
+ if (config != SkBitmap::kRGB_565_Config &&
+ config != SkBitmap::kARGB_4444_Config) {
+ config = SkBitmap::kARGB_8888_Config;
+ }
+ }
+ }
+
+ if (!this->chooseFromOneChoice(config, origWidth, origHeight)) {
+ return false;
+ }
+
+ const int sampleSize = this->getSampleSize();
+ SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
+
+ decodedBitmap->setConfig(config, sampler.scaledWidth(),
+ sampler.scaledHeight(), 0);
+ if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ return true;
+ }
+
+ // from here down we are concerned with colortables and pixels
+
+ // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
+ // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
+ // draw lots faster if we can flag the bitmap has being opaque
+ bool reallyHasAlpha = false;
+
+ SkColorTable* colorTable = NULL;
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ int num_palette;
+ png_colorp palette;
+ png_bytep trans;
+ int num_trans;
+
+ png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
+
+ /* BUGGY IMAGE WORKAROUND
+
+ We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
+ which is a problem since we use the byte as an index. To work around this we grow
+ the colortable by 1 (if its < 256) and duplicate the last color into that slot.
+ */
+ int colorCount = num_palette + (num_palette < 256);
+
+ colorTable = SkNEW_ARGS(SkColorTable, (colorCount));
+
+ SkPMColor* colorPtr = colorTable->lockColors();
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
+ hasAlpha = (num_trans > 0);
+ } else {
+ num_trans = 0;
+ colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+ }
+ // check for bad images that might make us crash
+ if (num_trans > num_palette) {
+ num_trans = num_palette;
+ }
+
+ int index = 0;
+ int transLessThanFF = 0;
+
+ for (; index < num_trans; index++) {
+ transLessThanFF |= (int)*trans - 0xFF;
+ *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue);
+ palette++;
+ }
+ reallyHasAlpha |= (transLessThanFF < 0);
+
+ for (; index < num_palette; index++) {
+ *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
+ palette++;
+ }
+
+ // see BUGGY IMAGE WORKAROUND comment above
+ if (num_palette < 256) {
+ *colorPtr = colorPtr[-1];
+ }
+ colorTable->unlockColors(true);
+ }
+
+ SkAutoUnref aur(colorTable);
+
+ if (!this->allocPixelRef(decodedBitmap, colorTable)) {
+ return false;
+ }
+
+ SkAutoLockPixels alp(*decodedBitmap);
+
+ /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
+// if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+// ; // png_set_swap_alpha(png_ptr);
+
+ /* swap bytes of 16 bit files to least significant byte first */
+ // png_set_swap(png_ptr);
+
+ /* Add filler (or alpha) byte (before/after each RGB triplet) */
+ if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) {
+ png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+ }
+
+ /* Turn on interlace handling. REQUIRED if you are not using
+ * png_read_image(). To see how to handle interlacing passes,
+ * see the png_read_row() method below:
+ */
+ const int number_passes = interlace_type != PNG_INTERLACE_NONE ?
+ png_set_interlace_handling(png_ptr) : 1;
+
+ /* Optional call to gamma correct and add the background to the palette
+ * and update info structure. REQUIRED if you are expecting libpng to
+ * update the palette for you (ie you selected such a transform above).
+ */
+ png_read_update_info(png_ptr, info_ptr);
+
+ if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
+ for (int i = 0; i < number_passes; i++) {
+ for (png_uint_32 y = 0; y < origHeight; y++) {
+ uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
+ png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
+ }
+ }
+ } else {
+ SkScaledBitmapSampler::SrcConfig sc;
+ int srcBytesPerPixel = 4;
+
+ if (SkBitmap::kIndex8_Config == config) {
+ sc = SkScaledBitmapSampler::kIndex;
+ srcBytesPerPixel = 1;
+ } else if (hasAlpha) {
+ sc = SkScaledBitmapSampler::kRGBA;
+ } else {
+ sc = SkScaledBitmapSampler::kRGBX;
+ }
+
+ SkAutoMalloc storage(origWidth * srcBytesPerPixel);
+ const int height = decodedBitmap->height();
+
+ for (int i = 0; i < number_passes; i++) {
+ if (!sampler.begin(decodedBitmap, sc, doDither)) {
+ return false;
+ }
+
+ uint8_t* srcRow = (uint8_t*)storage.get();
+ skip_src_rows(png_ptr, srcRow, sampler.srcY0());
+
+ for (int y = 0; y < height; y++) {
+ uint8_t* tmp = srcRow;
+ png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
+ reallyHasAlpha |= sampler.next(srcRow);
+ if (y < height - 1) {
+ skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
+ }
+ }
+
+ // skip the rest of the rows (if any)
+ png_uint_32 read = (height - 1) * sampler.srcDY() +
+ sampler.srcY0() + 1;
+ SkASSERT(read <= origHeight);
+ skip_src_rows(png_ptr, srcRow, origHeight - read);
+ }
+
+ if (hasAlpha && !reallyHasAlpha) {
+#if 0
+ SkDEBUGF(("Image doesn't really have alpha [%d %d]\n",
+ origWidth, origHeight));
+#endif
+ }
+ }
+
+ /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
+ png_read_end(png_ptr, info_ptr);
+
+ if (0 != theTranspColor) {
+ reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
+ }
+ decodedBitmap->setIsOpaque(!reallyHasAlpha);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_SUPPORT_IMAGE_ENCODE
+
+#include "SkColorPriv.h"
+#include "SkUnPreMultiply.h"
+
+static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
+ SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr;
+ if (!sk_stream->write(data, len)) {
+ png_error(png_ptr, "sk_write_fn Error!");
+ }
+}
+
+typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src,
+ int width, char* SK_RESTRICT dst);
+
+static void transform_scanline_565(const char* SK_RESTRICT src, int width,
+ char* SK_RESTRICT dst) {
+ const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src;
+ for (int i = 0; i < width; i++) {
+ unsigned c = *srcP++;
+ *dst++ = SkPacked16ToR32(c);
+ *dst++ = SkPacked16ToG32(c);
+ *dst++ = SkPacked16ToB32(c);
+ }
+}
+
+static void transform_scanline_888(const char* SK_RESTRICT src, int width,
+ char* SK_RESTRICT dst) {
+ const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
+ for (int i = 0; i < width; i++) {
+ SkPMColor c = *srcP++;
+ *dst++ = SkGetPackedR32(c);
+ *dst++ = SkGetPackedG32(c);
+ *dst++ = SkGetPackedB32(c);
+ }
+}
+
+static void transform_scanline_444(const char* SK_RESTRICT src, int width,
+ char* SK_RESTRICT dst) {
+ const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
+ for (int i = 0; i < width; i++) {
+ SkPMColor16 c = *srcP++;
+ *dst++ = SkPacked4444ToR32(c);
+ *dst++ = SkPacked4444ToG32(c);
+ *dst++ = SkPacked4444ToB32(c);
+ }
+}
+
+static void transform_scanline_8888(const char* SK_RESTRICT src, int width,
+ char* SK_RESTRICT dst) {
+ const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
+ const SkUnPreMultiply::Scale* SK_RESTRICT table =
+ SkUnPreMultiply::GetScaleTable();
+
+ for (int i = 0; i < width; i++) {
+ SkPMColor c = *srcP++;
+ unsigned a = SkGetPackedA32(c);
+ unsigned r = SkGetPackedR32(c);
+ unsigned g = SkGetPackedG32(c);
+ unsigned b = SkGetPackedB32(c);
+
+ if (0 != a && 255 != a) {
+ SkUnPreMultiply::Scale scale = table[a];
+ r = SkUnPreMultiply::ApplyScale(scale, r);
+ g = SkUnPreMultiply::ApplyScale(scale, g);
+ b = SkUnPreMultiply::ApplyScale(scale, b);
+ }
+ *dst++ = r;
+ *dst++ = g;
+ *dst++ = b;
+ *dst++ = a;
+ }
+}
+
+static void transform_scanline_4444(const char* SK_RESTRICT src, int width,
+ char* SK_RESTRICT dst) {
+ const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
+ const SkUnPreMultiply::Scale* SK_RESTRICT table =
+ SkUnPreMultiply::GetScaleTable();
+
+ for (int i = 0; i < width; i++) {
+ SkPMColor16 c = *srcP++;
+ unsigned a = SkPacked4444ToA32(c);
+ unsigned r = SkPacked4444ToR32(c);
+ unsigned g = SkPacked4444ToG32(c);
+ unsigned b = SkPacked4444ToB32(c);
+
+ if (0 != a && 255 != a) {
+ SkUnPreMultiply::Scale scale = table[a];
+ r = SkUnPreMultiply::ApplyScale(scale, r);
+ g = SkUnPreMultiply::ApplyScale(scale, g);
+ b = SkUnPreMultiply::ApplyScale(scale, b);
+ }
+ *dst++ = r;
+ *dst++ = g;
+ *dst++ = b;
+ *dst++ = a;
+ }
+}
+
+static void transform_scanline_index8(const char* SK_RESTRICT src, int width,
+ char* SK_RESTRICT dst) {
+ memcpy(dst, src, width);
+}
+
+static transform_scanline_proc choose_proc(SkBitmap::Config config,
+ bool hasAlpha) {
+ // we don't care about search on alpha if we're kIndex8, since only the
+ // colortable packing cares about that distinction, not the pixels
+ if (SkBitmap::kIndex8_Config == config) {
+ hasAlpha = false; // we store false in the table entries for kIndex8
+ }
+
+ static const struct {
+ SkBitmap::Config fConfig;
+ bool fHasAlpha;
+ transform_scanline_proc fProc;
+ } gMap[] = {
+ { SkBitmap::kRGB_565_Config, false, transform_scanline_565 },
+ { SkBitmap::kARGB_8888_Config, false, transform_scanline_888 },
+ { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 },
+ { SkBitmap::kARGB_4444_Config, false, transform_scanline_444 },
+ { SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 },
+ { SkBitmap::kIndex8_Config, false, transform_scanline_index8 },
+ };
+
+ for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
+ if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
+ return gMap[i].fProc;
+ }
+ }
+ sk_throw();
+ return NULL;
+}
+
+// return the minimum legal bitdepth (by png standards) for this many colortable
+// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
+// we can use fewer bits per in png
+static int computeBitDepth(int colorCount) {
+#if 0
+ int bits = SkNextLog2(colorCount);
+ SkASSERT(bits >= 1 && bits <= 8);
+ // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
+ return SkNextPow2(bits);
+#else
+ // for the moment, we don't know how to pack bitdepth < 8
+ return 8;
+#endif
+}
+
+/* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
+ pack trans[] and return the number of trans[] entries written. If hasAlpha
+ is false, the return value will always be 0.
+
+ Note: this routine takes care of unpremultiplying the RGB values when we
+ have alpha in the colortable, since png doesn't support premul colors
+*/
+static int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette,
+ png_byte* SK_RESTRICT trans, bool hasAlpha) {
+ SkAutoLockColors alc(ctable);
+ const SkPMColor* SK_RESTRICT colors = alc.colors();
+ const int ctCount = ctable->count();
+ int i, num_trans = 0;
+
+ if (hasAlpha) {
+ /* first see if we have some number of fully opaque at the end of the
+ ctable. PNG allows num_trans < num_palette, but all of the trans
+ entries must come first in the palette. If I was smarter, I'd
+ reorder the indices and ctable so that all non-opaque colors came
+ first in the palette. But, since that would slow down the encode,
+ I'm leaving the indices and ctable order as is, and just looking
+ at the tail of the ctable for opaqueness.
+ */
+ num_trans = ctCount;
+ for (i = ctCount - 1; i >= 0; --i) {
+ if (SkGetPackedA32(colors[i]) != 0xFF) {
+ break;
+ }
+ num_trans -= 1;
+ }
+
+ const SkUnPreMultiply::Scale* SK_RESTRICT table =
+ SkUnPreMultiply::GetScaleTable();
+
+ for (i = 0; i < num_trans; i++) {
+ const SkPMColor c = *colors++;
+ const unsigned a = SkGetPackedA32(c);
+ const SkUnPreMultiply::Scale s = table[a];
+ trans[i] = a;
+ palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
+ palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
+ palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
+ }
+ // now fall out of this if-block to use common code for the trailing
+ // opaque entries
+ }
+
+ // these (remaining) entries are opaque
+ for (i = num_trans; i < ctCount; i++) {
+ SkPMColor c = *colors++;
+ palette[i].red = SkGetPackedR32(c);
+ palette[i].green = SkGetPackedG32(c);
+ palette[i].blue = SkGetPackedB32(c);
+ }
+ return num_trans;
+}
+
+class SkPNGImageEncoder : public SkImageEncoder {
+protected:
+ virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
+};
+
+bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
+ int /*quality*/) {
+ SkBitmap::Config config = bitmap.getConfig();
+
+ const bool hasAlpha = !bitmap.isOpaque();
+ int colorType = PNG_COLOR_MASK_COLOR;
+ int bitDepth = 8; // default for color
+ png_color_8 sig_bit;
+
+ switch (config) {
+ case SkBitmap::kIndex8_Config:
+ colorType |= PNG_COLOR_MASK_PALETTE;
+ // fall through to the ARGB_8888 case
+ case SkBitmap::kARGB_8888_Config:
+ sig_bit.red = 8;
+ sig_bit.green = 8;
+ sig_bit.blue = 8;
+ sig_bit.alpha = 8;
+ break;
+ case SkBitmap::kARGB_4444_Config:
+ sig_bit.red = 4;
+ sig_bit.green = 4;
+ sig_bit.blue = 4;
+ sig_bit.alpha = 4;
+ break;
+ case SkBitmap::kRGB_565_Config:
+ sig_bit.red = 5;
+ sig_bit.green = 6;
+ sig_bit.blue = 5;
+ sig_bit.alpha = 0;
+ break;
+ default:
+ return false;
+ }
+
+ if (hasAlpha) {
+ // don't specify alpha if we're a palette, even if our ctable has alpha
+ if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
+ colorType |= PNG_COLOR_MASK_ALPHA;
+ }
+ } else {
+ sig_bit.alpha = 0;
+ }
+
+ SkAutoLockPixels alp(bitmap);
+ // readyToDraw checks for pixels (and colortable if that is required)
+ if (!bitmap.readyToDraw()) {
+ return false;
+ }
+
+ // we must do this after we have locked the pixels
+ SkColorTable* ctable = bitmap.getColorTable();
+ if (NULL != ctable) {
+ if (ctable->count() == 0) {
+ return false;
+ }
+ // check if we can store in fewer than 8 bits
+ bitDepth = computeBitDepth(ctable->count());
+ }
+
+ png_structp png_ptr;
+ png_infop info_ptr;
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
+ NULL);
+ if (NULL == png_ptr) {
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (NULL == info_ptr) {
+ png_destroy_write_struct(&png_ptr, png_infopp_NULL);
+ return false;
+ }
+
+ /* Set error handling. REQUIRED if you aren't supplying your own
+ * error handling functions in the png_create_write_struct() call.
+ */
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return false;
+ }
+
+ png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
+
+ /* Set the image information here. Width and height are up to 2^31,
+ * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
+ * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
+ * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
+ * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
+ * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
+ * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
+ */
+
+ png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
+ bitDepth, colorType,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
+ PNG_FILTER_TYPE_BASE);
+
+#if 0 // need to support this some day
+ /* set the palette if there is one. REQUIRED for indexed-color images */
+ palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH
+ * png_sizeof (png_color));
+ /* ... set palette colors ... */
+ png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
+ /* You must not free palette here, because png_set_PLTE only makes a link to
+ the palette that you malloced. Wait until you are about to destroy
+ the png structure. */
+#endif
+
+ png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+ png_write_info(png_ptr, info_ptr);
+
+ const char* srcImage = (const char*)bitmap.getPixels();
+ SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
+ char* storage = (char*)rowStorage.get();
+ transform_scanline_proc proc = choose_proc(config, hasAlpha);
+
+ for (int y = 0; y < bitmap.height(); y++) {
+ png_bytep row_ptr = (png_bytep)storage;
+ proc(srcImage, bitmap.width(), storage);
+ png_write_rows(png_ptr, &row_ptr, 1);
+ srcImage += bitmap.rowBytes();
+ }
+
+ png_write_end(png_ptr, info_ptr);
+
+ /* clean up after the write, and free any memory allocated */
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return true;
+}
+
+SkImageEncoder* SkImageEncoder_PNG_Factory();
+SkImageEncoder* SkImageEncoder_PNG_Factory() {
+ return SkNEW(SkPNGImageEncoder);
+}
+
+#endif /* SK_SUPPORT_IMAGE_ENCODE */
diff --git a/src/images/SkImageDecoder_libpvjpeg.cpp b/src/images/SkImageDecoder_libpvjpeg.cpp
new file mode 100644
index 0000000000..91777418b2
--- /dev/null
+++ b/src/images/SkImageDecoder_libpvjpeg.cpp
@@ -0,0 +1,206 @@
+#include "SkImageDecoder.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkMath.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+extern void ValidateHeap();
+
+class SkPVJPEGImageDecoder : public SkImageDecoder {
+protected:
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode);
+
+private:
+ enum {
+ STORAGE_SIZE = 8 * 1024
+ };
+ char fStorage[STORAGE_SIZE];
+};
+
+SkImageDecoder* SkImageDecoder_PVJPEG_Factory(SkStream* stream)
+{
+ return SkNEW(SkPVJPEGImageDecoder);
+}
+
+#include "pvjpgdecoderinterface.h"
+#include "pvjpgdecoder_factory.h"
+
+class AutoPVDelete {
+public:
+ AutoPVDelete(PVJpgDecoderInterface* codec) : fCodec(codec) {}
+ ~AutoPVDelete() {
+ fCodec->Reset();
+ PVJpgDecoderFactory::DeletePVJpgDecoder(fCodec);
+ }
+private:
+ PVJpgDecoderInterface* fCodec;
+};
+
+class MyObserver : public MPVJpegDecObserver {
+public:
+ MyObserver() : fCount(0) {}
+ ~MyObserver() {
+ if (fCount != 0) {
+ SkDebugf("--- pvjpeg left %d allocations\n", fCount);
+ }
+ }
+
+ virtual void allocateBuffer(uint8* &buffer, int32 buffersize) {
+ ++fCount;
+ // we double the allocation to work around bug when height is odd
+ buffer = (uint8*)sk_malloc_throw(buffersize << 1);
+ SkDebugf("--- pvjpeg alloc [%d] %d addr=%p\n", fCount, buffersize, buffer);
+ }
+
+ virtual void deallocateBuffer(uint8 *buffer) {
+ SkDebugf("--- pvjpeg free [%d] addr=%p\n", fCount, buffer);
+ --fCount;
+ sk_free(buffer);
+ }
+
+private:
+ int fCount;
+};
+
+static void check_status(TPvJpgDecStatus status) {
+ if (TPVJPGDEC_SUCCESS != status) {
+ SkDEBUGF(("--- pvjpeg status %d\n", status));
+ }
+}
+
+static bool getFrame(PVJpgDecoderInterface* codec, SkBitmap* bitmap,
+ SkBitmap::Config prefConfig, SkImageDecoder::Mode mode) {
+ TPvJpgDecInfo info;
+ TPvJpgDecStatus status = codec->GetInfo(&info);
+ if (status != TPVJPGDEC_SUCCESS)
+ return false;
+
+ int width = info.iWidth[0];
+ int height = info.iHeight[0];
+
+ bitmap->setConfig(SkBitmap::kRGB_565_Config, width, height);
+ bitmap->setIsOpaque(true);
+
+ if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ return true;
+ }
+
+ SkASSERT(info.iNumComponent == 3);
+
+ TPvJpgDecOutputFmt format;
+ format.iColorFormat = TPV_COLORFMT_RGB16;
+ format.iCropped.topLeftX = 0;
+ format.iCropped.topLeftY = 0;
+ format.iCropped.bottomRightX = width - 1;
+ format.iCropped.bottomRightY = height - 1;
+ format.iOutputPitch = bitmap->rowBytes() >> 1;
+ status = codec->SetOutput(&format);
+ if (status != TPVJPGDEC_SUCCESS) {
+ SkDebugf("--- PV SetOutput failed %d\n", status);
+ return false;
+ }
+
+ TPvJpgDecFrame frame;
+ uint8* ptrs[3];
+ int32 widths[3], heights[3];
+ bzero(ptrs, sizeof(ptrs));
+ frame.ptr = ptrs;
+ frame.iWidth = widths;
+ frame.iHeight = heights;
+
+ status = codec->GetFrame(&frame);
+ if (status != TPVJPGDEC_SUCCESS) {
+ SkDebugf("--- PV GetFrame failed %d\n", status);
+ return false;
+ }
+
+ bitmap->allocPixels();
+ memcpy(bitmap->getPixels(), ptrs[0], bitmap->getSize());
+ return true;
+}
+
+class OsclCleanupper {
+public:
+ OsclCleanupper() {
+ OsclBase::Init();
+ OsclErrorTrap::Init();
+ OsclMem::Init();
+ }
+ ~OsclCleanupper() {
+ OsclMem::Cleanup();
+ OsclErrorTrap::Cleanup();
+ OsclBase::Cleanup();
+ }
+};
+
+bool SkPVJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
+ SkBitmap::Config prefConfig, Mode mode)
+{
+ // do I need this guy?
+ OsclCleanupper oc;
+
+ PVJpgDecoderInterface* codec = PVJpgDecoderFactory::CreatePVJpgDecoder();
+ TPvJpgDecStatus status = codec->Init();
+ check_status(status);
+
+ MyObserver observer; // must create before autopvdelete
+ AutoPVDelete ad(codec);
+
+ status = codec->SetObserver(&observer);
+ check_status(status);
+
+ char* storage = fStorage;
+ int32 bytesInStorage = 0;
+ for (;;)
+ {
+ int32 bytesRead = stream->read(storage + bytesInStorage,
+ STORAGE_SIZE - bytesInStorage);
+ if (bytesRead <= 0) {
+ SkDEBUGF(("SkPVJPEGImageDecoder: stream read returned %d\n", bytesRead));
+ return false;
+ }
+
+ // update bytesInStorage to account for the read()
+ bytesInStorage += bytesRead;
+ SkASSERT(bytesInStorage <= STORAGE_SIZE);
+
+ // now call Decode to eat some of the bytes
+ int32 consumed = bytesInStorage;
+ status = codec->Decode((uint8*)storage, &consumed);
+
+ SkASSERT(bytesInStorage >= consumed);
+ bytesInStorage -= consumed;
+ // now bytesInStorage is the remaining unread bytes
+ if (bytesInStorage > 0) { // slide the leftovers to the beginning
+ SkASSERT(storage == fStorage);
+ SkASSERT(consumed >= 0 && bytesInStorage >= 0);
+ SkASSERT((size_t)(consumed + bytesInStorage) <= sizeof(fStorage));
+ SkASSERT(sizeof(fStorage) == STORAGE_SIZE);
+ // SkDebugf("-- memmov srcOffset=%d, numBytes=%d\n", consumed, bytesInStorage);
+ memmove(storage, storage + consumed, bytesInStorage);
+ }
+
+ switch (status) {
+ case TPVJPGDEC_SUCCESS:
+ SkDEBUGF(("SkPVJPEGImageDecoder::Decode returned success?\n");)
+ return false;
+ case TPVJPGDEC_FRAME_READY:
+ case TPVJPGDEC_DONE:
+ return getFrame(codec, decodedBitmap, prefConfig, mode);
+ case TPVJPGDEC_FAIL:
+ case TPVJPGDEC_INVALID_MEMORY:
+ case TPVJPGDEC_INVALID_PARAMS:
+ case TPVJPGDEC_NO_IMAGE_DATA:
+ SkDEBUGF(("SkPVJPEGImageDecoder: failed to decode err=%d\n", status);)
+ return false;
+ case TPVJPGDEC_WAITING_FOR_INPUT:
+ break; // loop around and eat more from the stream
+ }
+ }
+ return false;
+}
+
diff --git a/src/images/SkImageDecoder_wbmp.cpp b/src/images/SkImageDecoder_wbmp.cpp
new file mode 100644
index 0000000000..9d188f6016
--- /dev/null
+++ b/src/images/SkImageDecoder_wbmp.cpp
@@ -0,0 +1,167 @@
+/**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkImageDecoder.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkMath.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+
+class SkWBMPImageDecoder : public SkImageDecoder {
+public:
+ virtual Format getFormat() const {
+ return kWBMP_Format;
+ }
+
+protected:
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode);
+};
+
+static bool read_byte(SkStream* stream, uint8_t* data)
+{
+ return stream->read(data, 1) == 1;
+}
+
+static bool read_mbf(SkStream* stream, int* value)
+{
+ int n = 0;
+ uint8_t data;
+ do {
+ if (!read_byte(stream, &data)) {
+ return false;
+ }
+ n = (n << 7) | (data & 0x7F);
+ } while (data & 0x80);
+
+ *value = n;
+ return true;
+}
+
+struct wbmp_head {
+ int fWidth;
+ int fHeight;
+
+ bool init(SkStream* stream)
+ {
+ uint8_t data;
+
+ if (!read_byte(stream, &data) || data != 0) { // unknown type
+ return false;
+ }
+ if (!read_byte(stream, &data) || (data & 0x9F)) { // skip fixed header
+ return false;
+ }
+ if (!read_mbf(stream, &fWidth) || (unsigned)fWidth > 0xFFFF) {
+ return false;
+ }
+ if (!read_mbf(stream, &fHeight) || (unsigned)fHeight > 0xFFFF) {
+ return false;
+ }
+ return fWidth != 0 && fHeight != 0;
+ }
+};
+
+SkImageDecoder* SkImageDecoder_WBMP_Factory(SkStream* stream)
+{
+ wbmp_head head;
+
+ if (head.init(stream)) {
+ return SkNEW(SkWBMPImageDecoder);
+ }
+ return NULL;
+}
+
+static void expand_bits_to_bytes(uint8_t dst[], const uint8_t src[], int bits)
+{
+ int bytes = bits >> 3;
+
+ for (int i = 0; i < bytes; i++) {
+ unsigned mask = *src++;
+ dst[0] = (mask >> 7) & 1;
+ dst[1] = (mask >> 6) & 1;
+ dst[2] = (mask >> 5) & 1;
+ dst[3] = (mask >> 4) & 1;
+ dst[4] = (mask >> 3) & 1;
+ dst[5] = (mask >> 2) & 1;
+ dst[6] = (mask >> 1) & 1;
+ dst[7] = (mask >> 0) & 1;
+ dst += 8;
+ }
+
+ bits &= 7;
+ if (bits > 0) {
+ unsigned mask = *src;
+ do {
+ *dst++ = (mask >> 7) & 1;;
+ mask <<= 1;
+ } while (--bits != 0);
+ }
+}
+
+#define SkAlign8(x) (((x) + 7) & ~7)
+
+bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
+ SkBitmap::Config prefConfig, Mode mode)
+{
+ wbmp_head head;
+
+ if (!head.init(stream)) {
+ return false;
+ }
+
+ int width = head.fWidth;
+ int height = head.fHeight;
+
+ // assign these directly, in case we return kDimensions_Result
+ decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
+ decodedBitmap->setIsOpaque(true);
+
+ if (SkImageDecoder::kDecodeBounds_Mode == mode)
+ return true;
+
+ const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
+ SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2));
+ SkAutoUnref aur(ct);
+
+ if (!this->allocPixelRef(decodedBitmap, ct)) {
+ return false;
+ }
+
+ SkAutoLockPixels alp(*decodedBitmap);
+
+ uint8_t* dst = decodedBitmap->getAddr8(0, 0);
+ // store the 1-bit valuess at the end of our pixels, so we won't stomp
+ // on them before we're read them. Just trying to avoid a temp allocation
+ size_t srcRB = SkAlign8(width) >> 3;
+ size_t srcSize = height * srcRB;
+ uint8_t* src = dst + decodedBitmap->getSize() - srcSize;
+ if (stream->read(src, srcSize) != srcSize) {
+ return false;
+ }
+
+ for (int y = 0; y < height; y++)
+ {
+ expand_bits_to_bytes(dst, src, width);
+ dst += decodedBitmap->rowBytes();
+ src += srcRB;
+ }
+
+ return true;
+}
+
diff --git a/src/images/SkImageRef.cpp b/src/images/SkImageRef.cpp
new file mode 100644
index 0000000000..90c37b65e1
--- /dev/null
+++ b/src/images/SkImageRef.cpp
@@ -0,0 +1,165 @@
+#include "SkImageRef.h"
+#include "SkBitmap.h"
+#include "SkFlattenable.h"
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+
+// can't be static, as SkImageRef_Pool needs to see it
+SkMutex gImageRefMutex;
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageRef::SkImageRef(SkStream* stream, SkBitmap::Config config,
+ int sampleSize)
+ : SkPixelRef(&gImageRefMutex), fErrorInDecoding(false) {
+ SkASSERT(stream);
+ SkASSERT(1 == stream->getRefCnt());
+
+ fStream = stream;
+ fConfig = config;
+ fSampleSize = sampleSize;
+ fPrev = fNext = NULL;
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+ SkDebugf("add ImageRef %p [%d] data=%d\n",
+ this, config, (int)stream->getLength());
+#endif
+}
+
+SkImageRef::~SkImageRef() {
+ SkASSERT(&gImageRefMutex == this->mutex());
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+ SkDebugf("delete ImageRef %p [%d] data=%d\n",
+ this, fConfig, (int)fStream->getLength());
+#endif
+
+ delete fStream;
+}
+
+bool SkImageRef::getInfo(SkBitmap* bitmap) {
+ SkAutoMutexAcquire ac(gImageRefMutex);
+
+ if (!this->prepareBitmap(SkImageDecoder::kDecodeBounds_Mode)) {
+ return false;
+ }
+
+ SkASSERT(SkBitmap::kNo_Config != fBitmap.config());
+ if (bitmap) {
+ bitmap->setConfig(fBitmap.config(), fBitmap.width(), fBitmap.height());
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkImageRef::onDecode(SkImageDecoder* codec, SkStream* stream,
+ SkBitmap* bitmap, SkBitmap::Config config,
+ SkImageDecoder::Mode mode) {
+ return codec->decode(stream, bitmap, config, mode);
+}
+
+bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) {
+ SkASSERT(&gImageRefMutex == this->mutex());
+
+ if (fErrorInDecoding) {
+ return false;
+ }
+
+ /* As soon as we really know our config, we record it, so that on
+ subsequent calls to the codec, we are sure we will always get the same
+ result.
+ */
+ if (SkBitmap::kNo_Config != fBitmap.config()) {
+ fConfig = fBitmap.config();
+ }
+
+ if (NULL != fBitmap.getPixels() ||
+ (SkBitmap::kNo_Config != fBitmap.config() &&
+ SkImageDecoder::kDecodeBounds_Mode == mode)) {
+ return true;
+ }
+
+ SkASSERT(fBitmap.getPixels() == NULL);
+
+ fStream->rewind();
+
+ SkImageDecoder* codec = SkImageDecoder::Factory(fStream);
+ if (codec) {
+ SkAutoTDelete<SkImageDecoder> ad(codec);
+
+ codec->setSampleSize(fSampleSize);
+ if (this->onDecode(codec, fStream, &fBitmap, fConfig, mode)) {
+ return true;
+ }
+ }
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+ if (NULL == codec) {
+ SkDebugf("--- ImageRef: <%s> failed to find codec\n", this->getURI());
+ } else {
+ SkDebugf("--- ImageRef: <%s> failed in codec for %d mode\n",
+ this->getURI(), mode);
+ }
+#endif
+ fErrorInDecoding = true;
+ fBitmap.reset();
+ return false;
+}
+
+void* SkImageRef::onLockPixels(SkColorTable** ct) {
+ SkASSERT(&gImageRefMutex == this->mutex());
+
+ if (NULL == fBitmap.getPixels()) {
+ (void)this->prepareBitmap(SkImageDecoder::kDecodePixels_Mode);
+ }
+
+ if (ct) {
+ *ct = fBitmap.getColorTable();
+ }
+ return fBitmap.getPixels();
+}
+
+void SkImageRef::onUnlockPixels() {
+ // we're already have the mutex locked
+ SkASSERT(&gImageRefMutex == this->mutex());
+}
+
+size_t SkImageRef::ramUsed() const {
+ size_t size = 0;
+
+ if (fBitmap.getPixels()) {
+ size = fBitmap.getSize();
+ if (fBitmap.getColorTable()) {
+ size += fBitmap.getColorTable()->count() * sizeof(SkPMColor);
+ }
+ }
+ return size;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageRef::SkImageRef(SkFlattenableReadBuffer& buffer)
+ : INHERITED(buffer, &gImageRefMutex), fErrorInDecoding(false) {
+ fConfig = (SkBitmap::Config)buffer.readU8();
+ fSampleSize = buffer.readU8();
+ size_t length = buffer.readU32();
+ fStream = SkNEW_ARGS(SkMemoryStream, (length));
+ buffer.read((void*)fStream->getMemoryBase(), length);
+
+ fPrev = fNext = NULL;
+}
+
+void SkImageRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+ this->INHERITED::flatten(buffer);
+
+ buffer.write8(fConfig);
+ buffer.write8(fSampleSize);
+ size_t length = fStream->getLength();
+ buffer.write32(length);
+ fStream->rewind();
+ buffer.readFromStream(fStream, length);
+}
+
diff --git a/src/images/SkImageRefPool.cpp b/src/images/SkImageRefPool.cpp
new file mode 100644
index 0000000000..e322507d87
--- /dev/null
+++ b/src/images/SkImageRefPool.cpp
@@ -0,0 +1,186 @@
+#include "SkImageRefPool.h"
+#include "SkImageRef.h"
+#include "SkThread.h"
+
+SkImageRefPool::SkImageRefPool() {
+ fRAMBudget = 0; // means no explicit limit
+ fRAMUsed = 0;
+ fCount = 0;
+ fHead = fTail = NULL;
+}
+
+SkImageRefPool::~SkImageRefPool() {
+ // SkASSERT(NULL == fHead);
+}
+
+void SkImageRefPool::setRAMBudget(size_t size) {
+ if (fRAMBudget != size) {
+ fRAMBudget = size;
+ this->purgeIfNeeded();
+ }
+}
+
+void SkImageRefPool::justAddedPixels(SkImageRef* ref) {
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+ SkDebugf("=== ImagePool: add pixels %s [%d %d %d] bytes=%d heap=%d\n",
+ ref->getURI(),
+ ref->fBitmap.width(), ref->fBitmap.height(),
+ ref->fBitmap.bytesPerPixel(),
+ ref->fBitmap.getSize(), (int)fRAMUsed);
+#endif
+ fRAMUsed += ref->ramUsed();
+ this->purgeIfNeeded();
+}
+
+void SkImageRefPool::canLosePixels(SkImageRef* ref) {
+ // the refs near fHead have recently been released (used)
+ // if we purge, we purge from the tail
+ this->detach(ref);
+ this->addToHead(ref);
+ this->purgeIfNeeded();
+}
+
+void SkImageRefPool::purgeIfNeeded() {
+ // do nothing if we have a zero-budget (i.e. unlimited)
+ if (fRAMBudget != 0) {
+ this->setRAMUsed(fRAMBudget);
+ }
+}
+
+void SkImageRefPool::setRAMUsed(size_t limit) {
+ SkImageRef* ref = fTail;
+
+ while (NULL != ref && fRAMUsed > limit) {
+ // only purge it if its pixels are unlocked
+ if (0 == ref->getLockCount() && ref->fBitmap.getPixels()) {
+ size_t size = ref->ramUsed();
+ SkASSERT(size <= fRAMUsed);
+ fRAMUsed -= size;
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+ SkDebugf("=== ImagePool: purge %s [%d %d %d] bytes=%d heap=%d\n",
+ ref->getURI(),
+ ref->fBitmap.width(), ref->fBitmap.height(),
+ ref->fBitmap.bytesPerPixel(),
+ (int)size, (int)fRAMUsed);
+#endif
+
+ // remember the bitmap config (don't call reset),
+ // just clear the pixel memory
+ ref->fBitmap.setPixels(NULL);
+ SkASSERT(NULL == ref->fBitmap.getPixels());
+ }
+ ref = ref->fPrev;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkImageRefPool::addToHead(SkImageRef* ref) {
+ ref->fNext = fHead;
+ ref->fPrev = NULL;
+
+ if (fHead) {
+ SkASSERT(NULL == fHead->fPrev);
+ fHead->fPrev = ref;
+ }
+ fHead = ref;
+
+ if (NULL == fTail) {
+ fTail = ref;
+ }
+ fCount += 1;
+ SkASSERT(computeCount() == fCount);
+
+ fRAMUsed += ref->ramUsed();
+}
+
+void SkImageRefPool::addToTail(SkImageRef* ref) {
+ ref->fNext = NULL;
+ ref->fPrev = fTail;
+
+ if (fTail) {
+ SkASSERT(NULL == fTail->fNext);
+ fTail->fNext = ref;
+ }
+ fTail = ref;
+
+ if (NULL == fHead) {
+ fHead = ref;
+ }
+ fCount += 1;
+ SkASSERT(computeCount() == fCount);
+
+ fRAMUsed += ref->ramUsed();
+}
+
+void SkImageRefPool::detach(SkImageRef* ref) {
+ SkASSERT(fCount > 0);
+
+ if (fHead == ref) {
+ fHead = ref->fNext;
+ }
+ if (fTail == ref) {
+ fTail = ref->fPrev;
+ }
+ if (ref->fPrev) {
+ ref->fPrev->fNext = ref->fNext;
+ }
+ if (ref->fNext) {
+ ref->fNext->fPrev = ref->fPrev;
+ }
+
+ ref->fNext = ref->fPrev = NULL;
+
+ fCount -= 1;
+ SkASSERT(computeCount() == fCount);
+
+ SkASSERT(fRAMUsed >= ref->ramUsed());
+ fRAMUsed -= ref->ramUsed();
+}
+
+int SkImageRefPool::computeCount() const {
+ SkImageRef* ref = fHead;
+ int count = 0;
+
+ while (ref != NULL) {
+ count += 1;
+ ref = ref->fNext;
+ }
+
+#ifdef SK_DEBUG
+ ref = fTail;
+ int count2 = 0;
+
+ while (ref != NULL) {
+ count2 += 1;
+ ref = ref->fPrev;
+ }
+ SkASSERT(count2 == count);
+#endif
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+void SkImageRefPool::dump() const {
+#if defined(SK_DEBUG) || defined(DUMP_IMAGEREF_LIFECYCLE)
+ SkDebugf("ImagePool dump: bugdet: %d used: %d count: %d\n",
+ (int)fRAMBudget, (int)fRAMUsed, fCount);
+
+ SkImageRef* ref = fHead;
+
+ while (ref != NULL) {
+ SkDebugf(" [%3d %3d %d] ram=%d data=%d locks=%d %s\n", ref->fBitmap.width(),
+ ref->fBitmap.height(), ref->fBitmap.config(),
+ ref->ramUsed(), (int)ref->fStream->getLength(),
+ ref->getLockCount(), ref->getURI());
+
+ ref = ref->fNext;
+ }
+#endif
+}
+
diff --git a/src/images/SkImageRefPool.h b/src/images/SkImageRefPool.h
new file mode 100644
index 0000000000..b2eb7b3b1b
--- /dev/null
+++ b/src/images/SkImageRefPool.h
@@ -0,0 +1,43 @@
+#ifndef SkImageRefPool_DEFINED
+#define SkImageRefPool_DEFINED
+
+#include "SkTypes.h"
+
+class SkImageRef;
+class SkImageRef_GlobalPool;
+
+class SkImageRefPool {
+public:
+ SkImageRefPool();
+ ~SkImageRefPool();
+
+ size_t getRAMBudget() const { return fRAMBudget; }
+ void setRAMBudget(size_t);
+
+ size_t getRAMUsed() const { return fRAMUsed; }
+ void setRAMUsed(size_t limit);
+
+ void addToHead(SkImageRef*);
+ void addToTail(SkImageRef*);
+ void detach(SkImageRef*);
+
+ void dump() const;
+
+private:
+ size_t fRAMBudget;
+ size_t fRAMUsed;
+
+ int fCount;
+ SkImageRef* fHead, *fTail;
+
+ int computeCount() const;
+
+ friend class SkImageRef_GlobalPool;
+
+ void justAddedPixels(SkImageRef*);
+ void canLosePixels(SkImageRef*);
+ void purgeIfNeeded();
+};
+
+#endif
+
diff --git a/src/images/SkImageRef_GlobalPool.cpp b/src/images/SkImageRef_GlobalPool.cpp
new file mode 100644
index 0000000000..1f0bc4306b
--- /dev/null
+++ b/src/images/SkImageRef_GlobalPool.cpp
@@ -0,0 +1,83 @@
+#include "SkImageRef_GlobalPool.h"
+#include "SkImageRefPool.h"
+#include "SkThread.h"
+
+extern SkMutex gImageRefMutex;
+
+static SkImageRefPool gGlobalImageRefPool;
+
+SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkStream* stream,
+ SkBitmap::Config config,
+ int sampleSize)
+ : SkImageRef(stream, config, sampleSize) {
+ this->mutex()->acquire();
+ gGlobalImageRefPool.addToHead(this);
+ this->mutex()->release();
+}
+
+SkImageRef_GlobalPool::~SkImageRef_GlobalPool() {
+ this->mutex()->acquire();
+ gGlobalImageRefPool.detach(this);
+ this->mutex()->release();
+}
+
+bool SkImageRef_GlobalPool::onDecode(SkImageDecoder* codec, SkStream* stream,
+ SkBitmap* bitmap, SkBitmap::Config config,
+ SkImageDecoder::Mode mode) {
+ if (!this->INHERITED::onDecode(codec, stream, bitmap, config, mode)) {
+ return false;
+ }
+ if (mode == SkImageDecoder::kDecodePixels_Mode) {
+ gGlobalImageRefPool.justAddedPixels(this);
+ }
+ return true;
+}
+
+void SkImageRef_GlobalPool::onUnlockPixels() {
+ this->INHERITED::onUnlockPixels();
+
+ gGlobalImageRefPool.canLosePixels(this);
+}
+
+SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkFlattenableReadBuffer& buffer)
+ : INHERITED(buffer) {
+ this->mutex()->acquire();
+ gGlobalImageRefPool.addToHead(this);
+ this->mutex()->release();
+}
+
+SkPixelRef* SkImageRef_GlobalPool::Create(SkFlattenableReadBuffer& buffer) {
+ return SkNEW_ARGS(SkImageRef_GlobalPool, (buffer));
+}
+
+static SkPixelRef::Registrar::Registrar reg("SkImageRef_GlobalPool",
+ SkImageRef_GlobalPool::Create);
+
+///////////////////////////////////////////////////////////////////////////////
+// global imagerefpool wrappers
+
+size_t SkImageRef_GlobalPool::GetRAMBudget() {
+ SkAutoMutexAcquire ac(gImageRefMutex);
+ return gGlobalImageRefPool.getRAMBudget();
+}
+
+void SkImageRef_GlobalPool::SetRAMBudget(size_t size) {
+ SkAutoMutexAcquire ac(gImageRefMutex);
+ gGlobalImageRefPool.setRAMBudget(size);
+}
+
+size_t SkImageRef_GlobalPool::GetRAMUsed() {
+ SkAutoMutexAcquire ac(gImageRefMutex);
+ return gGlobalImageRefPool.getRAMUsed();
+}
+
+void SkImageRef_GlobalPool::SetRAMUsed(size_t usage) {
+ SkAutoMutexAcquire ac(gImageRefMutex);
+ gGlobalImageRefPool.setRAMUsed(usage);
+}
+
+void SkImageRef_GlobalPool::DumpPool() {
+ SkAutoMutexAcquire ac(gImageRefMutex);
+ gGlobalImageRefPool.dump();
+}
+
diff --git a/src/images/SkMMapStream.cpp b/src/images/SkMMapStream.cpp
new file mode 100644
index 0000000000..2aee9451c3
--- /dev/null
+++ b/src/images/SkMMapStream.cpp
@@ -0,0 +1,63 @@
+#include "SkMMapStream.h"
+
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+SkMMAPStream::SkMMAPStream(const char filename[])
+{
+ fFildes = -1; // initialize to failure case
+
+ int fildes = open(filename, O_RDONLY);
+ if (fildes < 0)
+ {
+ SkDEBUGF(("---- failed to open(%s) for mmap stream error=%d\n", filename, errno));
+ return;
+ }
+
+ off_t size = lseek(fildes, 0, SEEK_END); // find the file size
+ if (size == -1)
+ {
+ SkDEBUGF(("---- failed to lseek(%s) for mmap stream error=%d\n", filename, errno));
+ close(fildes);
+ return;
+ }
+ (void)lseek(fildes, 0, SEEK_SET); // restore file offset to beginning
+
+ void* addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fildes, 0);
+ if (MAP_FAILED == addr)
+ {
+ SkDEBUGF(("---- failed to mmap(%s) for mmap stream error=%d\n", filename, errno));
+ close(fildes);
+ return;
+ }
+
+ this->INHERITED::setMemory(addr, size);
+
+ fFildes = fildes;
+ fAddr = addr;
+ fSize = size;
+}
+
+SkMMAPStream::~SkMMAPStream()
+{
+ this->closeMMap();
+}
+
+void SkMMAPStream::setMemory(const void* data, size_t length)
+{
+ this->closeMMap();
+ this->INHERITED::setMemory(data, length);
+}
+
+void SkMMAPStream::closeMMap()
+{
+ if (fFildes >= 0)
+ {
+ munmap(fAddr, fSize);
+ close(fFildes);
+ fFildes = -1;
+ }
+}
+
diff --git a/src/images/SkMovie.cpp b/src/images/SkMovie.cpp
new file mode 100644
index 0000000000..7186ed51b5
--- /dev/null
+++ b/src/images/SkMovie.cpp
@@ -0,0 +1,101 @@
+#include "SkMovie.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+// We should never see this in normal operation since our time values are
+// 0-based. So we use it as a sentinal.
+#define UNINITIALIZED_MSEC ((SkMSec)-1)
+
+SkMovie::SkMovie()
+{
+ fInfo.fDuration = UNINITIALIZED_MSEC; // uninitialized
+ fCurrTime = UNINITIALIZED_MSEC; // uninitialized
+ fNeedBitmap = true;
+}
+
+void SkMovie::ensureInfo()
+{
+ if (fInfo.fDuration == UNINITIALIZED_MSEC && !this->onGetInfo(&fInfo))
+ memset(&fInfo, 0, sizeof(fInfo)); // failure
+}
+
+SkMSec SkMovie::duration()
+{
+ this->ensureInfo();
+ return fInfo.fDuration;
+}
+
+int SkMovie::width()
+{
+ this->ensureInfo();
+ return fInfo.fWidth;
+}
+
+int SkMovie::height()
+{
+ this->ensureInfo();
+ return fInfo.fHeight;
+}
+
+int SkMovie::isOpaque()
+{
+ this->ensureInfo();
+ return fInfo.fIsOpaque;
+}
+
+bool SkMovie::setTime(SkMSec time)
+{
+ SkMSec dur = this->duration();
+ if (time > dur)
+ time = dur;
+
+ bool changed = false;
+ if (time != fCurrTime)
+ {
+ fCurrTime = time;
+ changed = this->onSetTime(time);
+ fNeedBitmap |= changed;
+ }
+ return changed;
+}
+
+const SkBitmap& SkMovie::bitmap()
+{
+ if (fCurrTime == UNINITIALIZED_MSEC) // uninitialized
+ this->setTime(0);
+
+ if (fNeedBitmap)
+ {
+ if (!this->onGetBitmap(&fBitmap)) // failure
+ fBitmap.reset();
+ fNeedBitmap = false;
+ }
+ return fBitmap;
+}
+
+////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+SkMovie* SkMovie::DecodeMemory(const void* data, size_t length) {
+ SkMemoryStream stream(data, length, false);
+ return SkMovie::DecodeStream(&stream);
+}
+
+SkMovie* SkMovie::DecodeFile(const char path[])
+{
+ SkMovie* movie = NULL;
+
+ SkFILEStream stream(path);
+ if (stream.isValid()) {
+ movie = SkMovie::DecodeStream(&stream);
+ }
+#ifdef SK_DEBUG
+ else {
+ SkDebugf("Movie file not found <%s>\n", path);
+ }
+#endif
+
+ return movie;
+}
+
diff --git a/src/images/SkMovie_gif.cpp b/src/images/SkMovie_gif.cpp
new file mode 100644
index 0000000000..ca9c812712
--- /dev/null
+++ b/src/images/SkMovie_gif.cpp
@@ -0,0 +1,224 @@
+/* libs/graphics/images/SkImageDecoder_libgif.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkMovie.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+#include "gif_lib.h"
+
+class SkGIFMovie : public SkMovie {
+public:
+ SkGIFMovie(SkStream* stream);
+ virtual ~SkGIFMovie();
+
+protected:
+ virtual bool onGetInfo(Info*);
+ virtual bool onSetTime(SkMSec);
+ virtual bool onGetBitmap(SkBitmap*);
+
+private:
+ GifFileType* fGIF;
+ SavedImage* fCurrSavedImage;
+};
+
+SkMovie* SkMovie_GIF_Factory(SkStream* stream) {
+ char buf[GIF_STAMP_LEN];
+ if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
+ if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+ memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+ memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
+ stream->rewind();
+ return SkNEW_ARGS(SkGIFMovie, (stream));
+ }
+ }
+ return NULL;
+}
+
+static int Decode(GifFileType* fileType, GifByteType* out, int size) {
+ SkStream* stream = (SkStream*) fileType->UserData;
+ return (int) stream->read(out, size);
+}
+
+SkGIFMovie::SkGIFMovie(SkStream* stream)
+{
+ fGIF = DGifOpen( stream, Decode );
+ if (NULL == fGIF)
+ return;
+
+ if (DGifSlurp(fGIF) != GIF_OK)
+ {
+ DGifCloseFile(fGIF);
+ fGIF = NULL;
+ }
+ fCurrSavedImage = NULL;
+}
+
+SkGIFMovie::~SkGIFMovie()
+{
+ if (fGIF)
+ DGifCloseFile(fGIF);
+}
+
+static SkMSec savedimage_duration(const SavedImage* image)
+{
+ for (int j = 0; j < image->ExtensionBlockCount; j++)
+ {
+ if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
+ {
+ int size = image->ExtensionBlocks[j].ByteCount;
+ SkASSERT(size >= 4);
+ const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
+ return ((b[2] << 8) | b[1]) * 10;
+ }
+ }
+ return 0;
+}
+
+bool SkGIFMovie::onGetInfo(Info* info)
+{
+ if (NULL == fGIF)
+ return false;
+
+ SkMSec dur = 0;
+ for (int i = 0; i < fGIF->ImageCount; i++)
+ dur += savedimage_duration(&fGIF->SavedImages[i]);
+
+ info->fDuration = dur;
+ info->fWidth = fGIF->SWidth;
+ info->fHeight = fGIF->SHeight;
+ info->fIsOpaque = false; // how to compute?
+ return true;
+}
+
+bool SkGIFMovie::onSetTime(SkMSec time)
+{
+ if (NULL == fGIF)
+ return false;
+
+ SkMSec dur = 0;
+ for (int i = 0; i < fGIF->ImageCount; i++)
+ {
+ dur += savedimage_duration(&fGIF->SavedImages[i]);
+ if (dur >= time)
+ {
+ SavedImage* prev = fCurrSavedImage;
+ fCurrSavedImage = &fGIF->SavedImages[i];
+ return prev != fCurrSavedImage;
+ }
+ }
+ fCurrSavedImage = &fGIF->SavedImages[fGIF->ImageCount - 1];
+ return true;
+}
+
+bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
+{
+ GifFileType* gif = fGIF;
+ if (NULL == gif)
+ return false;
+
+ // should we check for the Image cmap or the global (SColorMap) first?
+ ColorMapObject* cmap = gif->SColorMap;
+ if (cmap == NULL)
+ cmap = gif->Image.ColorMap;
+
+ if (cmap == NULL || gif->ImageCount < 1 || cmap->ColorCount != (1 << cmap->BitsPerPixel))
+ {
+ SkASSERT(!"bad colortable setup");
+ return false;
+ }
+
+ const int width = gif->SWidth;
+ const int height = gif->SHeight;
+ if (width <= 0 || height <= 0) {
+ return false;
+ }
+
+ SavedImage* gif_image = fCurrSavedImage;
+ SkBitmap::Config config = SkBitmap::kIndex8_Config;
+
+ SkColorTable* colorTable = SkNEW_ARGS(SkColorTable, (cmap->ColorCount));
+ SkAutoUnref aur(colorTable);
+
+ bm->setConfig(config, width, height, 0);
+ if (!bm->allocPixels(colorTable)) {
+ return false;
+ }
+
+ int transparent = -1;
+ for (int i = 0; i < gif_image->ExtensionBlockCount; ++i) {
+ ExtensionBlock* eb = gif_image->ExtensionBlocks + i;
+ if (eb->Function == 0xF9 &&
+ eb->ByteCount == 4) {
+ bool has_transparency = ((eb->Bytes[0] & 1) == 1);
+ if (has_transparency) {
+ transparent = (unsigned char)eb->Bytes[3];
+ }
+ }
+ }
+
+ SkPMColor* colorPtr = colorTable->lockColors();
+
+ if (transparent >= 0)
+ memset(colorPtr, 0, cmap->ColorCount * 4);
+ else
+ colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
+
+ for (int index = 0; index < cmap->ColorCount; index++)
+ {
+ if (transparent != index)
+ colorPtr[index] = SkPackARGB32(0xFF, cmap->Colors[index].Red,
+ cmap->Colors[index].Green, cmap->Colors[index].Blue);
+ }
+ colorTable->unlockColors(true);
+
+ unsigned char* in = (unsigned char*)gif_image->RasterBits;
+ unsigned char* out = bm->getAddr8(0, 0);
+ if (gif->Image.Interlace) {
+
+ // deinterlace
+ int row;
+ // group 1 - every 8th row, starting with row 0
+ for (row = 0; row < height; row += 8) {
+ memcpy(out + width * row, in, width);
+ in += width;
+ }
+
+ // group 2 - every 8th row, starting with row 4
+ for (row = 4; row < height; row += 8) {
+ memcpy(out + width * row, in, width);
+ in += width;
+ }
+
+ // group 3 - every 4th row, starting with row 2
+ for (row = 2; row < height; row += 4) {
+ memcpy(out + width * row, in, width);
+ in += width;
+ }
+
+ for (row = 1; row < height; row += 2) {
+ memcpy(out + width * row, in, width);
+ in += width;
+ }
+
+ } else {
+ memcpy(out, in, width * height);
+ }
+ return true;
+}
diff --git a/src/images/SkPageFlipper.cpp b/src/images/SkPageFlipper.cpp
new file mode 100644
index 0000000000..526ba093d6
--- /dev/null
+++ b/src/images/SkPageFlipper.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPageFlipper.h"
+
+SkPageFlipper::SkPageFlipper() {
+ fWidth = 0;
+ fHeight = 0;
+ fDirty0 = &fDirty0Storage;
+ fDirty1 = &fDirty1Storage;
+
+ fDirty0->setEmpty();
+ fDirty1->setEmpty();
+}
+
+SkPageFlipper::SkPageFlipper(int width, int height) {
+ fWidth = width;
+ fHeight = height;
+ fDirty0 = &fDirty0Storage;
+ fDirty1 = &fDirty1Storage;
+
+ fDirty0->setRect(0, 0, width, height);
+ fDirty1->setEmpty();
+}
+
+void SkPageFlipper::resize(int width, int height) {
+ fWidth = width;
+ fHeight = height;
+
+ // this is the opposite of the constructors
+ fDirty1->setRect(0, 0, width, height);
+ fDirty0->setEmpty();
+}
+
+void SkPageFlipper::inval() {
+ fDirty1->setRect(0, 0, fWidth, fHeight);
+}
+
+void SkPageFlipper::inval(const SkIRect& rect) {
+ SkIRect r;
+ r.set(0, 0, fWidth, fHeight);
+ if (r.intersect(rect)) {
+ fDirty1->op(r, SkRegion::kUnion_Op);
+ }
+}
+
+void SkPageFlipper::inval(const SkRegion& rgn) {
+ SkRegion r;
+ r.setRect(0, 0, fWidth, fHeight);
+ if (r.op(rgn, SkRegion::kIntersect_Op)) {
+ fDirty1->op(r, SkRegion::kUnion_Op);
+ }
+}
+
+void SkPageFlipper::inval(const SkRect& rect, bool antialias) {
+ SkIRect r;
+ rect.round(&r);
+ if (antialias) {
+ r.inset(-1, -1);
+ }
+ this->inval(r);
+}
+
+const SkRegion& SkPageFlipper::update(SkRegion* copyBits) {
+ // Copy over anything new from page0 that isn't dirty in page1
+ copyBits->op(*fDirty0, *fDirty1, SkRegion::kDifference_Op);
+ SkTSwap<SkRegion*>(fDirty0, fDirty1);
+ fDirty1->setEmpty();
+ return *fDirty0;
+}
+
+
diff --git a/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp
new file mode 100644
index 0000000000..15f44327a7
--- /dev/null
+++ b/src/images/SkScaledBitmapSampler.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkScaledBitmapSampler.h"
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+
+// 8888
+
+static bool Sample_Gray_D8888(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int) {
+ SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+ for (int x = 0; x < width; x++) {
+ dst[x] = SkPackARGB32(0xFF, src[0], src[0], src[0]);
+ src += deltaSrc;
+ }
+ return false;
+}
+
+static bool Sample_RGBx_D8888(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int) {
+ SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+ for (int x = 0; x < width; x++) {
+ dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]);
+ src += deltaSrc;
+ }
+ return false;
+}
+
+static bool Sample_RGBA_D8888(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int) {
+ SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+ unsigned alphaMask = 0xFF;
+ for (int x = 0; x < width; x++) {
+ unsigned alpha = src[3];
+ dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
+ src += deltaSrc;
+ alphaMask &= alpha;
+ }
+ return alphaMask != 0xFF;
+}
+
+// 565
+
+static bool Sample_Gray_D565(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int) {
+ uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+ for (int x = 0; x < width; x++) {
+ dst[x] = SkPack888ToRGB16(src[0], src[0], src[0]);
+ src += deltaSrc;
+ }
+ return false;
+}
+
+static bool Sample_Gray_D565_D(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int y) {
+ uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+ DITHER_565_SCAN(y);
+ for (int x = 0; x < width; x++) {
+ dst[x] = SkDitherRGBTo565(src[0], src[0], src[0], DITHER_VALUE(x));
+ src += deltaSrc;
+ }
+ return false;
+}
+
+static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int) {
+ uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+ for (int x = 0; x < width; x++) {
+ dst[x] = SkPack888ToRGB16(src[0], src[1], src[2]);
+ src += deltaSrc;
+ }
+ return false;
+}
+
+static bool Sample_RGBx_D565_D(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int y) {
+ uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
+ DITHER_565_SCAN(y);
+ for (int x = 0; x < width; x++) {
+ dst[x] = SkDitherRGBTo565(src[0], src[1], src[2], DITHER_VALUE(x));
+ src += deltaSrc;
+ }
+ return false;
+}
+
+// 4444
+
+static bool Sample_Gray_D4444(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int) {
+ SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+ for (int x = 0; x < width; x++) {
+ unsigned gray = src[0] >> 4;
+ dst[x] = SkPackARGB4444(0xF, gray, gray, gray);
+ src += deltaSrc;
+ }
+ return false;
+}
+
+static bool Sample_Gray_D4444_D(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int y) {
+ SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+ DITHER_4444_SCAN(y);
+ for (int x = 0; x < width; x++) {
+ dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[0], src[0],
+ DITHER_VALUE(x));
+ src += deltaSrc;
+ }
+ return false;
+}
+
+static bool Sample_RGBx_D4444(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int) {
+ SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+ for (int x = 0; x < width; x++) {
+ dst[x] = SkPackARGB4444(0xF, src[0] >> 4, src[1] >> 4, src[2] >> 4);
+ src += deltaSrc;
+ }
+ return false;
+}
+
+static bool Sample_RGBx_D4444_D(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int y) {
+ SkPMColor16* dst = (SkPMColor16*)dstRow;
+ DITHER_4444_SCAN(y);
+
+ for (int x = 0; x < width; x++) {
+ dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[1], src[2],
+ DITHER_VALUE(x));
+ src += deltaSrc;
+ }
+ return false;
+}
+
+static bool Sample_RGBA_D4444(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int) {
+ SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+ unsigned alphaMask = 0xFF;
+
+ for (int x = 0; x < width; x++) {
+ unsigned alpha = src[3];
+ SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
+ dst[x] = SkPixel32ToPixel4444(c);
+ src += deltaSrc;
+ alphaMask &= alpha;
+ }
+ return alphaMask != 0xFF;
+}
+
+static bool Sample_RGBA_D4444_D(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int y) {
+ SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow;
+ unsigned alphaMask = 0xFF;
+ DITHER_4444_SCAN(y);
+
+ for (int x = 0; x < width; x++) {
+ unsigned alpha = src[3];
+ SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
+ dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x));
+ src += deltaSrc;
+ alphaMask &= alpha;
+ }
+ return alphaMask != 0xFF;
+}
+
+// Index
+
+static bool Sample_Index_DI(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int) {
+ if (1 == deltaSrc) {
+ memcpy(dstRow, src, width);
+ } else {
+ uint8_t* SK_RESTRICT dst = (uint8_t*)dstRow;
+ for (int x = 0; x < width; x++) {
+ dst[x] = src[0];
+ src += deltaSrc;
+ }
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkScaledBitmapSampler.h"
+
+SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height,
+ int sampleSize) {
+ if (width <= 0 || height <= 0) {
+ sk_throw();
+ }
+
+ if (sampleSize <= 1) {
+ fScaledWidth = width;
+ fScaledHeight = height;
+ fX0 = fY0 = 0;
+ fDX = fDY = 1;
+ return;
+ }
+
+ int dx = SkMin32(sampleSize, width);
+ int dy = SkMin32(sampleSize, height);
+
+ fScaledWidth = width / dx;
+ fScaledHeight = height / dy;
+
+ SkASSERT(fScaledWidth > 0);
+ SkASSERT(fScaledHeight > 0);
+
+ fX0 = dx >> 1;
+ fY0 = dy >> 1;
+
+ SkASSERT(fX0 >= 0 && fX0 < width);
+ SkASSERT(fY0 >= 0 && fY0 < height);
+
+ fDX = dx;
+ fDY = dy;
+
+ SkASSERT(fDX > 0 && (fX0 + fDX * (fScaledWidth - 1)) < width);
+ SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height);
+
+ fRowProc = NULL;
+}
+
+bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither) {
+ static const RowProc gProcs[] = {
+ // 8888 (no dither distinction)
+ Sample_Gray_D8888, Sample_Gray_D8888,
+ Sample_RGBx_D8888, Sample_RGBx_D8888,
+ Sample_RGBA_D8888, Sample_RGBA_D8888,
+ NULL, NULL,
+ // 565 (no alpha distinction)
+ Sample_Gray_D565, Sample_Gray_D565_D,
+ Sample_RGBx_D565, Sample_RGBx_D565_D,
+ Sample_RGBx_D565, Sample_RGBx_D565_D,
+ NULL, NULL,
+ // 4444
+ Sample_Gray_D4444, Sample_Gray_D4444_D,
+ Sample_RGBx_D4444, Sample_RGBx_D4444_D,
+ Sample_RGBA_D4444, Sample_RGBA_D4444_D,
+ NULL, NULL,
+ // Index8
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ Sample_Index_DI, Sample_Index_DI,
+ };
+
+
+ int index = 0;
+ if (dither) {
+ index += 1;
+ }
+ switch (sc) {
+ case SkScaledBitmapSampler::kGray:
+ fSrcPixelSize = 1;
+ index += 0;
+ break;
+ case SkScaledBitmapSampler::kRGB:
+ fSrcPixelSize = 3;
+ index += 2;
+ break;
+ case SkScaledBitmapSampler::kRGBX:
+ fSrcPixelSize = 4;
+ index += 2;
+ break;
+ case SkScaledBitmapSampler::kRGBA:
+ fSrcPixelSize = 4;
+ index += 4;
+ break;
+ case SkScaledBitmapSampler::kIndex:
+ fSrcPixelSize = 1;
+ index += 6;
+ break;
+ default:
+ return false;
+ }
+
+ switch (dst->config()) {
+ case SkBitmap::kARGB_8888_Config:
+ index += 0;
+ break;
+ case SkBitmap::kRGB_565_Config:
+ index += 8;
+ break;
+ case SkBitmap::kARGB_4444_Config:
+ index += 16;
+ break;
+ case SkBitmap::kIndex8_Config:
+ index += 24;
+ break;
+ default:
+ return false;
+ }
+
+ fRowProc = gProcs[index];
+ fDstRow = (char*)dst->getPixels();
+ fDstRowBytes = dst->rowBytes();
+ fCurrY = 0;
+ return fRowProc != NULL;
+}
+
+bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) {
+ SkASSERT((unsigned)fCurrY < (unsigned)fScaledHeight);
+
+ bool hadAlpha = fRowProc(fDstRow, src + fX0 * fSrcPixelSize, fScaledWidth,
+ fDX * fSrcPixelSize, fCurrY);
+ fDstRow += fDstRowBytes;
+ fCurrY += 1;
+ return hadAlpha;
+}
diff --git a/src/images/SkScaledBitmapSampler.h b/src/images/SkScaledBitmapSampler.h
new file mode 100644
index 0000000000..0bb99242a7
--- /dev/null
+++ b/src/images/SkScaledBitmapSampler.h
@@ -0,0 +1,55 @@
+#ifndef SkScaledBitmapSampler_DEFINED
+#define SkScaledBitmapSampler_DEFINED
+
+#include "SkTypes.h"
+
+class SkBitmap;
+
+class SkScaledBitmapSampler {
+public:
+ SkScaledBitmapSampler(int origWidth, int origHeight, int cellSize);
+
+ int scaledWidth() const { return fScaledWidth; }
+ int scaledHeight() const { return fScaledHeight; }
+
+ int srcY0() const { return fY0; }
+ int srcDY() const { return fDY; }
+
+ enum SrcConfig {
+ kGray, // 1 byte per pixel
+ kIndex, // 1 byte per pixel
+ kRGB, // 3 bytes per pixel
+ kRGBX, // 4 byes per pixel (ignore 4th)
+ kRGBA // 4 bytes per pixel
+ };
+
+ // Given a dst bitmap (with pixels already allocated) and a src-config,
+ // prepares iterator to process the src colors and write them into dst.
+ // Returns false if the request cannot be fulfulled.
+ bool begin(SkBitmap* dst, SrcConfig sc, bool doDither);
+ // call with row of src pixels, for y = 0...scaledHeight-1.
+ // returns true if the row had non-opaque alpha in it
+ bool next(const uint8_t* SK_RESTRICT src);
+
+private:
+ int fScaledWidth;
+ int fScaledHeight;
+
+ int fX0; // first X coord to sample
+ int fY0; // first Y coord (scanline) to sample
+ int fDX; // step between X samples
+ int fDY; // step between Y samples
+
+ typedef bool (*RowProc)(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int y);
+
+ // setup state
+ char* fDstRow; // points into bitmap's pixels
+ int fDstRowBytes;
+ int fCurrY; // used for dithering
+ int fSrcPixelSize; // 1, 3, 4
+ RowProc fRowProc;
+};
+
+#endif
diff --git a/src/images/SkStream.cpp b/src/images/SkStream.cpp
new file mode 100644
index 0000000000..b199a1b944
--- /dev/null
+++ b/src/images/SkStream.cpp
@@ -0,0 +1,856 @@
+/* libs/graphics/images/SkStream.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkStream.h"
+#include "SkFixed.h"
+#include "SkString.h"
+#include "SkOSFile.h"
+
+SkStream::~SkStream() {}
+
+const char* SkStream::getFileName()
+{
+ // override in subclass if you represent a file
+ return NULL;
+}
+
+const void* SkStream::getMemoryBase()
+{
+ // override in subclass if you represent a memory block
+ return NULL;
+}
+
+size_t SkStream::skip(size_t size)
+{
+ /* Check for size == 0, and just return 0. If we passed that
+ to read(), it would interpret it as a request for the entire
+ size of the stream.
+ */
+ return size ? this->read(NULL, size) : 0;
+}
+
+int8_t SkStream::readS8() {
+ int8_t value;
+ size_t len = this->read(&value, 1);
+ SkASSERT(1 == len);
+ return value;
+}
+
+int16_t SkStream::readS16() {
+ int16_t value;
+ size_t len = this->read(&value, 2);
+ SkASSERT(2 == len);
+ return value;
+}
+
+int32_t SkStream::readS32() {
+ int32_t value;
+ size_t len = this->read(&value, 4);
+ SkASSERT(4 == len);
+ return value;
+}
+
+SkScalar SkStream::readScalar() {
+ SkScalar value;
+ size_t len = this->read(&value, sizeof(SkScalar));
+ SkASSERT(sizeof(SkScalar) == len);
+ return value;
+}
+
+size_t SkStream::readPackedUInt() {
+ uint8_t byte;
+ if (!this->read(&byte, 1)) {
+ return 0;
+ }
+ if (byte != 0xFF) {
+ return byte;
+ }
+
+ uint16_t word;
+ if (!this->read(&word, 2)) {
+ return 0;
+ }
+ if (word != 0xFFFF) {
+ return word;
+ }
+
+ uint32_t quad;
+ if (!this->read(&quad, 4)) {
+ return 0;
+ }
+ return quad;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+SkWStream::~SkWStream()
+{
+}
+
+void SkWStream::newline()
+{
+ this->write("\n", 1);
+}
+
+void SkWStream::flush()
+{
+}
+
+bool SkWStream::writeText(const char text[])
+{
+ SkASSERT(text);
+ return this->write(text, strlen(text));
+}
+
+bool SkWStream::writeDecAsText(int32_t dec)
+{
+ SkString tmp;
+ tmp.appendS32(dec);
+ return this->write(tmp.c_str(), tmp.size());
+}
+
+bool SkWStream::writeHexAsText(uint32_t hex, int digits)
+{
+ SkString tmp;
+ tmp.appendHex(hex, digits);
+ return this->write(tmp.c_str(), tmp.size());
+}
+
+bool SkWStream::writeScalarAsText(SkScalar value)
+{
+ SkString tmp;
+ tmp.appendScalar(value);
+ return this->write(tmp.c_str(), tmp.size());
+}
+
+bool SkWStream::write8(U8CPU value) {
+ uint8_t v = SkToU8(value);
+ return this->write(&v, 1);
+}
+
+bool SkWStream::write16(U16CPU value) {
+ uint16_t v = SkToU16(value);
+ return this->write(&v, 2);
+}
+
+bool SkWStream::write32(uint32_t value) {
+ return this->write(&value, 4);
+}
+
+bool SkWStream::writeScalar(SkScalar value) {
+ return this->write(&value, sizeof(value));
+}
+
+bool SkWStream::writePackedUInt(size_t value) {
+ if (value < 0xFF) {
+ return this->write8(value);
+ } else if (value < 0xFFFF) {
+ return this->write8(0xFF) && this->write16(value);
+ } else {
+ return this->write16(0xFFFF) && this->write32(value);
+ }
+}
+
+bool SkWStream::writeStream(SkStream* stream, size_t length) {
+ char scratch[1024];
+ const size_t MAX = sizeof(scratch);
+
+ while (length != 0) {
+ size_t n = length;
+ if (n > MAX) {
+ n = MAX;
+ }
+ stream->read(scratch, n);
+ if (!this->write(scratch, n)) {
+ return false;
+ }
+ length -= n;
+ }
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkFILEStream::SkFILEStream(const char file[]) : fName(file)
+{
+#ifdef SK_BUILD_FOR_BREW
+ if (SkStrEndsWith(fName.c_str(), ".xml"))
+ fName.writable_str()[fName.size()-3] = 'b';
+#endif
+
+ fFILE = file ? sk_fopen(fName.c_str(), kRead_SkFILE_Flag) : NULL;
+}
+
+SkFILEStream::~SkFILEStream()
+{
+ if (fFILE)
+ sk_fclose(fFILE);
+}
+
+void SkFILEStream::setPath(const char path[])
+{
+ fName.set(path);
+#ifdef SK_BUILD_FOR_BREW
+ if (SkStrEndsWith(fName.c_str(), ".xml"))
+ fName.writable_str()[fName.size()-3] = 'b';
+#endif
+
+ if (fFILE)
+ {
+ sk_fclose(fFILE);
+ fFILE = NULL;
+ }
+ if (path)
+ fFILE = sk_fopen(fName.c_str(), kRead_SkFILE_Flag);
+}
+
+const char* SkFILEStream::getFileName()
+{
+ return fName.c_str();
+}
+
+bool SkFILEStream::rewind()
+{
+ if (fFILE)
+ {
+ if (sk_frewind(fFILE))
+ return true;
+ // we hit an error
+ sk_fclose(fFILE);
+ fFILE = NULL;
+ }
+ return false;
+}
+
+size_t SkFILEStream::read(void* buffer, size_t size)
+{
+ if (fFILE)
+ {
+ if (buffer == NULL && size == 0) // special signature, they want the total size
+ return sk_fgetsize(fFILE);
+ else
+ return sk_fread(buffer, size, fFILE);
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkMemoryStream::SkMemoryStream()
+{
+ fWeOwnTheData = false;
+ this->setMemory(NULL, 0);
+}
+
+SkMemoryStream::SkMemoryStream(size_t size) {
+ fWeOwnTheData = true;
+ fOffset = 0;
+ fSize = size;
+ fSrc = sk_malloc_throw(size);
+}
+
+SkMemoryStream::SkMemoryStream(const void* src, size_t size, bool copyData)
+{
+ fWeOwnTheData = false;
+ this->setMemory(src, size, copyData);
+}
+
+SkMemoryStream::~SkMemoryStream()
+{
+ if (fWeOwnTheData)
+ sk_free((void*)fSrc);
+}
+
+void SkMemoryStream::setMemory(const void* src, size_t size, bool copyData)
+{
+ if (fWeOwnTheData)
+ sk_free((void*)fSrc);
+
+ fSize = size;
+ fOffset = 0;
+ fWeOwnTheData = copyData;
+
+ if (copyData)
+ {
+ void* copy = sk_malloc_throw(size);
+ memcpy(copy, src, size);
+ src = copy;
+ }
+ fSrc = src;
+}
+
+void SkMemoryStream::skipToAlign4()
+{
+ // cast to remove unary-minus warning
+ fOffset += -(int)fOffset & 0x03;
+}
+
+bool SkMemoryStream::rewind()
+{
+ fOffset = 0;
+ return true;
+}
+
+size_t SkMemoryStream::read(void* buffer, size_t size)
+{
+ if (buffer == NULL && size == 0) // special signature, they want the total size
+ return fSize;
+
+ // if buffer is NULL, seek ahead by size
+
+ if (size == 0)
+ return 0;
+ if (size > fSize - fOffset)
+ size = fSize - fOffset;
+ if (buffer) {
+ memcpy(buffer, (const char*)fSrc + fOffset, size);
+ }
+ fOffset += size;
+ return size;
+}
+
+const void* SkMemoryStream::getMemoryBase()
+{
+ return fSrc;
+}
+
+const void* SkMemoryStream::getAtPos()
+{
+ return (const char*)fSrc + fOffset;
+}
+
+size_t SkMemoryStream::seek(size_t offset)
+{
+ if (offset > fSize)
+ offset = fSize;
+ fOffset = offset;
+ return offset;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkBufferStream::SkBufferStream(SkStream* proxy, size_t bufferSize)
+ : fProxy(proxy)
+{
+ SkASSERT(proxy != NULL);
+ proxy->ref();
+ this->init(NULL, bufferSize);
+}
+
+SkBufferStream::SkBufferStream(SkStream* proxy, void* buffer, size_t bufferSize)
+ : fProxy(proxy)
+{
+ SkASSERT(proxy != NULL);
+ SkASSERT(buffer == NULL || bufferSize != 0); // init(addr, 0) makes no sense, we must know how big their buffer is
+ proxy->ref();
+ this->init(buffer, bufferSize);
+}
+
+void SkBufferStream::init(void* buffer, size_t bufferSize)
+{
+ if (bufferSize == 0)
+ bufferSize = kDefaultBufferSize;
+
+ fOrigBufferSize = bufferSize;
+ fBufferSize = bufferSize;
+ fBufferOffset = bufferSize; // to trigger a reload on the first read()
+
+ if (buffer == NULL)
+ {
+ fBuffer = (char*)sk_malloc_throw(fBufferSize);
+ fWeOwnTheBuffer = true;
+ }
+ else
+ {
+ fBuffer = (char*)buffer;
+ fWeOwnTheBuffer = false;
+ }
+}
+
+SkBufferStream::~SkBufferStream()
+{
+ fProxy->unref();
+ if (fWeOwnTheBuffer)
+ sk_free(fBuffer);
+}
+
+bool SkBufferStream::rewind()
+{
+ fBufferOffset = fBufferSize = fOrigBufferSize;
+ return fProxy->rewind();
+}
+
+const char* SkBufferStream::getFileName()
+{
+ return fProxy->getFileName();
+}
+
+#ifdef SK_DEBUG
+// #define SK_TRACE_BUFFERSTREAM
+#endif
+
+size_t SkBufferStream::read(void* buffer, size_t size) {
+#ifdef SK_TRACE_BUFFERSTREAM
+ SkDebugf("Request %d", size);
+#endif
+
+ if (buffer == NULL && size == 0) {
+ return fProxy->read(buffer, size); // requesting total size
+ }
+
+ if (0 == size) {
+ return 0;
+ }
+
+ // skip size bytes
+ if (NULL == buffer) {
+ size_t remaining = fBufferSize - fBufferOffset;
+ if (remaining >= size) {
+ fBufferOffset += size;
+ return size;
+ }
+ // if we get here, we are being asked to skip beyond our current buffer
+ // so reset our offset to force a read next time, and skip the diff
+ // in our proxy
+ fBufferOffset = fOrigBufferSize;
+ return remaining + fProxy->read(NULL, size - remaining);
+ }
+
+ size_t s = size;
+ size_t actuallyRead = 0;
+
+ // flush what we can from our fBuffer
+ if (fBufferOffset < fBufferSize)
+ {
+ if (s > fBufferSize - fBufferOffset)
+ s = fBufferSize - fBufferOffset;
+ memcpy(buffer, fBuffer + fBufferOffset, s);
+#ifdef SK_TRACE_BUFFERSTREAM
+ SkDebugf(" flush %d", s);
+#endif
+ size -= s;
+ fBufferOffset += s;
+ buffer = (char*)buffer + s;
+ actuallyRead = s;
+ }
+
+ // check if there is more to read
+ if (size)
+ {
+ SkASSERT(fBufferOffset >= fBufferSize); // need to refill our fBuffer
+
+ if (size < fBufferSize) // lets try to read more than the request
+ {
+ s = fProxy->read(fBuffer, fBufferSize);
+#ifdef SK_TRACE_BUFFERSTREAM
+ SkDebugf(" read %d into fBuffer", s);
+#endif
+ if (size > s) // they asked for too much
+ size = s;
+ if (size)
+ {
+ memcpy(buffer, fBuffer, size);
+ actuallyRead += size;
+#ifdef SK_TRACE_BUFFERSTREAM
+ SkDebugf(" memcpy %d into dst", size);
+#endif
+ }
+
+ fBufferOffset = size;
+ fBufferSize = s; // record the (possibly smaller) size for the buffer
+ }
+ else // just do a direct read
+ {
+ actuallyRead += fProxy->read(buffer, size);
+#ifdef SK_TRACE_BUFFERSTREAM
+ SkDebugf(" direct read %d", size);
+#endif
+ }
+ }
+#ifdef SK_TRACE_BUFFERSTREAM
+ SkDebugf("\n");
+#endif
+ return actuallyRead;
+}
+
+const void* SkBufferStream::getMemoryBase()
+{
+ return fProxy->getMemoryBase();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkFILEWStream::SkFILEWStream(const char path[])
+{
+ fFILE = sk_fopen(path, kWrite_SkFILE_Flag);
+}
+
+SkFILEWStream::~SkFILEWStream()
+{
+ if (fFILE)
+ sk_fclose(fFILE);
+}
+
+bool SkFILEWStream::write(const void* buffer, size_t size)
+{
+ if (fFILE == NULL)
+ return false;
+
+ if (sk_fwrite(buffer, size, fFILE) != size)
+ {
+ SkDEBUGCODE(SkDebugf("SkFILEWStream failed writing %d bytes\n", size);)
+ sk_fclose(fFILE);
+ fFILE = NULL;
+ return false;
+ }
+ return true;
+}
+
+void SkFILEWStream::flush()
+{
+ if (fFILE)
+ sk_fflush(fFILE);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+SkMemoryWStream::SkMemoryWStream(void* buffer, size_t size)
+ : fBuffer((char*)buffer), fMaxLength(size), fBytesWritten(0)
+{
+}
+
+bool SkMemoryWStream::write(const void* buffer, size_t size)
+{
+ size = SkMin32(size, fMaxLength - fBytesWritten);
+ if (size > 0)
+ {
+ memcpy(fBuffer + fBytesWritten, buffer, size);
+ fBytesWritten += size;
+ return true;
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+#define SkDynamicMemoryWStream_MinBlockSize 256
+
+struct SkDynamicMemoryWStream::Block {
+ Block* fNext;
+ char* fCurr;
+ char* fStop;
+
+ const char* start() const { return (const char*)(this + 1); }
+ char* start() { return (char*)(this + 1); }
+ size_t avail() const { return fStop - fCurr; }
+ size_t written() const { return fCurr - this->start(); }
+
+ void init(size_t size)
+ {
+ fNext = NULL;
+ fCurr = this->start();
+ fStop = this->start() + size;
+ }
+
+ const void* append(const void* data, size_t size)
+ {
+ SkASSERT((size_t)(fStop - fCurr) >= size);
+ memcpy(fCurr, data, size);
+ fCurr += size;
+ return (const void*)((const char*)data + size);
+ }
+};
+
+SkDynamicMemoryWStream::SkDynamicMemoryWStream() : fHead(NULL), fTail(NULL), fBytesWritten(0), fCopyToCache(NULL)
+{
+}
+
+SkDynamicMemoryWStream::~SkDynamicMemoryWStream()
+{
+ reset();
+}
+
+const char* SkDynamicMemoryWStream::detach()
+{
+ const char* result = getStream();
+ fCopyToCache = NULL;
+ return result;
+}
+
+void SkDynamicMemoryWStream::reset()
+{
+ sk_free(fCopyToCache);
+ Block* block = fHead;
+
+ while (block != NULL) {
+ Block* next = block->fNext;
+ sk_free(block);
+ block = next;
+ }
+ fHead = fTail = NULL;
+ fBytesWritten = 0;
+ fCopyToCache = NULL;
+}
+
+bool SkDynamicMemoryWStream::write(const void* buffer, size_t count)
+{
+ if (count > 0) {
+
+ if (fCopyToCache) {
+ sk_free(fCopyToCache);
+ fCopyToCache = NULL;
+ }
+ fBytesWritten += count;
+
+ size_t size;
+
+ if (fTail != NULL && fTail->avail() > 0) {
+ size = SkMin32(fTail->avail(), count);
+ buffer = fTail->append(buffer, size);
+ SkASSERT(count >= size);
+ count -= size;
+ if (count == 0)
+ return true;
+ }
+
+ size = SkMax32(count, SkDynamicMemoryWStream_MinBlockSize);
+ Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
+ block->init(size);
+ block->append(buffer, count);
+
+ if (fTail != NULL)
+ fTail->fNext = block;
+ else
+ fHead = fTail = block;
+ fTail = block;
+ }
+ return true;
+}
+
+bool SkDynamicMemoryWStream::write(const void* buffer, size_t offset, size_t count)
+{
+ if (offset + count > fBytesWritten)
+ return false; // test does not partially modify
+ Block* block = fHead;
+ while (block != NULL) {
+ size_t size = block->written();
+ if (offset < size) {
+ size_t part = offset + count > size ? size - offset : count;
+ memcpy(block->start() + offset, buffer, part);
+ if (count <= part)
+ return true;
+ count -= part;
+ buffer = (const void*) ((char* ) buffer + part);
+ }
+ offset = offset > size ? offset - size : 0;
+ block = block->fNext;
+ }
+ return false;
+}
+
+bool SkDynamicMemoryWStream::read(void* buffer, size_t offset, size_t count)
+{
+ if (offset + count > fBytesWritten)
+ return false; // test does not partially modify
+ Block* block = fHead;
+ while (block != NULL) {
+ size_t size = block->written();
+ if (offset < size) {
+ size_t part = offset + count > size ? size - offset : count;
+ memcpy(buffer, block->start() + offset, part);
+ if (count <= part)
+ return true;
+ count -= part;
+ buffer = (void*) ((char* ) buffer + part);
+ }
+ offset = offset > size ? offset - size : 0;
+ block = block->fNext;
+ }
+ return false;
+}
+
+void SkDynamicMemoryWStream::copyTo(void* dst) const
+{
+ Block* block = fHead;
+
+ while (block != NULL) {
+ size_t size = block->written();
+ memcpy(dst, block->start(), size);
+ dst = (void*)((char*)dst + size);
+ block = block->fNext;
+ }
+}
+
+const char* SkDynamicMemoryWStream::getStream() const
+{
+ if (fCopyToCache == NULL) {
+ fCopyToCache = (char*)sk_malloc_throw(fBytesWritten);
+ this->copyTo(fCopyToCache);
+ }
+ return fCopyToCache;
+}
+
+void SkDynamicMemoryWStream::padToAlign4()
+{
+ // cast to remove unary-minus warning
+ int padBytes = -(int)fBytesWritten & 0x03;
+ if (padBytes == 0)
+ return;
+ int zero = 0;
+ write(&zero, padBytes);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkDebugWStream::newline()
+{
+#ifdef SK_DEBUG
+ SkDebugf("\n");
+#endif
+}
+
+bool SkDebugWStream::write(const void* buffer, size_t size)
+{
+#ifdef SK_DEBUG
+ char* s = new char[size+1];
+ memcpy(s, buffer, size);
+ s[size] = 0;
+ SkDebugf("%s", s);
+ delete[] s;
+#endif
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+#include "SkRandom.h"
+
+#ifdef SK_SUPPORT_UNITTEST
+#define MAX_SIZE (256 * 1024)
+
+static void random_fill(SkRandom& rand, void* buffer, size_t size) {
+ char* p = (char*)buffer;
+ char* stop = p + size;
+ while (p < stop) {
+ *p++ = (char)(rand.nextU() >> 8);
+ }
+}
+
+static void test_buffer() {
+ SkRandom rand;
+ SkAutoMalloc am(MAX_SIZE * 2);
+ char* storage = (char*)am.get();
+ char* storage2 = storage + MAX_SIZE;
+
+ random_fill(rand, storage, MAX_SIZE);
+
+ for (int sizeTimes = 0; sizeTimes < 100; sizeTimes++) {
+ int size = rand.nextU() % MAX_SIZE;
+ if (size == 0) {
+ size = MAX_SIZE;
+ }
+ for (int times = 0; times < 100; times++) {
+ int bufferSize = 1 + (rand.nextU() & 0xFFFF);
+ SkMemoryStream mstream(storage, size);
+ SkBufferStream bstream(&mstream, bufferSize);
+
+ int bytesRead = 0;
+ while (bytesRead < size) {
+ int s = 17 + (rand.nextU() & 0xFFFF);
+ int ss = bstream.read(storage2, s);
+ SkASSERT(ss > 0 && ss <= s);
+ SkASSERT(bytesRead + ss <= size);
+ SkASSERT(memcmp(storage + bytesRead, storage2, ss) == 0);
+ bytesRead += ss;
+ }
+ SkASSERT(bytesRead == size);
+ }
+ }
+}
+#endif
+
+void SkStream::UnitTest() {
+#ifdef SK_SUPPORT_UNITTEST
+ {
+ static const char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ char copy[sizeof(s)];
+ SkRandom rand;
+
+ for (int i = 0; i < 65; i++)
+ {
+ char* copyPtr = copy;
+ SkMemoryStream mem(s, sizeof(s));
+ SkBufferStream buff(&mem, i);
+
+ do {
+ copyPtr += buff.read(copyPtr, rand.nextU() & 15);
+ } while (copyPtr < copy + sizeof(s));
+ SkASSERT(copyPtr == copy + sizeof(s));
+ SkASSERT(memcmp(s, copy, sizeof(s)) == 0);
+ }
+ }
+ test_buffer();
+#endif
+}
+
+void SkWStream::UnitTest()
+{
+#ifdef SK_SUPPORT_UNITTEST
+ {
+ SkDebugWStream s;
+
+ s.writeText("testing wstream helpers\n");
+ s.writeText("compare: 0 "); s.writeDecAsText(0); s.newline();
+ s.writeText("compare: 591 "); s.writeDecAsText(591); s.newline();
+ s.writeText("compare: -9125 "); s.writeDecAsText(-9125); s.newline();
+ s.writeText("compare: 0 "); s.writeHexAsText(0, 0); s.newline();
+ s.writeText("compare: 03FA "); s.writeHexAsText(0x3FA, 4); s.newline();
+ s.writeText("compare: DEADBEEF "); s.writeHexAsText(0xDEADBEEF, 4); s.newline();
+ s.writeText("compare: 0 "); s.writeScalarAsText(SkIntToScalar(0)); s.newline();
+ s.writeText("compare: 27 "); s.writeScalarAsText(SkIntToScalar(27)); s.newline();
+ s.writeText("compare: -119 "); s.writeScalarAsText(SkIntToScalar(-119)); s.newline();
+ s.writeText("compare: 851.3333 "); s.writeScalarAsText(SkIntToScalar(851) + SK_Scalar1/3); s.newline();
+ s.writeText("compare: -0.08 "); s.writeScalarAsText(-SK_Scalar1*8/100); s.newline();
+ }
+
+ {
+ SkDynamicMemoryWStream ds;
+ const char s[] = "abcdefghijklmnopqrstuvwxyz";
+ int i;
+ for (i = 0; i < 100; i++) {
+ bool result = ds.write(s, 26);
+ SkASSERT(result);
+ }
+ SkASSERT(ds.getOffset() == 100 * 26);
+ char* dst = new char[100 * 26 + 1];
+ dst[100*26] = '*';
+ ds.copyTo(dst);
+ SkASSERT(dst[100*26] == '*');
+ // char* p = dst;
+ for (i = 0; i < 100; i++)
+ SkASSERT(memcmp(&dst[i * 26], s, 26) == 0);
+ SkASSERT(memcmp(dst, ds.getStream(), 100*26) == 0);
+ delete[] dst;
+ }
+#endif
+}
+
+#endif
diff --git a/src/images/bmpdecoderhelper.cpp b/src/images/bmpdecoderhelper.cpp
new file mode 100644
index 0000000000..acabf44156
--- /dev/null
+++ b/src/images/bmpdecoderhelper.cpp
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Author: cevans@google.com (Chris Evans)
+
+#include "bmpdecoderhelper.h"
+
+namespace image_codec {
+
+static const int kBmpHeaderSize = 14;
+static const int kBmpInfoSize = 40;
+static const int kBmpOS2InfoSize = 12;
+static const int kMaxDim = SHRT_MAX / 2;
+
+bool BmpDecoderHelper::DecodeImage(const char* p,
+ int len,
+ int max_pixels,
+ BmpDecoderCallback* callback) {
+ data_ = reinterpret_cast<const uint8*>(p);
+ pos_ = 0;
+ len_ = len;
+ inverted_ = true;
+ // Parse the header structure.
+ if (len < kBmpHeaderSize + 4) {
+ return false;
+ }
+ GetShort(); // Signature.
+ GetInt(); // Size.
+ GetInt(); // Reserved.
+ int offset = GetInt();
+ // Parse the info structure.
+ int infoSize = GetInt();
+ if (infoSize != kBmpOS2InfoSize && infoSize < kBmpInfoSize) {
+ return false;
+ }
+ int cols = 0;
+ int comp = 0;
+ int colLen = 4;
+ if (infoSize >= kBmpInfoSize) {
+ if (len < kBmpHeaderSize + kBmpInfoSize) {
+ return false;
+ }
+ width_ = GetInt();
+ height_ = GetInt();
+ GetShort(); // Planes.
+ bpp_ = GetShort();
+ comp = GetInt();
+ GetInt(); // Size.
+ GetInt(); // XPPM.
+ GetInt(); // YPPM.
+ cols = GetInt();
+ GetInt(); // Important colours.
+ } else {
+ if (len < kBmpHeaderSize + kBmpOS2InfoSize) {
+ return false;
+ }
+ colLen = 3;
+ width_ = GetShort();
+ height_ = GetShort();
+ GetShort(); // Planes.
+ bpp_ = GetShort();
+ }
+ if (height_ < 0) {
+ height_ = -height_;
+ inverted_ = false;
+ }
+ if (width_ <= 0 || width_ > kMaxDim || height_ <= 0 || height_ > kMaxDim) {
+ return false;
+ }
+ if (width_ * height_ > max_pixels) {
+ return false;
+ }
+ if (cols < 0 || cols > 256) {
+ return false;
+ }
+ // Allocate then read in the colour map.
+ if (cols == 0 && bpp_ <= 8) {
+ cols = 1 << bpp_;
+ }
+ if (bpp_ <= 8 || cols > 0) {
+ uint8* colBuf = new uint8[256 * 3];
+ memset(colBuf, '\0', 256 * 3);
+ colTab_.reset(colBuf);
+ }
+ if (cols > 0) {
+ if (pos_ + (cols * colLen) > len_) {
+ return false;
+ }
+ for (int i = 0; i < cols; ++i) {
+ int base = i * 3;
+ colTab_[base + 2] = GetByte();
+ colTab_[base + 1] = GetByte();
+ colTab_[base] = GetByte();
+ if (colLen == 4) {
+ GetByte();
+ }
+ }
+ }
+ // Read in the compression data if necessary.
+ redBits_ = 0x7c00;
+ greenBits_ = 0x03e0;
+ blueBits_ = 0x001f;
+ bool rle = false;
+ if (comp == 1 || comp == 2) {
+ rle = true;
+ } else if (comp == 3) {
+ if (pos_ + 12 > len_) {
+ return false;
+ }
+ redBits_ = GetInt() & 0xffff;
+ greenBits_ = GetInt() & 0xffff;
+ blueBits_ = GetInt() & 0xffff;
+ }
+ redShiftRight_ = CalcShiftRight(redBits_);
+ greenShiftRight_ = CalcShiftRight(greenBits_);
+ blueShiftRight_ = CalcShiftRight(blueBits_);
+ redShiftLeft_ = CalcShiftLeft(redBits_);
+ greenShiftLeft_ = CalcShiftLeft(greenBits_);
+ blueShiftLeft_ = CalcShiftLeft(blueBits_);
+ rowPad_ = 0;
+ pixelPad_ = 0;
+ int rowLen;
+ if (bpp_ == 32) {
+ rowLen = width_ * 4;
+ pixelPad_ = 1;
+ } else if (bpp_ == 24) {
+ rowLen = width_ * 3;
+ } else if (bpp_ == 16) {
+ rowLen = width_ * 2;
+ } else if (bpp_ == 8) {
+ rowLen = width_;
+ } else if (bpp_ == 4) {
+ rowLen = width_ / 2;
+ if (width_ & 1) {
+ rowLen++;
+ }
+ } else if (bpp_ == 1) {
+ rowLen = width_ / 8;
+ if (width_ & 7) {
+ rowLen++;
+ }
+ } else {
+ return false;
+ }
+ // Round the rowLen up to a multiple of 4.
+ if (rowLen % 4 != 0) {
+ rowPad_ = 4 - (rowLen % 4);
+ rowLen += rowPad_;
+ }
+
+ if (offset > 0 && offset > pos_ && offset < len_) {
+ pos_ = offset;
+ }
+ // Deliberately off-by-one; a load of BMPs seem to have their last byte
+ // missing.
+ if (!rle && (pos_ + (rowLen * height_) > len_ + 1)) {
+ return false;
+ }
+
+ output_ = callback->SetSize(width_, height_);
+ if (NULL == output_) {
+ return true; // meaning we succeeded, but they want us to stop now
+ }
+
+ if (rle && (bpp_ == 4 || bpp_ == 8)) {
+ DoRLEDecode();
+ } else {
+ DoStandardDecode();
+ }
+ return true;
+}
+
+void BmpDecoderHelper::DoRLEDecode() {
+ static const uint8 RLE_ESCAPE = 0;
+ static const uint8 RLE_EOL = 0;
+ static const uint8 RLE_EOF = 1;
+ static const uint8 RLE_DELTA = 2;
+ int x = 0;
+ int y = height_ - 1;
+ while (pos_ < len_ - 1) {
+ uint8 cmd = GetByte();
+ if (cmd != RLE_ESCAPE) {
+ uint8 pixels = GetByte();
+ int num = 0;
+ uint8 col = pixels;
+ while (cmd-- && x < width_) {
+ if (bpp_ == 4) {
+ if (num & 1) {
+ col = pixels & 0xf;
+ } else {
+ col = pixels >> 4;
+ }
+ }
+ PutPixel(x++, y, col);
+ num++;
+ }
+ } else {
+ cmd = GetByte();
+ if (cmd == RLE_EOF) {
+ return;
+ } else if (cmd == RLE_EOL) {
+ x = 0;
+ y--;
+ if (y < 0) {
+ return;
+ }
+ } else if (cmd == RLE_DELTA) {
+ if (pos_ < len_ - 1) {
+ uint8 dx = GetByte();
+ uint8 dy = GetByte();
+ x += dx;
+ if (x > width_) {
+ x = width_;
+ }
+ y -= dy;
+ if (y < 0) {
+ return;
+ }
+ }
+ } else {
+ int num = 0;
+ int bytesRead = 0;
+ uint8 val = 0;
+ while (cmd-- && pos_ < len_) {
+ if (bpp_ == 8 || !(num & 1)) {
+ val = GetByte();
+ bytesRead++;
+ }
+ uint8 col = val;
+ if (bpp_ == 4) {
+ if (num & 1) {
+ col = col & 0xf;
+ } else {
+ col >>= 4;
+ }
+ }
+ if (x < width_) {
+ PutPixel(x++, y, col);
+ }
+ num++;
+ }
+ // All pixel runs must be an even number of bytes - skip a byte if we
+ // read an odd number.
+ if ((bytesRead & 1) && pos_ < len_) {
+ GetByte();
+ }
+ }
+ }
+ }
+}
+
+void BmpDecoderHelper::PutPixel(int x, int y, uint8 col) {
+ CHECK(x >= 0 && x < width_);
+ CHECK(y >= 0 && y < height_);
+ if (!inverted_) {
+ y = height_ - (y + 1);
+ }
+
+ int base = ((y * width_) + x) * 3;
+ int colBase = col * 3;
+ output_[base] = colTab_[colBase];
+ output_[base + 1] = colTab_[colBase + 1];
+ output_[base + 2] = colTab_[colBase + 2];
+}
+
+void BmpDecoderHelper::DoStandardDecode() {
+ int row = 0;
+ uint8 currVal = 0;
+ for (int h = height_ - 1; h >= 0; h--, row++) {
+ int realH = h;
+ if (!inverted_) {
+ realH = height_ - (h + 1);
+ }
+ uint8* line = output_ + (3 * width_ * realH);
+ for (int w = 0; w < width_; w++) {
+ if (bpp_ >= 24) {
+ line[2] = GetByte();
+ line[1] = GetByte();
+ line[0] = GetByte();
+ } else if (bpp_ == 16) {
+ uint32 val = GetShort();
+ line[0] = ((val & redBits_) >> redShiftRight_) << redShiftLeft_;
+ line[1] = ((val & greenBits_) >> greenShiftRight_) << greenShiftLeft_;
+ line[2] = ((val & blueBits_) >> blueShiftRight_) << blueShiftLeft_;
+ } else if (bpp_ <= 8) {
+ uint8 col;
+ if (bpp_ == 8) {
+ col = GetByte();
+ } else if (bpp_ == 4) {
+ if ((w % 2) == 0) {
+ currVal = GetByte();
+ col = currVal >> 4;
+ } else {
+ col = currVal & 0xf;
+ }
+ } else {
+ if ((w % 8) == 0) {
+ currVal = GetByte();
+ }
+ int bit = w & 7;
+ col = ((currVal >> (7 - bit)) & 1);
+ }
+ int base = col * 3;
+ line[0] = colTab_[base];
+ line[1] = colTab_[base + 1];
+ line[2] = colTab_[base + 2];
+ }
+ line += 3;
+ for (int i = 0; i < pixelPad_; ++i) {
+ GetByte();
+ }
+ }
+ for (int i = 0; i < rowPad_; ++i) {
+ GetByte();
+ }
+ }
+}
+
+int BmpDecoderHelper::GetInt() {
+ uint8 b1 = GetByte();
+ uint8 b2 = GetByte();
+ uint8 b3 = GetByte();
+ uint8 b4 = GetByte();
+ return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
+}
+
+int BmpDecoderHelper::GetShort() {
+ uint8 b1 = GetByte();
+ uint8 b2 = GetByte();
+ return b1 | (b2 << 8);
+}
+
+uint8 BmpDecoderHelper::GetByte() {
+ CHECK(pos_ >= 0 && pos_ <= len_);
+ // We deliberately allow this off-by-one access to cater for BMPs with their
+ // last byte missing.
+ if (pos_ == len_) {
+ return 0;
+ }
+ return data_[pos_++];
+}
+
+int BmpDecoderHelper::CalcShiftRight(uint32 mask) {
+ int ret = 0;
+ while (mask != 0 && !(mask & 1)) {
+ mask >>= 1;
+ ret++;
+ }
+ return ret;
+}
+
+int BmpDecoderHelper::CalcShiftLeft(uint32 mask) {
+ int ret = 0;
+ while (mask != 0 && !(mask & 1)) {
+ mask >>= 1;
+ }
+ while (mask != 0 && !(mask & 0x80)) {
+ mask <<= 1;
+ ret++;
+ }
+ return ret;
+}
+
+} // namespace image_codec
diff --git a/src/images/bmpdecoderhelper.h b/src/images/bmpdecoderhelper.h
new file mode 100644
index 0000000000..07f0ae5646
--- /dev/null
+++ b/src/images/bmpdecoderhelper.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IMAGE_CODEC_BMPDECODERHELPER_H__
+#define IMAGE_CODEC_BMPDECODERHELPER_H__
+
+///////////////////////////////////////////////////////////////////////////////
+// this section is my current "glue" between google3 code and android.
+// will be fixed soon
+
+#include "SkTypes.h"
+#include <limits.h>
+#define DISALLOW_EVIL_CONSTRUCTORS(name)
+#define CHECK(predicate) SkASSERT(predicate)
+typedef uint8_t uint8;
+typedef uint32_t uint32;
+
+template <typename T> class scoped_array {
+private:
+ T* ptr_;
+ scoped_array(scoped_array const&);
+ scoped_array& operator=(const scoped_array&);
+
+public:
+ explicit scoped_array(T* p = 0) : ptr_(p) {}
+ ~scoped_array() {
+ delete[] ptr_;
+ }
+
+ void reset(T* p = 0) {
+ if (p != ptr_) {
+ delete[] ptr_;
+ ptr_ = p;
+ }
+ }
+
+ T& operator[](int i) const {
+ return ptr_[i];
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace image_codec {
+
+class BmpDecoderCallback {
+ public:
+ BmpDecoderCallback() { }
+ virtual ~BmpDecoderCallback() {}
+
+ /**
+ * This is called once for an image. It is passed the width and height and
+ * should return the address of a buffer that is large enough to store
+ * all of the resulting pixels (widht * height * 3 bytes). If it returns NULL,
+ * then the decoder will abort, but return true, as the caller has received
+ * valid dimensions.
+ */
+ virtual uint8* SetSize(int width, int height) = 0;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(BmpDecoderCallback);
+};
+
+class BmpDecoderHelper {
+ public:
+ BmpDecoderHelper() { }
+ ~BmpDecoderHelper() { }
+ bool DecodeImage(const char* data,
+ int len,
+ int max_pixels,
+ BmpDecoderCallback* callback);
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(BmpDecoderHelper);
+
+ void DoRLEDecode();
+ void DoStandardDecode();
+ void PutPixel(int x, int y, uint8 col);
+
+ int GetInt();
+ int GetShort();
+ uint8 GetByte();
+ int CalcShiftRight(uint32 mask);
+ int CalcShiftLeft(uint32 mask);
+
+ const uint8* data_;
+ int pos_;
+ int len_;
+ int width_;
+ int height_;
+ int bpp_;
+ int pixelPad_;
+ int rowPad_;
+ scoped_array<uint8> colTab_;
+ uint32 redBits_;
+ uint32 greenBits_;
+ uint32 blueBits_;
+ int redShiftRight_;
+ int greenShiftRight_;
+ int blueShiftRight_;
+ int redShiftLeft_;
+ int greenShiftLeft_;
+ int blueShiftLeft_;
+ uint8* output_;
+ bool inverted_;
+};
+
+} // namespace
+
+#endif
diff --git a/src/images/fpdfemb_ext.h b/src/images/fpdfemb_ext.h
new file mode 100644
index 0000000000..d82c4df37c
--- /dev/null
+++ b/src/images/fpdfemb_ext.h
@@ -0,0 +1,81 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Extended interfaces for JPEG, JPEG2000 and JBIG2 decoders **/
+typedef struct
+{
+ /** Initialize the decoding context, with memory allocator provided by FPDFEMB.
+ Implementation should return a pointer to the decoding context.
+ */
+ void* (*Init)(void* (*alloc_func)(unsigned int), void (*free_func)(void*));
+
+ /** Finish with the decoding. */
+ void (*Finish)(void* pContext);
+
+ /** Input JPEG encoded data from the source.
+ This function may be called multiple times during decoding progress.
+ */
+ void (*Input)(void* pContext, const unsigned char* src_buf, unsigned long src_size);
+
+ /** Read the header information. Return non-zero for success, 0 for failure */
+ int (*ReadHeader)(void* pContext);
+
+ /** Get info from the decoder, including image width, height and number of components */
+ void (*GetInfo)(void* pContext, int* width, int* height, int* nComps);
+
+ /** Read one scanline from decoded image */
+ int (*ReadScanline)(void* pContext, unsigned char* dest_buf);
+
+ /** Get number of available source bytes left in the input stream */
+ unsigned long (*GetAvailInput)(void* pContext);
+} FPDFEMB_JPEG_DECODER;
+
+void FPDFEMB_SetJpegDecoder(FPDFEMB_JPEG_DECODER* pDecoder);
+
+typedef struct
+{
+ /** Initialize the decoder with the full source data.
+ Implementation should return a pointer to the context.
+ */
+ void* (*Init)(const unsigned char* src_buf, unsigned long src_size);
+
+ /** Destroy the context */
+ void (*Finish)(void* context);
+
+ /** Get image info from the context, including width, height, number of components
+ in original codestream, and number of components in output image. For some
+ particular type of encoded image, like paletted image, these two numbers of
+ components may vary.
+ */
+ void (*GetInfo)(void* context, unsigned long* width, unsigned long* height,
+ unsigned long* codestream_nComps, unsigned long* output_nComps);
+
+ /** Do the real data decoding, output to a pre-allocated buffer.
+ bTranslateColor indicates whether the decoder should use JPEG2000 embedded
+ color space info to translate image into sRGB color space.
+ "offsets" array describes the byte order of all components. For example,
+ {2,1,0} means the first components is output to last byte.
+ */
+ void (*Decode)(void* context, unsigned char* dest_buf, int pitch,
+ int bTranslateColor, unsigned char* offsets);
+} FPDFEMB_JPEG2000_DECODER;
+
+void FPDFEMB_SetJpeg2000Decoder(FPDFEMB_JPEG2000_DECODER* pDecoder);
+
+typedef struct
+{
+ /** Do the whole decoding process. Supplied parameters include width, height, source image
+ data and size, global data and size (can be shared among different images), destination
+ buffer and scanline pitch in dest buffer.
+ */
+ void (*Decode)(unsigned long width, unsigned long height, const unsigned char* src_buf,
+ unsigned long src_size, const unsigned char* global_buf, unsigned long global_size,
+ unsigned char* dest_buf, int dest_pitch);
+} FPDFEMB_JBIG2_DECODER;
+
+void FPDFEMB_SetJbig2Decoder(FPDFEMB_JBIG2_DECODER* pDecoder);
+
+#ifdef __cplusplus
+};
+#endif