blob: 91777418b2b538b40d61830bc8f610156b30afac [file] [log] [blame]
#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;
}