aboutsummaryrefslogtreecommitdiffstats
path: root/src/images
diff options
context:
space:
mode:
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.cpp194
-rw-r--r--src/images/SkImageDecoder_Factory.cpp56
-rw-r--r--src/images/SkImageDecoder_fpdfemb.cpp236
-rw-r--r--src/images/SkImageDecoder_libbmp.cpp149
-rw-r--r--src/images/SkImageDecoder_libgif.cpp343
-rw-r--r--src/images/SkImageDecoder_libico.cpp392
-rw-r--r--src/images/SkImageDecoder_libjpeg.cpp800
-rw-r--r--src/images/SkImageDecoder_libpng.cpp799
-rw-r--r--src/images/SkImageDecoder_libpvjpeg.cpp206
-rw-r--r--src/images/SkImageDecoder_wbmp.cpp172
-rw-r--r--src/images/SkImageEncoder.cpp48
-rw-r--r--src/images/SkImageEncoder_Factory.cpp35
-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/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/bmpdecoderhelper.cpp376
-rw-r--r--src/images/bmpdecoderhelper.h123
-rw-r--r--src/images/fpdfemb.h1765
-rw-r--r--src/images/fpdfemb_ext.h81
29 files changed, 7406 insertions, 0 deletions
diff --git a/src/images/SkBitmap_RLEPixels.h b/src/images/SkBitmap_RLEPixels.h
new file mode 100644
index 0000000..c83bc69
--- /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 0000000..5756237
--- /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 0000000..db4a51a
--- /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 0000000..95403cc
--- /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 0000000..f0fff1b
--- /dev/null
+++ b/src/images/SkImageDecoder.cpp
@@ -0,0 +1,194 @@
+/* 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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* Technically, this should be 342, since that is the cutoff point between
+ an index and 32bit bitmap (they take equal ram), but since 32bit is almost
+ always faster, I bump up the value a bit.
+*/
+#define MIN_SIZE_FOR_INDEX (512)
+
+/* Return the "optimal" config for this bitmap. In this case, we just look to
+ promote index bitmaps to full-color, since those are a little faster to
+ draw (fewer memory lookups).
+
+ Seems like we could expose this to the caller through some exising or new
+ proxy object, allowing them to decide (after sniffing some aspect of the
+ original bitmap) what config they really want.
+ */
+static SkBitmap::Config optimal_config(const SkBitmap& bm,
+ SkBitmap::Config pref) {
+ if (bm.config() != pref) {
+ if (bm.config() == SkBitmap::kIndex8_Config) {
+ Sk64 size64 = bm.getSize64();
+ if (size64.is32()) {
+ int32_t size = size64.get32();
+ if (size < MIN_SIZE_FOR_INDEX) {
+ return SkBitmap::kARGB_8888_Config;
+ }
+ }
+ }
+ }
+ return bm.config();
+}
+
+bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
+ SkBitmap::Config pref, Mode mode) {
+ // pass a temporary bitmap, so that if we return false, we are assured of
+ // leaving the caller's bitmap untouched.
+ SkBitmap tmp;
+
+ // we reset this to false before calling onDecode
+ fShouldCancelDecode = false;
+
+ if (!this->onDecode(stream, &tmp, pref, mode)) {
+ return false;
+ }
+
+ SkBitmap::Config c = optimal_config(tmp, pref);
+ if (c != tmp.config()) {
+ if (mode == kDecodeBounds_Mode) {
+ tmp.setConfig(c, tmp.width(), tmp.height());
+ } else {
+ SkBitmap tmp2;
+ if (tmp.copyTo(&tmp2, c, this->getAllocator())) {
+ tmp.swap(tmp2);
+ }
+ }
+ }
+ bm->swap(tmp);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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;
+}
+
diff --git a/src/images/SkImageDecoder_Factory.cpp b/src/images/SkImageDecoder_Factory.cpp
new file mode 100644
index 0000000..9dd9913
--- /dev/null
+++ b/src/images/SkImageDecoder_Factory.cpp
@@ -0,0 +1,56 @@
+/* libs/graphics/ports/SkImageDecoder_Factory.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 "SkMovie.h"
+#include "SkStream.h"
+#include "SkTRegistry.h"
+
+typedef SkTRegistry<SkImageDecoder*, SkStream*> DecodeReg;
+
+template DecodeReg* DecodeReg::gHead;
+
+SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
+ const DecodeReg* curr = DecodeReg::Head();
+ while (curr) {
+ SkImageDecoder* codec = curr->factory()(stream);
+ stream->rewind();
+ if (codec) {
+ return codec;
+ }
+ curr = curr->next();
+ }
+ return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+typedef SkTRegistry<SkMovie*, SkStream*> MovieReg;
+
+SkMovie* SkMovie::DecodeStream(SkStream* stream) {
+ const MovieReg* curr = MovieReg::Head();
+ while (curr) {
+ SkMovie* movie = curr->factory()(stream);
+ if (movie) {
+ return movie;
+ }
+ stream->rewind();
+ curr = curr->next();
+ }
+ return NULL;
+}
+
diff --git a/src/images/SkImageDecoder_fpdfemb.cpp b/src/images/SkImageDecoder_fpdfemb.cpp
new file mode 100644
index 0000000..7f37e3d
--- /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 0000000..a4dcbf6
--- /dev/null
+++ b/src/images/SkImageDecoder_libbmp.cpp
@@ -0,0 +1,149 @@
+/*
+ * 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"
+#include "SkTRegistry.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);
+};
+
+static SkImageDecoder* 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;
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+
+///////////////////////////////////////////////////////////////////////////////
+
+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 0000000..ed8817a
--- /dev/null
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -0,0 +1,343 @@
+/* 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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* 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;
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
new file mode 100644
index 0000000..9f21e13
--- /dev/null
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -0,0 +1,392 @@
+/* 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)
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+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);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* Factory(SkStream* stream) {
+ // Check to see if the first four bytes are 0,0,1,0
+ // FIXME: 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) {
+ // This stream does not represent an ICO image.
+ return NULL;
+ }
+ return SkNEW(SkICOImageDecoder);
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
new file mode 100644
index 0000000..018c96c
--- /dev/null
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -0,0 +1,800 @@
+/*
+ * 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 "SkImageEncoder.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);
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+#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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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;
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* DFactory(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);
+}
+
+static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
+ return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);
+static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
+
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
new file mode 100644
index 0000000..b616eee
--- /dev/null
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -0,0 +1,799 @@
+/* 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 "SkImageEncoder.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;
+};
+
+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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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 inline 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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* DFactory(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 SkImageEncoder* EFactory(SkImageEncoder::Type t) {
+ return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
+}
+
+static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
+static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);
diff --git a/src/images/SkImageDecoder_libpvjpeg.cpp b/src/images/SkImageDecoder_libpvjpeg.cpp
new file mode 100644
index 0000000..9177741
--- /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 0000000..ac242ea
--- /dev/null
+++ b/src/images/SkImageDecoder_wbmp.cpp
@@ -0,0 +1,172 @@
+/**
+** 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;
+ }
+};
+
+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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* Factory(SkStream* stream) {
+ wbmp_head head;
+
+ if (head.init(stream)) {
+ return SkNEW(SkWBMPImageDecoder);
+ }
+ return NULL;
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+
diff --git a/src/images/SkImageEncoder.cpp b/src/images/SkImageEncoder.cpp
new file mode 100644
index 0000000..d359905
--- /dev/null
+++ b/src/images/SkImageEncoder.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009, 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 "SkImageEncoder.h"
+#include "SkBitmap.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+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);
+}
+
diff --git a/src/images/SkImageEncoder_Factory.cpp b/src/images/SkImageEncoder_Factory.cpp
new file mode 100644
index 0000000..d673167
--- /dev/null
+++ b/src/images/SkImageEncoder_Factory.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2009, 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 "SkImageEncoder.h"
+#include "SkTRegistry.h"
+
+typedef SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> EncodeReg;
+
+template EncodeReg* EncodeReg::gHead;
+
+SkImageEncoder* SkImageEncoder::Create(Type t) {
+ const EncodeReg* curr = EncodeReg::Head();
+ while (curr) {
+ SkImageEncoder* codec = curr->factory()(t);
+ if (codec) {
+ return codec;
+ }
+ curr = curr->next();
+ }
+ return NULL;
+}
+
diff --git a/src/images/SkImageRef.cpp b/src/images/SkImageRef.cpp
new file mode 100644
index 0000000..90c37b6
--- /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 0000000..e322507
--- /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 0000000..b2eb7b3
--- /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 0000000..1f0bc43
--- /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/SkMovie.cpp b/src/images/SkMovie.cpp
new file mode 100644
index 0000000..7186ed5
--- /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 0000000..ca9c812
--- /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 0000000..526ba09
--- /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 0000000..15f4432
--- /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 0000000..0bb9924
--- /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/bmpdecoderhelper.cpp b/src/images/bmpdecoderhelper.cpp
new file mode 100644
index 0000000..acabf44
--- /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 0000000..07f0ae5
--- /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.h b/src/images/fpdfemb.h
new file mode 100644
index 0000000..3c77116
--- /dev/null
+++ b/src/images/fpdfemb.h
@@ -0,0 +1,1765 @@
+// FPDFEMB.H - Header file for FPDFEMB SDK
+// Copyright (c) 2007-2008 Foxit Software Company, All Right Reserved.
+
+// Date: 2008-04-07
+
+// Embedded platforms have many different aspects from desktop platforms,
+// among them, the followings are most important for PDF processing:
+//
+// 1. Embedded platforms have only limited memory, and there is no virtual memory.
+// PDF is a very complicated format, processing PDF may consumes quite
+// large amount of memory, even for some smaller PDFs. And, in order to
+// increase the performance of PDF parsing and rendering, cache memory
+// is often used. For some big PDFs with many pages, the cache may grow
+// while user browing through pages, eventually, for some PDFs, the memory
+// on the device may run out.
+//
+// FPDFEMB SDK allows graceful out-of-memory (OOM) handling by returning
+// OOM error code for all functions that may involve memory allocation.
+// When an application detects OOM situation, it can do one of the followings:
+//
+// a) Give user some prompt and quit the application or close the document;
+// b) Or better, try to recover from the error. Sometimes OOM can be caused
+// by ever-growing cache. For example, when user browses a 1000-page
+// document, let's say OOM happen at page #300. In this case, the
+// application might close the whole document (cache will be gone with
+// it), reopen the document, then go directly to page #300. It's likely
+// the process will go through this time. This is called "OOM recovery".
+// If OOM happens again during a recovery, then, it's not possible to finish
+// the process, the application must quit of close the document.
+//
+// 2. Embedded platforms has only limited computing power. Since some PDFs
+// can be very complicated and require a lot of processing to be displayed,
+// it may take a lot of time for the process to finish. This may cause
+// some problem with user experience, especially for devices like mobile
+// phones, when an application may need to be put on hold any time. Therefore,
+// it's important to break lengthy process into small steps which can be
+// stopped or resumed at any time. We call this kind of process as
+// "progressive process".
+//
+// FPDFEMB SDK allows progressive page parsing and rendering, the most time-
+// consuming part of PDF processing.
+//
+// IMPORTANT:
+// FPDFEMB module is not intended to run in multi-threaded environment.
+
+// Components inside FPDFEMB:
+// * Library Memory Management
+// * Document Operations
+// * Page Basic Operations
+// * Page Parsing
+// * Page Rendering
+// * Coordination Conversion
+// * Text Search
+// * Text Information
+// * Device Independant Bitmap
+// * Custom Font Handler and CJK Support
+// * Bookmark Information
+// * Hyperlink Information
+// * Graphic Output
+
+#ifndef _FPDFEMB_H_
+#define _FPDFEMB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Standard return type for many FPDFEMB functions: FPDFERR_SUCCESS for success, otherwise error code
+typedef int FPDFEMB_RESULT;
+
+// Standard boolean type: 0 for false, non-zero for true
+typedef int FPDFEMB_BOOL;
+
+// Unicode character. FPDFEMB uses UTF16LE format for unicode string.
+typedef unsigned short FPDFEMB_WCHAR;
+
+// Error codes
+#define FPDFERR_SUCCESS 0
+#define FPDFERR_MEMORY 1 // Out of memory
+#define FPDFERR_ERROR 2 // Error of any kind, without specific reason
+#define FPDFERR_PASSWORD 3 // Incorrect password
+#define FPDFERR_FORMAT 4 // Not PDF format
+#define FPDFERR_FILE 5 // File access error
+#define FPDFERR_PARAM 6 // Parameter error
+#define FPDFERR_STATUS 7 // Not in correct status
+#define FPDFERR_TOBECONTINUED 8 // To be continued
+#define FPDFERR_NOTFOUND 9 // Search result not found
+
+/********************************************************************************************
+****
+**** Library Memory Management
+****
+********************************************************************************************/
+
+// Structure: FPDFEMB_MEMMGR
+// Including interfaces implemented by host application, providing memory allocation
+// facilities. All members are required.
+// A memory manager structure is required to be valid during the entire period
+// when an application using FPDFEMB module.
+//
+// IMPORTANT NOTE: using of this interface is strongly not recommended, because
+// FPDFEMB now internally use FPDFEMB_MEMMGR_EX interface, which allows more
+// advanced memory management. This interface is retained for backward compatibility
+// only, and maybe discontinued in the future.
+//
+struct FPDFEMB_MEMMGR {
+ // Interface: Alloc
+ // Allocate a memory block
+ // Parameters:
+ // pMgr - Pointer to the memory manager.
+ // size - Number of bytes for the memory block.
+ // Return Value:
+ // The pointer to allocated memory block. NULL if no memory available.
+ // Comments:
+ // In order to handle OOM situation, application can use longjmp() inside
+ // implementation of this function. If underlying memory manager fails to
+ // allocate enough memory, then application can use longjmp() to jump to
+ // OOM handling codes.
+ //
+ void* (*Alloc)(struct FPDFEMB_MEMMGR* pMgr, unsigned int size);
+
+ // Interface: AllocNL
+ // Allocate a memory block, without leaving
+ // Parameters:
+ // pMgr - Pointer to the memory manager.
+ // size - Number of bytes for the memory block.
+ // Return Value:
+ // The pointer to allocated memory block. NULL if no memory available.
+ // Comments:
+ // Implementation MUST return NULL if no memory available, no exception
+ // or longjmp() can be used.
+ //
+ void* (*AllocNL)(struct FPDFEMB_MEMMGR* pMgr, unsigned int size);
+
+ // Interfce: Realloc
+ // Reallocate a memory block
+ // Parameters:
+ // pMgr - Pointer to the memory manager.
+ // pointer - An existing memory block, or NULL.
+ // new_size - New size (number of bytes) of the memory block. Can be zero.
+ // Return value:
+ // The pointer of reallocated memory block, it could be a new block, or just
+ // the previous block with size modified.
+ // Comments:
+ // If an existing memory block specified, the data in the memory block will
+ // be copied to the new block, if reallocated.
+ //
+ // In order to handle OOM situation, application can use longjmp() inside
+ // implementation of this function. If underlying memory manager fails to
+ // allocate enough memory, then application can use longjmp() to jump to
+ // OOM handling codes.
+ //
+ void* (*Realloc)(struct FPDFEMB_MEMMGR* pMgr, void* pointer, unsigned int new_size);
+
+ // Interface: Free
+ // Free a memory block
+ // Parameters:
+ // pMgr - Pointer to the memory manager.
+ // pointer - An existing memory block.
+ // Return Value:
+ // None.
+ //
+ void (*Free)(struct FPDFEMB_MEMMGR* pMgr, void* pointer);
+};
+
+// Function: FPDFEMB_Init
+// Initialize the FPDFEMB module
+// Parameters:
+// mem_mgr - Pointer to memory manager structure
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Comments:
+// This function will allocate necessary internal data structure for
+// the whole module to operate.
+FPDFEMB_RESULT FPDFEMB_Init(FPDFEMB_MEMMGR* mem_mgr);
+
+typedef void (*FPDFEMB_FIXED_OOM_HANDLER)(void* memory, int size);
+
+// Function: FPDFEMB_InitFixedMemory
+// Initialize the FPDFEMB module, providing a fixed memory heap
+// Parameters:
+// memory - Pointer to a pre-allocated memory block
+// size - Number of bytes in the memory block
+// oom_handler - Pointer to a function which will be called when OOM happens. Can be
+// NULL if application doesn't want to be notified om OOM.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Comments:
+// In many embedded system, memory usage are predetermined. The application
+// is assigned with fixed size of available memory, then it can pre-allocate
+// a memory block with maximum size and pass to this function to initialize
+// FPDFEMB module. In this case, FPDFEMB won't need any additional memory
+// allocation.
+//
+// In case the pre-allocated memory has run out, the "oom_proc" callback
+// function will be called to notify the application that an OOM recovery
+// procedure needs to be performed.
+//
+FPDFEMB_RESULT FPDFEMB_InitFixedMemory(void* memory, int size, FPDFEMB_FIXED_OOM_HANDLER oom_handler);
+
+// Memory Management Flags
+#define FPDFEMB_NONLEAVE 1
+#define FPDFEMB_MOVABLE 2
+#define FPDFEMB_DISCARDABLE 4
+
+// Structure: FPDFEMB_MEMMGR_EX
+// This is an extended version of memory manager interface, allowing advanced
+// memory management, including movable and discardable memory blocks.
+//
+// Use this interface with FPDFEMB_InitExt function.
+//
+struct FPDFEMB_MEMMGR_EX {
+ // Interface: Alloc
+ // Allocate a memory block
+ // Parameters:
+ // pMgr - Pointer to the memory manager.
+ // size - Number of bytes for the memory block.
+ // flags - A combination of flags defined above.
+ // Return Value:
+ // The pointer to allocated memory block. NULL if no memory available.
+ // If FPDFEMB_MOVABLE flag is used, implementation should return a handle
+ // to the memory block, if it supports movable block allocation.
+ // Comments:
+ // The implementation should not do any action if no memory available,
+ // just return NULL. OOM handling can be done in OOM_Handler interface.
+ //
+ void* (*Alloc)(struct FPDFEMB_MEMMGR_EX* pMgr, unsigned int size, int flags);
+
+ // Interface: OOM_Handler
+ // OOM (out-of-memory) situation handler
+ // Parameters:
+ // pMgr - Pointer to the memory manager.
+ // Return Value:
+ // None.
+ // Comments:
+ // In order to handle OOM situation, application can use longjmp() inside
+ // implementation of this function.
+ //
+ void (*OOM_Handler)(struct FPDFEMB_MEMMGR_EX* pMgr);
+
+ // Interfce: Realloc
+ // Reallocate a memory block
+ // Parameters:
+ // pMgr - Pointer to the memory manager.
+ // pointer - Pointer to an existing memory block, or handle to a movable
+ // block. Can not be NULL.
+ // new_size - New size (number of bytes) of the memory block. Can not be zero.
+ // Return value:
+ // The pointer of reallocated memory block, it could be a new block, or just
+ // the previous block with size modified.
+ // If FPDFEMB_MOVABLE flag is used, implementation should return a handle
+ // to the memory block, if it supports movable block allocation.
+ // Comments:
+ // If an existing memory block specified, the data in the memory block should
+ // be copied to the new block, if reallocated.
+ //
+ // The implementation should not do any action if no memory available,
+ // just return NULL. OOM handling can be done in OOM_Handler interface.
+ //
+ void* (*Realloc)(struct FPDFEMB_MEMMGR_EX* pMgr, void* pointer, unsigned int new_size, int flags);
+
+ // Interface: Lock
+ // Lock a movable memory block
+ // Parameters:
+ // pMgr - Pointer to the memory manager.
+ // handle - Handle to movable memory block, returned by Alloc or Realloc.
+ // Return Value:
+ // The pointer of the memory block. NULL if the block was discarded.
+ // Comments:
+ // This interface is optional, if implementation doesn't support movable memory
+ // block, then this interface can be set to NULL.
+ //
+ void* (*Lock)(struct FPDFEMB_MEMMGR_EX* pMgr, void* handle);
+
+ // Interface: Unlock
+ // Unlock a locked movable memory block
+ // Parameters:
+ // pMgr - Pointer to the memory manager.
+ // handle - Handle to movable memory block, returned by Alloc or Realloc.
+ // Return Value:
+ // None.
+ // Comments:
+ // This interface is optional, if implementation doesn't support movable memory
+ // block, then this interface can be set to NULL.
+ //
+ void (*Unlock)(struct FPDFEMB_MEMMGR_EX* pMgr, void* handle);
+
+ // Interface: Free
+ // Free a memory block
+ // Parameters:
+ // pMgr - Pointer to the memory manager.
+ // pointer - Pointer to an existing memory block, or handle to a movable block.
+ // Return Value:
+ // None.
+ //
+ void (*Free)(struct FPDFEMB_MEMMGR_EX* pMgr, void* pointer, int flags);
+
+ void* user; // A user pointer, used by the application
+};
+
+// Function: FPDFEMB_LoadJbig2Decoder
+// Function: FPDFEMB_LoadJpeg2000Decoder
+// Enable JBIG2 or JPEG2000 image decoder
+// Parameters:
+// None.
+// Return Value:
+// None.
+// Comments:
+// If you want to display JBIG2 or JPEG2000 encoded images, you need to call
+// these functions after FPDFEMB initialized.
+//
+// Calling these functions will increase code size by about 200K-400K bytes.
+// Also JPEG2000 decoder may not be available on some platforms.
+//
+void FPDFEMB_LoadJbig2Decoder();
+void FPDFEMB_LoadJpeg2000Decoder();
+
+// Function: FPDFEMB_InitEx
+// Initialize the FPDFEMB module with the extended memory manager
+// Parameters:
+// mem_mgr - Pointer to memory manager structure
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Comments:
+// This function will allocate necessary internal data structure for
+// the whole module to operate.
+FPDFEMB_RESULT FPDFEMB_InitEx(FPDFEMB_MEMMGR_EX* mem_mgr);
+
+// Function: FPDFEMB_Exit
+// Stop using FPDFEMB module and release all resources
+// Parameters:
+// None.
+// Return Value:
+// None.
+// Comments:
+// All loaded documents and pages will become invalid after this call.
+//
+// This function is useful for OOM recovery: when your application hits
+// an OOM situation, calling this function will clear all memory allocated
+// by FPDFEMB module, then you can call one of the initialization functions,
+// reopen the document and recovery from OOM.
+//
+void FPDFEMB_Exit();
+
+// Function: FPDFEMB_AllocMemory
+// Allocate memory
+// Parameters:
+// size - Number of bytes
+// Return Value:
+// The allocated buffer pointer. NULL for out of memory.
+//
+void* FPDFEMB_AllocMemory(unsigned int size);
+
+// Function: FPDFEMB_FreeMemory
+// Free allocated memory
+// Parameters:
+// pointer - Pointer returned by FPDFEMB_AllocMemory
+// Return Value:
+// None.
+//
+void FPDFEMB_FreeMemory(void* pointer);
+
+// Function: FPDFEMB_FreeCaches
+// Free all expendable caches used by FPDFEMB in order to save memory
+// Parameters:
+// None.
+// Return Value:
+// None.
+// Comments:
+// When an application memory manager runs out of memory, before an OOM situation
+// is raised, the application can try this
+//
+void FPDFEMB_FreeCaches();
+
+/********************************************************************************************
+****
+**** Document Operations
+****
+********************************************************************************************/
+
+// Structure: FPDFEMB_FILE_ACCESS
+// Describe the way to access a file (readonly).
+struct FPDFEMB_FILE_ACCESS {
+ // Inteface: GetSize
+ // Get total size of the file
+ // Parameters:
+ // file - Pointer to this file access structure
+ // Return Value:
+ // File size, in bytes. Implementation can return 0 for any error.
+ //
+ unsigned int (*GetSize)(struct FPDFEMB_FILE_ACCESS* file);
+
+ // Interface: ReadBlock
+ // Read a data block from the file
+ // Parameters:
+ // file - Pointer to this file access structure
+ // buffer - Pointer to a buffer receiving read data
+ // offset - Byte offset for the block, from beginning of the file
+ // size - Number of bytes for the block.
+ // Return Value:
+ // Error code, or FPDFERR_SUCCESS for success.
+ //
+ FPDFEMB_RESULT (*ReadBlock)(struct FPDFEMB_FILE_ACCESS* file, void* buffer,
+ unsigned int offset, unsigned int size);
+
+ void* user; // A user pointer, used by the application
+};
+
+// Structure: FPDFEMB_PAUSE
+// An interface for pausing a progressive process.
+struct FPDFEMB_PAUSE {
+ // Interface: NeedPauseNow
+ // Check if we need to pause a progressive proccess now
+ // Parameters:
+ // pause - Pointer to the pause structure
+ // Return Value:
+ // Non-zero for pause now, 0 for continue.
+ // Comments:
+ // Typically implementation of this interface compares the current system tick
+ // with the previous one, if the time elapsed exceeds certain threshold, then
+ // the implementation returns TRUE, indicating a pause is needed.
+ //
+ FPDFEMB_BOOL (*NeedPauseNow)(struct FPDFEMB_PAUSE* pause);
+
+ void* user; // A user pointer, used by the application
+};
+
+typedef void* FPDFEMB_DOCUMENT;
+
+// Function: FPDFEMB_StartLoadDocument
+// Start loading a PDF document
+// Parameters:
+// file - Pointer to file access structure.
+// This structure must be kept valid as long as the document is open.
+// password - Pointer to a zero-terminated byte string, for the password.
+// Or NULL for no password.
+// document - Receiving the document handle
+// pause - A callback mechanism allowing the document loading process
+// to be paused before it's finished. This can be NULL if you
+// don't want to pause.
+// Return Value:
+// FPDFERR_SUCCESS: document successfully loaded.
+// FPDFERR_TOBECONTINUED: The document loading can't be finished now.
+// See comments below.
+// FPDFERR_PASSWORD: incorrect password.
+// FPDFERR_FORMAT: not a PDF or corrupted PDF.
+// FPDFERR_FILE: file access error.
+// FPDFERR_MEMORY: out of memory.
+// Comments:
+// Document loading is a progressive process. It might take a long time to
+// load a document, especiall when a file is corrupted, FPDFEMB will try to
+// recover the document contents by scanning the whole file. If "pause" parameter
+// is provided, this function may return FPDFERR_TOBECONTINUED any time during
+// the document loading.
+//
+// When FPDFERR_TOBECONTINUED is returned, the "document" parameter will
+// still receive a valid document handle, however, no further operations can
+// be performed on the document, except the "FPDFEMB_ContineLoadDocument" function
+// call, which resume the document loading.
+//
+FPDFEMB_RESULT FPDFEMB_StartLoadDocument(FPDFEMB_FILE_ACCESS* file, const char* password,
+ FPDFEMB_DOCUMENT* document, FPDFEMB_PAUSE* pause);
+
+// Function: FPDFEMB_ContinueLoadDocument
+// Continue loading a PDF document
+// Parameters:
+// document - Document handle returned by FPDFEMB_StartLoadDocument function
+// pause - A callback mechanism allowing the document loading process
+// to be paused before it's finished. This can be NULL if you
+// don't want to pause.
+// Return Value:
+// FPDFERR_SUCCESS: document successfully loaded.
+// FPDFERR_TOBECONTINUED: The document loading can't be finished now.
+// Further call to this function is needed.
+// FPDFERR_PASSWORD: incorrect password.
+// FPDFERR_FORMAT: not a PDF or corrupted PDF.
+// FPDFERR_FILE: file access error.
+// FPDFERR_MEMORY: out of memory.
+// FPDFERR_STATUS: document already loaded.
+// FPDFERR_PARAM: invalid parameter (like NULL document handle)
+//
+FPDFEMB_RESULT FPDFEMB_ContinueLoadDocument(FPDFEMB_DOCUMENT document, FPDFEMB_PAUSE* pause);
+
+// Function: FPDFEMB_CloseDocument
+// Close a PDF document and free all associated resources
+// Parameters:
+// document - Document handle
+// Return Value:
+// Error code. FPDFERR_SUCCESS for success.
+//
+FPDFEMB_RESULT FPDFEMB_CloseDocument(FPDFEMB_DOCUMENT document);
+
+// Function: Get page count
+// Get number of pages in the document
+// Parameters:
+// document - Document handle
+// Return Value:
+// Number of pages.
+//
+int FPDFEMB_GetPageCount(FPDFEMB_DOCUMENT document);
+
+// Function: FPDFEMB_SetFileBufferSize
+// Set size of internal buffer used to read from source file.
+// Parameters:
+// size - Number of bytes
+// Return Value:
+// None.
+// Comments:
+// Currently FPDFEMB uses 512 bytes as default buffer size. The new buffer size
+// takes effect next time you call FPDFEMB_StartLoadDocument.
+//
+void FPDFEMB_SetFileBufferSize(int size);
+
+/********************************************************************************************
+****
+**** Page Basic Operations
+****
+********************************************************************************************/
+
+typedef void* FPDFEMB_PAGE;
+
+// Function: FPDFEMB_LoadPage
+// Load a page
+// Parameters:
+// document - Document handle
+// index - Page index, starting from zero
+// page - Receiving the loaded page handler
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+//
+FPDFEMB_RESULT FPDFEMB_LoadPage(FPDFEMB_DOCUMENT document, int index, FPDFEMB_PAGE* page);
+
+// Function: FPDFEMB_ClosePage
+// Close a page and release all related resources
+// Parameters:
+// page - Page handle
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+//
+FPDFEMB_RESULT FPDFEMB_ClosePage(FPDFEMB_PAGE page);
+
+// Function: FPDFEMB_GetPageSize
+// Get size of a page
+// Parameters:
+// page - Page handle
+// width - Receiving page width, in hundredth of points
+// height - Receiving page height, in hundredth of points
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+//
+FPDFEMB_RESULT FPDFEMB_GetPageSize(FPDFEMB_PAGE page, int* width, int* height);
+
+// Structure: FPDFEMB_RECT
+// Rectangle area in device or page coordination system
+//
+struct FPDFEMB_RECT
+{
+ // For device system, coordinations are measured in pixels;
+ // For page system, coordinations are measured in hundredth of points.
+ int left;
+ int top;
+ int right;
+ int bottom;
+};
+
+// Function: FPDFEMB_GetPageBBox
+// Get displayable area (bounding box) of a page
+// Parameters:
+// page - Page handle
+// rect - Pointer to a structure receiving the rectangle
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+//
+FPDFEMB_RESULT FPDFEMB_GetPageBBox(FPDFEMB_PAGE page, FPDFEMB_RECT* rect);
+
+/********************************************************************************************
+****
+**** Page Parsing
+****
+********************************************************************************************/
+
+// Function: FPDFEMB_StartParse
+// Start parsing a page, so it can get rendered or searched
+// Parameters:
+// page - Page handle
+// text_only - flag for parsing texts only (used for searching)
+// pause - A structure that can pause the parsing process.
+// Or NULL if you don't want to pause the process.
+// Return Value:
+// FPDFERR_SUCCESS: parsing successfully finished;
+// FPDFERR_TOBECONTINUED: parsing started successfully, but not finished;
+// FPDFERR_STATUS: page already parsed, or parsing already started.
+// Other return value: error code.
+// Comments:
+// Parsing is a progressive process. This function starts the parsing process,
+// and may return before parsing is finished, if a pause structure is provided.
+//
+// Application should call FPDFEMB_ContinueParse repeatedly to finish the parsing
+// when return value is FPDFERR_TOBECONTINUED.
+//
+// There can be only one parsing procedure active for a page, and if a page
+// has already been parsed, you can't start a parsing again.
+//
+FPDFEMB_RESULT FPDFEMB_StartParse(FPDFEMB_PAGE page, FPDFEMB_BOOL text_only,
+ FPDFEMB_PAUSE* pause);
+
+// Function: FPDFEMB_ContinueParse
+// Continue the page parsing
+// Parameters:
+// page - Page handle
+// pause - A structure that can pause the parsing process.
+// Or NULL if you don't want to pause the process.
+// Return Value:
+// FPDFERR_SUCCESS: parsing successfully finished;
+// FPDFERR_TOBECONTINUED: parsing performed successfully, but not finished;
+// FPDFERR_STATUS: page already parsed (or parsing not started).
+// Other return value: error code.
+// Comments:
+// FPDFEMB_StartParse should be called before on the page.
+//
+// Application should call FPDFEMB_ContinueParse repeatedly to finish the parsing
+// when return value is FPDFERR_TOBECONTINUED.
+//
+FPDFEMB_RESULT FPDFEMB_ContinueParse(FPDFEMB_PAGE page, FPDFEMB_PAUSE* pause);
+
+// Function: FPDFEMB_GetParseProgress
+// Get an estimated parsing progress in percentage
+// Parameters:
+// page - Page handle
+// Return Value:
+// An integer between 0 and 100 (inclusive) indicating the parsing progress.
+// The result is just a rough estimation.
+//
+int FPDFEMB_GetParseProgress(FPDFEMB_PAGE page);
+
+/********************************************************************************************
+****
+**** Page Rendering
+****
+********************************************************************************************/
+
+typedef void* FPDFEMB_BITMAP;
+
+// Function: FPDFEMB_StartQuickDraw
+// Start drawing a quick preview of a page
+// Parameters:
+// dib - DIB handle, as the rendering device
+// page - Page handle. The page has to be parsed first.
+// start_x - Left pixel position of the display area in the device coordination
+// start_y - Top pixel position of the display area in the device coordination
+// size_x - Horizontal size (in pixels) for displaying the page
+// size_y - Vertical size (in pixels) for displaying the page
+// rotate - Page orientation: 0 (normal), 1 (rotated 90 degrees clockwise),
+// 2 (rotated 180 degrees), 3 (rotated 90 degrees counter-clockwise).
+// flags - Reserved, must be zero.
+// pause - Pointer to a structure that can pause the rendering process.
+// Can be NULL if no pausing is needed.
+// Return Value:
+// FPDFERR_SUCCESS: quickdraw successly finished;
+// FPDFERR_TOBECONTINUED: quickdraw started successfully, but not finished.
+// FPDFEMB_ContinueQuickDraw needs to be called to finish the quickdraw;
+// FPDFERR_STATUS: quickdraw already in progress, or page not parsed;
+// Other return value: error code.
+// Comments:
+// It's often useful to present user a quick preview of a page, right after the
+// page is parsed. This preview renders only a limited set of easy features in the
+// page, so it'll be rather quick to finish this process.
+//
+FPDFEMB_RESULT FPDFEMB_StartQuickDraw(FPDFEMB_BITMAP dib, FPDFEMB_PAGE page,
+ int start_x, int start_y, int size_x, int size_y, int rotate,
+ int flags, FPDFEMB_PAUSE* pause);
+
+// Function: FPDFEMB_ContinueQuickDraw
+// Continue a quick draw processing
+// Parameters:
+// page - Page handle. The page has to be parsed first.
+// pause - Pointer to a structure that can pause the rendering process.
+// Can be NULL if no pausing is needed.
+// Return Value:
+// FPDFERR_SUCCESS: quickdraw successly finished;
+// FPDFERR_TOBECONTINUED: quickdraw started successfully, but not finished.
+// more calls to this function needed to finish the quickdraw;
+// FPDFERR_STATUS: quickdraw not started yet;
+// Other return value: error code.
+//
+FPDFEMB_RESULT FPDFEMB_ContinueQuickDraw(FPDFEMB_PAGE page, FPDFEMB_PAUSE* pause);
+
+#define FPDFEMB_ANNOT 0x01 // Set if annotations are to be rendered
+#define FPDFEMB_LCD_TEXT 0x02 // Set if using text rendering optimized for LCD display
+#define FPDFEMB_BGR_STRIPE 0x04 // Set if the device is using BGR LCD stripe
+
+// Function: FPDFEMB_StartRender
+// Start rendering of a page.
+// Parameter:
+// dib - DIB handle, as the rendering device
+// page - Page handle. The page has to be parsed first.
+// start_x - Left pixel position of the display area in the device coordination
+// start_y - Top pixel position of the display area in the device coordination
+// size_x - Horizontal size (in pixels) for displaying the page
+// size_y - Vertical size (in pixels) for displaying the page
+// rotate - Page orientation: 0 (normal), 1 (rotated 90 degrees clockwise),
+// 2 (rotated 180 degrees), 3 (rotated 90 degrees counter-clockwise).
+// flags - 0 for normal display, or combination of flags defined above
+// clip - Pointer to clip rectangle (in DIB device coordinations),
+// or NULL if no clipping needed.
+// pause - Pointer to a structure that can pause the rendering process.
+// Can be NULL if no pausing is needed.
+// Return Value:
+// FPDFERR_SUCCESS: rendering successfully finished;
+// FPDFERR_TOBECONTINUED: rendering started successfully, but not finished;
+// Other return value: error code.
+// Comments:
+// Rendering is a progressive process. This function starts the rendering process,
+// and may return before rendering is finished, if a pause structure is provided.
+//
+// Application should call FPDFEMB_ContinueRender repeatedly to finish the rendering
+// when return value is FPDFERR_TOBECONTINUED.
+//
+// There can be only one rendering procedure for a page at any time. And rendering
+// can be started over and over again for the same page. If a page rendering is already
+// active, starting another one will cancel the previous rendering.
+//
+// Rendering of a page doesn't draw the page background, therefore, you usually need
+// to draw the background in the DIB yourself.
+//
+FPDFEMB_RESULT FPDFEMB_StartRender(FPDFEMB_BITMAP dib, FPDFEMB_PAGE page,
+ int start_x, int start_y, int size_x, int size_y, int rotate, int flags,
+ FPDFEMB_RECT* clip, FPDFEMB_PAUSE* pause);
+
+// Function: FPDFEMB_ContinueRender
+// Continue the page rendering
+// Parameters:
+// page - Page handle
+// pause - Pointer to a structure that can pause the rendering process.
+// Can be NULL if no pausing is needed.
+// Return Value:
+// FPDFERR_SUCCESS: rendering successfully finished.
+// FPDFERR_TOBECONTINUED: rendering needs to be continued;
+// Other return value: error code.
+// Comments:
+// This function may return any time when the pause interface indicates
+// a pause is needed. Application can call FPDFEMB_ContinueRender any number
+// of times, until FPDFERR_TOBECONTINUED is not returned.
+//
+FPDFEMB_RESULT FPDFEMB_ContinueRender(FPDFEMB_PAGE page, FPDFEMB_PAUSE* pause);
+
+// Function: FPDFEMB_GetRenderProgress
+// Get an estimated rendering progress in percentage
+// Parameters:
+// page - Page handle
+// Return Value:
+// An integer between 0 and 100 (inclusive) indicating the rendering progress.
+// The result is just a rough estimation.
+// If the rendering just finished, this function will return 0.
+//
+int FPDFEMB_GetRenderProgress(FPDFEMB_PAGE page);
+
+// Function: FPDFEMB_SetHalftoneLimit
+// Set pixel count limit for using halftone when display image
+// Parameter:
+// limit - Number of pixels for the limit
+// Return Value:
+// None.
+// Comments:
+// By default, FPDFEMB displays all bitmaps using downsamping, which means
+// if the image is shrinked onto screen, only part of pixels will be picked
+// and displayed. This saves a lot of calculation, especially for big images
+// with millions of pixels. However the display quality can be bad. In order to
+// reach a balance between performance and quality, application can use this
+// function to set a limit, if number of pixels in an image is more than this
+// limit, then FPDFEMB will use downsampling for quick drawing, otherwise, if
+// the image has less pixels, FPDFEMB will use halftoning for better quality.
+//
+void FPDFEMB_SetHalftoneLimit(int limit);
+
+/********************************************************************************************
+****
+**** Coordination Conversion
+****
+********************************************************************************************/
+
+// Structure: FPDFEMB_POINT
+// A point in device or page coordination system
+//
+struct FPDFEMB_POINT
+{
+ // For device system, coordinations are measured in pixels;
+ // For page system, coordinations are measured hundredth of points.
+ int x;
+ int y;
+};
+
+// Function: FPDFEMB_DeviceToPagePoint, FPDFEMB_DeviceToPageRect
+// Convert the device coordinations of a point or a rectangle to page coordinations.
+// Parameters:
+// page - Handle to the page. Returned by FPDFEMB_LoadPage function.
+// start_x - Left pixel position of the display area in the device coordination
+// start_y - Top pixel position of the display area in the device coordination
+// size_x - Horizontal size (in pixels) for displaying the page
+// size_y - Vertical size (in pixels) for displaying the page
+// rotate - Page orientation: 0 (normal), 1 (rotated 90 degrees clockwise),
+// 2 (rotated 180 degrees), 3 (rotated 90 degrees counter-clockwise).
+// point - A point structure with device coordinations upon the call,
+// also receiving the result page coordinations.
+// rect - A rectangle structure with device coordinations upon the call,
+// also receiving the result page coordinations.
+// Return value:
+// None.
+// Comments:
+// The page coordination system has its origin at left-bottom corner of the page,
+// with X axis goes along the bottom side to the right, and Y axis goes along the
+// left side upward. No matter how you zoom, scroll, or rotate a page, a particular
+// element (like text or image) on the page should always have the same coordination
+// values in the page coordination system.
+//
+// The device coordination system is device dependant. For bitmap device, its origin
+// is at left-top corner of the window. You must make sure the start_x, start_y, size_x,
+// size_y and rotate parameters have exactly same values as you used in
+// FPDFEMB_StartRender() function call.
+//
+// For rectangle conversion, the result rectangle is always "normalized", meaning for
+// page coordinations, left is always smaller than right, bottom is smaller than top.
+//
+void FPDFEMB_DeviceToPagePoint(FPDFEMB_PAGE page,
+ int start_x, int start_y, int size_x, int size_y, int rotate,
+ FPDFEMB_POINT* point);
+
+void FPDFEMB_DeviceToPageRect(FPDFEMB_PAGE page,
+ int start_x, int start_y, int size_x, int size_y, int rotate,
+ FPDFEMB_RECT* rect);
+
+// Function: FPDFEMB_PageToDevicePoint, FPDFEMB_PageToDeviceRect
+// Convert the page coordinations of a point or a rectangle to device coordinations.
+// Parameters:
+// page - Handle to the page. Returned by FPDFEMB_LoadPage function.
+// start_x - Left pixel position of the display area in the device coordination
+// start_y - Top pixel position of the display area in the device coordination
+// size_x - Horizontal size (in pixels) for displaying the page
+// size_y - Vertical size (in pixels) for displaying the page
+// rotate - Page orientation: 0 (normal), 1 (rotated 90 degrees clockwise),
+// 2 (rotated 180 degrees), 3 (rotated 90 degrees counter-clockwise).
+// point - A point structure with page coordinations upon the call,
+// also receiving the result device coordinations.
+// rect - A rectangle structure with page coordinations upon the call,
+// also receiving the result device coordinations.
+// Return value:
+// None
+// Comments:
+// For rectangle conversion, the result rectangle is always "normalized", meaning for
+// device coordinations, left is always smaller than right, top is smaller than bottom.
+//
+void FPDFEMB_PageToDevicePoint(FPDFEMB_PAGE page,
+ int start_x, int start_y, int size_x, int size_y, int rotate,
+ FPDFEMB_POINT* point);
+
+void FPDFEMB_PageToDeviceRect(FPDFEMB_PAGE page,
+ int start_x, int start_y, int size_x, int size_y, int rotate,
+ FPDFEMB_RECT* rect);
+
+/********************************************************************************************
+****
+**** Text Search
+****
+********************************************************************************************/
+
+// Search flags for FPDFEMB_FindFirst function
+#define FPDFEMB_MATCHCASE 1 // whether matching case
+#define FPDFEMB_MATCHWHOLEWORD 2 // whether matching whole word
+#define FPDFEMB_CONSECUTIVE 4 // whether matching consecutively (for example, "CC" will
+ // match twice in "CCC").
+
+// Function: FPDFEMB_FindFirst
+// Find first occurance of a pattern string in a page
+// Parameters:
+// page - Page handle.
+// pattern - A zero-terminated unicode string to be found.
+// from_last - Whether we start from the end of page
+// flags - Search flags, see above defined constants
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Is not found, FPDFERR_NOTFOUND is returned.
+// Comments:
+// A page must be parsed first before it can be searched.
+// There can be only one search in progress for a page. A new search will
+// cancel the previous one.
+//
+// IMPORTANT: this function is now obsolete and kept for back compatibility
+// only, please use FPDFEMB_FindFrom function below.
+//
+FPDFEMB_RESULT FPDFEMB_FindFirst(FPDFEMB_PAGE page, const FPDFEMB_WCHAR* pattern,
+ FPDFEMB_BOOL from_last, unsigned int flags);
+
+// Function: FPDFEMB_FindFrom
+// Find first occurance of a pattern string in a page, from a particular position
+// Parameters:
+// page - Page handle.
+// pattern - A zero-terminated unicode string to be found.
+// pos - The position, returned from FPDFEMB_GetSearchPos.
+// Or 0 from the beginning of page, -1 from the end of page.
+// flags - Search flags, see above defined constants
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Is not found, FPDFERR_NOTFOUND is returned.
+// Comments:
+// A page must be parsed first before it can be searched.
+// There can be only one search in progress for a page. A new search will
+// cancel the previous one.
+//
+FPDFEMB_RESULT FPDFEMB_FindFrom(FPDFEMB_PAGE page, const FPDFEMB_WCHAR* pattern,
+ int pos, unsigned int flags);
+
+// Function: FPDFEMB_FindNext
+// Find next occurance of a search
+// Parameters:
+// page - Page handle.
+// FPDFEMB_FindFirst must be called for this page first.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Is not found, FPDFERR_NOTFOUND is returned.
+//
+FPDFEMB_RESULT FPDFEMB_FindNext(FPDFEMB_PAGE page);
+
+// Function: FPDFEMB_FindPrev
+// Find previous occurance of a search
+// Parameters:
+// page - Page handle.
+// FPDFEMB_FindFirst must be called for this page first.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Is not found, FPDFERR_NOTFOUND is returned.
+//
+FPDFEMB_RESULT FPDFEMB_FindPrev(FPDFEMB_PAGE page);
+
+// Function: FPDFEMB_CountFoundRects
+// Get number of rectangles for last found result
+// Parameters:
+// page - Page handle.
+// Return Value:
+// Number of rectangles for last found result. 0 for not found or failure.
+//
+int FPDFEMB_CountFoundRects(FPDFEMB_PAGE page);
+
+// Function: FPDFEMB_GetFoundRect
+// Get a particular found rectangle
+// Parameters:
+// page - Page handle.
+// index - Zero-based index for the rectangle.
+// rect - Receiving the result rectangle, in hundredth of points
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Comments:
+// Application should always call FPDFEMB_CountFoundRects first to get
+// number of rectangles, then use this function to get each rectangle.
+//
+// The returned rectangle uses page coordination system.
+//
+FPDFEMB_RESULT FPDFEMB_GetFoundRect(FPDFEMB_PAGE page, int index, FPDFEMB_RECT* rect);
+
+// Function: FPDFEMB_GetSearchPos
+// Return position of current search result
+// Parameters:
+// page - Page handle.
+// Return Value:
+// Zero based character index for the current search result. -1 if not found.
+//
+int FPDFEMB_GetSearchPos(FPDFEMB_PAGE page);
+
+// Function: FPDFEMB_QuickSearch
+// Search a pattern in a page quickly, without the page to be parsed
+// Parameters:
+// document - Document handle returned by FPDFEMB_StartLoadDocument function
+// page_index - Zero-based index of the page
+// pattern - A zero-terminated unicode string to be found.
+// case_sensitive - Non-zero for case-sensitive searching, zero for case-insensitive
+// Return Value:
+// FPDFERR_SUCCESS if pattern found, FPDFERR_NOTFOUND if pattern not found.
+// Otherwise error code is returned.
+// Comments:
+// This function does a rough and quick search in a page, before the page is loaded.
+// The quick search will not generate an exact result saying where the pattern is
+// found, and, it might be possible if a quick search result is "pattern found", and
+// a real search for the same pattern, in the same page, will result in "not found".
+//
+// However, if quick search doesn't find a pattern in a page, then we can be sure the
+// pattern won't be found in this page when we do a real search. So, this function is
+// very useful when we search in a multiple-page document, and we want to quickly skip
+// those pages in which the pattern can't possibly be found.
+//
+FPDFEMB_RESULT FPDFEMB_QuickSearch(FPDFEMB_DOCUMENT document, int page_index,
+ const FPDFEMB_WCHAR* pattern, int case_sensitive);
+
+/********************************************************************************************
+****
+**** Text Information
+****
+********************************************************************************************/
+
+// Function: FPDFEMB_GetCharCount
+// Get number of characters in the page
+// Parameters:
+// page - Page handle
+// count - Receiving number of characters
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+//
+FPDFEMB_RESULT FPDFEMB_GetCharCount(FPDFEMB_PAGE page, int* count);
+
+// Structure: FPDFEMB_CHAR_INFO
+// Character information.
+struct FPDFEMB_CHAR_INFO {
+ int unicode; // Unicode for the character. 0 if not available.
+ // Space and new line charcters (U+0020 and U+000A) may be generated
+ // according to the text formatting.
+ FPDFEMB_POINT origin; // X/Y position for the character origin, in hundredth of points
+ FPDFEMB_RECT bbox; // Bounding box of the character, in hundredth of points
+ // Maybe an empty box (left == right or top == bottom).
+};
+
+// Function: FPDFEMB_GetCharInfo
+// Get character information
+// Parameters:
+// page - Page handle
+// index - Character index, starting from zero
+// char_info - Receiving the character info
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+// Comments:
+// Application must call FPDFEMB_GetCharCount first before it can call this function
+// for any particular characters.
+//
+FPDFEMB_RESULT FPDFEMB_GetCharInfo(FPDFEMB_PAGE page, int index, FPDFEMB_CHAR_INFO* char_info);
+
+// Function: FPDFEMB_GetCharIndexAtPos()
+// Get index of character nearest to a certain position on the page
+// Parameters:
+// page - Page handle
+// x - X position in PDF page coordination system
+// y - Y position in PDF page coordination system
+// index - Pointer to an integer receiving zero-based character index.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+// Comments:
+// This function finds the character that's nearest to the particular page position.
+// If there is no character, the output index will be -1.
+//
+FPDFEMB_RESULT FPDFEMB_GetCharIndexAtPos(FPDFEMB_PAGE page, double x, double y, int* index);
+
+/********************************************************************************************
+****
+**** Device Independant Bitmap
+****
+********************************************************************************************/
+
+#define FPDFDIB_BGR 1 // 3 bytes per pixel, byte order: Blue, Green, Red
+#define FPDFDIB_BGRx 2 // 4 bytes per pixel, byte order: Blue, Green, Red, not used
+#define FPDFDIB_BGRA 3 // 4 bytes per pixel, byte order: Blue, Green, Red, Alpha
+#define FPDFDIB_GRAY 4 // 1 byte per pixel (grayscale)
+
+// Function: FPDFEMB_CreateDIB
+// Create a DIB (Device Independant Bitmap)
+// Parameters:
+// width - Width pixels;
+// height - Height pixels;
+// format - Format type. See FPDFDIB_xxx constants
+// buffer - External buffer provided for the DIB,
+// or NULL if new buffer is to be allocated.
+// stride - Number of bytes for each scan line, for external buffer only.
+// If not specified, 4-byte alignment assumed.
+// dib - Receiving the created DIB handle
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+// Comments:
+// If "buffer" parameter is not NULL, then the provided buffer must conform
+// to standard DIB format (see comments of FPDFEMB_GetDIBData function below).
+//
+// This function doesn't initialize the pixels inside the DIB buffer. So if you
+// want to use the DIB to display a PDF page, you usually need to initialize
+// the DIB to white background by youself.
+//
+FPDFEMB_RESULT FPDFEMB_CreateDIB(int width, int height, int format,
+ void* buffer, int stride, FPDFEMB_BITMAP* dib);
+
+// Function: FPDFEMB_DestroyDIB
+// Destroy a DIB
+// Parameters:
+// dib - DIB handle
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+// Comments:
+// If external buffer is used (specified in "buffer" parameter when calling
+// FPDFEMB_CreateDIB), the buffer will not be destroyed.
+//
+FPDFEMB_RESULT FPDFEMB_DestroyDIB(FPDFEMB_BITMAP dib);
+
+// Function: FPDFEMB_GetDIBWidth
+// Get width (in pixels) of a DIB
+// Parameters:
+// dib - DIB handle
+// Return Value:
+// DIB width in pixels.
+//
+int FPDFEMB_GetDIBWidth(FPDFEMB_BITMAP dib);
+
+// Function: FPDFEMB_GetDIBHeight
+// Get height (in pixels) of a DIB
+// Parameters:
+// dib - DIB handle
+// Return Value:
+// DIB height in pixels.
+//
+int FPDFEMB_GetDIBHeight(FPDFEMB_BITMAP dib);
+
+// Function: FPDFEMB_GetDIBData
+// Get data pointer to a DIB
+// Parameters:
+// dib - DIB handle
+// Return Value:
+// Pointer to the DIB data.
+// Comments:
+// DIB data are organized in scanlines, from top down.
+//
+void* FPDFEMB_GetDIBData(FPDFEMB_BITMAP dib);
+
+// Function: FPDFEMB_GetDIBStride
+// Get scan line stride of a DIB
+// Parameters:
+// dib - DIB handle
+// Return Value:
+// Number of bytes occupied by a scanline
+//
+int FPDFEMB_GetDIBStride(FPDFEMB_BITMAP dib);
+
+// Function: FPDFEMB_GetRotatedDIB
+// Swap X/Y dimensions of a DIB to generate a rotated new DIB
+// Parameters:
+// dib - DIB handle
+// flip_x - Whether flip pixels on the destination X dimension (left/right)
+// flip_y - Whether flip pixels on the destination Y dimension (up/down)
+// result_dib - Receiving the result DIB handle
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+//
+FPDFEMB_RESULT FPDFEMB_GetRotatedDIB(FPDFEMB_BITMAP dib,
+ FPDFEMB_BOOL bFlipX, FPDFEMB_BOOL bFlipY,
+ FPDFEMB_BITMAP* result_dib);
+
+// Function: FPDFEMB_StretchDIB
+// Stretch a source DIB into another destination DIB
+// Parameters:
+// dest_dib - The destination DIB handle
+// dest_left - Left position in the destination DIB
+// dest_top - Top position in the destination DIB
+// dest_width - Destination width, in pixels. Can be negative for horizontal flipping
+// dest_height - Destination height, in pixels. Can be negative for vertical flipping
+// clip - Destination clipping rectangle, or NULL for no clipping.
+// The coordinations are measured in destination bitmap.
+// src_dib - Source DIB handle.
+// interpol - Whether we use interpolation to improve the result quality
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+//
+FPDFEMB_RESULT FPDFEMB_StretchDIB(FPDFEMB_BITMAP dest_dib, int dest_left, int dest_top,
+ int dest_width, int dest_height, FPDFEMB_RECT* clip_rect,
+ FPDFEMB_BITMAP src_dib, FPDFEMB_BOOL interpol);
+
+// Function: FPDFEMB_TransformDIB
+// Transform a source DIB into another destination DIB
+// Parameters:
+// dest_dib - The destination DIB handle
+// clip - Destination clipping rectangle, or NULL for no clipping.
+// The coordinations are measured in destination bitmap.
+// src_dib - Source DIB handle.
+// x - X coordination of the dest origin
+// y - Y coordination of the dest origin
+// xx - X distance of the dest X vector
+// yx - Y distance of the dest X vector
+// xy - X distance of the dest Y vector
+// yy - Y distance of the dest Y vector
+// interpol - Whether we use interpolation to improve the result quality
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+// Comments:
+// All coordinations and distances are measured in destination bitmap system.
+//
+// This function places the bottom-left pixel of the image at the destination
+// origin, then the bottom sideline along the destination X vector, and left
+// sideline along the destination Y vector.
+//
+FPDFEMB_RESULT FPDFEMB_TransformDIB(FPDFEMB_BITMAP dest_dib, FPDFEMB_RECT* clip_rect,
+ FPDFEMB_BITMAP src_dib, int x, int y, int xx, int yx,
+ int xy, int yy, FPDFEMB_BOOL interpol);
+
+/********************************************************************************************
+****
+**** Custom Font Handler and CJK Support
+****
+********************************************************************************************/
+
+// FPDFEMB comes with standard fonts for Latin characters. If your device is targeted to
+// Eastern Asian markets, then system fonts must be provided and registered with FPDFEMB.
+// Depending on your device configuration, those system fonts might be in TrueType or Type1
+// format, or some other non-standard compact format. For the first case, you should register
+// a font mapper so FPDFEMB can pick the right font file, and for the second case, you
+// should register a glyph provider so FPDFEMB can get glyph bitmap for each character.
+
+#define FPDFEMB_CHARSET_DEFAULT 0
+#define FPDFEMB_CHARSET_GB 936
+#define FPDFEMB_CHARSET_BIG5 950
+#define FPDFEMB_CHARSET_JIS 932
+#define FPDFEMB_CHARSET_KOREA 949
+#define FPDFEMB_CHARSET_UNICODE 1200
+
+#define FPDFEMB_FONT_FIXEDPITCH 1
+#define FPDFEMB_FONT_SERIF 2
+#define FPDFEMB_FONT_SYMBOLIC 4
+#define FPDFEMB_FONT_SCRIPT 8
+#define FPDFEMB_FONT_NONSYMBOLIC 32
+#define FPDFEMB_FONT_ITALIC 64
+#define FPDFEMB_FONT_ALLCAP 0x10000
+#define FPDFEMB_FONT_SMALLCAP 0x20000
+#define FPDFEMB_FONT_FORCEBOLD 0x40000
+
+// Structure: FPDFEMB_FONT_MAPPER
+// Defines interface for system font mapper.
+//
+struct FPDFEMB_FONT_MAPPER
+{
+ // Interface: MapFont
+ // Find font file path for a particular PDF font
+ // Parameters:
+ // mapper - Pointer to the FPDFEMB_FONT_MAPPER structure
+ // name - Font name
+ // charset - Charset ID (see above FPDFEMB_CHARSET_xxx constants)
+ // flags - Font flags (see above FPDFEMB_FONT_xxx constants)
+ // weight - Weight of the font. Range from 100 to 900. 400 is normal,
+ // 700 is bold.
+ // path - Receiving the full file path. The buffer size is 512 bytes.
+ // face_index - Receiving an zero-based index value for the font face, if the
+ // mapped font file is a "collection" (meaning a number of faces
+ // are stored in the same file). If the font file is not a
+ // collection, the index value should be zero.
+ // Return Value:
+ // Non-zero for success, 0 for failure.
+ //
+ FPDFEMB_BOOL (*MapFont)(struct FPDFEMB_FONT_MAPPER* mapper, const char* name, int charset,
+ unsigned int flags, int weight,
+ char* path, int* face_index);
+
+ void* user; // A user pointer, used by the application
+};
+
+// Function: FPDFEMB_SetFontMapper
+// Use a system font mapper (typically for Chinese/Japanese/Korean charsets)
+// Parameters:
+// mapper - Pointer to FPDFEMB_FONT_MAPPER structure.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+// Comments:
+// This function is used with devices that come with one or more system fonts,
+// and those fonts are in standard TT or T1 format.
+//
+FPDFEMB_RESULT FPDFEMB_SetFontMapper(FPDFEMB_FONT_MAPPER* mapper);
+
+// Structure: FPDFEMB_GLYPH_PROVIDER
+// Interface for providing glyph bitmap of non-latin characters.
+// This is usually used for embedded devices with Chinese/Japanese/Korean
+// fonts installed, but those fonts are not in TrueType or Type1 format.
+//
+struct FPDFEMB_GLYPH_PROVIDER
+{
+ // Interface: MapFont
+ // Return an internal handle for a font
+ // Parameters:
+ // provider - Pointer to this structure
+ // name - Font name
+ // charset - Charset ID (see above FPDFEMB_CHARSET_xxx constants)
+ // flags - Font flags (see above FPDFEMB_FONT_xxx constants)
+ // weight - Weight of the font. Range from 100 to 900. 400 is normal,
+ // 700 is bold.
+ // Return Value:
+ // An internal handle to the mapped font. If the embedded device supports
+ // multiple fonts, then this value can serve as an identifier to differentiate
+ // among them. If the device supports only one font, then implementation of
+ // this function can simply return NULL.
+ //
+ void* (*MapFont)(struct FPDFEMB_GLYPH_PROVIDER* provider, const char* name, int charset,
+ unsigned int flags, int weight);
+ // Interface: GetGlyphBBox
+ // Get glyph bounding box
+ // Parameters:
+ // provider - Pointer to this structure
+ // font - Internal handle to the font. Returned by MapFont interface.
+ // unicode - Unicode of the character
+ // CID - Adobe CID for this character. Or zero if not available.
+ // bbox - Receiving the result bounding box. See comments below.
+ // Return Value:
+ // None.
+ // Comments:
+ // The bounding box is measure in a glyph coordination system, in which the
+ // origin is set to character origin, and unit is set to one-thousandth of
+ // "em size" (representing the font size).
+ //
+ // In most CJK fonts, all CJK characters (except some symbols or western
+ // characters) have same glyph bounding box:
+ // left = 0, right = 1000, bottom = -220, top = 780.
+ //
+ // It's OK to return a box that's larger than the actual glyph box.
+ //
+ void (*GetGlyphBBox)(struct FPDFEMB_GLYPH_PROVIDER* provider, void* font,
+ FPDFEMB_WCHAR unicode, unsigned short CID,
+ FPDFEMB_RECT* bbox);
+
+ // Interface: GetGlyphBitmap
+ // Get bitmap of a glyph
+ // Parameters:
+ // provider - Pointer to this structure
+ // font - Internal handle to the font. Returned by MapFont interface.
+ // unicode - Unicode of the character
+ // CID - Adobe CID for this character. Or zero if not available.
+ // font_width - Width of the font em square, measured in device units.
+ // font_height - Height of the font em square, measured in device units.
+ // left - Receiving the left offset, from the character origin, of the
+ // result glyph bitmap. Positive value will move the bitmap to
+ // the right side, negative to the left.
+ // top - Receiving the top offset, from the character origin, of the
+ // result glyph bitmap. Positive value will move the bitmap upward,
+ // negative downward.
+ // bitmap_width - Receiving number of width pixels in the result bitmap
+ // bitmap_height - Receiving number of height pixels in the result bitmap
+ // buffer - Receiving a data buffer pointer, allocated by the implementation.
+ // See comments below.
+ // stride - Receiving number of bytes per scanline, in the data buffer.
+ // pdf_width - Width of the character specified in PDF. It is measured in one-
+ // thousandth of the font width. It can be 0 if width not specified
+ // in PDF. See comments below.
+ // Return Value:
+ // Non-zero for success. 0 for failure. In this case the glyph can not be displayed.
+ // Comments:
+ // The buffer should be allocated by implemenation. And it must be allocated
+ // using FPDFEMB_AllocMemory function. The result buffer will be destroyed by
+ // FPDFEMB SDK, so implementation should not destroy it.
+ //
+ // The implementation should write "coverage" data into allocated buffer, one byte
+ // for each pixel, from top scanline to bottom scanline, within each scan line,
+ // from left pixel to right. Coverage 0 means the pixel is outside the glyph,
+ // coverage 255 means the pixel is inside the glyph.
+ //
+ // The "pdf_width" parameter can be used to scale the character in system font
+ // horizontally to match the font width specified in PDF. For example, if we have
+ // a PDF file which requires a character in half-width (pdf_width is 500), but
+ // in the system font the character has full-width (1000), then the glyph provider
+ // implementation should scale the font horizontally to half of its original width.
+ //
+ FPDFEMB_BOOL (*GetGlyphBitmap)(struct FPDFEMB_GLYPH_PROVIDER* provider, void* font,
+ FPDFEMB_WCHAR unicode, unsigned short CID,
+ double font_width, double font_height, int* left, int* top,
+ int* bitmap_width, int* bitmap_height,
+ void** buffer, int* stride, int pdf_width);
+
+ void* user; // A user pointer, used by the application
+};
+
+// Function: FPDFEMB_SetGlyphProvider
+// Make use of a glyph provider: generating glyph bitmap for non-Latin characters
+// Parameters:
+// provider - Pointer to the glyph provider structure.
+// This structure must stay valid throughout usage of FPDFEMB module.
+// Return Value:
+// None.
+// Comments:
+// FPDFEMB embeds some standard fonts for Latin characters and symbols, like
+// Times, Courier and Helvetica (Arial). For non-Latin characters, however,
+// FPDFEMB has to ask glyph provide for help.
+//
+// If an embedded device carries fonts for non-Latin character sets, especially
+// those for CJK markets, then the application can implement a glyph provider,
+// allowing PDFs using non-embedded CJK fonts to be properly displayed.
+//
+void FPDFEMB_SetGlyphProvider(FPDFEMB_GLYPH_PROVIDER* provider);
+
+// Function: FPDFEMB_LoadCMap_GB
+// Function: FPDFEMB_LoadCMap_GB_Ext
+// Function: FPDFEMB_LoadCMap_CNS
+// Function: FPDFEMB_LoadCMap_Korean
+// Function: FPDFEMB_LoadCMap_Japan
+// Function: FPDFEMB_LoadCMap_Japan_Ext
+// Make use of character encoding maps embedded with FPDFEMB
+// Parameters:
+// None.
+// Return Value:
+// None.
+// Comments:
+// These functions add character encoding data to your application. Each call
+// will increase the code size of your application. Total data size for all
+// character sets is 151K bytes.
+void FPDFEMB_LoadCMap_GB();
+void FPDFEMB_LoadCMap_GB_Ext(); // Load full code table for GB
+void FPDFEMB_LoadCMap_CNS();
+void FPDFEMB_LoadCMap_Korea();
+void FPDFEMB_LoadCMap_Japan();
+void FPDFEMB_LoadCMap_Japan_Ext(); // Load full code table for Japan
+
+/********************************************************************************************
+****
+**** Document Information
+****
+********************************************************************************************/
+
+// Function: PDFEMB_GetDocInfoString
+// Get information string about the document, like creator, modifcation date, etc.
+// Parameters:
+// document - Handle to the document
+// key - A byte string for the information key. Currently can be one of the followings:
+// "Title", "Author", "Subject", "Keywords", "Creator", "Producer", "CreationDate",
+// "ModDate", or some custom information key, if supported by the PDF file.
+// buffer - A buffer allocated by the application, or NULL.
+// bufsize - [IN/OUT] A pointer to a number indicating the buffer size (number of bytes),
+// before this function call. After return, this place will store
+// number of bytes used by the output (including terminator).
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+// Comments:
+// The string is output in Unicode, using UTF-16LE format. It's terminated by
+// two consecutive zero bytes.
+//
+// If the "buffer" parameter is NULL, then the "bufsize" parameter will receive
+// number of bytes required to store the string (including the two-byte terminator).
+//
+FPDFEMB_RESULT FPDFEMB_GetDocInfoString(FPDFEMB_DOCUMENT document, const char* key, void* buffer, unsigned int* bufsize);
+
+/********************************************************************************************
+****
+**** Action (Destination) Information
+****
+********************************************************************************************/
+
+typedef void* FPDFEMB_ACTION;
+
+// Action types supported by FPDFEMB
+#define FPDFEMB_DEST_NONE 0 // No or unsupported destination
+#define FPDFEMB_DEST_PAGE 1 // A page inside the same document
+#define FPDFEMB_DEST_DOC 2 // An external PDF document
+#define FPDFEMB_DEST_URL 3 // An external URL
+#define FPDFEMB_ACTION_LAUNCH 4 // Launch an external file or command
+
+// Zoom mode for destination
+#define FPDFEMB_ZOOM_NONE 0 // Zoom mode not specified
+#define FPDFEMB_ZOOM_FACTOR 1 // Specific zoom factor is used
+#define FPDFEMB_ZOOM_FITPAGE 2 // Fit the whole page on screen
+#define FPDFEMB_ZOOM_FITWIDTH 3 // Fit width of the page on screen
+#define FPDFEMB_ZOOM_FITHEIGHT 4 // Fit height of the page on screen
+#define FPDFEMB_ZOOM_FITRECT 5 // Fit a particular rectangle on screen
+#define FPDFEMB_ZOOM_FITCONTENT 6 // Fit whole content of page on screen
+#define FPDFEMB_ZOOM_FITCONTENTW 7 // Fit content width of page on screen
+#define FPDFEMB_ZOOM_FITCONTENTH 8 // Fit content height of page on screen
+
+// Data structure for page destination
+struct FPDFEMB_PAGEDEST
+{
+ int page_index; // Zero based index for the page
+ int zoom_mode; // See FPDFEMB_ZOOM_xxx definition above
+ int zoom_factor; // For FPDFEMB_ZOOM_FACTOR only: the zoom factor (in percentage)
+ FPDFEMB_RECT position; // Specify position inside the page. Depends on the zoom mode,
+ // different members of the rectangle are used:
+ // FPDFEMB_ZOOM_NONE: left, top
+ // FPDFEMB_ZOOM_FACTOR: left, top
+ // FPDFEMB_ZOOM_FITPAGE: none
+ // FPDFEMB_ZOOM_FITWIDTH: top
+ // FPDFEMB_ZOOM_FITHEIGHT: left
+ // FPDFEMB_ZOOM_FITRECT: left, top, bottom, right
+ // FPDFEMB_ZOOM_FITCONTENT: none
+ // FPDFEMB_ZOOM_FITCONTENTW: top
+ // FPDFEMB_ZOOM_FITCONTENTH: left
+};
+
+// Data structure for document destination
+struct FPDFEMB_DOCDEST
+{
+ FPDFEMB_PAGEDEST page_data; // page data
+ char* file_name; // The file name, encoded in original charset (maybe MBCS)
+};
+
+// Data structure for URL destination
+struct FPDFEMB_URLDEST
+{
+ char* url; // URL encoded in 7-bit ASCII
+};
+
+// Data structure for Launch action
+struct FPDFEMB_LAUNCHACTION
+{
+ int new_window; // Whether a new window should be opened
+ char* file_name; // The file name, encoded in original charset (maybe MBCS)
+};
+
+// Function: FPDFEMB_Action_GetType
+// Get type of an action
+// Parameters:
+// document - Handle to the document
+// action - Handle to the action
+// dest_type - Pointer to an integer receiving type of the destination. See the above
+// FPDFEMB_DEST_xxx definitions
+// data_size - Pointer to an integer receiving data block size for the destination.
+// If this parameter is NULL, then data size won't be retrieved.
+// Comments:
+// Each different type of destination has different data structure. The "data_size" result
+// indicates how many bytes is required to hold the destination data structure. The application
+// can then allocate sufficient buffer and then call FPDFEMB_Bookmark_GetDest function to
+// get the real data.
+//
+FPDFEMB_RESULT FPDFEMB_Action_GetType(FPDFEMB_DOCUMENT document, FPDFEMB_ACTION action, int* dest_type, int* data_size);
+
+// Function: FPDFEMB_Action_GetData
+// Get detailed data of a particular action
+// Parameters:
+// document - Handle to the document
+// action - Handle to the action
+// buffer - Application allocated buffer receiving the destination data
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Comments:
+// See data structure definition for different action type above. Please note
+// the actual action data might use more space than the structure definition
+// shows, to store things like file name or URL. So you should always call
+// FPDFEMB_Action_GetType first to get data size then allocate enough buffer
+// for this call.
+//
+FPDFEMB_RESULT FPDFEMB_Action_GetData(FPDFEMB_DOCUMENT document, FPDFEMB_ACTION action, void* buffer);
+
+// Function: FPDFEMB_Action_GetNext
+// Get next action in an action chain
+// Parameters:
+// document - Handle to the document
+// action - Handle to current action
+// next - Receiving handle to next action.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Comments:
+// If there is no next action, the "next" parameter will be set to NULL after the function returns.
+//
+FPDFEMB_RESULT FPDFEMB_Action_GetNext(FPDFEMB_ACTION action, FPDFEMB_ACTION* next);
+
+/********************************************************************************************
+****
+**** Bookmark Information
+****
+********************************************************************************************/
+
+typedef void* FPDFEMB_BOOKMARK;
+
+// Function: FPDFEMB_Bookmark_GetFirstChild
+// Get first child of a bookmark item, or first top level bookmark item
+// Parameters:
+// document - Handle to the document
+// parent - Handle to the parent bookmark.
+// Can be NULL if you want to get the first top level item.
+// bookmark - Receiving handle to the first child or top level bookmark item.
+// If result is NULL, then bookmark not found.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+//
+FPDFEMB_RESULT FPDFEMB_Bookmark_GetFirstChild(FPDFEMB_DOCUMENT document, FPDFEMB_BOOKMARK parent,
+ FPDFEMB_BOOKMARK* bookmark);
+
+// Function: FPDFEMB_Bookmark_GetFirstChild
+// Get next sibling of a bookmark item
+// Parameters:
+// document - Handle to the document
+// bookmark - Handle to the bookmark
+// sibling - Receiving the handle of next sibling.
+// If result is NULL, then this is the last bookmark in this level.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+//
+FPDFEMB_RESULT FPDFEMB_Bookmark_GetNextSibling(FPDFEMB_DOCUMENT document, FPDFEMB_BOOKMARK bookmark,
+ FPDFEMB_BOOKMARK* sibling);
+
+// Function: FPDFEMB_Bookmark_GetTitle
+// Get title of a bookmark
+// Parameters:
+// bookmark - Handle to the bookmark
+// buffer - A buffer allocated by the application, or NULL.
+// bufsize - [IN/OUT] A pointer to a number indicating the buffer size,
+// before this function call. After return, this place will store
+// number of bytes used by the output (including terminator).
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+// Comments:
+// The title is output in Unicode, using UTF-16LE format. It's terminated by
+// two consecutive zero bytes.
+//
+// If the "buffer" parameter is NULL, then the "bufsize" parameter will receive
+// number of bytes required to store the bookmark title (including the two-
+// byte terminator).
+//
+// If the buffer provided is smaller than the required size, then this function
+// will not copy any data, return FPDFEMB_PARAM, and the required buffer size will
+// also be put in "bufsize" parameter.
+//
+FPDFEMB_RESULT FPDFEMB_Bookmark_GetTitle(FPDFEMB_BOOKMARK bookmark, void* buffer, unsigned int* bufsize);
+
+// Function: FPDFEMB_Bookmark_GetPage
+// Get page number of a bookmark pointing to
+// Parameters:
+// document - Handle to the document
+// bookmark - Handle to the bookmark
+// page - Receiving the page number. -1 if this bookmark doesn't actually
+// point to a page inside the document.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success
+// Comments:
+// Some bookmark might not point to a page, some bookmark might have more than one destination
+// (action), for detailed information about a bookmark, you should call FPDFEMB_Bookmark_GetAction.
+//
+FPDFEMB_RESULT FPDFEMB_Bookmark_GetPage(FPDFEMB_DOCUMENT document, FPDFEMB_BOOKMARK bookmark, int* page);
+
+// Function: FPDFEMB_Bookmark_GetAction
+// Get action(s) associated with a particular bookmark
+// Parameters:
+// document - Handle to the document
+// bookmark - Handle to the bookmark
+// action - Receiving handle of first action
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+//
+FPDFEMB_RESULT FPDFEMB_Bookmark_GetAction(FPDFEMB_DOCUMENT document, FPDFEMB_BOOKMARK bookmark, FPDFEMB_ACTION* action);
+
+/********************************************************************************************
+****
+**** Hyperlink Information
+****
+********************************************************************************************/
+
+// Function: FPDFEMB_Link_GetCount
+// Get number of hyperlinks inside a page
+// Parameters:
+// page - Page handle.
+// link_count - Pointer to an integer receiving the number of links
+// reserved - Must be zero now.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Comments:
+// This function must be called before any other link related function can
+// be called for the page.
+//
+FPDFEMB_RESULT FPDFEMB_Link_GetCount(FPDFEMB_PAGE page, int* link_count, int reserved);
+
+// Function: FPDFEMB_Link_GetAction
+// Get action(s) associated with a particular hyperlink
+// Parameters:
+// page - Page handle
+// link_index - Zero-based index for the link
+// action - Receiving handle of first action
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+//
+FPDFEMB_RESULT FPDFEMB_Link_GetAction(FPDFEMB_PAGE page, int link_index, FPDFEMB_ACTION* action);
+
+// Function: FPDFEMB_Link_GetAreaCount
+// Get number of area (quadrilaterals) for a link
+// Parameters:
+// page - Page handle
+// link_index - Zero-based index for the link
+// count - Pointer to an integer receiving number of quadrilaterals
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+//
+FPDFEMB_RESULT FPDFEMB_Link_GetAreaCount(FPDFEMB_PAGE page, int link_index, int* count);
+
+// Function: FPDFEMB_Link_GetArea
+// Get a particular quadrilateral for a link
+// Parameters:
+// page - Page handle
+// link_index - Zero-based index for the link
+// area_index - Zero-based index for the quadrilateral
+// points - Pointer to an array consists 4 points, receiving coordinations
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Comments:
+// The result in "points" array are the X/Y coordinations for the four vertices
+// of the quadrilateral. Vertices are in the following order: lower left, lower
+// right, upper right, upper left.
+//
+FPDFEMB_RESULT FPDFEMB_Link_GetArea(FPDFEMB_PAGE page, int link_index, int area_index,
+ FPDFEMB_POINT* points);
+
+/********************************************************************************************
+****
+**** Graphic Output (onto DIB)
+****
+********************************************************************************************/
+
+typedef void* FPDFEMB_FONT;
+
+// Function: FPDFEMB_OpenStandardFont
+// Get ready to use a standard PDF font
+// Parameters:
+// font_name - Name of the font. See a list of supported fonts below.
+// font_handle - Receiving the font handle.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Comments:
+// Currently supported standard fonts:
+// Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
+// Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
+// Times-Roman, Times-Bold, Times-Italic, Times-BoldItalic,
+// Symbol, ZapfDingbats.
+//
+FPDFEMB_RESULT FPDFEMB_OpenStandardFont(const char* font_name, FPDFEMB_FONT* font_handle);
+
+// Function: FPDFEMB_OpenSystemFont
+// Get ready to use a system font
+// Parameters:
+// font_name - Font name
+// charset - Charset ID (see above FPDFEMB_CHARSET_xxx constants)
+// flags - Font flags (see above FPDFEMB_FONT_xxx constants)
+// weight - Weight of the font. Range from 100 to 900. 400 is normal,
+// 700 is bold.
+// font_handle - Receiving the font handle.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+// Comments:
+// System font is supported only if either FPDFEMB_SetFontMapper or
+// FPDFEMB_SetGlyphProvider is called.
+// Font attributes (name, charset, flags and weight) can be all optional, if the
+// font mapper or glyph provider doesn't make use of them.
+//
+FPDFEMB_RESULT FPDFEMB_OpenSystemFont(const char* font_name, int charset, unsigned int flags, int weight,
+ FPDFEMB_FONT* font_handle);
+
+// Function: FPDFEMB_CloseFont
+// Close a font handle.
+// Parameters:
+// font_handle - Handle to the font.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+//
+FPDFEMB_RESULT FPDFEMB_CloseFont(FPDFEMB_FONT font_handle);
+
+struct FPDFEMB_TEXTMATRIX
+{
+ double a, b, c, d;
+};
+
+// Function: FPDFEMB_OutputText
+// Output text string onto a DIB device.
+// Parameters:
+// dib - DIB handle, as the output device
+// x, y - DIB coordinations for the origin point of the first character.
+// font_handle - Handle to the font
+// font_size - Font size in pixels
+// matrix - Matrix for the text output. Can be NULL.
+// text - Zero-terminated unicode text string
+// argb - Color of the text, in 0xaarrggbb format.
+// Return Value:
+// Error code, or FPDFERR_SUCCESS for success.
+//
+FPDFEMB_RESULT FPDFEMB_OutputText(FPDFEMB_BITMAP dib, int x, int y, FPDFEMB_FONT font_handle, double font_size,
+ FPDFEMB_TEXTMATRIX* matrix, const FPDFEMB_WCHAR* text, unsigned long argb);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // #ifdef _FPDFEMB_H_
diff --git a/src/images/fpdfemb_ext.h b/src/images/fpdfemb_ext.h
new file mode 100644
index 0000000..d82c4df
--- /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