Files
Mozilla/mozilla/modules/libpr0n/decoders/mng/imgContainerMNG.cpp
tor%cs.brown.edu b80293dbc6 Bug 115994 - improved MNG decoder error handling. r=pavlov, sr=blizzard
git-svn-id: svn://10.0.0.236/trunk@112097 18797224-902f-48f8-a5cc-f745e15eee43
2002-01-14 21:00:12 +00:00

605 lines
16 KiB
C++

/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Tim Rowley.
* Portions created by Tim Rowley are
* Copyright (C) 2001 Tim Rowley. Rights Reserved.
*
* Contributor(s):
* Tim Rowley <tor@cs.brown.edu>
*/
#include "imgContainerMNG.h"
#include "nsIServiceManager.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "gfxIImageFrame.h"
#include "nsIImage.h"
#include "nsIInputStream.h"
#include "imgIDecoderObserver.h"
#include "nsMemory.h"
#include "prinrval.h"
static void il_mng_timeout_func(nsITimer *timer, void *data);
NS_IMPL_ISUPPORTS1(imgContainerMNG, imgIContainer)
//////////////////////////////////////////////////////////////////////////////
imgContainerMNG::imgContainerMNG() :
mObserver(0),
mDecoder(0),
mAnimationMode(0),
mFrozen(PR_FALSE)
{
NS_INIT_ISUPPORTS();
}
//////////////////////////////////////////////////////////////////////////////
imgContainerMNG::~imgContainerMNG()
{
if (mTimer) {
mTimer->Cancel();
mTimer = nsnull;
}
mng_display_freeze(mHandle);
mng_cleanup(&mHandle);
if (alpha)
nsMemory::Free(alpha);
if (image)
nsMemory::Free(image);
if (mBuffer)
nsMemory::Free(mBuffer);
mFrame = 0;
}
//****************************************************************************
/* void init (in nscoord aWidth, in nscoord aHeight, in imgIContainerObserver aObserver); */
NS_IMETHODIMP
imgContainerMNG::Init(nscoord aWidth,
nscoord aHeight,
imgIContainerObserver *aObserver)
{
if (aWidth <= 0 || aHeight <= 0) {
NS_WARNING("error - negative image size\n");
return NS_ERROR_FAILURE;
}
mSize.SizeTo(aWidth, aHeight);
mObserver = getter_AddRefs(NS_GetWeakReference(aObserver));
return NS_OK;
}
//****************************************************************************
/* readonly attribute gfx_format preferredAlphaChannelFormat; */
NS_IMETHODIMP
imgContainerMNG::GetPreferredAlphaChannelFormat(gfx_format *aFormat)
{
/* default.. platform's should probably overwrite this */
*aFormat = gfxIFormats::RGB_A8;
return NS_OK;
}
//****************************************************************************
/* readonly attribute nscoord width; */
NS_IMETHODIMP
imgContainerMNG::GetWidth(nscoord *aWidth)
{
*aWidth = mSize.width;
return NS_OK;
}
//****************************************************************************
/* readonly attribute nscoord height; */
NS_IMETHODIMP
imgContainerMNG::GetHeight(nscoord *aHeight)
{
*aHeight = mSize.height;
return NS_OK;
}
//****************************************************************************
/* readonly attribute gfxIImageFrame currentFrame; */
NS_IMETHODIMP
imgContainerMNG::GetCurrentFrame(gfxIImageFrame * *aCurrentFrame)
{
if (mFrame) {
*aCurrentFrame = mFrame;
NS_ADDREF(*aCurrentFrame);
return NS_OK;
} else {
*aCurrentFrame = 0;
return NS_ERROR_FAILURE;
}
}
//****************************************************************************
/* readonly attribute unsigned long numFrames; */
NS_IMETHODIMP
imgContainerMNG::GetNumFrames(PRUint32 *aNumFrames)
{
if (mFrame)
*aNumFrames = 1;
else
*aNumFrames = 0;
return NS_OK;
}
//****************************************************************************
/* gfxIImageFrame getFrameAt (in unsigned long index); */
NS_IMETHODIMP
imgContainerMNG::GetFrameAt(PRUint32 index, gfxIImageFrame **_retval)
{
return GetCurrentFrame(_retval);
}
//****************************************************************************
/* void appendFrame (in gfxIImageFrame item); */
NS_IMETHODIMP
imgContainerMNG::AppendFrame(gfxIImageFrame *item)
{
mFrame = item;
return NS_OK;
}
//****************************************************************************
/* void removeFrame (in gfxIImageFrame item); */
NS_IMETHODIMP
imgContainerMNG::RemoveFrame(gfxIImageFrame *item)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
//****************************************************************************
/* void endFrameDecode (in gfxIImageFrame item, in unsigned long timeout); */
NS_IMETHODIMP
imgContainerMNG::EndFrameDecode(PRUint32 aFrameNum, PRUint32 aTimeout)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
//****************************************************************************
/* void decodingComplete (); */
NS_IMETHODIMP
imgContainerMNG::DecodingComplete(void)
{
return NS_OK;
}
//****************************************************************************
/* nsIEnumerator enumerate (); */
NS_IMETHODIMP
imgContainerMNG::Enumerate(nsIEnumerator **_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void clear (); */
NS_IMETHODIMP
imgContainerMNG::Clear()
{
mFrame = 0;
return NS_OK;
}
//****************************************************************************
/* void startAnimation () */
NS_IMETHODIMP
imgContainerMNG::StartAnimation()
{
if (mFrozen) {
mFrozen = PR_FALSE;
il_mng_timeout_func(0, (void *)mHandle);
}
return NS_OK;
}
//****************************************************************************
/* void stopAnimation (); */
NS_IMETHODIMP
imgContainerMNG::StopAnimation()
{
if (!mTimer)
return NS_OK;
else {
mTimer->Cancel();
mTimer = nsnull;
mFrozen = PR_TRUE;
}
return NS_OK;
}
//****************************************************************************
/* attribute long loopCount; */
NS_IMETHODIMP
imgContainerMNG::GetLoopCount(PRInt32 *aLoopCount)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
imgContainerMNG::SetLoopCount(PRInt32 aLoopCount)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
//****************************************************************************
NS_IMETHODIMP
imgContainerMNG::GetAnimationMode(PRUint16 *aAnimationMode)
{
if (!aAnimationMode)
return NS_ERROR_NULL_POINTER;
*aAnimationMode = mAnimationMode;
return NS_OK;
}
NS_IMETHODIMP
imgContainerMNG::SetAnimationMode(PRUint16 aAnimationMode)
{
mAnimationMode = aAnimationMode;
return NS_OK;
}
// -------------------------------------------------------------------
// -------------------------------------------------------------------
// -------------------------------------------------------------------
#define EXTRACT_CONTAINER \
imgContainerMNG *container = (imgContainerMNG *)mng_get_userdata(handle)
//===========================================================
// Callbacks for libmng
//===========================================================
static mng_bool
il_mng_openstream(mng_handle handle)
{
return MNG_TRUE;
}
static mng_bool
il_mng_closestream(mng_handle handle)
{
return MNG_TRUE;
}
static mng_bool
il_mng_readdata(mng_handle handle, mng_ptr buf,
mng_uint32 size, mng_uint32 *stored)
{
EXTRACT_CONTAINER;
size = PR_MIN(size, container->mBufferEnd - container->mBufferPtr);
memcpy(buf, container->mBuffer + container->mBufferPtr, size);
container->mBufferPtr += size;
*stored = size;
return MNG_TRUE;
}
static mng_bool
il_mng_processheader(mng_handle handle, mng_uint32 width, mng_uint32 height)
{
EXTRACT_CONTAINER;
gfx_format format;
#if defined(XP_PC) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
if (mng_get_alphadepth(handle)) {
format = gfxIFormats::BGR_A8;
mng_set_canvasstyle(handle, MNG_CANVAS_RGB8_A8);
} else {
format = gfxIFormats::BGR;
mng_set_canvasstyle(handle, MNG_CANVAS_RGB8);
}
#else
if (mng_get_alphadepth(handle)) {
format = gfxIFormats::RGB_A8;
mng_set_canvasstyle(handle, MNG_CANVAS_RGB8_A8);
} else {
format = gfxIFormats::RGB;
mng_set_canvasstyle(handle, MNG_CANVAS_RGB8);
}
#endif
nsMNGDecoder* decoder = container->mDecoder;
if (decoder->mObserver)
decoder->mObserver->OnStartDecode(nsnull, nsnull);
if(decoder->mImageContainer)
decoder->mImageContainer->Init(width,
height,
decoder->mObserver);
if (decoder->mObserver)
decoder->mObserver->OnStartContainer(nsnull,
nsnull,
decoder->mImageContainer);
// initalize the frame and append it to the container
decoder->mImageFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2");
decoder->mImageFrame->Init(0, 0, width, height, format);
decoder->mImageContainer->AppendFrame(decoder->mImageFrame);
if (decoder->mObserver)
decoder->mObserver->OnStartFrame(nsnull, nsnull, decoder->mImageFrame);
container->mFrame->GetImageBytesPerRow(&container->mByteWidth);
container->mFrame->GetAlphaBytesPerRow(&container->mByteWidthAlpha);
if ((format!=gfxIFormats::RGB) && (format!=gfxIFormats::BGR)) {
container->alpha =
(unsigned char*)nsMemory::Alloc(container->mByteWidthAlpha*height);
memset(container->alpha, 0, container->mByteWidthAlpha*height);
} else
container->alpha = 0;
container->image =
(unsigned char*)nsMemory::Alloc(container->mByteWidth*height);
memset(container->image, 0, container->mByteWidth*height);
return MNG_TRUE;
}
static mng_ptr
il_mng_getcanvasline(mng_handle handle, mng_uint32 iLine)
{
EXTRACT_CONTAINER;
return container->image+container->mByteWidth*iLine;
}
static mng_ptr
il_mng_getalphaline(mng_handle handle, mng_uint32 iLine)
{
EXTRACT_CONTAINER;
return container->alpha+container->mByteWidthAlpha*iLine;
}
static mng_bool
il_mng_refresh(mng_handle handle,
mng_uint32 left, mng_uint32 top,
mng_uint32 width, mng_uint32 height)
{
EXTRACT_CONTAINER;
PRUint32 bpr, abpr;
container->mFrame->GetImageBytesPerRow(&bpr);
container->mFrame->GetAlphaBytesPerRow(&abpr);
// stupid Mac code that shouldn't be in the image decoders...
#if defined(XP_MAC) || defined(XP_MACOSX) || defined(XP_PC) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
PRInt32 iwidth;
container->mFrame->GetWidth(&iwidth);
PRUint8 *buf = (PRUint8 *)nsMemory::Alloc(bpr);
#endif
for (mng_uint32 y=top; y<top+height; y++) {
if (container->alpha)
container->mFrame->SetAlphaData(container->alpha +
y*container->mByteWidthAlpha,
container->mByteWidthAlpha,
abpr*y);
#if defined(XP_MAC) || defined(XP_MACOSX)
PRUint8 *cptr = buf;
PRUint8 *row = container->image+y*container->mByteWidth;
for (PRUint32 x=0; x<iwidth; x++) {
*cptr++ = 0;
*cptr++ = *row++;
*cptr++ = *row++;
*cptr++ = *row++;
}
container->mFrame->SetImageData(buf, bpr, bpr*y);
#elif defined(XP_PC) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
PRUint8 *cptr = buf;
PRUint8 *row = container->image+y*container->mByteWidth;
for (PRUint32 x=0; x<iwidth; x++) {
*cptr++ = row[2];
*cptr++ = row[1];
*cptr++ = row[0];
row += 3;
}
container->mFrame->SetImageData(buf, bpr, bpr*y);
#else
container->mFrame->SetImageData(container->image +
y*container->mByteWidth,
container->mByteWidth,
bpr*y);
#endif
}
#if defined(XP_MAC) || defined(XP_MACOSX) || defined(XP_PC) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
nsMemory::Free(buf);
#endif
nsRect r(left, top, width, height);
nsCOMPtr<imgIDecoderObserver>
ob(do_QueryReferent(container->mObserver));
if (ob)
ob->OnDataAvailable(nsnull, nsnull, container->mFrame, &r);
nsCOMPtr<imgIContainerObserver>
observer(do_QueryReferent(container->mObserver));
if (observer) {
nsRect dirtyRect;
container->mFrame->GetRect(dirtyRect);
// do notification to FE to draw this frame
observer->FrameChanged(container, nsnull, container->mFrame, &dirtyRect);
}
return MNG_TRUE;
}
static mng_uint32
il_mng_gettickcount(mng_handle handle)
{
return PR_IntervalToMilliseconds(PR_IntervalNow());
}
static void
il_mng_timeout_func(nsITimer *timer, void *data)
{
mng_handle handle = (mng_handle)data;
EXTRACT_CONTAINER;
if (container->mTimer) {
container->mTimer->Cancel();
container->mTimer = 0;
}
int ret = mng_display_resume(handle);
if (ret == MNG_NEEDMOREDATA)
container->mResumeNeeded = PR_TRUE;
else if ((ret != MNG_NOERROR) &&
(ret != MNG_NEEDTIMERWAIT) &&
(ret != MNG_NEEDSECTIONWAIT))
container->mErrorPending = PR_TRUE;
}
static mng_bool
il_mng_settimer(mng_handle handle, mng_uint32 msec)
{
EXTRACT_CONTAINER;
container->mTimer = do_CreateInstance("@mozilla.org/timer;1");
container->mTimer->Init(il_mng_timeout_func, (void *)handle, msec);
return MNG_TRUE;
}
static mng_ptr
il_mng_alloc(mng_size_t size)
{
void *ptr = nsMemory::Alloc(size);
memset(ptr, 0, size);
return ptr;
}
static void
il_mng_free(mng_ptr ptr, mng_size_t size)
{
nsMemory::Free(ptr);
}
/* -----------------------------
* Setup an mng_struct for decoding.
*/
void
imgContainerMNG::InitMNG(nsMNGDecoder *decoder)
{
mDecoder = decoder;
// init data members
image = alpha = 0;
mBufferPtr = mBufferEnd = 0;
mBuffer = 0;
mResumeNeeded = PR_FALSE;
mErrorPending = PR_FALSE;
mTimer = 0;
// pass mng container as user data
mHandle = mng_initialize(this, il_mng_alloc, il_mng_free, NULL);
////////////
// Gamma correction - gross hack, but it's what mozilla's PNG
// decoder does and nobody has complained yet (except
// for me, but the bug is in eternal limbo)
double LUT_exponent, CRT_exponent = 2.2, display_exponent;
/* set up gamma correction for Mac, Unix and (Win32 and everything else)
* using educated guesses for display-system exponents; do preferences
* later */
#if defined(XP_MAC) || defined(XP_MACOSX)
LUT_exponent = 1.8 / 2.61;
#elif defined(XP_UNIX)
# if defined(__sgi)
LUT_exponent = 1.0 / 1.7; /* typical default for SGI console */
# elif defined(NeXT)
LUT_exponent = 1.0 / 2.2; /* typical default for NeXT cube */
# else
LUT_exponent = 1.0; /* default for most other Unix workstations */
# endif
#else
LUT_exponent = 1.0; /* virtually all PCs and most other systems */
#endif
display_exponent = LUT_exponent * CRT_exponent;
mng_set_dfltimggamma(mHandle, 0.45455);
mng_set_displaygamma(mHandle, display_exponent);
////////////
mng_setcb_openstream(mHandle, il_mng_openstream);
mng_setcb_closestream(mHandle, il_mng_closestream);
mng_setcb_readdata(mHandle, il_mng_readdata);
mng_setcb_processheader(mHandle, il_mng_processheader);
mng_setcb_getcanvasline(mHandle, il_mng_getcanvasline);
mng_setcb_getalphaline(mHandle, il_mng_getalphaline);
mng_setcb_refresh(mHandle, il_mng_refresh);
mng_setcb_gettickcount(mHandle, il_mng_gettickcount);
mng_setcb_settimer(mHandle, il_mng_settimer);
mng_setcb_memalloc(mHandle, il_mng_alloc);
mng_setcb_memfree(mHandle, il_mng_free);
mng_set_suspensionmode(mHandle, MNG_TRUE);
int ret = mng_readdisplay(mHandle);
if (ret == MNG_NEEDMOREDATA)
mResumeNeeded = PR_TRUE;
else if ((ret != MNG_NOERROR) &&
(ret != MNG_NEEDTIMERWAIT) &&
(ret != MNG_NEEDSECTIONWAIT))
mErrorPending = PR_TRUE;
}
/* ----------------------------------------------------------
* Process data arriving from the stream for the MNG decoder.
*/
NS_IMETHODIMP
imgContainerMNG::WriteMNG(nsIInputStream *inStr,
PRInt32 count,
PRUint32 *_retval)
{
mBuffer = (PRUint8 *) nsMemory::Realloc(mBuffer, mBufferEnd+count);
inStr->Read((char *)mBuffer+mBufferEnd, count, _retval);
mBufferEnd += count;
if (mResumeNeeded) {
mResumeNeeded = PR_FALSE;
int ret = mng_display_resume(mHandle);
if (ret == MNG_NEEDMOREDATA)
mResumeNeeded = PR_TRUE;
else if ((ret != MNG_NOERROR) &&
(ret != MNG_NEEDTIMERWAIT) &&
(ret != MNG_NEEDSECTIONWAIT))
mErrorPending = PR_TRUE;
}
if (mErrorPending)
return NS_ERROR_FAILURE;
else
return NS_OK;
}