diff options
Diffstat (limited to 'src/images')
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(©Bits); + + 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 |