diff options
author | brettw@google.com <brettw@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-12 21:01:41 +0000 |
---|---|---|
committer | brettw@google.com <brettw@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-12 21:01:41 +0000 |
commit | 52e935d04c59135739c3a68fb6e19d313dc6d5ad (patch) | |
tree | 95f7ab178b045bef4456cbf92c6aa7e476becd99 /skia/images | |
parent | 30fab79877b4bb067944b74d98346ac9bb6bfc7e (diff) | |
download | chromium_src-52e935d04c59135739c3a68fb6e19d313dc6d5ad.zip chromium_src-52e935d04c59135739c3a68fb6e19d313dc6d5ad.tar.gz chromium_src-52e935d04c59135739c3a68fb6e19d313dc6d5ad.tar.bz2 |
New drop of Skia. This is up to CL 121320.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@6925 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'skia/images')
-rw-r--r-- | skia/images/SkFDStream.cpp | 85 | ||||
-rw-r--r-- | skia/images/SkFlipPixelRef.cpp | 127 | ||||
-rw-r--r-- | skia/images/SkImageDecoder.cpp | 44 | ||||
-rw-r--r-- | skia/images/SkImageDecoder_fpdfemb.cpp | 236 | ||||
-rw-r--r-- | skia/images/SkImageDecoder_libbmp.cpp | 2 | ||||
-rw-r--r-- | skia/images/SkImageDecoder_libgif.cpp | 39 | ||||
-rw-r--r-- | skia/images/SkImageDecoder_libico.cpp | 28 | ||||
-rw-r--r-- | skia/images/SkImageDecoder_libjpeg.cpp | 367 | ||||
-rw-r--r-- | skia/images/SkImageDecoder_libpng.cpp | 242 | ||||
-rw-r--r-- | skia/images/SkImageDecoder_wbmp.cpp | 2 | ||||
-rw-r--r-- | skia/images/SkImageRef.cpp | 10 | ||||
-rw-r--r-- | skia/images/SkMovie_gif.cpp | 19 | ||||
-rw-r--r-- | skia/images/SkScaledBitmapSampler.cpp | 2 | ||||
-rw-r--r-- | skia/images/SkStream.cpp | 114 | ||||
-rw-r--r-- | skia/images/bmpdecoderhelper.cpp | 2 | ||||
-rw-r--r-- | skia/images/bmpdecoderhelper.h | 5 |
16 files changed, 1100 insertions, 224 deletions
diff --git a/skia/images/SkFDStream.cpp b/skia/images/SkFDStream.cpp new file mode 100644 index 0000000..db4a51a --- /dev/null +++ b/skia/images/SkFDStream.cpp @@ -0,0 +1,85 @@ +#include "SkStream.h" +#include <unistd.h> + +//#define TRACE_FDSTREAM + +SkFDStream::SkFDStream(int fileDesc, bool closeWhenDone) + : fFD(fileDesc), fCloseWhenDone(closeWhenDone) { +} + +SkFDStream::~SkFDStream() { + if (fFD >= 0 && fCloseWhenDone) { + ::close(fFD); + } +} + +bool SkFDStream::rewind() { + if (fFD >= 0) { + off_t value = ::lseek(fFD, 0, SEEK_SET); +#ifdef TRACE_FDSTREAM + if (value) { + SkDebugf("xxxxxxxxxxxxxx rewind failed %d\n", value); + } +#endif + return value == 0; + } + return false; +} + +size_t SkFDStream::read(void* buffer, size_t size) { + if (fFD >= 0) { + if (buffer == NULL && size == 0) { // request total size + off_t curr = ::lseek(fFD, 0, SEEK_CUR); + if (curr < 0) { +#ifdef TRACE_FDSTREAM + SkDebugf("xxxxxxxxxxxxx lseek failed 0 CURR\n"); +#endif + return 0; // error + } + off_t size = ::lseek(fFD, 0, SEEK_END); + if (size < 0) { +#ifdef TRACE_FDSTREAM + SkDebugf("xxxxxxxxxxxxx lseek failed 0 END\n"); +#endif + size = 0; // error + } + if (::lseek(fFD, curr, SEEK_SET) != curr) { + // can't restore, error +#ifdef TRACE_FDSTREAM + SkDebugf("xxxxxxxxxxxxx lseek failed %d SET\n", curr); +#endif + return 0; + } + return size; + } else if (NULL == buffer) { // skip + off_t oldCurr = ::lseek(fFD, 0, SEEK_CUR); + if (oldCurr < 0) { +#ifdef TRACE_FDSTREAM + SkDebugf("xxxxxxxxxxxxx lseek1 failed %d CUR\n", oldCurr); +#endif + return 0; // error; + } + off_t newCurr = ::lseek(fFD, size, SEEK_CUR); + if (newCurr < 0) { +#ifdef TRACE_FDSTREAM + SkDebugf("xxxxxxxxxxxxx lseek2 failed %d CUR\n", newCurr); +#endif + return 0; // error; + } + // return the actual amount we skipped + return newCurr - oldCurr; + } else { // read + ssize_t actual = ::read(fFD, buffer, size); + // our API can't return an error, so we return 0 + if (actual < 0) { +#ifdef TRACE_FDSTREAM + SkDebugf("xxxxxxxxxxxxx read failed %d actual %d\n", size, actual); +#endif + actual = 0; + } + return actual; + } + } + return 0; +} + diff --git a/skia/images/SkFlipPixelRef.cpp b/skia/images/SkFlipPixelRef.cpp new file mode 100644 index 0000000..95403cc --- /dev/null +++ b/skia/images/SkFlipPixelRef.cpp @@ -0,0 +1,127 @@ +#include "SkFlipPixelRef.h" +#include "SkFlattenable.h" +#include "SkRegion.h" + +SkFlipPixelRef::SkFlipPixelRef(SkBitmap::Config config, int width, int height) +: fFlipper(width, height) { + fConfig = config; + fSize = SkBitmap::ComputeSize(config, width, height); + fStorage = sk_malloc_throw(fSize << 1); + fPage0 = fStorage; + fPage1 = (char*)fStorage + fSize; +} + +SkFlipPixelRef::~SkFlipPixelRef() { + sk_free(fStorage); +} + +const SkRegion& SkFlipPixelRef::beginUpdate(SkBitmap* device) { + void* writeAddr; + const void* readAddr; + this->getFrontBack(&readAddr, &writeAddr); + + device->setConfig(fConfig, fFlipper.width(), fFlipper.height()); + device->setPixels(writeAddr); + + SkRegion copyBits; + const SkRegion& dirty = fFlipper.update(©Bits); + + SkFlipPixelRef::CopyBitsFromAddr(*device, copyBits, readAddr); + return dirty; +} + +void SkFlipPixelRef::endUpdate() { + this->swapPages(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void* SkFlipPixelRef::onLockPixels(SkColorTable** ct) { + fMutex.acquire(); + *ct = NULL; + return fPage0; +} + +void SkFlipPixelRef::onUnlockPixels() { + fMutex.release(); +} + +void SkFlipPixelRef::swapPages() { + fMutex.acquire(); + SkTSwap<void*>(fPage0, fPage1); + fMutex.release(); +} + +void SkFlipPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + + buffer.write32(fSize); + // only need to write page0 + buffer.writePad(fPage0, fSize); +} + +SkFlipPixelRef::SkFlipPixelRef(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer, NULL) { + fSize = buffer.readU32(); + fStorage = sk_malloc_throw(fSize << 1); + fPage0 = fStorage; + fPage1 = (char*)fStorage + fSize; + buffer.read(fPage0, fSize); +} + +SkPixelRef* SkFlipPixelRef::Create(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkFlipPixelRef, (buffer)); +} + +static SkPixelRef::Registrar::Registrar reg("SkFlipPixelRef", + SkFlipPixelRef::Create); + +/////////////////////////////////////////////////////////////////////////////// + +static void copyRect(const SkBitmap& dst, const SkIRect& rect, + const void* srcAddr, int shift) { + const size_t offset = rect.fTop * dst.rowBytes() + (rect.fLeft << shift); + char* dstP = static_cast<char*>(dst.getPixels()) + offset; + const char* srcP = static_cast<const char*>(srcAddr) + offset; + const size_t rb = dst.rowBytes(); + const size_t bytes = rect.width() << shift; + + int height = rect.height(); + while (--height >= 0) { + memcpy(dstP, srcP, bytes); + dstP += rb; + srcP += rb; + } +} + +static int getShift(SkBitmap::Config config) { + switch (config) { + case SkBitmap::kARGB_8888_Config: + return 2; + case SkBitmap::kRGB_565_Config: + case SkBitmap::kARGB_4444_Config: + return 1; + case SkBitmap::kIndex8_Config: + case SkBitmap::kA8_Config: + return 0; + default: + return -1; // signal not supported + } +} + +void SkFlipPixelRef::CopyBitsFromAddr(const SkBitmap& dst, const SkRegion& clip, + const void* srcAddr) { + const int shift = getShift(dst.config()); + if (shift < 0) { + return; + } + + const SkIRect bounds = {0, 0, dst.width(), dst.height()}; + SkRegion::Cliperator iter(clip, bounds); + + while (!iter.done()) { + copyRect(dst, iter.rect(), srcAddr, shift); + iter.next(); + } +} + diff --git a/skia/images/SkImageDecoder.cpp b/skia/images/SkImageDecoder.cpp index 3579e3c..405b660 100644 --- a/skia/images/SkImageDecoder.cpp +++ b/skia/images/SkImageDecoder.cpp @@ -1,6 +1,6 @@ /* libs/graphics/images/SkImageDecoder.cpp ** -** Copyright 2006, Google Inc. +** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -89,6 +89,29 @@ bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap, /////////////////////////////////////////////////////////////////////////////// +bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode mode) { + SkBitmap tmp; + + // we reset this to false before calling onDecode + fShouldCancelDecode = false; + + // pass a temporary bitmap, so that if we return false, we are assured of + // leaving the caller's bitmap untouched. + if (this->onDecode(stream, &tmp, pref, mode)) { + /* We operate on a tmp bitmap until we know we succeed. This way + we're sure we don't change the caller's bitmap and then later + return false. Returning false must mean that their parameter + is unchanged. + */ + bm->swap(tmp); + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm, SkBitmap::Config pref, Mode mode) { SkASSERT(file); @@ -115,23 +138,14 @@ bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm, SkASSERT(stream); SkASSERT(bm); + bool success = false; 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; - } + success = codec->decode(stream, bm, pref, mode); + delete codec; } - return false; + return success; } /////////////////////////////////////////////////////////////////////////////// diff --git a/skia/images/SkImageDecoder_fpdfemb.cpp b/skia/images/SkImageDecoder_fpdfemb.cpp new file mode 100644 index 0000000..7f37e3d --- /dev/null +++ b/skia/images/SkImageDecoder_fpdfemb.cpp @@ -0,0 +1,236 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkImageDecoder.h" +#include "SkScaledBitmapSampler.h" +#include "SkStream.h" +#include "SkColorPriv.h" +#include "SkTDArray.h" + +#include "fpdfemb.h" + +class SkFPDFEMBImageDecoder : public SkImageDecoder { +public: + SkFPDFEMBImageDecoder() {} + + virtual Format getFormat() const { + return kBMP_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode mode); + +private: + bool render(FPDFEMB_PAGE page, const FPDFEMB_RECT& bounds, SkBitmap* bm, + SkBitmap::Config prefConfig, SkImageDecoder::Mode mode); +}; + +SkImageDecoder* SkImageDecoder_FPDFEMB_Factory(SkStream*); +SkImageDecoder* SkImageDecoder_FPDFEMB_Factory(SkStream* stream) { + static const char kPDFSig[] = { '%', 'P', 'D', 'F' }; + + size_t len = stream->getLength(); + char buffer[sizeof(kPDFSig)]; + + SkDebugf("---- SkImageDecoder_FPDFEMB_Factory len=%d\n", len); + + if (len != 12683) { return NULL; } + + if (len > sizeof(kPDFSig) && + stream->read(buffer, sizeof(kPDFSig)) == sizeof(kPDFSig) && + !memcmp(buffer, kPDFSig, sizeof(kPDFSig))) { + return SkNEW(SkFPDFEMBImageDecoder); + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +extern "C" { + static void* pdf_alloc(FPDFEMB_MEMMGR* pMgr, unsigned int size) { + void* addr = sk_malloc_throw(size); + // SkDebugf("---- pdf_alloc %d %p\n", size, addr); + return addr; + } + + static void* pdf_alloc_nl(FPDFEMB_MEMMGR* pMgr, unsigned int size) { + void* addr = sk_malloc_flags(size, 0); + // SkDebugf("---- pdf_alloc_nl %d %p\n", size, addr); + return addr; + } + + static void* pdf_realloc(FPDFEMB_MEMMGR*, void* addr, unsigned int size) { + void* newaddr = sk_realloc_throw(addr, size); + // SkDebugf("---- pdf_realloc %p %d %p\n", addr, size, newaddr); + return newaddr; + } + + static void pdf_free(FPDFEMB_MEMMGR* pMgr, void* pointer) { + // SkDebugf("---- pdf_free %p\n", pointer); + sk_free(pointer); + } + + void FX_OUTPUT_LOG_FUNC(const char* format, ...) { + SkDebugf("---- LOG_FUNC %s\n", format); + } + + static unsigned int file_getsize(FPDFEMB_FILE_ACCESS* file) { + SkStream* stream = (SkStream*)file->user; + return stream->getLength(); + } + + static FPDFEMB_RESULT file_readblock(FPDFEMB_FILE_ACCESS* file, void* dst, + unsigned int offset, unsigned int size) { + SkStream* stream = (SkStream*)file->user; +// SkDebugf("---- readblock %p %p %d %d\n", stream, dst, offset, size); + if (!stream->rewind()) { + SkDebugf("---- rewind failed\n"); + return FPDFERR_ERROR; + } + if (stream->skip(offset) != offset) { + SkDebugf("---- skip failed\n"); + return FPDFERR_ERROR; + } + if (stream->read(dst, size) != size) { + SkDebugf("---- read failed\n"); + return FPDFERR_ERROR; + } + return FPDFERR_SUCCESS; + } + + static void pdf_oom_handler(void* memory, int size) { + SkDebugf("======== pdf OOM %p %d\n", memory, size); + } +} + +static inline int PDF2Pixels(int x) { return x / 100; } +static inline SkScalar PDF2Scalar(int x) { + return SkScalarMulDiv(SK_Scalar1, x, 100); +} + +bool SkFPDFEMBImageDecoder::render(FPDFEMB_PAGE page, const FPDFEMB_RECT& bounds, SkBitmap* bm, + SkBitmap::Config prefConfig, SkImageDecoder::Mode mode) { + int width = PDF2Pixels(bounds.right - bounds.left); + int height = PDF2Pixels(bounds.top - bounds.bottom); + + SkDebugf("----- bitmap size [%d %d], mode=%d\n", width, height, mode); + bm->setConfig(SkBitmap::kARGB_8888_Config, width, height); + if (SkImageDecoder::kDecodeBounds_Mode == mode) { + return true; + } + + // USE THE CODEC TO ALLOCATE THE PIXELS!!!! + if (!this->allocPixelRef(bm, NULL)) { + SkDebugf("----- failed to alloc pixels\n"); + return false; + } + + bm->eraseColor(0); + + FPDFEMB_RESULT result; + FPDFEMB_BITMAP dib; + + result = FPDFEMB_CreateDIB(width, height, FPDFDIB_BGRA, bm->getPixels(), + bm->rowBytes(), &dib); + SkDebugf("---- createdib %d\n", result); + + result = FPDFEMB_StartRender(dib, page, 0, 0, width, height, 0, 0, NULL, NULL); + SkDebugf("---- render %d\n", result); + + result = FPDFEMB_DestroyDIB(dib); + SkDebugf("---- destroydib %d\n", result); + + SkPMColor* dst = bm->getAddr32(0, 0); + const uint8_t* src = (uint8_t*)dst; + int n = bm->getSize() >> 2; + for (int i = 0; i < n; i++) { + int b = *src++; + int g = *src++; + int r = *src++; + int a = *src++; + *dst++ = SkPackARGB32(a, r, g, b); + } + + return true; +} + +#define USE_FIXED_MEM (4 * 1024 * 1024) + +bool SkFPDFEMBImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config prefConfig, Mode mode) { + + FPDFEMB_RESULT result; +#ifdef USE_FIXED_MEM + SkAutoMalloc storage(USE_FIXED_MEM); + result = FPDFEMB_InitFixedMemory(storage.get(), USE_FIXED_MEM, + pdf_oom_handler); +#else + FPDFEMB_MEMMGR memmgr; + memmgr.Alloc = pdf_alloc; + memmgr.AllocNL = pdf_alloc_nl; + memmgr.Realloc = pdf_realloc; + memmgr.Free = pdf_free; + + result = FPDFEMB_Init(&memmgr); +#endif + SkDebugf("----- SkImageDecoder_FPDFEMB_Factory init %d, streamLen = %d\n", result, stream->getLength()); + + FPDFEMB_FILE_ACCESS file; + file.GetSize = file_getsize; + file.ReadBlock = file_readblock; + file.user = stream; + + FPDFEMB_DOCUMENT document; + result = FPDFEMB_StartLoadDocument(&file, NULL, &document, NULL); + SkDebugf("----- SkImageDecoder_FPDFEMB_Factory open %d %p\n", result, document); + + int pageCount = FPDFEMB_GetPageCount(document); + SkDebugf("----- SkImageDecoder_FPDFEMB_Factory pageCount %d\n", pageCount); + + if (pageCount > 0) { + FPDFEMB_PAGE page; + result = FPDFEMB_LoadPage(document, 0, &page); + SkDebugf("----- SkImageDecoder_FPDFEMB_Factory load page %d\n", result); + + int width, height; + result = FPDFEMB_GetPageSize(page, &width, &height); + SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page size %d [%d %d]\n", result, width, height); + + FPDFEMB_RECT rect; + result = FPDFEMB_GetPageBBox(page, &rect); + SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page rect %d [%d %d %d %d]\n", result, + rect.left, rect.top, rect.right, rect.bottom); + + SkDebugf("----- SkImageDecoder_FPDFEMB_Factory begin page parse...\n"); + result = FPDFEMB_StartParse(page, false, NULL); + SkDebugf("----- SkImageDecoder_FPDFEMB_Factory page parse %d\n", result); + + if (0 == result) { + this->render(page, rect, bm, prefConfig, mode); + } + + result = FPDFEMB_ClosePage(page); + SkDebugf("----- SkImageDecoder_FPDFEMB_Factory close page %d\n", result); + } + + result = FPDFEMB_CloseDocument(document); + SkDebugf("----- SkImageDecoder_FPDFEMB_Factory close %d\n", result); + + // FPDFEMB_Exit(); + + return true; +} diff --git a/skia/images/SkImageDecoder_libbmp.cpp b/skia/images/SkImageDecoder_libbmp.cpp index 0de760f..32a7a6d 100644 --- a/skia/images/SkImageDecoder_libbmp.cpp +++ b/skia/images/SkImageDecoder_libbmp.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2007, Google Inc. + * Copyright 2007, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/skia/images/SkImageDecoder_libgif.cpp b/skia/images/SkImageDecoder_libgif.cpp index 3b5e420..c894c9d 100644 --- a/skia/images/SkImageDecoder_libgif.cpp +++ b/skia/images/SkImageDecoder_libgif.cpp @@ -1,6 +1,6 @@ /* libs/graphics/images/SkImageDecoder_libgif.cpp ** -** Copyright 2006, Google Inc. +** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -145,11 +145,20 @@ static int find_transpIndex(const SavedImage& image, int colorCount) { return transpIndex; } +static bool error_return(GifFileType* gif, const SkBitmap& bm, + const char msg[]) { +#if 0 + SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n", + msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable()); +#endif + return false; +} + bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, SkBitmap::Config prefConfig, Mode mode) { GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); if (NULL == gif) { - return false; + return error_return(gif, *bm, "DGifOpen"); } SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif); @@ -165,17 +174,17 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, do { if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { - return false; + return error_return(gif, *bm, "DGifGetRecordType"); } switch (recType) { case IMAGE_DESC_RECORD_TYPE: { if (DGifGetImageDesc(gif) == GIF_ERROR) { - return false; + return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE"); } if (gif->ImageCount < 1) { // sanity check - return false; + return error_return(gif, *bm, "ImageCount < 1"); } width = gif->SWidth; @@ -183,7 +192,7 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, if (width <= 0 || height <= 0 || !this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, height)) { - return false; + return error_return(gif, *bm, "chooseFromOneChoice"); } bm->setConfig(SkBitmap::kIndex8_Config, width, height); @@ -197,7 +206,7 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, if ( (desc.Top | desc.Left) < 0 || desc.Left + desc.Width > width || desc.Top + desc.Height > height) { - return false; + return error_return(gif, *bm, "TopLeft"); } // now we decode the colortable @@ -205,7 +214,7 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, { const ColorMapObject* cmap = find_colormap(gif); if (NULL == cmap) { - return false; + return error_return(gif, *bm, "null cmap"); } colorCount = cmap->ColorCount; @@ -226,7 +235,7 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, SkAutoUnref aurts(ctable); if (!this->allocPixelRef(bm, ctable)) { - return false; + return error_return(gif, *bm, "allocPixelRef"); } } @@ -241,7 +250,7 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, // abort if either inner dimension is <= 0 if (innerWidth <= 0 || innerHeight <= 0) { - return false; + return error_return(gif, *bm, "non-pos inner width/height"); } // are we only a subset of the total bounds? @@ -266,7 +275,7 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, { uint8_t* row = scanline + iter.currY() * rowBytes; if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) { - return false; + return error_return(gif, *bm, "interlace DGifGetLine"); } iter.next(); } @@ -276,7 +285,7 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, // easy, non-interlace case for (int y = 0; y < innerHeight; y++) { if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { - return false; + return error_return(gif, *bm, "DGifGetLine"); } scanline += rowBytes; } @@ -287,17 +296,17 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, case EXTENSION_RECORD_TYPE: if (DGifGetExtension(gif, &temp_save.Function, &extData) == GIF_ERROR) { - return false; + return error_return(gif, *bm, "DGifGetExtension"); } while (extData != NULL) { /* Create an extension block with our data */ if (AddExtensionBlock(&temp_save, extData[0], &extData[1]) == GIF_ERROR) { - return false; + return error_return(gif, *bm, "AddExtensionBlock"); } if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { - return false; + return error_return(gif, *bm, "DGifGetExtensionNext"); } temp_save.Function = 0; } diff --git a/skia/images/SkImageDecoder_libico.cpp b/skia/images/SkImageDecoder_libico.cpp index 8b1be08..b179a6b 100644 --- a/skia/images/SkImageDecoder_libico.cpp +++ b/skia/images/SkImageDecoder_libico.cpp @@ -1,6 +1,6 @@ /* libs/graphics/images/SkImageDecoder_libico.cpp ** -** Copyright 2006, Google Inc. +** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -82,6 +82,20 @@ static void editPixelBit32(const int pixelNo, const unsigned char* buf, const int xorOffset, int& x, int y, const int w, SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); + +static int calculateRowBytesFor8888(int w, int bitCount) +{ + // Default rowBytes is w << 2 for kARGB_8888 + // In the case of a 4 bit image with an odd width, we need to add some + // so we can go off the end of the drawn bitmap. + // Add 4 to ensure that it is still a multiple of 4. + if (4 == bitCount && (w & 0x1)) { + return (w + 1) << 2; + } + // Otherwise return 0, which will allow it to be calculated automatically. + return 0; +} + bool SkICOImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, SkBitmap::Config pref, Mode mode) { @@ -241,11 +255,8 @@ bool SkICOImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, //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))); + + bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount)); if (SkImageDecoder::kDecodeBounds_Mode == mode) { delete[] colors; @@ -298,7 +309,12 @@ static void editPixelBit1(const int pixelNo, const unsigned char* buf, int byte = readByte(buf, xorOffset + (pixelNo >> 3)); int colorBit; int alphaBit; + // Read all of the bits in this byte. int i = x + 8; + // Pin to the width so we do not write outside the bounds of + // our color table. + i = i > w ? w : i; + // While loop to check all 8 bits individually. while (x < i) { diff --git a/skia/images/SkImageDecoder_libjpeg.cpp b/skia/images/SkImageDecoder_libjpeg.cpp index 0d8da7ef..492de23 100644 --- a/skia/images/SkImageDecoder_libjpeg.cpp +++ b/skia/images/SkImageDecoder_libjpeg.cpp @@ -1,16 +1,16 @@ /* - * Copyright 2007, Google Inc. + * Copyright 2007, The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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 + * 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 + * 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. */ @@ -25,6 +25,7 @@ #include <stdio.h> extern "C" { #include "jpeglib.h" + #include "jerror.h" } // this enables timing code to report milliseconds for an encode @@ -49,8 +50,21 @@ protected: SkBitmap::Config pref, Mode); }; -SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream* stream) { - // !!! unimplemented; rely on PNG test first for now +SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream* stream) { + static const char gHeader[] = { 0xFF, 0xD8, 0xFF }; + static const size_t HEADER_SIZE = sizeof(gHeader); + + char buffer[HEADER_SIZE]; + size_t len = stream->read(buffer, HEADER_SIZE); + + if (len != HEADER_SIZE) { + return NULL; // can't read enough + } + + if (memcmp(buffer, gHeader, HEADER_SIZE)) { + return NULL; + } + return SkNEW(SkJPEGImageDecoder); } @@ -77,10 +91,12 @@ private: /* our source struct for directing jpeg to our stream object */ struct sk_source_mgr : jpeg_source_mgr { - sk_source_mgr(SkStream* stream); + sk_source_mgr(SkStream* stream, SkImageDecoder* decoder); SkStream* fStream; - + const void* fMemoryBase; + size_t fMemoryBaseSize; + SkImageDecoder* fDecoder; enum { kBufferSize = 1024 }; @@ -111,11 +127,13 @@ static void sk_init_source(j_decompress_ptr cinfo) { static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) { sk_source_mgr* src = (sk_source_mgr*)cinfo->src; + if (src->fDecoder != NULL && src->fDecoder->shouldCancelDecode()) { + return FALSE; + } size_t bytes = src->fStream->read(src->fBuffer, sk_source_mgr::kBufferSize); // note that JPEG is happy with less than the full read, // as long as the result is non-zero if (bytes == 0) { - cinfo->err->error_exit((j_common_ptr)cinfo); return FALSE; } @@ -129,11 +147,13 @@ static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { sk_source_mgr* src = (sk_source_mgr*)cinfo->src; - long skip = num_bytes - src->bytes_in_buffer; + long bytesToSkip = num_bytes - src->bytes_in_buffer; - if (skip > 0) { - size_t bytes = src->fStream->read(NULL, skip); - if (bytes != (size_t)skip) { + // check if the skip amount exceeds the current buffer + if (bytesToSkip > 0) { + size_t bytes = src->fStream->skip(bytesToSkip); + if (bytes != (size_t)bytesToSkip) { +// SkDebugf("xxxxxxxxxxxxxx failure to skip request %d actual %d\n", bytesToSkip, bytes); cinfo->err->error_exit((j_common_ptr)cinfo); } src->next_input_byte = (const JOCTET*)src->fBuffer; @@ -150,22 +170,69 @@ static boolean sk_resync_to_restart(j_decompress_ptr cinfo, int desired) { // what is the desired param for??? if (!src->fStream->rewind()) { - printf("------------- sk_resync_to_restart: stream->rewind() failed\n"); + SkDebugf("xxxxxxxxxxxxxx failure to rewind\n"); cinfo->err->error_exit((j_common_ptr)cinfo); return FALSE; } + src->next_input_byte = (const JOCTET*)src->fBuffer; + src->bytes_in_buffer = 0; return TRUE; } static void sk_term_source(j_decompress_ptr /*cinfo*/) {} -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; +/////////////////////////////////////////////////////////////////////////////// + +static void skmem_init_source(j_decompress_ptr cinfo) { + sk_source_mgr* src = (sk_source_mgr*)cinfo->src; + src->next_input_byte = (const JOCTET*)src->fMemoryBase; + src->bytes_in_buffer = src->fMemoryBaseSize; +} + +static boolean skmem_fill_input_buffer(j_decompress_ptr cinfo) { + SkDebugf("xxxxxxxxxxxxxx skmem_fill_input_buffer called\n"); + return FALSE; +} + +static void skmem_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { + sk_source_mgr* src = (sk_source_mgr*)cinfo->src; +// SkDebugf("xxxxxxxxxxxxxx skmem_skip_input_data called %d\n", num_bytes); + src->next_input_byte = (const JOCTET*)((const char*)src->next_input_byte + num_bytes); + src->bytes_in_buffer -= num_bytes; +} + +static boolean skmem_resync_to_restart(j_decompress_ptr cinfo, int desired) { + SkDebugf("xxxxxxxxxxxxxx skmem_resync_to_restart called\n"); + return TRUE; +} + +static void skmem_term_source(j_decompress_ptr /*cinfo*/) {} + +/////////////////////////////////////////////////////////////////////////////// + +sk_source_mgr::sk_source_mgr(SkStream* stream, SkImageDecoder* decoder) : fStream(stream) { + fDecoder = decoder; + const void* baseAddr = stream->getMemoryBase(); + if (baseAddr && false) { + fMemoryBase = baseAddr; + fMemoryBaseSize = stream->getLength(); + + init_source = skmem_init_source; + fill_input_buffer = skmem_fill_input_buffer; + skip_input_data = skmem_skip_input_data; + resync_to_restart = skmem_resync_to_restart; + term_source = skmem_term_source; + } else { + fMemoryBase = NULL; + fMemoryBaseSize = 0; + + init_source = sk_init_source; + fill_input_buffer = sk_fill_input_buffer; + skip_input_data = sk_skip_input_data; + resync_to_restart = sk_resync_to_restart; + term_source = sk_term_source; + } +// SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize); } #include <setjmp.h> @@ -187,14 +254,29 @@ static void sk_error_exit(j_common_ptr cinfo) { /////////////////////////////////////////////////////////////////////////////// -static void skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, +static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) { for (int i = 0; i < count; i++) { JSAMPLE* rowptr = (JSAMPLE*)buffer; int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1); - SkASSERT(row_count == 1); + if (row_count != 1) { + return false; + } } -} + return true; +} + +// This guy exists just to aid in debugging, as it allows debuggers to just +// set a break-point in one place to see all error exists. +static bool return_false(const jpeg_decompress_struct& cinfo, + const SkBitmap& bm, const char msg[]) { +#if 0 + SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code, + cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg, + bm.width(), bm.height()); +#endif + return false; // must always return false +} bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, SkBitmap::Config prefConfig, Mode mode) { @@ -207,7 +289,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, jpeg_decompress_struct cinfo; sk_error_mgr sk_err; - sk_source_mgr sk_stream(stream); + sk_source_mgr sk_stream(stream, this); cinfo.err = jpeg_std_error(&sk_err); sk_err.error_exit = sk_error_exit; @@ -215,7 +297,7 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, // 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; + return return_false(cinfo, *bm, "setjmp"); } jpeg_create_decompress(&cinfo); @@ -224,7 +306,10 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, //jpeg_stdio_src(&cinfo, file); cinfo.src = &sk_stream; - jpeg_read_header(&cinfo, true); + int status = jpeg_read_header(&cinfo, true); + if (status != JPEG_HEADER_OK) { + return return_false(cinfo, *bm, "read_header"); + } /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it can) much faster that we, just use their num/denom api to approximate @@ -236,55 +321,128 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, cinfo.scale_num = 1; cinfo.scale_denom = sampleSize; + /* this gives about 30% performance improvement. In theory it may + reduce the visual quality, in practice I'm not seeing a difference + */ + cinfo.do_fancy_upsampling = 0; + + /* this gives another few percents */ + cinfo.do_block_smoothing = 0; + + /* default format is RGB */ + cinfo.out_color_space = JCS_RGB; + + SkBitmap::Config config = prefConfig; + // if no user preference, see what the device recommends + if (config == SkBitmap::kNo_Config) + config = SkImageDecoder::GetDeviceConfig(); + + // only these make sense for jpegs + if (config != SkBitmap::kARGB_8888_Config && + config != SkBitmap::kARGB_4444_Config && + config != SkBitmap::kRGB_565_Config) { + config = SkBitmap::kARGB_8888_Config; + } + +#ifdef ANDROID_RGB + cinfo.dither_mode = JDITHER_NONE; + if (config == SkBitmap::kARGB_8888_Config) { + cinfo.out_color_space = JCS_RGBA_8888; + } else if (config == SkBitmap::kRGB_565_Config) { + if (sampleSize == 1) { + // SkScaledBitmapSampler can't handle RGB_565 yet, + // so don't even try. + cinfo.out_color_space = JCS_RGB_565; + if (this->getDitherImage()) { + cinfo.dither_mode = JDITHER_ORDERED; + } + } + } +#endif + /* image_width and image_height are the original dimensions, available after jpeg_read_header(). To see the scaled dimensions, we have to call jpeg_start_decompress(), and then read output_width and output_height. */ - jpeg_start_decompress(&cinfo); + if (!jpeg_start_decompress(&cinfo)) { + return return_false(cinfo, *bm, "start_decompress"); + } /* If we need to better match the request, we might examine the image and output dimensions, and determine if the downsampling jpeg provided is not sufficient. If so, we can recompute a modified sampleSize value to make up the difference. - + To skip this additional scaling, just set sampleSize = 1; below. */ sampleSize = sampleSize * cinfo.output_width / cinfo.image_width; - // 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; + return return_false(cinfo, *bm, "chooseFromOneChoice"); + } + +#ifdef ANDROID_RGB + /* short-circuit the SkScaledBitmapSampler when possible, as this gives + a significant performance boost. + */ + if (sampleSize == 1 && + ((config == SkBitmap::kARGB_8888_Config && + cinfo.out_color_space == JCS_RGBA_8888) || + (config == SkBitmap::kRGB_565_Config && + cinfo.out_color_space == JCS_RGB_565))) + { + bm->setConfig(config, cinfo.output_width, cinfo.output_height); + bm->setIsOpaque(true); + if (SkImageDecoder::kDecodeBounds_Mode == mode) { + return true; + } + if (!this->allocPixelRef(bm, NULL)) { + return return_false(cinfo, *bm, "allocPixelRef"); + } + SkAutoLockPixels alp(*bm); + JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels(); + INT32 const bpr = bm->rowBytes(); + + while (cinfo.output_scanline < cinfo.output_height) { + int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); + // if row_count == 0, then we didn't get a scanline, so abort. + // if we supported partial images, we might return true in this case + if (0 == row_count) { + return return_false(cinfo, *bm, "read_scanlines"); + } + if (this->shouldCancelDecode()) { + return return_false(cinfo, *bm, "shouldCancelDecode"); + } + rowptr += bpr; + } + jpeg_finish_decompress(&cinfo); + return true; + } +#endif + + // check for supported formats + SkScaledBitmapSampler::SrcConfig sc; + if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) { + sc = SkScaledBitmapSampler::kRGB; +#ifdef ANDROID_RGB + } else if (JCS_RGBA_8888 == cinfo.out_color_space) { + sc = SkScaledBitmapSampler::kRGBX; + //} else if (JCS_RGB_565 == cinfo.out_color_space) { + // sc = SkScaledBitmapSampler::kRGB_565; +#endif + } else if (1 == cinfo.out_color_components && + JCS_GRAYSCALE == cinfo.out_color_space) { + sc = SkScaledBitmapSampler::kGray; + } else { + return return_false(cinfo, *bm, "jpeg colorspace"); } SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize); - + bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight()); // jpegs are always opauqe (i.e. have no per-pixel alpha) bm->setIsOpaque(true); @@ -293,38 +451,51 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, return true; } if (!this->allocPixelRef(bm, NULL)) { - return false; + return return_false(cinfo, *bm, "allocPixelRef"); } - SkAutoLockPixels alp(*bm); - - if (!sampler.begin(bm, - isRGB ? SkScaledBitmapSampler::kRGB : - SkScaledBitmapSampler::kGray, - this->getDitherImage())) { - return false; + SkAutoLockPixels alp(*bm); + if (!sampler.begin(bm, sc, this->getDitherImage())) { + return return_false(cinfo, *bm, "sampler.begin"); } - uint8_t* srcRow = (uint8_t*)srcStorage.alloc(cinfo.output_width * 3); + uint8_t* srcRow = (uint8_t*)srcStorage.alloc(cinfo.output_width * 4); + + // Possibly skip initial rows [sampler.srcY0] + if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) { + return return_false(cinfo, *bm, "skip rows"); + } - skip_src_rows(&cinfo, srcRow, sampler.srcY0()); + // now loop through scanlines until y == bm->height() - 1 for (int y = 0;; y++) { JSAMPLE* rowptr = (JSAMPLE*)srcRow; int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); - SkASSERT(row_count == 1); + if (0 == row_count) { + return return_false(cinfo, *bm, "read_scanlines"); + } + if (this->shouldCancelDecode()) { + return return_false(cinfo, *bm, "shouldCancelDecode"); + } sampler.next(srcRow); if (bm->height() - 1 == y) { + // we're done break; } - skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1); + + if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) { + return return_false(cinfo, *bm, "skip rows"); + } } - // ??? If I don't do this, I get an error from finish_decompress - skip_src_rows(&cinfo, srcRow, cinfo.output_height - cinfo.output_scanline); - + // we formally skip the rest, so we don't get a complaint from libjpeg + if (!skip_src_rows(&cinfo, srcRow, + cinfo.output_height - cinfo.output_scanline)) { + return return_false(cinfo, *bm, "skip rows"); + } jpeg_finish_decompress(&cinfo); +// SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config()); return true; } @@ -383,11 +554,11 @@ 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); @@ -397,11 +568,11 @@ 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); @@ -519,9 +690,9 @@ static boolean sk_empty_output_buffer(j_compress_ptr cinfo) { // 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); + return false; } - // ERREXIT(cinfo, JERR_FILE_WRITE); dest->next_output_byte = dest->fBuffer; dest->free_in_buffer = sk_destination_mgr::kBufferSize; @@ -534,7 +705,8 @@ static void sk_term_destination (j_compress_ptr cinfo) { size_t size = sk_destination_mgr::kBufferSize - dest->free_in_buffer; if (size > 0) { if (!dest->fStream->write(dest->fBuffer, size)) { - sk_throw(); + ERREXIT(cinfo, JERR_FILE_WRITE); + return; } } dest->fStream->flush(); @@ -547,23 +719,6 @@ sk_destination_mgr::sk_destination_mgr(SkWStream* stream) 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) { @@ -585,6 +740,10 @@ protected: sk_error_mgr sk_err; sk_destination_mgr sk_wstream(stream); + // allocate these before set call setjmp + SkAutoMalloc oneRow; + SkAutoLockColors ctLocker; + cinfo.err = jpeg_std_error(&sk_err); sk_err.error_exit = sk_error_exit; if (setjmp(sk_err.fJmpBuf)) { @@ -606,17 +765,15 @@ protected: 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(); + uint8_t* oneRowP = (uint8_t*)oneRow.alloc(width * 3); - SkAutoLockColors alc(bm); - const SkPMColor* colors = alc.colors(); + const SkPMColor* colors = ctLocker.lockColors(bm); const void* srcRow = bm.getPixels(); - + while (cinfo.next_scanline < cinfo.image_height) { JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ diff --git a/skia/images/SkImageDecoder_libpng.cpp b/skia/images/SkImageDecoder_libpng.cpp index 4378ca9..862ebf1 100644 --- a/skia/images/SkImageDecoder_libpng.cpp +++ b/skia/images/SkImageDecoder_libpng.cpp @@ -1,6 +1,6 @@ /* libs/graphics/images/SkImageDecoder_libpng.cpp ** -** Copyright 2006, Google Inc. +** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -83,7 +83,9 @@ static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { } static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { +#if 0 SkDebugf("------ png error %s\n", msg); +#endif longjmp(png_jmpbuf(png_ptr), 1); } @@ -98,6 +100,24 @@ static bool pos_le(int value, int max) { return value > 0 && value <= max; } +static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) { + SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config); + + bool reallyHasAlpha = false; + + for (int y = bm->height() - 1; y >= 0; --y) { + SkPMColor* p = bm->getAddr32(0, y); + for (int x = bm->width() - 1; x >= 0; --x) { + if (match == *p) { + *p = 0; + reallyHasAlpha = true; + } + p += 1; + } + } + return reallyHasAlpha; +} + bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, SkBitmap::Config prefConfig, Mode mode) { // SkAutoTrace apr("SkPNGImageDecoder::onDecode"); @@ -153,9 +173,30 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL); + /* tell libpng to strip 16 bit/color files down to 8 bits/color */ + if (bit_depth == 16) { + png_set_strip_16(png_ptr); + } + /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). */ + if (bit_depth < 8) { + png_set_packing(png_ptr); + } + /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { + png_set_gray_1_2_4_to_8(png_ptr); + } + + /* Make a grayscale image into RGB. */ + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { + png_set_gray_to_rgb(png_ptr); + } + SkBitmap::Config config; bool hasAlpha = false; bool doDither = this->getDitherImage(); + SkPMColor theTranspColor = 0; // 0 tells us not to try to match // check for sBIT chunk data, in case we should disable dithering because // our data is not truely 8bits per component @@ -176,11 +217,41 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, if (color_type == PNG_COLOR_TYPE_PALETTE) { config = SkBitmap::kIndex8_Config; // defer sniffing for hasAlpha } else { - png_color_16p transColor; + png_color_16p transpColor = NULL; + int numTransp = 0; + + png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor); - png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &transColor); + bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS); - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) || + if (valid && numTransp == 1 && transpColor != NULL) { + /* Compute our transparent color, which we'll match against later. + We don't really handle 16bit components properly here, since we + do our compare *after* the values have been knocked down to 8bit + which means we will find more matches than we should. The real + fix seems to be to see the actual 16bit components, do the + compare, and then knock it down to 8bits ourselves. + */ + if (color_type & PNG_COLOR_MASK_COLOR) { + if (16 == bit_depth) { + theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8, + transpColor->green >> 8, transpColor->blue >> 8); + } else { + theTranspColor = SkPackARGB32(0xFF, transpColor->red, + transpColor->green, transpColor->blue); + } + } else { // gray + if (16 == bit_depth) { + theTranspColor = SkPackARGB32(0xFF, transpColor->gray >> 8, + transpColor->gray >> 8, transpColor->gray >> 8); + } else { + theTranspColor = SkPackARGB32(0xFF, transpColor->gray, + transpColor->gray, transpColor->gray); + } + } + } + + if (valid || PNG_COLOR_TYPE_RGB_ALPHA == color_type || PNG_COLOR_TYPE_GRAY_ALPHA == color_type) { hasAlpha = true; @@ -212,26 +283,6 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, // 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 @@ -295,7 +346,6 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, SkAutoUnref aur(colorTable); if (!this->allocPixelRef(decodedBitmap, colorTable)) { - delete colorTable; return false; } @@ -374,14 +424,19 @@ bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, } if (hasAlpha && !reallyHasAlpha) { +#if 0 SkDEBUGF(("Image doesn't really have alpha [%d %d]\n", origWidth, origHeight)); +#endif } } /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ png_read_end(png_ptr, info_ptr); + if (0 != theTranspColor) { + reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor); + } decodedBitmap->setIsOpaque(!reallyHasAlpha); return true; } @@ -488,8 +543,19 @@ static void transform_scanline_4444(const char* SK_RESTRICT src, int width, } } +static void transform_scanline_index8(const char* SK_RESTRICT src, int width, + char* SK_RESTRICT dst) { + memcpy(dst, src, width); +} + static transform_scanline_proc choose_proc(SkBitmap::Config config, bool hasAlpha) { + // we don't care about search on alpha if we're kIndex8, since only the + // colortable packing cares about that distinction, not the pixels + if (SkBitmap::kIndex8_Config == config) { + hasAlpha = false; // we store false in the table entries for kIndex8 + } + static const struct { SkBitmap::Config fConfig; bool fHasAlpha; @@ -500,6 +566,7 @@ static transform_scanline_proc choose_proc(SkBitmap::Config config, { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 }, { SkBitmap::kARGB_4444_Config, false, transform_scanline_444 }, { SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 }, + { SkBitmap::kIndex8_Config, false, transform_scanline_index8 }, }; for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) { @@ -511,6 +578,78 @@ static transform_scanline_proc choose_proc(SkBitmap::Config config, return NULL; } +// return the minimum legal bitdepth (by png standards) for this many colortable +// entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16, +// we can use fewer bits per in png +static int computeBitDepth(int colorCount) { +#if 0 + int bits = SkNextLog2(colorCount); + SkASSERT(bits >= 1 && bits <= 8); + // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8) + return SkNextPow2(bits); +#else + // for the moment, we don't know how to pack bitdepth < 8 + return 8; +#endif +} + +/* Pack palette[] with the corresponding colors, and if hasAlpha is true, also + pack trans[] and return the number of trans[] entries written. If hasAlpha + is false, the return value will always be 0. + + Note: this routine takes care of unpremultiplying the RGB values when we + have alpha in the colortable, since png doesn't support premul colors +*/ +static int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette, + png_byte* SK_RESTRICT trans, bool hasAlpha) { + SkAutoLockColors alc(ctable); + const SkPMColor* SK_RESTRICT colors = alc.colors(); + const int ctCount = ctable->count(); + int i, num_trans = 0; + + if (hasAlpha) { + /* first see if we have some number of fully opaque at the end of the + ctable. PNG allows num_trans < num_palette, but all of the trans + entries must come first in the palette. If I was smarter, I'd + reorder the indices and ctable so that all non-opaque colors came + first in the palette. But, since that would slow down the encode, + I'm leaving the indices and ctable order as is, and just looking + at the tail of the ctable for opaqueness. + */ + num_trans = ctCount; + for (i = ctCount - 1; i >= 0; --i) { + if (SkGetPackedA32(colors[i]) != 0xFF) { + break; + } + num_trans -= 1; + } + + const SkUnPreMultiply::Scale* SK_RESTRICT table = + SkUnPreMultiply::GetScaleTable(); + + for (i = 0; i < num_trans; i++) { + const SkPMColor c = *colors++; + const unsigned a = SkGetPackedA32(c); + const SkUnPreMultiply::Scale s = table[a]; + trans[i] = a; + palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c)); + palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c)); + palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c)); + } + // now fall out of this if-block to use common code for the trailing + // opaque entries + } + + // these (remaining) entries are opaque + for (i = num_trans; i < ctCount; i++) { + SkPMColor c = *colors++; + palette[i].red = SkGetPackedR32(c); + palette[i].green = SkGetPackedG32(c); + palette[i].blue = SkGetPackedB32(c); + } + return num_trans; +} + class SkPNGImageEncoder : public SkImageEncoder { protected: virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); @@ -521,20 +660,25 @@ bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, SkBitmap::Config config = bitmap.getConfig(); const bool hasAlpha = !bitmap.isOpaque(); + int colorType = PNG_COLOR_MASK_COLOR; + int bitDepth = 8; // default for color png_color_8 sig_bit; switch (config) { + case SkBitmap::kIndex8_Config: + colorType |= PNG_COLOR_MASK_PALETTE; + // fall through to the ARGB_8888 case case SkBitmap::kARGB_8888_Config: sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; - sig_bit.alpha = hasAlpha ? 8 : 0; + sig_bit.alpha = 8; break; case SkBitmap::kARGB_4444_Config: sig_bit.red = 4; sig_bit.green = 4; sig_bit.blue = 4; - sig_bit.alpha = hasAlpha ? 4 : 0; + sig_bit.alpha = 4; break; case SkBitmap::kRGB_565_Config: sig_bit.red = 5; @@ -543,16 +687,34 @@ bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, sig_bit.alpha = 0; break; default: - SkDEBUGF(("SkPNGImageEncoder::onEncode can't encode %d config\n", - config)); return false; } - + + if (hasAlpha) { + // don't specify alpha if we're a palette, even if our ctable has alpha + if (!(colorType & PNG_COLOR_MASK_PALETTE)) { + colorType |= PNG_COLOR_MASK_ALPHA; + } + } else { + sig_bit.alpha = 0; + } + SkAutoLockPixels alp(bitmap); - if (NULL == bitmap.getPixels()) { + // readyToDraw checks for pixels (and colortable if that is required) + if (!bitmap.readyToDraw()) { return false; } - + + // we must do this after we have locked the pixels + SkColorTable* ctable = bitmap.getColorTable(); + if (NULL != ctable) { + if (ctable->count() == 0) { + return false; + } + // check if we can store in fewer than 8 bits + bitDepth = computeBitDepth(ctable->count()); + } + png_structp png_ptr; png_infop info_ptr; @@ -587,12 +749,12 @@ bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, * 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_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(), + bitDepth, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); -#if 0 // need to support this some day <reed> +#if 0 // need to support this some day /* set the palette if there is one. REQUIRED for indexed-color images */ palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * png_sizeof (png_color)); @@ -620,15 +782,6 @@ bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, 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; @@ -640,4 +793,3 @@ SkImageEncoder* SkImageEncoder_PNG_Factory() { } #endif /* SK_SUPPORT_IMAGE_ENCODE */ - diff --git a/skia/images/SkImageDecoder_wbmp.cpp b/skia/images/SkImageDecoder_wbmp.cpp index d2fea75..9d188f6 100644 --- a/skia/images/SkImageDecoder_wbmp.cpp +++ b/skia/images/SkImageDecoder_wbmp.cpp @@ -1,5 +1,5 @@ /** -** Copyright 2006, Google Inc. +** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. diff --git a/skia/images/SkImageRef.cpp b/skia/images/SkImageRef.cpp index 05960a2..bd7a07d 100644 --- a/skia/images/SkImageRef.cpp +++ b/skia/images/SkImageRef.cpp @@ -58,7 +58,7 @@ bool SkImageRef::getInfo(SkBitmap* bitmap) { bool SkImageRef::onDecode(SkImageDecoder* codec, SkStream* stream, SkBitmap* bitmap, SkBitmap::Config config, SkImageDecoder::Mode mode) { - return codec->onDecode(stream, bitmap, config, mode); + return codec->decode(stream, bitmap, config, mode); } bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) { @@ -68,6 +68,14 @@ bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) { return false; } + /* As soon as we really know our config, we record it, so that on + subsequent calls to the codec, we are sure we will always get the same + result. + */ + if (SkBitmap::kNo_Config != fBitmap.config()) { + fConfig = fBitmap.config(); + } + if (NULL != fBitmap.getPixels() || (SkBitmap::kNo_Config != fBitmap.config() && SkImageDecoder::kDecodeBounds_Mode == mode)) { diff --git a/skia/images/SkMovie_gif.cpp b/skia/images/SkMovie_gif.cpp index 907ea82..c7db017 100644 --- a/skia/images/SkMovie_gif.cpp +++ b/skia/images/SkMovie_gif.cpp @@ -1,6 +1,6 @@ /* libs/graphics/images/SkImageDecoder_libgif.cpp ** -** Copyright 2006, Google Inc. +** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -143,7 +143,7 @@ bool SkGIFMovie::onGetBitmap(SkBitmap* bm) if (NULL == gif) return false; - // should we check for the Image cmap or the global (SColorMap) first? <reed> + // should we check for the Image cmap or the global (SColorMap) first? ColorMapObject* cmap = gif->SColorMap; if (cmap == NULL) cmap = gif->Image.ColorMap; @@ -154,15 +154,22 @@ bool SkGIFMovie::onGetBitmap(SkBitmap* bm) return false; } - SavedImage* gif_image = fCurrSavedImage; const int width = gif->SWidth; const int height = gif->SHeight; - SkBitmap::Config config = SkBitmap::kIndex8_Config; + if (width <= 0 || height <= 0) { + return false; + } + + SavedImage* gif_image = fCurrSavedImage; + SkBitmap::Config config = SkBitmap::kIndex8_Config; SkColorTable* colorTable = SkNEW_ARGS(SkColorTable, (cmap->ColorCount)); + SkAutoUnref aur(colorTable); + bm->setConfig(config, width, height, 0); - bm->allocPixels(colorTable); - colorTable->unref(); + if (!bm->allocPixels(colorTable)) { + return false; + } int transparent = -1; for (int i = 0; i < gif_image->ExtensionBlockCount; ++i) { diff --git a/skia/images/SkScaledBitmapSampler.cpp b/skia/images/SkScaledBitmapSampler.cpp index 529be61..15f4432 100644 --- a/skia/images/SkScaledBitmapSampler.cpp +++ b/skia/images/SkScaledBitmapSampler.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2007, Google Inc. + * Copyright 2007, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/skia/images/SkStream.cpp b/skia/images/SkStream.cpp index 5c1eebe..b199a1b 100644 --- a/skia/images/SkStream.cpp +++ b/skia/images/SkStream.cpp @@ -1,6 +1,6 @@ /* libs/graphics/images/SkStream.cpp ** -** Copyright 2006, Google Inc. +** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -248,7 +248,7 @@ size_t SkFILEStream::read(void* buffer, size_t size) return 0; } -//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// SkMemoryStream::SkMemoryStream() { @@ -343,19 +343,20 @@ size_t SkMemoryStream::seek(size_t offset) ///////////////////////////////////////////////////////////////////////////////////////////////////////// -SkBufferStream::SkBufferStream(SkStream& proxy, size_t bufferSize) +SkBufferStream::SkBufferStream(SkStream* proxy, size_t bufferSize) : fProxy(proxy) { - SkASSERT(&proxy != NULL); + SkASSERT(proxy != NULL); + proxy->ref(); this->init(NULL, bufferSize); } -SkBufferStream::SkBufferStream(SkStream& proxy, void* buffer, size_t bufferSize) +SkBufferStream::SkBufferStream(SkStream* proxy, void* buffer, size_t bufferSize) : fProxy(proxy) { - SkASSERT(&proxy != NULL); + SkASSERT(proxy != NULL); SkASSERT(buffer == NULL || bufferSize != 0); // init(addr, 0) makes no sense, we must know how big their buffer is - + proxy->ref(); this->init(buffer, bufferSize); } @@ -382,6 +383,7 @@ void SkBufferStream::init(void* buffer, size_t bufferSize) SkBufferStream::~SkBufferStream() { + fProxy->unref(); if (fWeOwnTheBuffer) sk_free(fBuffer); } @@ -389,31 +391,43 @@ SkBufferStream::~SkBufferStream() bool SkBufferStream::rewind() { fBufferOffset = fBufferSize = fOrigBufferSize; - return fProxy.rewind(); + return fProxy->rewind(); } const char* SkBufferStream::getFileName() { - return fProxy.getFileName(); + return fProxy->getFileName(); } #ifdef SK_DEBUG // #define SK_TRACE_BUFFERSTREAM #endif -size_t SkBufferStream::read(void* buffer, size_t size) -{ +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) { + return fProxy->read(buffer, size); // requesting total size + } + + if (0 == size) { + return 0; + } - if (buffer == NULL || size == 0) - { - fBufferOffset += size; - return fProxy.read(buffer, size); + // skip size bytes + if (NULL == buffer) { + size_t remaining = fBufferSize - fBufferOffset; + if (remaining >= size) { + fBufferOffset += size; + return size; + } + // if we get here, we are being asked to skip beyond our current buffer + // so reset our offset to force a read next time, and skip the diff + // in our proxy + fBufferOffset = fOrigBufferSize; + return remaining + fProxy->read(NULL, size - remaining); } size_t s = size; @@ -441,7 +455,7 @@ size_t SkBufferStream::read(void* buffer, size_t size) if (size < fBufferSize) // lets try to read more than the request { - s = fProxy.read(fBuffer, fBufferSize); + s = fProxy->read(fBuffer, fBufferSize); #ifdef SK_TRACE_BUFFERSTREAM SkDebugf(" read %d into fBuffer", s); #endif @@ -461,7 +475,7 @@ size_t SkBufferStream::read(void* buffer, size_t size) } else // just do a direct read { - actuallyRead += fProxy.read(buffer, size); + actuallyRead += fProxy->read(buffer, size); #ifdef SK_TRACE_BUFFERSTREAM SkDebugf(" direct read %d", size); #endif @@ -475,7 +489,7 @@ size_t SkBufferStream::read(void* buffer, size_t size) const void* SkBufferStream::getMemoryBase() { - return fProxy.getMemoryBase(); + return fProxy->getMemoryBase(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -729,20 +743,63 @@ bool SkDebugWStream::write(const void* buffer, size_t size) #include "SkRandom.h" -void SkWStream::UnitTest() -{ +#ifdef SK_SUPPORT_UNITTEST +#define MAX_SIZE (256 * 1024) + +static void random_fill(SkRandom& rand, void* buffer, size_t size) { + char* p = (char*)buffer; + char* stop = p + size; + while (p < stop) { + *p++ = (char)(rand.nextU() >> 8); + } +} + +static void test_buffer() { + SkRandom rand; + SkAutoMalloc am(MAX_SIZE * 2); + char* storage = (char*)am.get(); + char* storage2 = storage + MAX_SIZE; + + random_fill(rand, storage, MAX_SIZE); + + for (int sizeTimes = 0; sizeTimes < 100; sizeTimes++) { + int size = rand.nextU() % MAX_SIZE; + if (size == 0) { + size = MAX_SIZE; + } + for (int times = 0; times < 100; times++) { + int bufferSize = 1 + (rand.nextU() & 0xFFFF); + SkMemoryStream mstream(storage, size); + SkBufferStream bstream(&mstream, bufferSize); + + int bytesRead = 0; + while (bytesRead < size) { + int s = 17 + (rand.nextU() & 0xFFFF); + int ss = bstream.read(storage2, s); + SkASSERT(ss > 0 && ss <= s); + SkASSERT(bytesRead + ss <= size); + SkASSERT(memcmp(storage + bytesRead, storage2, ss) == 0); + bytesRead += ss; + } + SkASSERT(bytesRead == size); + } + } +} +#endif + +void SkStream::UnitTest() { #ifdef SK_SUPPORT_UNITTEST { static const char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; char copy[sizeof(s)]; SkRandom rand; - + for (int i = 0; i < 65; i++) { char* copyPtr = copy; SkMemoryStream mem(s, sizeof(s)); - SkBufferStream buff(mem, i); - + SkBufferStream buff(&mem, i); + do { copyPtr += buff.read(copyPtr, rand.nextU() & 15); } while (copyPtr < copy + sizeof(s)); @@ -750,6 +807,13 @@ void SkWStream::UnitTest() SkASSERT(memcmp(s, copy, sizeof(s)) == 0); } } + test_buffer(); +#endif +} + +void SkWStream::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST { SkDebugWStream s; diff --git a/skia/images/bmpdecoderhelper.cpp b/skia/images/bmpdecoderhelper.cpp index e390731..acabf44 100644 --- a/skia/images/bmpdecoderhelper.cpp +++ b/skia/images/bmpdecoderhelper.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2007, Google Inc. + * Copyright 2007, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/skia/images/bmpdecoderhelper.h b/skia/images/bmpdecoderhelper.h index 7da1326..07f0ae5 100644 --- a/skia/images/bmpdecoderhelper.h +++ b/skia/images/bmpdecoderhelper.h @@ -1,5 +1,5 @@ /* - * Copyright 2007, Google Inc. + * Copyright 2007, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,8 @@ #define IMAGE_CODEC_BMPDECODERHELPER_H__ /////////////////////////////////////////////////////////////////////////////// -// Some glue code that should be removed soon. +// this section is my current "glue" between google3 code and android. +// will be fixed soon #include "SkTypes.h" #include <limits.h> |