diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:09:42 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:09:42 +0000 |
commit | ae2c20f398933a9e86c387dcc465ec0f71065ffc (patch) | |
tree | de668b1411e2ee0b4e49b6d8f8b68183134ac990 /skia/images | |
parent | 09911bf300f1a419907a9412154760efd0b7abc3 (diff) | |
download | chromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.zip chromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.tar.gz chromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.tar.bz2 |
Add skia to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'skia/images')
24 files changed, 7201 insertions, 0 deletions
diff --git a/skia/images/SkBitmap_RLEPixels.h b/skia/images/SkBitmap_RLEPixels.h new file mode 100644 index 0000000..c83bc69 --- /dev/null +++ b/skia/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/skia/images/SkCreateRLEPixelRef.cpp b/skia/images/SkCreateRLEPixelRef.cpp new file mode 100644 index 0000000..5756237 --- /dev/null +++ b/skia/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/skia/images/SkImageDecoder.cpp b/skia/images/SkImageDecoder.cpp new file mode 100644 index 0000000..3579e3c --- /dev/null +++ b/skia/images/SkImageDecoder.cpp @@ -0,0 +1,157 @@ +/* libs/graphics/images/SkImageDecoder.cpp +** +** Copyright 2006, Google Inc. +** +** 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 "SkTemplates.h" + +static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config; + +SkBitmap::Config SkImageDecoder::GetDeviceConfig() +{ + return gDeviceConfig; +} + +void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config) +{ + gDeviceConfig = config; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkImageDecoder::SkImageDecoder() + : fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1), + fDitherImage(true) { +} + +SkImageDecoder::~SkImageDecoder() { + fPeeker->safeUnref(); + fChooser->safeUnref(); + fAllocator->safeUnref(); +} + +SkImageDecoder::Format SkImageDecoder::getFormat() const { + return kUnknown_Format; +} + +SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) { + SkRefCnt_SafeAssign(fPeeker, peeker); + return peeker; +} + +SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) { + SkRefCnt_SafeAssign(fChooser, chooser); + return chooser; +} + +SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) { + SkRefCnt_SafeAssign(fAllocator, alloc); + return alloc; +} + +void SkImageDecoder::setSampleSize(int size) { + if (size < 1) { + size = 1; + } + fSampleSize = size; +} + +bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width, + int height) const { + Chooser* chooser = fChooser; + + if (NULL == chooser) { // no chooser, we just say YES to decoding :) + return true; + } + chooser->begin(1); + chooser->inspect(0, config, width, height); + return chooser->choose() == 0; +} + +bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap, + SkColorTable* ctable) const { + return bitmap->allocPixels(fAllocator, ctable); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm, + SkBitmap::Config pref, Mode mode) { + SkASSERT(file); + SkASSERT(bm); + + SkFILEStream stream(file); + return stream.isValid() && + SkImageDecoder::DecodeStream(&stream, bm, pref, mode); +} + +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); + + SkImageDecoder* codec = SkImageDecoder::Factory(stream); + if (NULL != codec) { + SkBitmap tmp; + + SkAutoTDelete<SkImageDecoder> ad(codec); + + if (codec->onDecode(stream, &tmp, pref, mode)) { + /* We operate on a tmp bitmap until we know we succeed. This way + we're sure we don't change the caller's bitmap and then later + return false. Returning false must mean that their parameter + is unchanged. + */ + bm->swap(tmp); + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +SkImageEncoder::~SkImageEncoder() {} + +bool SkImageEncoder::encodeStream(SkWStream* stream, const SkBitmap& bm, + int quality) { + quality = SkMin32(100, SkMax32(0, quality)); + return this->onEncode(stream, bm, quality); +} + +bool SkImageEncoder::encodeFile(const char file[], const SkBitmap& bm, + int quality) { + quality = SkMin32(100, SkMax32(0, quality)); + SkFILEWStream stream(file); + return this->onEncode(&stream, bm, quality); +} + +#endif + diff --git a/skia/images/SkImageDecoder_libbmp.cpp b/skia/images/SkImageDecoder_libbmp.cpp new file mode 100644 index 0000000..0de760f --- /dev/null +++ b/skia/images/SkImageDecoder_libbmp.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2007, Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bmpdecoderhelper.h" +#include "SkImageDecoder.h" +#include "SkScaledBitmapSampler.h" +#include "SkStream.h" +#include "SkColorPriv.h" +#include "SkTDArray.h" + +class SkBMPImageDecoder : public SkImageDecoder { +public: + SkBMPImageDecoder() {} + + virtual Format getFormat() const { + return kBMP_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode mode); +}; + +SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream*); +SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream* stream) { + static const char kBmpMagic[] = { 'B', 'M' }; + + size_t len = stream->getLength(); + char buffer[sizeof(kBmpMagic)]; + + if (len > sizeof(kBmpMagic) && + stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) && + !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) { + return SkNEW(SkBMPImageDecoder); + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback { +public: + // we don't copy the bitmap, just remember the pointer + SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {} + + // override from BmpDecoderCallback + virtual uint8* SetSize(int width, int height) { + fWidth = width; + fHeight = height; + if (fJustBounds) { + return NULL; + } + + fRGB.setCount(width * height * 3); // 3 == r, g, b + return fRGB.begin(); + } + + int width() const { return fWidth; } + int height() const { return fHeight; } + uint8_t* rgb() const { return fRGB.begin(); } + +private: + SkTDArray<uint8_t> fRGB; + int fWidth; + int fHeight; + bool fJustBounds; +}; + +bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config prefConfig, Mode mode) { + + size_t length = stream->getLength(); + SkAutoMalloc storage(length); + + if (stream->read(storage.get(), length) != length) { + return false; + } + + const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode; + SkBmpDecoderCallback callback(justBounds); + + // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...] + { + image_codec::BmpDecoderHelper helper; + const int max_pixels = 16383*16383; // max width*height + if (!helper.DecodeImage((const char*)storage.get(), length, + max_pixels, &callback)) { + return false; + } + } + + // we don't need this anymore, so free it now (before we try to allocate + // the bitmap's pixels) rather than waiting for its destructor + storage.free(); + + int width = callback.width(); + int height = callback.height(); + SkBitmap::Config config = SkBitmap::kARGB_8888_Config; + + // only accept prefConfig if it makes sense for us + if (SkBitmap::kARGB_4444_Config == prefConfig || + SkBitmap::kRGB_565_Config == config) { + config = prefConfig; + } + + SkScaledBitmapSampler sampler(width, height, getSampleSize()); + + bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight()); + bm->setIsOpaque(true); + if (justBounds) { + return true; + } + + if (!this->allocPixelRef(bm, NULL)) { + return false; + } + + SkAutoLockPixels alp(*bm); + + if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) { + return false; + } + + const int srcRowBytes = width * 3; + const int dstHeight = sampler.scaledHeight(); + const uint8_t* srcRow = callback.rgb(); + + srcRow += sampler.srcY0() * srcRowBytes; + for (int y = 0; y < dstHeight; y++) { + sampler.next(srcRow); + srcRow += sampler.srcDY() * srcRowBytes; + } + return true; +} diff --git a/skia/images/SkImageDecoder_libgif.cpp b/skia/images/SkImageDecoder_libgif.cpp new file mode 100644 index 0000000..3b5e420 --- /dev/null +++ b/skia/images/SkImageDecoder_libgif.cpp @@ -0,0 +1,331 @@ +/* libs/graphics/images/SkImageDecoder_libgif.cpp +** +** Copyright 2006, Google Inc. +** +** 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->SColorMap; + if (NULL == cmap) { + cmap = gif->Image.ColorMap; + } + // 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; +} + +bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, + SkBitmap::Config prefConfig, Mode mode) { + GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); + if (NULL == gif) { + return false; + } + + 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 false; + } + + switch (recType) { + case IMAGE_DESC_RECORD_TYPE: { + if (DGifGetImageDesc(gif) == GIF_ERROR) { + return false; + } + + if (gif->ImageCount < 1) { // sanity check + return false; + } + + width = gif->SWidth; + height = gif->SHeight; + if (width <= 0 || height <= 0 || + !this->chooseFromOneChoice(SkBitmap::kIndex8_Config, + width, height)) { + return false; + } + + 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 false; + } + + // now we decode the colortable + int colorCount = 0; + { + const ColorMapObject* cmap = find_colormap(gif); + if (NULL == cmap) { + return false; + } + + 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 false; + } + } + + 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 false; + } + + // 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 false; + } + iter.next(); + } + } + else + { + // easy, non-interlace case + for (int y = 0; y < innerHeight; y++) { + if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { + return false; + } + scanline += rowBytes; + } + } + goto DONE; + } break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(gif, &temp_save.Function, + &extData) == GIF_ERROR) { + return false; + } + + while (extData != NULL) { + /* Create an extension block with our data */ + if (AddExtensionBlock(&temp_save, extData[0], + &extData[1]) == GIF_ERROR) { + return false; + } + if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { + return false; + } + temp_save.Function = 0; + } + break; + + case TERMINATE_RECORD_TYPE: + break; + + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } while (recType != TERMINATE_RECORD_TYPE); + +DONE: + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream* stream) { + char buf[GIF_STAMP_LEN]; + if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { + if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { + return SkNEW(SkGIFImageDecoder); + } + } + return NULL; +} + diff --git a/skia/images/SkImageDecoder_libico.cpp b/skia/images/SkImageDecoder_libico.cpp new file mode 100644 index 0000000..8b1be08 --- /dev/null +++ b/skia/images/SkImageDecoder_libico.cpp @@ -0,0 +1,372 @@ +/* libs/graphics/images/SkImageDecoder_libico.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkImageDecoder.h" +#include "SkStream.h" +#include "SkColorPriv.h" +#include "SkTypes.h" + +class SkICOImageDecoder : public SkImageDecoder { +public: + SkICOImageDecoder(); + + virtual Format getFormat() const { + return kICO_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode); +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +//read bytes starting from the begin-th index in the buffer +//read in Intel order, and return an integer + +#define readByte(buffer,begin) buffer[begin] +#define read2Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8) +#define read4Bytes(buffer,begin) buffer[begin]+(buffer[begin+1]<<8)+(buffer[begin+2]<<16)+(buffer[begin+3]<<24) + +///////////////////////////////////////////////////////////////////////////////////////// + +SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream*); +SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream* stream) +{ + //i'm going to check if we basically have 0,0,1,0 (reserved = 0, type = 1) + //is that required and sufficient? + SkAutoMalloc autoMal(4); + unsigned char* buf = (unsigned char*)autoMal.get(); + stream->read((void*)buf, 4); + int reserved = read2Bytes(buf, 0); + int type = read2Bytes(buf, 2); + if (reserved != 0 || type != 1) //it's not an ico + return NULL; + return SkNEW(SkICOImageDecoder); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +SkICOImageDecoder::SkICOImageDecoder() +{ +} + +//helpers - my function pointer will call one of these, depending on the bitCount, each time through the inner loop +static void editPixelBit1(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); +static void editPixelBit4(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); +static void editPixelBit8(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); +static void editPixelBit24(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); +static void editPixelBit32(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); + +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 + //default rowBytes is w << 2 for kARGB_8888 + //i'm adding one - it's only truly necessary in the case that w is odd and we are four bit + //so we can go off the end of the drawn bitmap + //FIXME: need to test with an odd width bitmap that is 4bit + bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, (w << 2) + (0x1 & w & (bitCount >> 2))); + + 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; + int i = x + 8; + while (x < i) + { + + colorBit = (byte & m) >> shift; + alphaBit = (alphaByte & m) >> shift; + *address = (alphaBit-1)&(colors[colorBit]); + x++; + // setup for the next pixel + address = address + 1; + m = m >> 1; + shift -= 1; + } + x--; +} +static void editPixelBit4(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) +{ + SkPMColor* address = bm->getAddr32(x, y); + int byte = readByte(buf, xorOffset + (pixelNo >> 1)); + int pixel = (byte >> 4) & 0xF; + int alphaBit = (alphaByte & m) >> shift; + *address = (alphaBit-1)&(colors[pixel]); + x++; + //if w is odd, x may be the same as w, which means we are writing to an unused portion of the bitmap + //but that's okay, since i've added an extra rowByte for just this purpose + address = address + 1; + pixel = byte & 0xF; + m = m >> 1; + alphaBit = (alphaByte & m) >> (shift-1); + //speed up trick here + *address = (alphaBit-1)&(colors[pixel]); +} + +static void editPixelBit8(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) +{ + SkPMColor* address = bm->getAddr32(x, y); + int pixel = readByte(buf, xorOffset + pixelNo); + int alphaBit = (alphaByte & m) >> shift; + *address = (alphaBit-1)&(colors[pixel]); +} + +static void editPixelBit24(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) +{ + SkPMColor* address = bm->getAddr32(x, y); + int blue = readByte(buf, xorOffset + 3*pixelNo); + int green = readByte(buf, xorOffset + 3*pixelNo + 1); + int red = readByte(buf, xorOffset + 3*pixelNo + 2); + int alphaBit = (alphaByte & m) >> shift; + //alphaBit == 1 => alpha = 0 + int alpha = (alphaBit-1) & 0xFF; + *address = SkPackARGB32(alpha, red & alpha, green & alpha, blue & alpha); +} + +static void editPixelBit32(const int pixelNo, const unsigned char* buf, + const int xorOffset, int& x, int y, const int w, + SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) +{ + SkPMColor* address = bm->getAddr32(x, y); + int blue = readByte(buf, xorOffset + 4*pixelNo); + int green = readByte(buf, xorOffset + 4*pixelNo + 1); + int red = readByte(buf, xorOffset + 4*pixelNo + 2); + int alphaBit = (alphaByte & m) >> shift; + int alpha = readByte(buf, xorOffset + 4*pixelNo + 3) & ((alphaBit-1)&0xFF); + *address = SkPackARGB32(alpha, red & alpha, green & alpha, blue & alpha); +} + diff --git a/skia/images/SkImageDecoder_libjpeg.cpp b/skia/images/SkImageDecoder_libjpeg.cpp new file mode 100644 index 0000000..0d8da7ef --- /dev/null +++ b/skia/images/SkImageDecoder_libjpeg.cpp @@ -0,0 +1,654 @@ +/* + * Copyright 2007, Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkImageDecoder.h" +#include "SkColorPriv.h" +#include "SkDither.h" +#include "SkScaledBitmapSampler.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkUtils.h" + +#include <stdio.h> +extern "C" { + #include "jpeglib.h" +} + +// this enables timing code to report milliseconds for an encode +//#define TIME_ENCODE +//#define TIME_DECODE + +// this enables our rgb->yuv code, which is faster than libjpeg on ARM +// disable for the moment, as we have some glitches when width != multiple of 4 +#define WE_CONVERT_TO_YUV + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +class SkJPEGImageDecoder : public SkImageDecoder { +public: + virtual Format getFormat() const { + return kJPEG_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode); +}; + +SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream* stream) { + // !!! unimplemented; rely on PNG test first for now + return SkNEW(SkJPEGImageDecoder); +} + +////////////////////////////////////////////////////////////////////////// + +#include "SkTime.h" + +class AutoTimeMillis { +public: + AutoTimeMillis(const char label[]) : fLabel(label) { + if (!fLabel) { + fLabel = ""; + } + fNow = SkTime::GetMSecs(); + } + ~AutoTimeMillis() { + SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow); + } +private: + const char* fLabel; + SkMSec fNow; +}; + +/* our source struct for directing jpeg to our stream object +*/ +struct sk_source_mgr : jpeg_source_mgr { + sk_source_mgr(SkStream* stream); + + SkStream* fStream; + + 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; + 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) { + cinfo->err->error_exit((j_common_ptr)cinfo); + 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 skip = num_bytes - src->bytes_in_buffer; + + if (skip > 0) { + size_t bytes = src->fStream->read(NULL, skip); + if (bytes != (size_t)skip) { + 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()) { + printf("------------- sk_resync_to_restart: stream->rewind() failed\n"); + cinfo->err->error_exit((j_common_ptr)cinfo); + return FALSE; + } + return TRUE; +} + +static void sk_term_source(j_decompress_ptr /*cinfo*/) {} + +sk_source_mgr::sk_source_mgr(SkStream* stream) + : fStream(stream) { + 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; +} + +#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 void 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); + SkASSERT(row_count == 1); + } +} + +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); + + 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 false; + } + + jpeg_create_decompress(&cinfo); + autoClean.set(&cinfo); + + //jpeg_stdio_src(&cinfo, file); + cinfo.src = &sk_stream; + + jpeg_read_header(&cinfo, true); + + /* 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; + + /* 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. + */ + jpeg_start_decompress(&cinfo); + + /* 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; + + // check for supported formats + bool isRGB; // as opposed to gray8 + if (3 == cinfo.num_components && JCS_RGB == cinfo.out_color_space) { + isRGB = true; + } else if (1 == cinfo.num_components && + JCS_GRAYSCALE == cinfo.out_color_space) { + isRGB = false; // could use Index8 config if we want... + } else { + SkDEBUGF(("SkJPEGImageDecoder: unsupported jpeg colorspace %d with %d components\n", + cinfo.jpeg_color_space, cinfo.num_components)); + return false; + } + + 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; + } + + // should we allow the Chooser (if present) to pick a config for us??? + if (!this->chooseFromOneChoice(config, cinfo.output_width, + cinfo.output_height)) { + return false; + } + + 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 false; + } + + SkAutoLockPixels alp(*bm); + + if (!sampler.begin(bm, + isRGB ? SkScaledBitmapSampler::kRGB : + SkScaledBitmapSampler::kGray, + this->getDitherImage())) { + return false; + } + + uint8_t* srcRow = (uint8_t*)srcStorage.alloc(cinfo.output_width * 3); + + skip_src_rows(&cinfo, srcRow, sampler.srcY0()); + for (int y = 0;; y++) { + JSAMPLE* rowptr = (JSAMPLE*)srcRow; + int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); + SkASSERT(row_count == 1); + + sampler.next(srcRow); + if (bm->height() - 1 == y) { + break; + } + skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1); + } + + // ??? If I don't do this, I get an error from finish_decompress + skip_src_rows(&cinfo, srcRow, cinfo.output_height - cinfo.output_scanline); + + jpeg_finish_decompress(&cinfo); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +#include "SkColorPriv.h" + +// taken from jcolor.c in libjpeg +#if 0 // 16bit - precise but slow + #define CYR 19595 // 0.299 + #define CYG 38470 // 0.587 + #define CYB 7471 // 0.114 + + #define CUR -11059 // -0.16874 + #define CUG -21709 // -0.33126 + #define CUB 32768 // 0.5 + + #define CVR 32768 // 0.5 + #define CVG -27439 // -0.41869 + #define CVB -5329 // -0.08131 + + #define CSHIFT 16 +#else // 8bit - fast, slightly less precise + #define CYR 77 // 0.299 + #define CYG 150 // 0.587 + #define CYB 29 // 0.114 + + #define CUR -43 // -0.16874 + #define CUG -85 // -0.33126 + #define CUB 128 // 0.5 + + #define CVR 128 // 0.5 + #define CVG -107 // -0.41869 + #define CVB -21 // -0.08131 + + #define CSHIFT 8 +#endif + +static void rgb2yuv_32(uint8_t dst[], SkPMColor c) { + int r = SkGetPackedR32(c); + int g = SkGetPackedG32(c); + int b = SkGetPackedB32(c); + + int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT; + int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT; + int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT; + + dst[0] = SkToU8(y); + dst[1] = SkToU8(u + 128); + dst[2] = SkToU8(v + 128); +} + +static void rgb2yuv_4444(uint8_t dst[], U16CPU c) { + int r = SkGetPackedR4444(c); + int g = SkGetPackedG4444(c); + int b = SkGetPackedB4444(c); + + int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4); + int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4); + int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4); + + dst[0] = SkToU8(y); + dst[1] = SkToU8(u + 128); + dst[2] = SkToU8(v + 128); +} + +static void rgb2yuv_16(uint8_t dst[], U16CPU c) { + int r = SkGetPackedR16(c); + int g = SkGetPackedG16(c); + int b = SkGetPackedB16(c); + + int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2); + int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2); + int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2); + + dst[0] = SkToU8(y); + dst[1] = SkToU8(u + 128); + dst[2] = SkToU8(v + 128); +} + +/////////////////////////////////////////////////////////////////////////////// + +typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst, + const void* SK_RESTRICT src, int width, + const SkPMColor* SK_RESTRICT ctable); + +static void Write_32_YUV(uint8_t* SK_RESTRICT dst, + const void* SK_RESTRICT srcRow, int width, + const SkPMColor*) { + const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow; + while (--width >= 0) { +#ifdef WE_CONVERT_TO_YUV + rgb2yuv_32(dst, *src++); +#else + uint32_t c = *src++; + dst[0] = SkGetPackedR32(c); + dst[1] = SkGetPackedG32(c); + dst[2] = SkGetPackedB32(c); +#endif + dst += 3; + } +} + +static void Write_4444_YUV(uint8_t* SK_RESTRICT dst, + const void* SK_RESTRICT srcRow, int width, + const SkPMColor*) { + const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow; + while (--width >= 0) { +#ifdef WE_CONVERT_TO_YUV + rgb2yuv_4444(dst, *src++); +#else + SkPMColor16 c = *src++; + dst[0] = SkPacked4444ToR32(c); + dst[1] = SkPacked4444ToG32(c); + dst[2] = SkPacked4444ToB32(c); +#endif + dst += 3; + } +} + +static void Write_16_YUV(uint8_t* SK_RESTRICT dst, + const void* SK_RESTRICT srcRow, int width, + const SkPMColor*) { + const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow; + while (--width >= 0) { +#ifdef WE_CONVERT_TO_YUV + rgb2yuv_16(dst, *src++); +#else + uint16_t c = *src++; + dst[0] = SkPacked16ToR32(c); + dst[1] = SkPacked16ToG32(c); + dst[2] = SkPacked16ToB32(c); +#endif + dst += 3; + } +} + +static void Write_Index_YUV(uint8_t* SK_RESTRICT dst, + const void* SK_RESTRICT srcRow, int width, + const SkPMColor* SK_RESTRICT ctable) { + const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow; + while (--width >= 0) { +#ifdef WE_CONVERT_TO_YUV + rgb2yuv_32(dst, ctable[*src++]); +#else + uint32_t c = ctable[*src++]; + dst[0] = SkGetPackedR32(c); + dst[1] = SkGetPackedG32(c); + dst[2] = SkGetPackedB32(c); +#endif + dst += 3; + } +} + +static WriteScanline ChooseWriter(const SkBitmap& bm) { + switch (bm.config()) { + case SkBitmap::kARGB_8888_Config: + return Write_32_YUV; + case SkBitmap::kRGB_565_Config: + return Write_16_YUV; + case SkBitmap::kARGB_4444_Config: + return Write_4444_YUV; + case SkBitmap::kIndex8_Config: + return Write_Index_YUV; + default: + return NULL; + } +} + +struct sk_destination_mgr : jpeg_destination_mgr { + sk_destination_mgr(SkWStream* stream); + + SkWStream* fStream; + + enum { + kBufferSize = 1024 + }; + uint8_t fBuffer[kBufferSize]; +}; + +static void sk_init_destination(j_compress_ptr cinfo) { + sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest; + + dest->next_output_byte = dest->fBuffer; + dest->free_in_buffer = sk_destination_mgr::kBufferSize; +} + +static boolean sk_empty_output_buffer(j_compress_ptr cinfo) { + sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest; + +// if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize - dest->free_in_buffer)) + if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize)) { + sk_throw(); + } + // ERREXIT(cinfo, JERR_FILE_WRITE); + + 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)) { + sk_throw(); + } + } + 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 SkAutoLockColors : public SkNoncopyable { +public: + SkAutoLockColors(const SkBitmap& bm) { + fCTable = bm.getColorTable(); + fColors = fCTable ? fCTable->lockColors() : NULL; + } + ~SkAutoLockColors() { + if (fCTable) { + fCTable->unlockColors(false); + } + } + const SkPMColor* colors() const { return fColors; } +private: + SkColorTable* fCTable; + const SkPMColor* fColors; +}; + +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); + + 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(); + SkAutoMalloc oneRow(width * 3); + uint8_t* oneRowP = (uint8_t*)oneRow.get(); + + SkAutoLockColors alc(bm); + const SkPMColor* colors = alc.colors(); + const void* srcRow = bm.getPixels(); + + while (cinfo.next_scanline < cinfo.image_height) { + JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + + writer(oneRowP, srcRow, width, colors); + row_pointer[0] = oneRowP; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + srcRow = (const void*)((const char*)srcRow + bm.rowBytes()); + } + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + return true; + } +}; + +SkImageEncoder* SkImageEncoder_JPEG_Factory(); +SkImageEncoder* SkImageEncoder_JPEG_Factory() { + return SkNEW(SkJPEGImageEncoder); +} + +#endif /* SK_SUPPORT_IMAGE_ENCODE */ + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkImageDecoder::UnitTest() { + SkBitmap bm; + + (void)SkImageDecoder::DecodeFile("logo.jpg", &bm); +} + +#endif diff --git a/skia/images/SkImageDecoder_libpng.cpp b/skia/images/SkImageDecoder_libpng.cpp new file mode 100644 index 0000000..4378ca9 --- /dev/null +++ b/skia/images/SkImageDecoder_libpng.cpp @@ -0,0 +1,643 @@ +/* libs/graphics/images/SkImageDecoder_libpng.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkImageDecoder.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkDither.h" +#include "SkMath.h" +#include "SkScaledBitmapSampler.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkUtils.h" + +extern "C" { +#include "png.h" +} + +class SkPNGImageDecoder : public SkImageDecoder { +public: + virtual Format getFormat() const { + return kPNG_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode); +}; + +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#endif + +#define PNG_BYTES_TO_CHECK 4 + +/* Automatically clean up after throwing an exception */ +struct PNGAutoClean { + PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {} + ~PNGAutoClean() { + png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); + } +private: + png_structp png_ptr; + png_infop info_ptr; +}; + +SkImageDecoder* SkImageDecoder_PNG_Factory(SkStream* stream) { + char buf[PNG_BYTES_TO_CHECK]; + if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK && + !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) { + return SkNEW(SkPNGImageDecoder); + } + return NULL; +} + +static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) { + SkStream* sk_stream = (SkStream*) png_ptr->io_ptr; + size_t bytes = sk_stream->read(data, length); + if (bytes != length) { + png_error(png_ptr, "Read Error!"); + } +} + +static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { + SkImageDecoder::Peeker* peeker = + (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr); + // peek() returning true means continue decoding + return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ? + 1 : -1; +} + +static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { + SkDebugf("------ png error %s\n", msg); + 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; +} + +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); + + SkBitmap::Config config; + bool hasAlpha = false; + bool doDither = this->getDitherImage(); + + // 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 transColor; + + png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &transColor); + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) || + 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 + + /* 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); + } + + // 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)) { + delete 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) { + SkDEBUGF(("Image doesn't really have alpha [%d %d]\n", + origWidth, origHeight)); + } + } + + /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + decodedBitmap->setIsOpaque(!reallyHasAlpha); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_IMAGE_ENCODE + +#include "SkColorPriv.h" +#include "SkUnPreMultiply.h" + +static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { + SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr; + if (!sk_stream->write(data, len)) { + png_error(png_ptr, "sk_write_fn Error!"); + } +} + +typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src, + int width, char* SK_RESTRICT dst); + +static void transform_scanline_565(const char* SK_RESTRICT src, int width, + char* SK_RESTRICT dst) { + const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src; + for (int i = 0; i < width; i++) { + unsigned c = *srcP++; + *dst++ = SkPacked16ToR32(c); + *dst++ = SkPacked16ToG32(c); + *dst++ = SkPacked16ToB32(c); + } +} + +static void transform_scanline_888(const char* SK_RESTRICT src, int width, + char* SK_RESTRICT dst) { + const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src; + for (int i = 0; i < width; i++) { + SkPMColor c = *srcP++; + *dst++ = SkGetPackedR32(c); + *dst++ = SkGetPackedG32(c); + *dst++ = SkGetPackedB32(c); + } +} + +static void transform_scanline_444(const char* SK_RESTRICT src, int width, + char* SK_RESTRICT dst) { + const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src; + for (int i = 0; i < width; i++) { + SkPMColor16 c = *srcP++; + *dst++ = SkPacked4444ToR32(c); + *dst++ = SkPacked4444ToG32(c); + *dst++ = SkPacked4444ToB32(c); + } +} + +static void transform_scanline_8888(const char* SK_RESTRICT src, int width, + char* SK_RESTRICT dst) { + const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src; + const SkUnPreMultiply::Scale* SK_RESTRICT table = + SkUnPreMultiply::GetScaleTable(); + + for (int i = 0; i < width; i++) { + SkPMColor c = *srcP++; + unsigned a = SkGetPackedA32(c); + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + + if (0 != a && 255 != a) { + SkUnPreMultiply::Scale scale = table[a]; + r = SkUnPreMultiply::ApplyScale(scale, r); + g = SkUnPreMultiply::ApplyScale(scale, g); + b = SkUnPreMultiply::ApplyScale(scale, b); + } + *dst++ = r; + *dst++ = g; + *dst++ = b; + *dst++ = a; + } +} + +static void transform_scanline_4444(const char* SK_RESTRICT src, int width, + char* SK_RESTRICT dst) { + const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src; + const SkUnPreMultiply::Scale* SK_RESTRICT table = + SkUnPreMultiply::GetScaleTable(); + + for (int i = 0; i < width; i++) { + SkPMColor16 c = *srcP++; + unsigned a = SkPacked4444ToA32(c); + unsigned r = SkPacked4444ToR32(c); + unsigned g = SkPacked4444ToG32(c); + unsigned b = SkPacked4444ToB32(c); + + if (0 != a && 255 != a) { + SkUnPreMultiply::Scale scale = table[a]; + r = SkUnPreMultiply::ApplyScale(scale, r); + g = SkUnPreMultiply::ApplyScale(scale, g); + b = SkUnPreMultiply::ApplyScale(scale, b); + } + *dst++ = r; + *dst++ = g; + *dst++ = b; + *dst++ = a; + } +} + +static transform_scanline_proc choose_proc(SkBitmap::Config config, + bool hasAlpha) { + 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 }, + }; + + 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; +} + +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(); + png_color_8 sig_bit; + + switch (config) { + case SkBitmap::kARGB_8888_Config: + sig_bit.red = 8; + sig_bit.green = 8; + sig_bit.blue = 8; + sig_bit.alpha = hasAlpha ? 8 : 0; + break; + case SkBitmap::kARGB_4444_Config: + sig_bit.red = 4; + sig_bit.green = 4; + sig_bit.blue = 4; + sig_bit.alpha = hasAlpha ? 4 : 0; + break; + case SkBitmap::kRGB_565_Config: + sig_bit.red = 5; + sig_bit.green = 6; + sig_bit.blue = 5; + sig_bit.alpha = 0; + break; + default: + SkDEBUGF(("SkPNGImageEncoder::onEncode can't encode %d config\n", + config)); + return false; + } + + SkAutoLockPixels alp(bitmap); + if (NULL == bitmap.getPixels()) { + return false; + } + + 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(), 8, + hasAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + +#if 0 // need to support this some day <reed> + /* 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); + +#if 0 + /* If you png_malloced a palette, free it here (don't free info_ptr->palette, + as recommended in versions 1.0.5m and earlier of this example; if + libpng mallocs info_ptr->palette, libpng will free it). If you + allocated it with malloc() instead of png_malloc(), use free() instead + of png_free(). */ + png_free(png_ptr, palette); +#endif + + /* clean up after the write, and free any memory allocated */ + png_destroy_write_struct(&png_ptr, &info_ptr); + return true; +} + +SkImageEncoder* SkImageEncoder_PNG_Factory(); +SkImageEncoder* SkImageEncoder_PNG_Factory() { + return SkNEW(SkPNGImageEncoder); +} + +#endif /* SK_SUPPORT_IMAGE_ENCODE */ + diff --git a/skia/images/SkImageDecoder_libpvjpeg.cpp b/skia/images/SkImageDecoder_libpvjpeg.cpp new file mode 100644 index 0000000..9177741 --- /dev/null +++ b/skia/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/skia/images/SkImageDecoder_wbmp.cpp b/skia/images/SkImageDecoder_wbmp.cpp new file mode 100644 index 0000000..d2fea75 --- /dev/null +++ b/skia/images/SkImageDecoder_wbmp.cpp @@ -0,0 +1,167 @@ +/** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkImageDecoder.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkMath.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkUtils.h" + +class SkWBMPImageDecoder : public SkImageDecoder { +public: + virtual Format getFormat() const { + return kWBMP_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode); +}; + +static bool read_byte(SkStream* stream, uint8_t* data) +{ + return stream->read(data, 1) == 1; +} + +static bool read_mbf(SkStream* stream, int* value) +{ + int n = 0; + uint8_t data; + do { + if (!read_byte(stream, &data)) { + return false; + } + n = (n << 7) | (data & 0x7F); + } while (data & 0x80); + + *value = n; + return true; +} + +struct wbmp_head { + int fWidth; + int fHeight; + + bool init(SkStream* stream) + { + uint8_t data; + + if (!read_byte(stream, &data) || data != 0) { // unknown type + return false; + } + if (!read_byte(stream, &data) || (data & 0x9F)) { // skip fixed header + return false; + } + if (!read_mbf(stream, &fWidth) || (unsigned)fWidth > 0xFFFF) { + return false; + } + if (!read_mbf(stream, &fHeight) || (unsigned)fHeight > 0xFFFF) { + return false; + } + return fWidth != 0 && fHeight != 0; + } +}; + +SkImageDecoder* SkImageDecoder_WBMP_Factory(SkStream* stream) +{ + wbmp_head head; + + if (head.init(stream)) { + return SkNEW(SkWBMPImageDecoder); + } + return NULL; +} + +static void expand_bits_to_bytes(uint8_t dst[], const uint8_t src[], int bits) +{ + int bytes = bits >> 3; + + for (int i = 0; i < bytes; i++) { + unsigned mask = *src++; + dst[0] = (mask >> 7) & 1; + dst[1] = (mask >> 6) & 1; + dst[2] = (mask >> 5) & 1; + dst[3] = (mask >> 4) & 1; + dst[4] = (mask >> 3) & 1; + dst[5] = (mask >> 2) & 1; + dst[6] = (mask >> 1) & 1; + dst[7] = (mask >> 0) & 1; + dst += 8; + } + + bits &= 7; + if (bits > 0) { + unsigned mask = *src; + do { + *dst++ = (mask >> 7) & 1;; + mask <<= 1; + } while (--bits != 0); + } +} + +#define SkAlign8(x) (((x) + 7) & ~7) + +bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, + SkBitmap::Config prefConfig, Mode mode) +{ + wbmp_head head; + + if (!head.init(stream)) { + return false; + } + + int width = head.fWidth; + int height = head.fHeight; + + // assign these directly, in case we return kDimensions_Result + decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height); + decodedBitmap->setIsOpaque(true); + + if (SkImageDecoder::kDecodeBounds_Mode == mode) + return true; + + const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE }; + SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2)); + SkAutoUnref aur(ct); + + if (!this->allocPixelRef(decodedBitmap, ct)) { + return false; + } + + SkAutoLockPixels alp(*decodedBitmap); + + uint8_t* dst = decodedBitmap->getAddr8(0, 0); + // store the 1-bit valuess at the end of our pixels, so we won't stomp + // on them before we're read them. Just trying to avoid a temp allocation + size_t srcRB = SkAlign8(width) >> 3; + size_t srcSize = height * srcRB; + uint8_t* src = dst + decodedBitmap->getSize() - srcSize; + if (stream->read(src, srcSize) != srcSize) { + return false; + } + + for (int y = 0; y < height; y++) + { + expand_bits_to_bytes(dst, src, width); + dst += decodedBitmap->rowBytes(); + src += srcRB; + } + + return true; +} + diff --git a/skia/images/SkImageRef.cpp b/skia/images/SkImageRef.cpp new file mode 100644 index 0000000..05960a2 --- /dev/null +++ b/skia/images/SkImageRef.cpp @@ -0,0 +1,157 @@ +#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->onDecode(stream, bitmap, config, mode); +} + +bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) { + SkASSERT(&gImageRefMutex == this->mutex()); + + if (fErrorInDecoding) { + return false; + } + + 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", fName.c_str()); + } else { + SkDebugf("--- ImageRef: <%s> failed in codec for %d mode\n", + fName.c_str(), 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/skia/images/SkImageRefPool.cpp b/skia/images/SkImageRefPool.cpp new file mode 100644 index 0000000..fa3ef8a --- /dev/null +++ b/skia/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->fName.c_str(), + 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->fName.c_str(), + 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->fName.c_str()); + + ref = ref->fNext; + } +#endif +} + diff --git a/skia/images/SkImageRefPool.h b/skia/images/SkImageRefPool.h new file mode 100644 index 0000000..b2eb7b3 --- /dev/null +++ b/skia/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/skia/images/SkImageRef_GlobalPool.cpp b/skia/images/SkImageRef_GlobalPool.cpp new file mode 100644 index 0000000..1f0bc43 --- /dev/null +++ b/skia/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/skia/images/SkMMapStream.cpp b/skia/images/SkMMapStream.cpp new file mode 100644 index 0000000..2aee945 --- /dev/null +++ b/skia/images/SkMMapStream.cpp @@ -0,0 +1,63 @@ +#include "SkMMapStream.h" + +#include <unistd.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <errno.h> + +SkMMAPStream::SkMMAPStream(const char filename[]) +{ + fFildes = -1; // initialize to failure case + + int fildes = open(filename, O_RDONLY); + if (fildes < 0) + { + SkDEBUGF(("---- failed to open(%s) for mmap stream error=%d\n", filename, errno)); + return; + } + + off_t size = lseek(fildes, 0, SEEK_END); // find the file size + if (size == -1) + { + SkDEBUGF(("---- failed to lseek(%s) for mmap stream error=%d\n", filename, errno)); + close(fildes); + return; + } + (void)lseek(fildes, 0, SEEK_SET); // restore file offset to beginning + + void* addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fildes, 0); + if (MAP_FAILED == addr) + { + SkDEBUGF(("---- failed to mmap(%s) for mmap stream error=%d\n", filename, errno)); + close(fildes); + return; + } + + this->INHERITED::setMemory(addr, size); + + fFildes = fildes; + fAddr = addr; + fSize = size; +} + +SkMMAPStream::~SkMMAPStream() +{ + this->closeMMap(); +} + +void SkMMAPStream::setMemory(const void* data, size_t length) +{ + this->closeMMap(); + this->INHERITED::setMemory(data, length); +} + +void SkMMAPStream::closeMMap() +{ + if (fFildes >= 0) + { + munmap(fAddr, fSize); + close(fFildes); + fFildes = -1; + } +} + diff --git a/skia/images/SkMovie.cpp b/skia/images/SkMovie.cpp new file mode 100644 index 0000000..4b8f16a --- /dev/null +++ b/skia/images/SkMovie.cpp @@ -0,0 +1,97 @@ +#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::DecodeFile(const char path[]) +{ + SkMovie* movie = NULL; + + // since the movie might hold onto the stream + // we dynamically allocate it and then unref() + SkFILEStream* stream = new SkFILEStream(path); + if (stream->isValid()) + movie = SkMovie::DecodeStream(stream); +#ifdef SK_DEBUG + else + SkDebugf("Movie file not found <%s>\n", path); +#endif + stream->unref(); + + return movie; +} + diff --git a/skia/images/SkMovie_gif.cpp b/skia/images/SkMovie_gif.cpp new file mode 100644 index 0000000..907ea82 --- /dev/null +++ b/skia/images/SkMovie_gif.cpp @@ -0,0 +1,227 @@ +/* libs/graphics/images/SkImageDecoder_libgif.cpp +** +** Copyright 2006, Google Inc. +** +** 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: + // we do NOT hold onto the stream, so it is OK if it is on the + // stack of the caller + 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_StreamFactory(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; +} + +SkMovie* SkMovie_GIF_MemoryFactory(const void* data, size_t length) { + if (length > GIF_STAMP_LEN && !memcmp(GIF_STAMP, data, GIF_STAMP_LEN)) { + SkMemoryStream stream(data, length); + 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? <reed> + 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; + } + + SavedImage* gif_image = fCurrSavedImage; + const int width = gif->SWidth; + const int height = gif->SHeight; + SkBitmap::Config config = SkBitmap::kIndex8_Config; + + SkColorTable* colorTable = SkNEW_ARGS(SkColorTable, (cmap->ColorCount)); + bm->setConfig(config, width, height, 0); + bm->allocPixels(colorTable); + colorTable->unref(); + + 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/skia/images/SkScaledBitmapSampler.cpp b/skia/images/SkScaledBitmapSampler.cpp new file mode 100644 index 0000000..529be61 --- /dev/null +++ b/skia/images/SkScaledBitmapSampler.cpp @@ -0,0 +1,338 @@ +/* + * Copyright 2007, Google Inc. + * + * 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/skia/images/SkScaledBitmapSampler.h b/skia/images/SkScaledBitmapSampler.h new file mode 100644 index 0000000..0bb9924 --- /dev/null +++ b/skia/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/skia/images/SkStream.cpp b/skia/images/SkStream.cpp new file mode 100644 index 0000000..5c1eebe --- /dev/null +++ b/skia/images/SkStream.cpp @@ -0,0 +1,792 @@ +/* libs/graphics/images/SkStream.cpp +** +** Copyright 2006, Google Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkStream.h" +#include "SkFixed.h" +#include "SkString.h" +#include "SkOSFile.h" + +SkStream::~SkStream() {} + +const char* SkStream::getFileName() +{ + // override in subclass if you represent a file + return NULL; +} + +const void* SkStream::getMemoryBase() +{ + // override in subclass if you represent a memory block + return NULL; +} + +size_t SkStream::skip(size_t size) +{ + /* Check for size == 0, and just return 0. If we passed that + to read(), it would interpret it as a request for the entire + size of the stream. + */ + return size ? this->read(NULL, size) : 0; +} + +int8_t SkStream::readS8() { + int8_t value; + size_t len = this->read(&value, 1); + SkASSERT(1 == len); + return value; +} + +int16_t SkStream::readS16() { + int16_t value; + size_t len = this->read(&value, 2); + SkASSERT(2 == len); + return value; +} + +int32_t SkStream::readS32() { + int32_t value; + size_t len = this->read(&value, 4); + SkASSERT(4 == len); + return value; +} + +SkScalar SkStream::readScalar() { + SkScalar value; + size_t len = this->read(&value, sizeof(SkScalar)); + SkASSERT(sizeof(SkScalar) == len); + return value; +} + +size_t SkStream::readPackedUInt() { + uint8_t byte; + if (!this->read(&byte, 1)) { + return 0; + } + if (byte != 0xFF) { + return byte; + } + + uint16_t word; + if (!this->read(&word, 2)) { + return 0; + } + if (word != 0xFFFF) { + return word; + } + + uint32_t quad; + if (!this->read(&quad, 4)) { + return 0; + } + return quad; +} + +////////////////////////////////////////////////////////////////////////////////////// + +SkWStream::~SkWStream() +{ +} + +void SkWStream::newline() +{ + this->write("\n", 1); +} + +void SkWStream::flush() +{ +} + +bool SkWStream::writeText(const char text[]) +{ + SkASSERT(text); + return this->write(text, strlen(text)); +} + +bool SkWStream::writeDecAsText(int32_t dec) +{ + SkString tmp; + tmp.appendS32(dec); + return this->write(tmp.c_str(), tmp.size()); +} + +bool SkWStream::writeHexAsText(uint32_t hex, int digits) +{ + SkString tmp; + tmp.appendHex(hex, digits); + return this->write(tmp.c_str(), tmp.size()); +} + +bool SkWStream::writeScalarAsText(SkScalar value) +{ + SkString tmp; + tmp.appendScalar(value); + return this->write(tmp.c_str(), tmp.size()); +} + +bool SkWStream::write8(U8CPU value) { + uint8_t v = SkToU8(value); + return this->write(&v, 1); +} + +bool SkWStream::write16(U16CPU value) { + uint16_t v = SkToU16(value); + return this->write(&v, 2); +} + +bool SkWStream::write32(uint32_t value) { + return this->write(&value, 4); +} + +bool SkWStream::writeScalar(SkScalar value) { + return this->write(&value, sizeof(value)); +} + +bool SkWStream::writePackedUInt(size_t value) { + if (value < 0xFF) { + return this->write8(value); + } else if (value < 0xFFFF) { + return this->write8(0xFF) && this->write16(value); + } else { + return this->write16(0xFFFF) && this->write32(value); + } +} + +bool SkWStream::writeStream(SkStream* stream, size_t length) { + char scratch[1024]; + const size_t MAX = sizeof(scratch); + + while (length != 0) { + size_t n = length; + if (n > MAX) { + n = MAX; + } + stream->read(scratch, n); + if (!this->write(scratch, n)) { + return false; + } + length -= n; + } + return true; +} + +//////////////////////////////////////////////////////////////////////////// + +SkFILEStream::SkFILEStream(const char file[]) : fName(file) +{ +#ifdef SK_BUILD_FOR_BREW + if (SkStrEndsWith(fName.c_str(), ".xml")) + fName.writable_str()[fName.size()-3] = 'b'; +#endif + + fFILE = file ? sk_fopen(fName.c_str(), kRead_SkFILE_Flag) : NULL; +} + +SkFILEStream::~SkFILEStream() +{ + if (fFILE) + sk_fclose(fFILE); +} + +void SkFILEStream::setPath(const char path[]) +{ + fName.set(path); +#ifdef SK_BUILD_FOR_BREW + if (SkStrEndsWith(fName.c_str(), ".xml")) + fName.writable_str()[fName.size()-3] = 'b'; +#endif + + if (fFILE) + { + sk_fclose(fFILE); + fFILE = NULL; + } + if (path) + fFILE = sk_fopen(fName.c_str(), kRead_SkFILE_Flag); +} + +const char* SkFILEStream::getFileName() +{ + return fName.c_str(); +} + +bool SkFILEStream::rewind() +{ + if (fFILE) + { + if (sk_frewind(fFILE)) + return true; + // we hit an error + sk_fclose(fFILE); + fFILE = NULL; + } + return false; +} + +size_t SkFILEStream::read(void* buffer, size_t size) +{ + if (fFILE) + { + if (buffer == NULL && size == 0) // special signature, they want the total size + return sk_fgetsize(fFILE); + else + return sk_fread(buffer, size, fFILE); + } + return 0; +} + +//////////////////////////////////////////////////////////////////////// + +SkMemoryStream::SkMemoryStream() +{ + fWeOwnTheData = false; + this->setMemory(NULL, 0); +} + +SkMemoryStream::SkMemoryStream(size_t size) { + fWeOwnTheData = true; + fOffset = 0; + fSize = size; + fSrc = sk_malloc_throw(size); +} + +SkMemoryStream::SkMemoryStream(const void* src, size_t size, bool copyData) +{ + fWeOwnTheData = false; + this->setMemory(src, size, copyData); +} + +SkMemoryStream::~SkMemoryStream() +{ + if (fWeOwnTheData) + sk_free((void*)fSrc); +} + +void SkMemoryStream::setMemory(const void* src, size_t size, bool copyData) +{ + if (fWeOwnTheData) + sk_free((void*)fSrc); + + fSize = size; + fOffset = 0; + fWeOwnTheData = copyData; + + if (copyData) + { + void* copy = sk_malloc_throw(size); + memcpy(copy, src, size); + src = copy; + } + fSrc = src; +} + +void SkMemoryStream::skipToAlign4() +{ + // cast to remove unary-minus warning + fOffset += -(int)fOffset & 0x03; +} + +bool SkMemoryStream::rewind() +{ + fOffset = 0; + return true; +} + +size_t SkMemoryStream::read(void* buffer, size_t size) +{ + if (buffer == NULL && size == 0) // special signature, they want the total size + return fSize; + + // if buffer is NULL, seek ahead by size + + if (size == 0) + return 0; + if (size > fSize - fOffset) + size = fSize - fOffset; + if (buffer) { + memcpy(buffer, (const char*)fSrc + fOffset, size); + } + fOffset += size; + return size; +} + +const void* SkMemoryStream::getMemoryBase() +{ + return fSrc; +} + +const void* SkMemoryStream::getAtPos() +{ + return (const char*)fSrc + fOffset; +} + +size_t SkMemoryStream::seek(size_t offset) +{ + if (offset > fSize) + offset = fSize; + fOffset = offset; + return offset; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +SkBufferStream::SkBufferStream(SkStream& proxy, size_t bufferSize) + : fProxy(proxy) +{ + SkASSERT(&proxy != NULL); + this->init(NULL, bufferSize); +} + +SkBufferStream::SkBufferStream(SkStream& proxy, void* buffer, size_t bufferSize) + : fProxy(proxy) +{ + SkASSERT(&proxy != NULL); + SkASSERT(buffer == NULL || bufferSize != 0); // init(addr, 0) makes no sense, we must know how big their buffer is + + this->init(buffer, bufferSize); +} + +void SkBufferStream::init(void* buffer, size_t bufferSize) +{ + if (bufferSize == 0) + bufferSize = kDefaultBufferSize; + + fOrigBufferSize = bufferSize; + fBufferSize = bufferSize; + fBufferOffset = bufferSize; // to trigger a reload on the first read() + + if (buffer == NULL) + { + fBuffer = (char*)sk_malloc_throw(fBufferSize); + fWeOwnTheBuffer = true; + } + else + { + fBuffer = (char*)buffer; + fWeOwnTheBuffer = false; + } +} + +SkBufferStream::~SkBufferStream() +{ + if (fWeOwnTheBuffer) + sk_free(fBuffer); +} + +bool SkBufferStream::rewind() +{ + fBufferOffset = fBufferSize = fOrigBufferSize; + return fProxy.rewind(); +} + +const char* SkBufferStream::getFileName() +{ + return fProxy.getFileName(); +} + +#ifdef SK_DEBUG +// #define SK_TRACE_BUFFERSTREAM +#endif + +size_t SkBufferStream::read(void* buffer, size_t size) +{ +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf("Request %d", size); +#endif + + if (buffer == NULL && size == 0) + return fProxy.read(buffer, size); // requesting total size + + if (buffer == NULL || size == 0) + { + fBufferOffset += size; + return fProxy.read(buffer, size); + } + + size_t s = size; + size_t actuallyRead = 0; + + // flush what we can from our fBuffer + if (fBufferOffset < fBufferSize) + { + if (s > fBufferSize - fBufferOffset) + s = fBufferSize - fBufferOffset; + memcpy(buffer, fBuffer + fBufferOffset, s); +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" flush %d", s); +#endif + size -= s; + fBufferOffset += s; + buffer = (char*)buffer + s; + actuallyRead = s; + } + + // check if there is more to read + if (size) + { + SkASSERT(fBufferOffset >= fBufferSize); // need to refill our fBuffer + + if (size < fBufferSize) // lets try to read more than the request + { + s = fProxy.read(fBuffer, fBufferSize); +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" read %d into fBuffer", s); +#endif + if (size > s) // they asked for too much + size = s; + if (size) + { + memcpy(buffer, fBuffer, size); + actuallyRead += size; +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" memcpy %d into dst", size); +#endif + } + + fBufferOffset = size; + fBufferSize = s; // record the (possibly smaller) size for the buffer + } + else // just do a direct read + { + actuallyRead += fProxy.read(buffer, size); +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf(" direct read %d", size); +#endif + } + } +#ifdef SK_TRACE_BUFFERSTREAM + SkDebugf("\n"); +#endif + return actuallyRead; +} + +const void* SkBufferStream::getMemoryBase() +{ + return fProxy.getMemoryBase(); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +SkFILEWStream::SkFILEWStream(const char path[]) +{ + fFILE = sk_fopen(path, kWrite_SkFILE_Flag); +} + +SkFILEWStream::~SkFILEWStream() +{ + if (fFILE) + sk_fclose(fFILE); +} + +bool SkFILEWStream::write(const void* buffer, size_t size) +{ + if (fFILE == NULL) + return false; + + if (sk_fwrite(buffer, size, fFILE) != size) + { + SkDEBUGCODE(SkDebugf("SkFILEWStream failed writing %d bytes\n", size);) + sk_fclose(fFILE); + fFILE = NULL; + return false; + } + return true; +} + +void SkFILEWStream::flush() +{ + if (fFILE) + sk_fflush(fFILE); +} + +//////////////////////////////////////////////////////////////////////// + +SkMemoryWStream::SkMemoryWStream(void* buffer, size_t size) + : fBuffer((char*)buffer), fMaxLength(size), fBytesWritten(0) +{ +} + +bool SkMemoryWStream::write(const void* buffer, size_t size) +{ + size = SkMin32(size, fMaxLength - fBytesWritten); + if (size > 0) + { + memcpy(fBuffer + fBytesWritten, buffer, size); + fBytesWritten += size; + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////// + +#define SkDynamicMemoryWStream_MinBlockSize 256 + +struct SkDynamicMemoryWStream::Block { + Block* fNext; + char* fCurr; + char* fStop; + + const char* start() const { return (const char*)(this + 1); } + char* start() { return (char*)(this + 1); } + size_t avail() const { return fStop - fCurr; } + size_t written() const { return fCurr - this->start(); } + + void init(size_t size) + { + fNext = NULL; + fCurr = this->start(); + fStop = this->start() + size; + } + + const void* append(const void* data, size_t size) + { + SkASSERT((size_t)(fStop - fCurr) >= size); + memcpy(fCurr, data, size); + fCurr += size; + return (const void*)((const char*)data + size); + } +}; + +SkDynamicMemoryWStream::SkDynamicMemoryWStream() : fHead(NULL), fTail(NULL), fBytesWritten(0), fCopyToCache(NULL) +{ +} + +SkDynamicMemoryWStream::~SkDynamicMemoryWStream() +{ + reset(); +} + +const char* SkDynamicMemoryWStream::detach() +{ + const char* result = getStream(); + fCopyToCache = NULL; + return result; +} + +void SkDynamicMemoryWStream::reset() +{ + sk_free(fCopyToCache); + Block* block = fHead; + + while (block != NULL) { + Block* next = block->fNext; + sk_free(block); + block = next; + } + fHead = fTail = NULL; + fBytesWritten = 0; + fCopyToCache = NULL; +} + +bool SkDynamicMemoryWStream::write(const void* buffer, size_t count) +{ + if (count > 0) { + + if (fCopyToCache) { + sk_free(fCopyToCache); + fCopyToCache = NULL; + } + fBytesWritten += count; + + size_t size; + + if (fTail != NULL && fTail->avail() > 0) { + size = SkMin32(fTail->avail(), count); + buffer = fTail->append(buffer, size); + SkASSERT(count >= size); + count -= size; + if (count == 0) + return true; + } + + size = SkMax32(count, SkDynamicMemoryWStream_MinBlockSize); + Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size); + block->init(size); + block->append(buffer, count); + + if (fTail != NULL) + fTail->fNext = block; + else + fHead = fTail = block; + fTail = block; + } + return true; +} + +bool SkDynamicMemoryWStream::write(const void* buffer, size_t offset, size_t count) +{ + if (offset + count > fBytesWritten) + return false; // test does not partially modify + Block* block = fHead; + while (block != NULL) { + size_t size = block->written(); + if (offset < size) { + size_t part = offset + count > size ? size - offset : count; + memcpy(block->start() + offset, buffer, part); + if (count <= part) + return true; + count -= part; + buffer = (const void*) ((char* ) buffer + part); + } + offset = offset > size ? offset - size : 0; + block = block->fNext; + } + return false; +} + +bool SkDynamicMemoryWStream::read(void* buffer, size_t offset, size_t count) +{ + if (offset + count > fBytesWritten) + return false; // test does not partially modify + Block* block = fHead; + while (block != NULL) { + size_t size = block->written(); + if (offset < size) { + size_t part = offset + count > size ? size - offset : count; + memcpy(buffer, block->start() + offset, part); + if (count <= part) + return true; + count -= part; + buffer = (void*) ((char* ) buffer + part); + } + offset = offset > size ? offset - size : 0; + block = block->fNext; + } + return false; +} + +void SkDynamicMemoryWStream::copyTo(void* dst) const +{ + Block* block = fHead; + + while (block != NULL) { + size_t size = block->written(); + memcpy(dst, block->start(), size); + dst = (void*)((char*)dst + size); + block = block->fNext; + } +} + +const char* SkDynamicMemoryWStream::getStream() const +{ + if (fCopyToCache == NULL) { + fCopyToCache = (char*)sk_malloc_throw(fBytesWritten); + this->copyTo(fCopyToCache); + } + return fCopyToCache; +} + +void SkDynamicMemoryWStream::padToAlign4() +{ + // cast to remove unary-minus warning + int padBytes = -(int)fBytesWritten & 0x03; + if (padBytes == 0) + return; + int zero = 0; + write(&zero, padBytes); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +void SkDebugWStream::newline() +{ +#ifdef SK_DEBUG + SkDebugf("\n"); +#endif +} + +bool SkDebugWStream::write(const void* buffer, size_t size) +{ +#ifdef SK_DEBUG + char* s = new char[size+1]; + memcpy(s, buffer, size); + s[size] = 0; + SkDebugf("%s", s); + delete[] s; +#endif + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +#include "SkRandom.h" + +void SkWStream::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + { + static const char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + char copy[sizeof(s)]; + SkRandom rand; + + for (int i = 0; i < 65; i++) + { + char* copyPtr = copy; + SkMemoryStream mem(s, sizeof(s)); + SkBufferStream buff(mem, i); + + do { + copyPtr += buff.read(copyPtr, rand.nextU() & 15); + } while (copyPtr < copy + sizeof(s)); + SkASSERT(copyPtr == copy + sizeof(s)); + SkASSERT(memcmp(s, copy, sizeof(s)) == 0); + } + } + { + SkDebugWStream s; + + s.writeText("testing wstream helpers\n"); + s.writeText("compare: 0 "); s.writeDecAsText(0); s.newline(); + s.writeText("compare: 591 "); s.writeDecAsText(591); s.newline(); + s.writeText("compare: -9125 "); s.writeDecAsText(-9125); s.newline(); + s.writeText("compare: 0 "); s.writeHexAsText(0, 0); s.newline(); + s.writeText("compare: 03FA "); s.writeHexAsText(0x3FA, 4); s.newline(); + s.writeText("compare: DEADBEEF "); s.writeHexAsText(0xDEADBEEF, 4); s.newline(); + s.writeText("compare: 0 "); s.writeScalarAsText(SkIntToScalar(0)); s.newline(); + s.writeText("compare: 27 "); s.writeScalarAsText(SkIntToScalar(27)); s.newline(); + s.writeText("compare: -119 "); s.writeScalarAsText(SkIntToScalar(-119)); s.newline(); + s.writeText("compare: 851.3333 "); s.writeScalarAsText(SkIntToScalar(851) + SK_Scalar1/3); s.newline(); + s.writeText("compare: -0.08 "); s.writeScalarAsText(-SK_Scalar1*8/100); s.newline(); + } + + { + SkDynamicMemoryWStream ds; + const char s[] = "abcdefghijklmnopqrstuvwxyz"; + int i; + for (i = 0; i < 100; i++) { + bool result = ds.write(s, 26); + SkASSERT(result); + } + SkASSERT(ds.getOffset() == 100 * 26); + char* dst = new char[100 * 26 + 1]; + dst[100*26] = '*'; + ds.copyTo(dst); + SkASSERT(dst[100*26] == '*'); + // char* p = dst; + for (i = 0; i < 100; i++) + SkASSERT(memcmp(&dst[i * 26], s, 26) == 0); + SkASSERT(memcmp(dst, ds.getStream(), 100*26) == 0); + delete[] dst; + } +#endif +} + +#endif diff --git a/skia/images/bmpdecoderhelper.cpp b/skia/images/bmpdecoderhelper.cpp new file mode 100644 index 0000000..e390731 --- /dev/null +++ b/skia/images/bmpdecoderhelper.cpp @@ -0,0 +1,376 @@ +/* + * Copyright 2007, Google Inc. + * + * 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/skia/images/bmpdecoderhelper.h b/skia/images/bmpdecoderhelper.h new file mode 100644 index 0000000..7da1326 --- /dev/null +++ b/skia/images/bmpdecoderhelper.h @@ -0,0 +1,122 @@ +/* + * Copyright 2007, Google Inc. + * + * 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__ + +/////////////////////////////////////////////////////////////////////////////// +// Some glue code that should be removed 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/skia/images/fpdfemb.h b/skia/images/fpdfemb.h new file mode 100644 index 0000000..3c77116 --- /dev/null +++ b/skia/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/skia/images/fpdfemb_ext.h b/skia/images/fpdfemb_ext.h new file mode 100644 index 0000000..d82c4df --- /dev/null +++ b/skia/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 |