зеркало из https://github.com/mozilla/pjs.git
bug 296818. discard uncompressed image data after a period of time. original patch from Federico Mena-Quintero <federico@ximian.com>. Changes from me. r=vlad
This commit is contained in:
Родитель
137c086687
Коммит
d070896f7a
|
@ -22,6 +22,7 @@
|
||||||
*
|
*
|
||||||
* Contributor(s):
|
* Contributor(s):
|
||||||
* Stuart Parmenter <stuart@mozilla.com>
|
* Stuart Parmenter <stuart@mozilla.com>
|
||||||
|
* Federico Mena-Quintero <federico@novell.com>
|
||||||
*
|
*
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
@ -63,8 +64,10 @@ NS_IMPL_ISUPPORTS1(nsJPEGDecoder, imgIDecoder)
|
||||||
|
|
||||||
#if defined(PR_LOGGING)
|
#if defined(PR_LOGGING)
|
||||||
PRLogModuleInfo *gJPEGlog = PR_NewLogModule("JPEGDecoder");
|
PRLogModuleInfo *gJPEGlog = PR_NewLogModule("JPEGDecoder");
|
||||||
|
static PRLogModuleInfo *gJPEGDecoderAccountingLog = PR_NewLogModule("JPEGDecoderAccounting");
|
||||||
#else
|
#else
|
||||||
#define gJPEGlog
|
#define gJPEGlog
|
||||||
|
#define gJPEGDecoderAccountingLog
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,6 +99,10 @@ nsJPEGDecoder::nsJPEGDecoder()
|
||||||
|
|
||||||
mInProfile = nsnull;
|
mInProfile = nsnull;
|
||||||
mTransform = nsnull;
|
mTransform = nsnull;
|
||||||
|
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("nsJPEGDecoder::nsJPEGDecoder: Creating JPEG decoder %p",
|
||||||
|
this));
|
||||||
}
|
}
|
||||||
|
|
||||||
nsJPEGDecoder::~nsJPEGDecoder()
|
nsJPEGDecoder::~nsJPEGDecoder()
|
||||||
|
@ -106,6 +113,10 @@ nsJPEGDecoder::~nsJPEGDecoder()
|
||||||
cmsDeleteTransform(mTransform);
|
cmsDeleteTransform(mTransform);
|
||||||
if (mInProfile)
|
if (mInProfile)
|
||||||
cmsCloseProfile(mInProfile);
|
cmsCloseProfile(mInProfile);
|
||||||
|
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("nsJPEGDecoder::~nsJPEGDecoder: Destroying JPEG decoder %p",
|
||||||
|
this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,6 +158,34 @@ NS_IMETHODIMP nsJPEGDecoder::Init(imgILoad *aLoad)
|
||||||
for (PRUint32 m = 0; m < 16; m++)
|
for (PRUint32 m = 0; m < 16; m++)
|
||||||
jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF);
|
jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Check if the request already has an image container.
|
||||||
|
* this is the case when multipart/x-mixed-replace is being downloaded
|
||||||
|
* if we already have one and it has the same width and height, reuse it.
|
||||||
|
* This is also the case when an existing container is reloading itself from
|
||||||
|
* us.
|
||||||
|
*
|
||||||
|
* If we have a mismatch in width/height for the container later on we will
|
||||||
|
* generate an error.
|
||||||
|
*/
|
||||||
|
mImageLoad->GetImage(getter_AddRefs(mImage));
|
||||||
|
|
||||||
|
if (!mImage) {
|
||||||
|
mImage = do_CreateInstance("@mozilla.org/image/container;1");
|
||||||
|
if (!mImage)
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
mImageLoad->SetImage(mImage);
|
||||||
|
nsresult result = mImage->SetDiscardable("image/jpeg");
|
||||||
|
if (NS_FAILED(result)) {
|
||||||
|
mState = JPEG_ERROR;
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
(" (could not set image container to discardable)"));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,11 +224,20 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
{
|
{
|
||||||
LOG_SCOPE_WITH_PARAM(gJPEGlog, "nsJPEGDecoder::WriteFrom", "count", count);
|
LOG_SCOPE_WITH_PARAM(gJPEGlog, "nsJPEGDecoder::WriteFrom", "count", count);
|
||||||
|
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("nsJPEGDecoder::WriteFrom(decoder = %p) {\n"
|
||||||
|
" image container %s; %u bytes to be added",
|
||||||
|
this,
|
||||||
|
mImage ? "exists" : "does not exist",
|
||||||
|
count));
|
||||||
|
|
||||||
if (inStr) {
|
if (inStr) {
|
||||||
if (!mBuffer) {
|
if (!mBuffer) {
|
||||||
mBuffer = (JOCTET *)PR_Malloc(count);
|
mBuffer = (JOCTET *)PR_Malloc(count);
|
||||||
if (!mBuffer) {
|
if (!mBuffer) {
|
||||||
mState = JPEG_ERROR;
|
mState = JPEG_ERROR;
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (out of memory allocating buffer)"));
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
mBufferSize = count;
|
mBufferSize = count;
|
||||||
|
@ -197,6 +245,8 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
JOCTET *buf = (JOCTET *)PR_Realloc(mBuffer, count);
|
JOCTET *buf = (JOCTET *)PR_Realloc(mBuffer, count);
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
mState = JPEG_ERROR;
|
mState = JPEG_ERROR;
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (out of memory resizing buffer)"));
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
mBuffer = buf;
|
mBuffer = buf;
|
||||||
|
@ -204,9 +254,29 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv = inStr->Read((char*)mBuffer, count, &mBufferLen);
|
nsresult rv = inStr->Read((char*)mBuffer, count, &mBufferLen);
|
||||||
|
NS_ASSERTION(NS_SUCCEEDED(rv), "nsJPEGDecoder::WriteFrom -- inStr->Read failed");
|
||||||
|
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("nsJPEGDecoder::WriteFrom(): decoder %p got %u bytes, read %u from the stream (buffer size %u)",
|
||||||
|
this,
|
||||||
|
count,
|
||||||
|
mBufferLen,
|
||||||
|
mBufferSize));
|
||||||
|
|
||||||
*_retval = mBufferLen;
|
*_retval = mBufferLen;
|
||||||
|
|
||||||
NS_ASSERTION(NS_SUCCEEDED(rv), "nsJPEGDecoder::WriteFrom -- inStr->Read failed");
|
nsresult result = mImage->AddRestoreData((char *) mBuffer, count);
|
||||||
|
|
||||||
|
if (NS_FAILED(result)) {
|
||||||
|
mState = JPEG_ERROR;
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (could not add restore data)"));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
(" added %u bytes to restore data",
|
||||||
|
count));
|
||||||
}
|
}
|
||||||
// else no input stream.. Flush() ?
|
// else no input stream.. Flush() ?
|
||||||
|
|
||||||
|
@ -217,11 +287,15 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
if (error_code == NS_ERROR_FAILURE) {
|
if (error_code == NS_ERROR_FAILURE) {
|
||||||
/* Error due to corrupt stream - return NS_OK so that libpr0n
|
/* Error due to corrupt stream - return NS_OK so that libpr0n
|
||||||
doesn't throw away a partial image load */
|
doesn't throw away a partial image load */
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (setjmp returned NS_ERROR_FAILURE)"));
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
} else {
|
} else {
|
||||||
/* Error due to reasons external to the stream (probably out of
|
/* Error due to reasons external to the stream (probably out of
|
||||||
memory) - let libpr0n attempt to clean up, even though
|
memory) - let libpr0n attempt to clean up, even though
|
||||||
mozilla is seconds away from falling flat on its face. */
|
mozilla is seconds away from falling flat on its face. */
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (setjmp returned an error)"));
|
||||||
return error_code;
|
return error_code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,8 +309,11 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_HEADER case");
|
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_HEADER case");
|
||||||
|
|
||||||
/* Step 3: read file parameters with jpeg_read_header() */
|
/* Step 3: read file parameters with jpeg_read_header() */
|
||||||
if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED)
|
if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) {
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (JPEG_SUSPENDED)"));
|
||||||
return NS_OK; /* I/O suspension */
|
return NS_OK; /* I/O suspension */
|
||||||
|
}
|
||||||
|
|
||||||
JOCTET *profile;
|
JOCTET *profile;
|
||||||
PRUint32 profileLength;
|
PRUint32 profileLength;
|
||||||
|
@ -278,6 +355,8 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mState = JPEG_ERROR;
|
mState = JPEG_ERROR;
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (unknown colorpsace (1))"));
|
||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,6 +380,8 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mState = JPEG_ERROR;
|
mState = JPEG_ERROR;
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (unknown colorpsace (2))"));
|
||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,6 +417,8 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mState = JPEG_ERROR;
|
mState = JPEG_ERROR;
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (unknown colorpsace (3))"));
|
||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -352,30 +435,21 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
|
|
||||||
mObserver->OnStartDecode(nsnull);
|
mObserver->OnStartDecode(nsnull);
|
||||||
|
|
||||||
/* Check if the request already has an image container.
|
/* verify that the width and height of the image are the same as
|
||||||
this is the case when multipart/x-mixed-replace is being downloaded
|
* the container we're about to put things in to.
|
||||||
if we already have one and it has the same width and height, reuse it.
|
* XXX it might not matter maybe we should just resize the image.
|
||||||
*/
|
*/
|
||||||
mImageLoad->GetImage(getter_AddRefs(mImage));
|
PRInt32 width, height;
|
||||||
if (mImage) {
|
mImage->GetWidth(&width);
|
||||||
PRInt32 width, height;
|
mImage->GetHeight(&height);
|
||||||
mImage->GetWidth(&width);
|
if (width == 0 && height == 0) {
|
||||||
mImage->GetHeight(&height);
|
mImage->Init(mInfo.image_width, mInfo.image_height, mObserver);
|
||||||
if ((width != (PRInt32)mInfo.image_width) ||
|
} else if ((width != (PRInt32)mInfo.image_width) || (height != (PRInt32)mInfo.image_height)) {
|
||||||
(height != (PRInt32)mInfo.image_height)) {
|
mState = JPEG_ERROR;
|
||||||
mImage = nsnull;
|
return NS_ERROR_UNEXPECTED;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mImage) {
|
mImage->Init(mInfo.image_width, mInfo.image_height, mObserver);
|
||||||
mImage = do_CreateInstance("@mozilla.org/image/container;1");
|
|
||||||
if (!mImage) {
|
|
||||||
mState = JPEG_ERROR;
|
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
mImageLoad->SetImage(mImage);
|
|
||||||
mImage->Init(mInfo.image_width, mInfo.image_height, mObserver);
|
|
||||||
}
|
|
||||||
|
|
||||||
mObserver->OnStartContainer(nsnull, mImage);
|
mObserver->OnStartContainer(nsnull, mImage);
|
||||||
|
|
||||||
|
@ -400,6 +474,8 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
mFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2");
|
mFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2");
|
||||||
if (!mFrame) {
|
if (!mFrame) {
|
||||||
mState = JPEG_ERROR;
|
mState = JPEG_ERROR;
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (could not create image frame)"));
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,11 +486,17 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
|
|
||||||
if (NS_FAILED(mFrame->Init(0, 0, mInfo.image_width, mInfo.image_height, format, 24))) {
|
if (NS_FAILED(mFrame->Init(0, 0, mInfo.image_width, mInfo.image_height, format, 24))) {
|
||||||
mState = JPEG_ERROR;
|
mState = JPEG_ERROR;
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (could not initialize image frame)"));
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
mImage->AppendFrame(mFrame);
|
mImage->AppendFrame(mFrame);
|
||||||
}
|
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
(" JPEGDecoderAccounting: nsJPEGDecoder::WriteFrom -- created image frame with %ux%u pixels",
|
||||||
|
mInfo.image_width, mInfo.image_height));
|
||||||
|
}
|
||||||
|
|
||||||
mObserver->OnStartFrame(nsnull, mFrame);
|
mObserver->OnStartFrame(nsnull, mFrame);
|
||||||
mState = JPEG_START_DECOMPRESS;
|
mState = JPEG_START_DECOMPRESS;
|
||||||
|
@ -435,8 +517,11 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
mInfo.do_block_smoothing = TRUE;
|
mInfo.do_block_smoothing = TRUE;
|
||||||
|
|
||||||
/* Step 5: Start decompressor */
|
/* Step 5: Start decompressor */
|
||||||
if (jpeg_start_decompress(&mInfo) == FALSE)
|
if (jpeg_start_decompress(&mInfo) == FALSE) {
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (I/O suspension after jpeg_start_decompress())"));
|
||||||
return NS_OK; /* I/O suspension */
|
return NS_OK; /* I/O suspension */
|
||||||
|
}
|
||||||
|
|
||||||
/* If this is a progressive JPEG ... */
|
/* If this is a progressive JPEG ... */
|
||||||
if (mInfo.buffered_image) {
|
if (mInfo.buffered_image) {
|
||||||
|
@ -452,8 +537,11 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
{
|
{
|
||||||
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- JPEG_DECOMPRESS_SEQUENTIAL case");
|
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- JPEG_DECOMPRESS_SEQUENTIAL case");
|
||||||
|
|
||||||
if (!OutputScanlines())
|
if (!OutputScanlines()) {
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (I/O suspension after OutputScanlines() - SEQUENTIAL)"));
|
||||||
return NS_OK; /* I/O suspension */
|
return NS_OK; /* I/O suspension */
|
||||||
|
}
|
||||||
|
|
||||||
/* If we've completed image output ... */
|
/* If we've completed image output ... */
|
||||||
NS_ASSERTION(mInfo.output_scanline == mInfo.output_height, "We didn't process all of the data!");
|
NS_ASSERTION(mInfo.output_scanline == mInfo.output_height, "We didn't process all of the data!");
|
||||||
|
@ -485,8 +573,11 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
(status != JPEG_REACHED_EOI))
|
(status != JPEG_REACHED_EOI))
|
||||||
scan--;
|
scan--;
|
||||||
|
|
||||||
if (!jpeg_start_output(&mInfo, scan))
|
if (!jpeg_start_output(&mInfo, scan)) {
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (I/O suspension after jpeg_start_output() - PROGRESSIVE)"));
|
||||||
return NS_OK; /* I/O suspension */
|
return NS_OK; /* I/O suspension */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mInfo.output_scanline == 0xffffff)
|
if (mInfo.output_scanline == 0xffffff)
|
||||||
|
@ -498,13 +589,18 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
jpeg_start_output() multiple times for the same scan */
|
jpeg_start_output() multiple times for the same scan */
|
||||||
mInfo.output_scanline = 0xffffff;
|
mInfo.output_scanline = 0xffffff;
|
||||||
}
|
}
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (I/O suspension after OutputScanlines() - PROGRESSIVE)"));
|
||||||
return NS_OK; /* I/O suspension */
|
return NS_OK; /* I/O suspension */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mInfo.output_scanline == mInfo.output_height)
|
if (mInfo.output_scanline == mInfo.output_height)
|
||||||
{
|
{
|
||||||
if (!jpeg_finish_output(&mInfo))
|
if (!jpeg_finish_output(&mInfo)) {
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (I/O suspension after jpeg_finish_output() - PROGRESSIVE)"));
|
||||||
return NS_OK; /* I/O suspension */
|
return NS_OK; /* I/O suspension */
|
||||||
|
}
|
||||||
|
|
||||||
if (jpeg_input_complete(&mInfo) &&
|
if (jpeg_input_complete(&mInfo) &&
|
||||||
(mInfo.input_scan_number == mInfo.output_scan_number))
|
(mInfo.input_scan_number == mInfo.output_scan_number))
|
||||||
|
@ -520,15 +616,28 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
|
|
||||||
case JPEG_DONE:
|
case JPEG_DONE:
|
||||||
{
|
{
|
||||||
|
nsresult result;
|
||||||
|
|
||||||
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_DONE case");
|
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_DONE case");
|
||||||
|
|
||||||
/* Step 7: Finish decompression */
|
/* Step 7: Finish decompression */
|
||||||
|
|
||||||
if (jpeg_finish_decompress(&mInfo) == FALSE)
|
if (jpeg_finish_decompress(&mInfo) == FALSE) {
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (I/O suspension after jpeg_finish_decompress() - DONE)"));
|
||||||
return NS_OK; /* I/O suspension */
|
return NS_OK; /* I/O suspension */
|
||||||
|
}
|
||||||
|
|
||||||
mState = JPEG_SINK_NON_JPEG_TRAILER;
|
mState = JPEG_SINK_NON_JPEG_TRAILER;
|
||||||
|
|
||||||
|
result = mImage->RestoreDataDone();
|
||||||
|
if (NS_FAILED (result)) {
|
||||||
|
mState = JPEG_ERROR;
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (could not mark image container with RestoreDataDone)"));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* we're done dude */
|
/* we're done dude */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -545,6 +654,8 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("} (end of function)"));
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,10 @@ public:
|
||||||
cmsHTRANSFORM mTransform;
|
cmsHTRANSFORM mTransform;
|
||||||
|
|
||||||
PRPackedBool mReading;
|
PRPackedBool mReading;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
nsresult AddToTmpAccumulateBuffer(JOCTET *src, PRUint32 len);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // nsJPEGDecoder_h__
|
#endif // nsJPEGDecoder_h__
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
* Contributor(s):
|
* Contributor(s):
|
||||||
* Stuart Parmenter <stuart@mozilla.com>
|
* Stuart Parmenter <stuart@mozilla.com>
|
||||||
* Andrew Smith
|
* Andrew Smith
|
||||||
|
* Federico Mena-Quintero <federico@novell.com>
|
||||||
*
|
*
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
@ -72,7 +73,8 @@ static void PNGAPI error_callback(png_structp png_ptr, png_const_charp error_msg
|
||||||
static void PNGAPI warning_callback(png_structp png_ptr, png_const_charp warning_msg);
|
static void PNGAPI warning_callback(png_structp png_ptr, png_const_charp warning_msg);
|
||||||
|
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
PRLogModuleInfo *gPNGLog = PR_NewLogModule("PNGDecoder");
|
static PRLogModuleInfo *gPNGLog = PR_NewLogModule("PNGDecoder");
|
||||||
|
static PRLogModuleInfo *gPNGDecoderAccountingLog = PR_NewLogModule("PNGDecoderAccounting");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS1(nsPNGDecoder, imgIDecoder)
|
NS_IMPL_ISUPPORTS1(nsPNGDecoder, imgIDecoder)
|
||||||
|
@ -120,6 +122,12 @@ void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
|
||||||
if (mObserver)
|
if (mObserver)
|
||||||
mObserver->OnStartFrame(nsnull, mFrame);
|
mObserver->OnStartFrame(nsnull, mFrame);
|
||||||
|
|
||||||
|
|
||||||
|
PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("PNGDecoderAccounting: nsPNGDecoder::CreateFrame -- created image frame with %dx%d pixels in container %p",
|
||||||
|
width, height,
|
||||||
|
mImage.get ()));
|
||||||
|
|
||||||
mFrameHasNoAlpha = PR_TRUE;
|
mFrameHasNoAlpha = PR_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +217,25 @@ NS_IMETHODIMP nsPNGDecoder::Init(imgILoad *aLoad)
|
||||||
png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this),
|
png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this),
|
||||||
info_callback, row_callback, end_callback);
|
info_callback, row_callback, end_callback);
|
||||||
|
|
||||||
|
|
||||||
|
/* The image container may already exist if it is reloading itself from us.
|
||||||
|
* Check that it has the same width/height; otherwise create a new container.
|
||||||
|
*/
|
||||||
|
mImageLoad->GetImage(getter_AddRefs(mImage));
|
||||||
|
if (!mImage) {
|
||||||
|
mImage = do_CreateInstance("@mozilla.org/image/container;1");
|
||||||
|
if (!mImage)
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
mImageLoad->SetImage(mImage);
|
||||||
|
if (NS_FAILED(mImage->SetDiscardable("image/png"))) {
|
||||||
|
PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("PNGDecoderAccounting: info_callback(): failed to set image container %p as discardable",
|
||||||
|
mImage.get()));
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,13 +245,28 @@ NS_IMETHODIMP nsPNGDecoder::Close()
|
||||||
if (mPNG)
|
if (mPNG)
|
||||||
png_destroy_read_struct(&mPNG, mInfo ? &mInfo : NULL, NULL);
|
png_destroy_read_struct(&mPNG, mInfo ? &mInfo : NULL, NULL);
|
||||||
|
|
||||||
|
if (mImage) { // mImage could be null in the case of an error
|
||||||
|
nsresult result = mImage->RestoreDataDone();
|
||||||
|
if (NS_FAILED(result)) {
|
||||||
|
PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("PNGDecoderAccounting: nsPNGDecoder::Close(): failure in RestoreDataDone() for image container %p",
|
||||||
|
mImage.get()));
|
||||||
|
|
||||||
|
mError = PR_TRUE;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("PNGDecoderAccounting: nsPNGDecoder::Close(): image container %p is now with RestoreDataDone",
|
||||||
|
mImage.get()));
|
||||||
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* void flush (); */
|
/* void flush (); */
|
||||||
NS_IMETHODIMP nsPNGDecoder::Flush()
|
NS_IMETHODIMP nsPNGDecoder::Flush()
|
||||||
{
|
{
|
||||||
return NS_ERROR_NOT_IMPLEMENTED;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -250,10 +292,24 @@ static NS_METHOD ReadDataOut(nsIInputStream* in,
|
||||||
*writeCount = 0;
|
*writeCount = 0;
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
png_process_data(decoder->mPNG, decoder->mInfo,
|
png_process_data(decoder->mPNG, decoder->mInfo,
|
||||||
reinterpret_cast<unsigned char *>(const_cast<char *>(fromRawSegment)), count);
|
reinterpret_cast<unsigned char *>(const_cast<char *>(fromRawSegment)), count);
|
||||||
|
|
||||||
|
nsresult result = decoder->mImage->AddRestoreData((char *) fromRawSegment, count);
|
||||||
|
if (NS_FAILED (result)) {
|
||||||
|
PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("PNGDecoderAccounting: ReadDataOut(): failed to add restore data to image container %p",
|
||||||
|
decoder->mImage.get()));
|
||||||
|
|
||||||
|
decoder->mError = PR_TRUE;
|
||||||
|
*writeCount = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("PNGDecoderAccounting: ReadDataOut(): Added restore data to image container %p",
|
||||||
|
decoder->mImage.get()));
|
||||||
|
|
||||||
*writeCount = count;
|
*writeCount = count;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -513,13 +569,18 @@ info_callback(png_structp png_ptr, png_infop info_ptr)
|
||||||
if (decoder->mObserver)
|
if (decoder->mObserver)
|
||||||
decoder->mObserver->OnStartDecode(nsnull);
|
decoder->mObserver->OnStartDecode(nsnull);
|
||||||
|
|
||||||
decoder->mImage = do_CreateInstance("@mozilla.org/image/container;1");
|
/* The image container may already exist if it is reloading itself from us.
|
||||||
if (!decoder->mImage)
|
* Check that it has the same width/height; otherwise create a new container.
|
||||||
longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_OUT_OF_MEMORY
|
*/
|
||||||
|
PRInt32 containerWidth, containerHeight;
|
||||||
decoder->mImageLoad->SetImage(decoder->mImage);
|
decoder->mImage->GetWidth(&containerWidth);
|
||||||
|
decoder->mImage->GetHeight(&containerHeight);
|
||||||
decoder->mImage->Init(width, height, decoder->mObserver);
|
if (containerWidth == 0 && containerHeight == 0) {
|
||||||
|
// the image hasn't been inited yet
|
||||||
|
decoder->mImage->Init(width, height, decoder->mObserver);
|
||||||
|
} else if (containerWidth != width || containerHeight != height) {
|
||||||
|
longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_UNEXPECTED
|
||||||
|
}
|
||||||
|
|
||||||
if (decoder->mObserver)
|
if (decoder->mObserver)
|
||||||
decoder->mObserver->OnStartContainer(nsnull, decoder->mImage);
|
decoder->mObserver->OnStartContainer(nsnull, decoder->mImage);
|
||||||
|
@ -761,7 +822,7 @@ end_callback(png_structp png_ptr, png_infop info_ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder->mImage->DecodingComplete();
|
decoder->mImage->DecodingComplete();
|
||||||
|
|
||||||
if (decoder->mObserver) {
|
if (decoder->mObserver) {
|
||||||
if (!(decoder->apngFlags & FRAME_HIDDEN))
|
if (!(decoder->apngFlags & FRAME_HIDDEN))
|
||||||
decoder->mObserver->OnStopFrame(nsnull, decoder->mFrame);
|
decoder->mObserver->OnStopFrame(nsnull, decoder->mFrame);
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
*
|
*
|
||||||
* Contributor(s):
|
* Contributor(s):
|
||||||
* Stuart Parmenter <pavlov@netscape.com>
|
* Stuart Parmenter <pavlov@netscape.com>
|
||||||
|
* Federico Mena-Quintero <federico@novell.com>
|
||||||
*
|
*
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
@ -144,4 +145,10 @@ interface imgIContainer : nsISupports
|
||||||
* @note -1 means forever.
|
* @note -1 means forever.
|
||||||
*/
|
*/
|
||||||
attribute long loopCount;
|
attribute long loopCount;
|
||||||
|
|
||||||
|
/* Methods to discard uncompressed images and restore them again */
|
||||||
|
[noscript] void setDiscardable(in string aMimeType);
|
||||||
|
[noscript] void addRestoreData([array, size_is(aCount), const] in char data,
|
||||||
|
in unsigned long aCount);
|
||||||
|
[noscript] void restoreDataDone();
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
* Asko Tontti <atontti@cc.hut.fi>
|
* Asko Tontti <atontti@cc.hut.fi>
|
||||||
* Arron Mogge <paper@animecity.nu>
|
* Arron Mogge <paper@animecity.nu>
|
||||||
* Andrew Smith
|
* Andrew Smith
|
||||||
|
* Federico Mena-Quintero <federico@novell.com>
|
||||||
*
|
*
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
@ -42,23 +43,47 @@
|
||||||
|
|
||||||
#include "nsComponentManagerUtils.h"
|
#include "nsComponentManagerUtils.h"
|
||||||
#include "imgIContainerObserver.h"
|
#include "imgIContainerObserver.h"
|
||||||
|
#include "ImageErrors.h"
|
||||||
#include "nsIImage.h"
|
#include "nsIImage.h"
|
||||||
|
#include "imgILoad.h"
|
||||||
|
#include "imgIDecoder.h"
|
||||||
|
#include "imgIDecoderObserver.h"
|
||||||
#include "imgContainer.h"
|
#include "imgContainer.h"
|
||||||
#include "nsIInterfaceRequestor.h"
|
#include "nsIInterfaceRequestor.h"
|
||||||
#include "nsIInterfaceRequestorUtils.h"
|
#include "nsIInterfaceRequestorUtils.h"
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
|
#include "nsStringStream.h"
|
||||||
|
#include "prmem.h"
|
||||||
|
#include "prlog.h"
|
||||||
|
#include "prenv.h"
|
||||||
|
|
||||||
#include "gfxContext.h"
|
#include "gfxContext.h"
|
||||||
|
|
||||||
|
/* Accounting for compressed data */
|
||||||
|
#if defined(PR_LOGGING)
|
||||||
|
static PRLogModuleInfo *gCompressedImageAccountingLog = PR_NewLogModule ("CompressedImageAccounting");
|
||||||
|
#else
|
||||||
|
#define gCompressedImageAccountingLog
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int num_containers_with_discardable_data;
|
||||||
|
static PRInt64 num_compressed_image_bytes;
|
||||||
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS3(imgContainer, imgIContainer, nsITimerCallback, nsIProperties)
|
NS_IMPL_ISUPPORTS3(imgContainer, imgIContainer, nsITimerCallback, nsIProperties)
|
||||||
|
|
||||||
//******************************************************************************
|
//******************************************************************************
|
||||||
imgContainer::imgContainer() :
|
imgContainer::imgContainer() :
|
||||||
mSize(0,0),
|
mSize(0,0),
|
||||||
|
mNumFrames(0),
|
||||||
mAnim(nsnull),
|
mAnim(nsnull),
|
||||||
mAnimationMode(kNormalAnimMode),
|
mAnimationMode(kNormalAnimMode),
|
||||||
mLoopCount(-1),
|
mLoopCount(-1),
|
||||||
mObserver(nsnull)
|
mObserver(nsnull),
|
||||||
|
mDiscardable(PR_FALSE),
|
||||||
|
mDiscarded(PR_FALSE),
|
||||||
|
mRestoreDataDone(PR_FALSE),
|
||||||
|
mDiscardTimer(nsnull)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +92,23 @@ imgContainer::~imgContainer()
|
||||||
{
|
{
|
||||||
if (mAnim)
|
if (mAnim)
|
||||||
delete mAnim;
|
delete mAnim;
|
||||||
|
|
||||||
|
if (!mRestoreData.IsEmpty()) {
|
||||||
|
num_containers_with_discardable_data--;
|
||||||
|
num_compressed_image_bytes -= mRestoreData.Length();
|
||||||
|
|
||||||
|
PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("CompressedImageAccounting: destroying imgContainer %p. "
|
||||||
|
"Compressed containers: %d, Compressed data bytes: %lld",
|
||||||
|
this,
|
||||||
|
num_containers_with_discardable_data,
|
||||||
|
num_compressed_image_bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDiscardTimer) {
|
||||||
|
mDiscardTimer->Cancel ();
|
||||||
|
mDiscardTimer = nsnull;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//******************************************************************************
|
//******************************************************************************
|
||||||
|
@ -124,15 +166,53 @@ NS_IMETHODIMP imgContainer::GetHeight(PRInt32 *aHeight)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult imgContainer::GetCurrentFrameNoRef(gfxIImageFrame **aFrame)
|
||||||
|
{
|
||||||
|
nsresult result;
|
||||||
|
|
||||||
|
result = RestoreDiscardedData();
|
||||||
|
if (NS_FAILED (result)) {
|
||||||
|
PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("CompressedImageAccounting: imgContainer::GetCurrentFrameNoRef(): error %d in RestoreDiscardedData(); "
|
||||||
|
"returning a null frame from imgContainer %p",
|
||||||
|
result,
|
||||||
|
this));
|
||||||
|
|
||||||
|
*aFrame = nsnull;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mAnim)
|
||||||
|
*aFrame = mFrames.SafeObjectAt(0);
|
||||||
|
else if (mAnim->lastCompositedFrameIndex == mAnim->currentAnimationFrameIndex)
|
||||||
|
*aFrame = mAnim->compositingFrame;
|
||||||
|
else
|
||||||
|
*aFrame = mFrames.SafeObjectAt(mAnim->currentAnimationFrameIndex);
|
||||||
|
|
||||||
|
if (!*aFrame)
|
||||||
|
PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("CompressedImageAccounting: imgContainer::GetCurrentFrameNoRef(): returning null frame from imgContainer %p "
|
||||||
|
"(no errors when restoring data)",
|
||||||
|
this));
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
//******************************************************************************
|
//******************************************************************************
|
||||||
/* readonly attribute gfxIImageFrame currentFrame; */
|
/* readonly attribute gfxIImageFrame currentFrame; */
|
||||||
NS_IMETHODIMP imgContainer::GetCurrentFrame(gfxIImageFrame **aCurrentFrame)
|
NS_IMETHODIMP imgContainer::GetCurrentFrame(gfxIImageFrame **aCurrentFrame)
|
||||||
{
|
{
|
||||||
|
nsresult result;
|
||||||
|
|
||||||
NS_ASSERTION(aCurrentFrame, "imgContainer::GetCurrentFrame; Invalid Arg");
|
NS_ASSERTION(aCurrentFrame, "imgContainer::GetCurrentFrame; Invalid Arg");
|
||||||
if (!aCurrentFrame)
|
if (!aCurrentFrame)
|
||||||
return NS_ERROR_INVALID_POINTER;
|
return NS_ERROR_INVALID_POINTER;
|
||||||
|
|
||||||
if (!(*aCurrentFrame = inlinedGetCurrentFrame()))
|
result = GetCurrentFrameNoRef (aCurrentFrame);
|
||||||
|
if (NS_FAILED (result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (!*aCurrentFrame)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
NS_ADDREF(*aCurrentFrame);
|
NS_ADDREF(*aCurrentFrame);
|
||||||
|
@ -148,7 +228,7 @@ NS_IMETHODIMP imgContainer::GetNumFrames(PRUint32 *aNumFrames)
|
||||||
if (!aNumFrames)
|
if (!aNumFrames)
|
||||||
return NS_ERROR_INVALID_ARG;
|
return NS_ERROR_INVALID_ARG;
|
||||||
|
|
||||||
*aNumFrames = mFrames.Count();
|
*aNumFrames = mNumFrames;
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -157,16 +237,24 @@ NS_IMETHODIMP imgContainer::GetNumFrames(PRUint32 *aNumFrames)
|
||||||
/* gfxIImageFrame getFrameAt (in unsigned long index); */
|
/* gfxIImageFrame getFrameAt (in unsigned long index); */
|
||||||
NS_IMETHODIMP imgContainer::GetFrameAt(PRUint32 index, gfxIImageFrame **_retval)
|
NS_IMETHODIMP imgContainer::GetFrameAt(PRUint32 index, gfxIImageFrame **_retval)
|
||||||
{
|
{
|
||||||
|
nsresult result;
|
||||||
|
|
||||||
NS_ASSERTION(_retval, "imgContainer::GetFrameAt; Invalid Arg");
|
NS_ASSERTION(_retval, "imgContainer::GetFrameAt; Invalid Arg");
|
||||||
if (!_retval)
|
if (!_retval)
|
||||||
return NS_ERROR_INVALID_POINTER;
|
return NS_ERROR_INVALID_POINTER;
|
||||||
|
|
||||||
if (!mFrames.Count()) {
|
if (mNumFrames == 0) {
|
||||||
*_retval = nsnull;
|
*_retval = nsnull;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_ENSURE_ARG(index < static_cast<PRUint32>(mFrames.Count()));
|
NS_ENSURE_ARG((int) index < mNumFrames);
|
||||||
|
|
||||||
|
result = RestoreDiscardedData ();
|
||||||
|
if (NS_FAILED (result)) {
|
||||||
|
*_retval = nsnull;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(*_retval = mFrames[index]))
|
if (!(*_retval = mFrames[index]))
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
@ -183,16 +271,17 @@ NS_IMETHODIMP imgContainer::AppendFrame(gfxIImageFrame *item)
|
||||||
NS_ASSERTION(item, "imgContainer::AppendFrame; Invalid Arg");
|
NS_ASSERTION(item, "imgContainer::AppendFrame; Invalid Arg");
|
||||||
if (!item)
|
if (!item)
|
||||||
return NS_ERROR_INVALID_ARG;
|
return NS_ERROR_INVALID_ARG;
|
||||||
|
|
||||||
PRInt32 numFrames = mFrames.Count();
|
if (mFrames.Count () == 0) {
|
||||||
|
|
||||||
if (numFrames == 0) {
|
|
||||||
// This may not be an animated image, don't do all the animation stuff.
|
// This may not be an animated image, don't do all the animation stuff.
|
||||||
mFrames.AppendObject(item);
|
mFrames.AppendObject(item);
|
||||||
|
|
||||||
|
mNumFrames++;
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numFrames == 1) {
|
if (mFrames.Count () == 1) {
|
||||||
// Now that we got a second frame, initialize animation stuff.
|
// Now that we got a second frame, initialize animation stuff.
|
||||||
if (!ensureAnimExists())
|
if (!ensureAnimExists())
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
@ -216,11 +305,13 @@ NS_IMETHODIMP imgContainer::AppendFrame(gfxIImageFrame *item)
|
||||||
itemRect);
|
itemRect);
|
||||||
|
|
||||||
mFrames.AppendObject(item);
|
mFrames.AppendObject(item);
|
||||||
|
|
||||||
|
mNumFrames++;
|
||||||
|
|
||||||
// If this is our second frame, start the animation.
|
// If this is our second frame, start the animation.
|
||||||
// Must be called after AppendObject because StartAnimation checks for > 1
|
// Must be called after AppendObject because StartAnimation checks for > 1
|
||||||
// frame
|
// frame
|
||||||
if (numFrames == 1)
|
if (mFrames.Count () == 1)
|
||||||
StartAnimation();
|
StartAnimation();
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -230,6 +321,7 @@ NS_IMETHODIMP imgContainer::AppendFrame(gfxIImageFrame *item)
|
||||||
/* void removeFrame (in gfxIImageFrame item); */
|
/* void removeFrame (in gfxIImageFrame item); */
|
||||||
NS_IMETHODIMP imgContainer::RemoveFrame(gfxIImageFrame *item)
|
NS_IMETHODIMP imgContainer::RemoveFrame(gfxIImageFrame *item)
|
||||||
{
|
{
|
||||||
|
/* Remember to decrement mNumFrames if you implement this */
|
||||||
return NS_ERROR_NOT_IMPLEMENTED;
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +345,7 @@ NS_IMETHODIMP imgContainer::DecodingComplete(void)
|
||||||
mAnim->doneDecoding = PR_TRUE;
|
mAnim->doneDecoding = PR_TRUE;
|
||||||
// If there's only 1 frame, optimize it.
|
// If there's only 1 frame, optimize it.
|
||||||
// Optimizing animated images is not supported
|
// Optimizing animated images is not supported
|
||||||
if (mFrames.Count() == 1)
|
if (mNumFrames == 1)
|
||||||
mFrames[0]->SetMutable(PR_FALSE);
|
mFrames[0]->SetMutable(PR_FALSE);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -292,11 +384,11 @@ NS_IMETHODIMP imgContainer::SetAnimationMode(PRUint16 aAnimationMode)
|
||||||
break;
|
break;
|
||||||
case kNormalAnimMode:
|
case kNormalAnimMode:
|
||||||
if (mLoopCount != 0 ||
|
if (mLoopCount != 0 ||
|
||||||
(mAnim && (mAnim->currentAnimationFrameIndex + 1 < mFrames.Count())))
|
(mAnim && (mAnim->currentAnimationFrameIndex + 1 < mNumFrames)))
|
||||||
StartAnimation();
|
StartAnimation();
|
||||||
break;
|
break;
|
||||||
case kLoopOnceAnimMode:
|
case kLoopOnceAnimMode:
|
||||||
if (mAnim && (mAnim->currentAnimationFrameIndex + 1 < mFrames.Count()))
|
if (mAnim && (mAnim->currentAnimationFrameIndex + 1 < mNumFrames))
|
||||||
StartAnimation();
|
StartAnimation();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -312,12 +404,18 @@ NS_IMETHODIMP imgContainer::StartAnimation()
|
||||||
(mAnim && (mAnim->timer || mAnim->animating)))
|
(mAnim && (mAnim->timer || mAnim->animating)))
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
if (mFrames.Count() > 1) {
|
if (mNumFrames > 1) {
|
||||||
if (!ensureAnimExists())
|
if (!ensureAnimExists())
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
PRInt32 timeout;
|
PRInt32 timeout;
|
||||||
gfxIImageFrame *currentFrame = inlinedGetCurrentFrame();
|
nsresult result;
|
||||||
|
gfxIImageFrame *currentFrame;
|
||||||
|
|
||||||
|
result = GetCurrentFrameNoRef (¤tFrame);
|
||||||
|
if (NS_FAILED (result))
|
||||||
|
return result;
|
||||||
|
|
||||||
if (currentFrame) {
|
if (currentFrame) {
|
||||||
currentFrame->GetTimeout(&timeout);
|
currentFrame->GetTimeout(&timeout);
|
||||||
if (timeout <= 0) // -1 means display this frame forever
|
if (timeout <= 0) // -1 means display this frame forever
|
||||||
|
@ -376,8 +474,15 @@ NS_IMETHODIMP imgContainer::ResetAnimation()
|
||||||
mAnim->currentAnimationFrameIndex = 0;
|
mAnim->currentAnimationFrameIndex = 0;
|
||||||
// Update display
|
// Update display
|
||||||
nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
|
nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
|
||||||
if (observer)
|
if (observer) {
|
||||||
|
nsresult result;
|
||||||
|
|
||||||
|
result = RestoreDiscardedData ();
|
||||||
|
if (NS_FAILED (result))
|
||||||
|
return result;
|
||||||
|
|
||||||
observer->FrameChanged(this, mFrames[0], &(mAnim->firstFrameRefreshArea));
|
observer->FrameChanged(this, mFrames[0], &(mAnim->firstFrameRefreshArea));
|
||||||
|
}
|
||||||
|
|
||||||
if (oldAnimating)
|
if (oldAnimating)
|
||||||
return StartAnimation();
|
return StartAnimation();
|
||||||
|
@ -411,10 +516,150 @@ NS_IMETHODIMP imgContainer::SetLoopCount(PRInt32 aLoopCount)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PRBool
|
||||||
|
DiscardingEnabled(void)
|
||||||
|
{
|
||||||
|
static PRBool inited;
|
||||||
|
static PRBool enabled;
|
||||||
|
|
||||||
|
if (!inited) {
|
||||||
|
inited = PR_TRUE;
|
||||||
|
|
||||||
|
enabled = (PR_GetEnv("MOZ_DISABLE_IMAGE_DISCARD") == nsnull);
|
||||||
|
}
|
||||||
|
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
/* void setDiscardable(in string mime_type); */
|
||||||
|
NS_IMETHODIMP imgContainer::SetDiscardable(const char* aMimeType)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(aMimeType, "imgContainer::SetDiscardable() called with null aMimeType");
|
||||||
|
|
||||||
|
if (!DiscardingEnabled())
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
if (mDiscardable) {
|
||||||
|
NS_WARNING ("imgContainer::SetDiscardable(): cannot change an imgContainer which is already discardable");
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDiscardableMimeType.Assign(aMimeType);
|
||||||
|
mDiscardable = PR_TRUE;
|
||||||
|
|
||||||
|
num_containers_with_discardable_data++;
|
||||||
|
PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("CompressedImageAccounting: Making imgContainer %p (%s) discardable. "
|
||||||
|
"Compressed containers: %d, Compressed data bytes: %lld",
|
||||||
|
this,
|
||||||
|
aMimeType,
|
||||||
|
num_containers_with_discardable_data,
|
||||||
|
num_compressed_image_bytes));
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
/* void addRestoreData(in nsIInputStream aInputStream, in unsigned long aCount); */
|
||||||
|
NS_IMETHODIMP imgContainer::AddRestoreData(const char *aBuffer, PRUint32 aCount)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(aBuffer, "imgContainer::AddRestoreData() called with null aBuffer");
|
||||||
|
|
||||||
|
if (!DiscardingEnabled ())
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
if (!mDiscardable) {
|
||||||
|
NS_WARNING ("imgContainer::AddRestoreData() can only be called if SetDiscardable is called first");
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mRestoreDataDone) {
|
||||||
|
/* We are being called from the decoder while the data is being restored
|
||||||
|
* (i.e. we were fully loaded once, then we discarded the image data, then
|
||||||
|
* we are being restored). We don't want to save the compressed data again,
|
||||||
|
* since we already have it.
|
||||||
|
*/
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mRestoreData.AppendElements(aBuffer, aCount))
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
num_compressed_image_bytes += aCount;
|
||||||
|
|
||||||
|
PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("CompressedImageAccounting: Added compressed data to imgContainer %p (%s). "
|
||||||
|
"Compressed containers: %d, Compressed data bytes: %lld",
|
||||||
|
this,
|
||||||
|
mDiscardableMimeType.get(),
|
||||||
|
num_containers_with_discardable_data,
|
||||||
|
num_compressed_image_bytes));
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note! buf must be declared as char buf[9]; */
|
||||||
|
// just used for logging and hashing the header
|
||||||
|
static void
|
||||||
|
get_header_str (char *buf, char *data, PRSize data_len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int n;
|
||||||
|
static char hex[] = "0123456789abcdef";
|
||||||
|
|
||||||
|
n = data_len < 4 ? data_len : 4;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
buf[i * 2] = hex[(data[i] >> 4) & 0x0f];
|
||||||
|
buf[i * 2 + 1] = hex[data[i] & 0x0f];
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[i * 2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
/* void restoreDataDone(); */
|
||||||
|
NS_IMETHODIMP imgContainer::RestoreDataDone (void)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!DiscardingEnabled ())
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
if (mRestoreDataDone)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
mRestoreData.Compact();
|
||||||
|
|
||||||
|
mRestoreDataDone = PR_TRUE;
|
||||||
|
|
||||||
|
if (PR_LOG_TEST(gCompressedImageAccountingLog, PR_LOG_DEBUG)) {
|
||||||
|
char buf[9];
|
||||||
|
get_header_str(buf, mRestoreData.Elements(), mRestoreData.Length());
|
||||||
|
PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("CompressedImageAccounting: imgContainer::RestoreDataDone() - data is done for container %p (%s), %d real frames (cached as %d frames) - header %p is 0x%s (length %d)",
|
||||||
|
this,
|
||||||
|
mDiscardableMimeType.get(),
|
||||||
|
mFrames.Count (),
|
||||||
|
mNumFrames,
|
||||||
|
mRestoreData.Elements(),
|
||||||
|
buf,
|
||||||
|
mRestoreData.Length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResetDiscardTimer();
|
||||||
|
}
|
||||||
|
|
||||||
//******************************************************************************
|
//******************************************************************************
|
||||||
/* void notify(in nsITimer timer); */
|
/* void notify(in nsITimer timer); */
|
||||||
NS_IMETHODIMP imgContainer::Notify(nsITimer *timer)
|
NS_IMETHODIMP imgContainer::Notify(nsITimer *timer)
|
||||||
{
|
{
|
||||||
|
nsresult result;
|
||||||
|
|
||||||
|
result = RestoreDiscardedData();
|
||||||
|
if (NS_FAILED (result))
|
||||||
|
return result;
|
||||||
|
|
||||||
// This should never happen since the timer is only set up in StartAnimation()
|
// This should never happen since the timer is only set up in StartAnimation()
|
||||||
// after mAnim is checked to exist.
|
// after mAnim is checked to exist.
|
||||||
NS_ASSERTION(mAnim, "imgContainer::Notify() called but mAnim is null");
|
NS_ASSERTION(mAnim, "imgContainer::Notify() called but mAnim is null");
|
||||||
|
@ -433,8 +678,7 @@ NS_IMETHODIMP imgContainer::Notify(nsITimer *timer)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
PRInt32 numFrames = mFrames.Count();
|
if (mNumFrames == 0)
|
||||||
if (!numFrames)
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
gfxIImageFrame *nextFrame = nsnull;
|
gfxIImageFrame *nextFrame = nsnull;
|
||||||
|
@ -448,7 +692,7 @@ NS_IMETHODIMP imgContainer::Notify(nsITimer *timer)
|
||||||
// finished decoding (see EndFrameDecode)
|
// finished decoding (see EndFrameDecode)
|
||||||
if (mAnim->doneDecoding ||
|
if (mAnim->doneDecoding ||
|
||||||
(nextFrameIndex < mAnim->currentDecodingFrameIndex)) {
|
(nextFrameIndex < mAnim->currentDecodingFrameIndex)) {
|
||||||
if (numFrames == nextFrameIndex) {
|
if (mNumFrames == nextFrameIndex) {
|
||||||
// End of Animation
|
// End of Animation
|
||||||
|
|
||||||
// If animation mode is "loop once", it's time to stop animating
|
// If animation mode is "loop once", it's time to stop animating
|
||||||
|
@ -906,3 +1150,308 @@ NS_IMETHODIMP imgContainer::GetKeys(PRUint32 *count, char ***keys)
|
||||||
}
|
}
|
||||||
return mProperties->GetKeys(count, keys);
|
return mProperties->GetKeys(count, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_discard_timer_ms (void)
|
||||||
|
{
|
||||||
|
/* FIXME: don't hardcode this */
|
||||||
|
return 45000; /* 45 seconds */
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
imgContainer::sDiscardTimerCallback(nsITimer *aTimer, void *aClosure)
|
||||||
|
{
|
||||||
|
imgContainer *self = (imgContainer *) aClosure;
|
||||||
|
int old_frame_count;
|
||||||
|
|
||||||
|
NS_ASSERTION(aTimer == self->mDiscardTimer,
|
||||||
|
"imgContainer::DiscardTimerCallback() got a callback for an unknown timer");
|
||||||
|
|
||||||
|
self->mDiscardTimer = nsnull;
|
||||||
|
|
||||||
|
old_frame_count = self->mFrames.Count();
|
||||||
|
|
||||||
|
if (self->mAnim) {
|
||||||
|
delete self->mAnim;
|
||||||
|
self->mAnim = nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->mFrames.Clear();
|
||||||
|
|
||||||
|
self->mDiscarded = PR_TRUE;
|
||||||
|
|
||||||
|
PR_LOG(gCompressedImageAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("CompressedImageAccounting: discarded uncompressed image data from imgContainer %p (%s) - %d frames (cached count: %d); "
|
||||||
|
"Compressed containers: %d, Compressed data bytes: %lld",
|
||||||
|
self,
|
||||||
|
self->mDiscardableMimeType.get(),
|
||||||
|
old_frame_count,
|
||||||
|
self->mNumFrames,
|
||||||
|
num_containers_with_discardable_data,
|
||||||
|
num_compressed_image_bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
imgContainer::ResetDiscardTimer (void)
|
||||||
|
{
|
||||||
|
if (!DiscardingEnabled())
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
if (!mDiscardTimer) {
|
||||||
|
mDiscardTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||||
|
|
||||||
|
if (!mDiscardTimer)
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
} else {
|
||||||
|
if (NS_FAILED(mDiscardTimer->Cancel()))
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mDiscardTimer->InitWithFuncCallback(sDiscardTimerCallback,
|
||||||
|
(void *) this,
|
||||||
|
get_discard_timer_ms (),
|
||||||
|
nsITimer::TYPE_ONE_SHOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
imgContainer::RestoreDiscardedData(void)
|
||||||
|
{
|
||||||
|
nsresult result;
|
||||||
|
int num_expected_frames;
|
||||||
|
|
||||||
|
if (!mDiscardable)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
result = ResetDiscardTimer();
|
||||||
|
if (NS_FAILED (result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (!mDiscarded)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
num_expected_frames = mNumFrames;
|
||||||
|
|
||||||
|
result = ReloadImages ();
|
||||||
|
if (NS_FAILED (result)) {
|
||||||
|
PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("CompressedImageAccounting: imgContainer::RestoreDiscardedData() for container %p failed to ReloadImages()",
|
||||||
|
this));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDiscarded = PR_FALSE;
|
||||||
|
|
||||||
|
NS_ASSERTION (mNumFrames == mFrames.Count(),
|
||||||
|
"number of restored image frames doesn't match");
|
||||||
|
NS_ASSERTION (num_expected_frames == mNumFrames,
|
||||||
|
"number of restored image frames doesn't match the original number of frames!");
|
||||||
|
|
||||||
|
PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
|
||||||
|
("CompressedImageAccounting: imgContainer::RestoreDiscardedData() restored discarded data "
|
||||||
|
"for imgContainer %p (%s) - %d image frames. "
|
||||||
|
"Compressed containers: %d, Compressed data bytes: %lld",
|
||||||
|
this,
|
||||||
|
mDiscardableMimeType.get(),
|
||||||
|
mNumFrames,
|
||||||
|
num_containers_with_discardable_data,
|
||||||
|
num_compressed_image_bytes));
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ContainerLoader : public imgILoad,
|
||||||
|
public imgIDecoderObserver,
|
||||||
|
public nsSupportsWeakReference
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_IMGILOAD
|
||||||
|
NS_DECL_IMGIDECODEROBSERVER
|
||||||
|
NS_DECL_IMGICONTAINEROBSERVER
|
||||||
|
|
||||||
|
ContainerLoader(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
imgIContainer *mContainer;
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS4 (ContainerLoader, imgILoad, imgIDecoderObserver, imgIContainerObserver, nsISupportsWeakReference)
|
||||||
|
|
||||||
|
ContainerLoader::ContainerLoader (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgILoad::image getter */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::GetImage(imgIContainer **aImage)
|
||||||
|
{
|
||||||
|
*aImage = mContainer;
|
||||||
|
NS_IF_ADDREF (*aImage);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgILoad::image setter */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::SetImage(imgIContainer *aImage)
|
||||||
|
{
|
||||||
|
mContainer = aImage;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgILoad::isMultiPartChannel getter */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::GetIsMultiPartChannel(PRBool *aIsMultiPartChannel)
|
||||||
|
{
|
||||||
|
*aIsMultiPartChannel = PR_FALSE; /* FIXME: is this always right? */
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgIDecoderObserver::onStartRequest() */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::OnStartRequest(imgIRequest *aRequest)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgIDecoderObserver::onStartDecode() */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::OnStartDecode(imgIRequest *aRequest)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgIDecoderObserver::onStartContainer() */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::OnStartContainer(imgIRequest *aRequest, imgIContainer *aContainer)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgIDecoderObserver::onStartFrame() */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::OnStartFrame(imgIRequest *aRequest, gfxIImageFrame *aFrame)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgIDecoderObserver::onDataAvailable() */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::OnDataAvailable(imgIRequest *aRequest, gfxIImageFrame *aFrame, const nsIntRect * aRect)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgIDecoderObserver::onStopFrame() */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::OnStopFrame(imgIRequest *aRequest, gfxIImageFrame *aFrame)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgIDecoderObserver::onStopContainer() */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::OnStopContainer(imgIRequest *aRequest, imgIContainer *aContainer)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgIDecoderObserver::onStopDecode() */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::OnStopDecode(imgIRequest *aRequest, nsresult status, const PRUnichar *statusArg)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement imgIDecoderObserver::onStopRequest() */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::OnStopRequest(imgIRequest *aRequest, PRBool aIsLastPart)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* implement imgIContainerObserver::frameChanged() */
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContainerLoader::FrameChanged(imgIContainer *aContainer, gfxIImageFrame *aFrame, nsIntRect * aDirtyRect)
|
||||||
|
{
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
imgContainer::ReloadImages(void)
|
||||||
|
{
|
||||||
|
nsresult result = NS_ERROR_FAILURE;
|
||||||
|
nsCOMPtr<nsIInputStream> stream;
|
||||||
|
|
||||||
|
NS_ASSERTION(!mRestoreData.IsEmpty(),
|
||||||
|
"imgContainer::ReloadImages(): mRestoreData should not be empty");
|
||||||
|
NS_ASSERTION(mRestoreDataDone,
|
||||||
|
"imgContainer::ReloadImages(): mRestoreDataDone shoudl be true!");
|
||||||
|
|
||||||
|
mNumFrames = 0;
|
||||||
|
NS_ASSERTION(mFrames.Count() == 0,
|
||||||
|
"imgContainer::ReloadImages(): mFrames should be empty");
|
||||||
|
|
||||||
|
nsCAutoString decoderCID(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + mDiscardableMimeType);
|
||||||
|
|
||||||
|
nsCOMPtr<imgIDecoder> decoder = do_CreateInstance(decoderCID.get());
|
||||||
|
if (!decoder) {
|
||||||
|
PR_LOG(gCompressedImageAccountingLog, PR_LOG_WARNING,
|
||||||
|
("CompressedImageAccounting: imgContainer::ReloadImages() could not create decoder for %s",
|
||||||
|
mDiscardableMimeType.get()));
|
||||||
|
return NS_IMAGELIB_ERROR_NO_DECODER;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<imgILoad> loader = new ContainerLoader();
|
||||||
|
if (!loader) {
|
||||||
|
PR_LOG(gCompressedImageAccountingLog, PR_LOG_WARNING,
|
||||||
|
("CompressedImageAccounting: imgContainer::ReloadImages() could not allocate ContainerLoader "
|
||||||
|
"when reloading the images for container %p",
|
||||||
|
this));
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
loader->SetImage(this);
|
||||||
|
|
||||||
|
result = decoder->Init(loader);
|
||||||
|
if (NS_FAILED(result)) {
|
||||||
|
PR_LOG(gCompressedImageAccountingLog, PR_LOG_WARNING,
|
||||||
|
("CompressedImageAccounting: imgContainer::ReloadImages() image container %p "
|
||||||
|
"failed to initialize the decoder (%s)",
|
||||||
|
this,
|
||||||
|
mDiscardableMimeType.get()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = NS_NewByteInputStream(getter_AddRefs(stream), mRestoreData.Elements(), mRestoreData.Length(), NS_ASSIGNMENT_DEPEND);
|
||||||
|
NS_ENSURE_SUCCESS(result, result);
|
||||||
|
|
||||||
|
if (PR_LOG_TEST(gCompressedImageAccountingLog, PR_LOG_DEBUG)) {
|
||||||
|
char buf[9];
|
||||||
|
get_header_str(buf, mRestoreData.Elements(), mRestoreData.Length());
|
||||||
|
PR_LOG(gCompressedImageAccountingLog, PR_LOG_WARNING,
|
||||||
|
("CompressedImageAccounting: imgContainer::ReloadImages() starting to restore images for container %p (%s) - "
|
||||||
|
"header %p is 0x%s (length %d)",
|
||||||
|
this,
|
||||||
|
mDiscardableMimeType.get(),
|
||||||
|
mRestoreData.Elements(),
|
||||||
|
buf,
|
||||||
|
mRestoreData.Length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
PRUint32 written;
|
||||||
|
result = decoder->WriteFrom(stream, mRestoreData.Length(), &written);
|
||||||
|
NS_ENSURE_SUCCESS(result, result);
|
||||||
|
|
||||||
|
if (NS_FAILED(decoder->Flush()))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result = decoder->Close();
|
||||||
|
NS_ENSURE_SUCCESS(result, result);
|
||||||
|
|
||||||
|
NS_ASSERTION(mFrames.Count() == mNumFrames,
|
||||||
|
"imgContainer::ReloadImages(): the restored mFrames.Count() doesn't match mNumFrames!");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
* Contributor(s):
|
* Contributor(s):
|
||||||
* Stuart Parmenter <pavlov@netscape.com>
|
* Stuart Parmenter <pavlov@netscape.com>
|
||||||
* Chris Saari <saari@netscape.com>
|
* Chris Saari <saari@netscape.com>
|
||||||
|
* Federico Mena-Quintero <federico@novell.com>
|
||||||
*
|
*
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
@ -58,6 +59,7 @@
|
||||||
#include "nsIProperties.h"
|
#include "nsIProperties.h"
|
||||||
#include "nsITimer.h"
|
#include "nsITimer.h"
|
||||||
#include "nsWeakReference.h"
|
#include "nsWeakReference.h"
|
||||||
|
#include "nsTArray.h"
|
||||||
|
|
||||||
#define NS_IMGCONTAINER_CID \
|
#define NS_IMGCONTAINER_CID \
|
||||||
{ /* 27f0682c-ff64-4dd2-ae7a-668e59f2fd38 */ \
|
{ /* 27f0682c-ff64-4dd2-ae7a-668e59f2fd38 */ \
|
||||||
|
@ -192,14 +194,8 @@ private:
|
||||||
timer->Cancel();
|
timer->Cancel();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline gfxIImageFrame* inlinedGetCurrentFrame() {
|
nsresult GetCurrentFrameNoRef(gfxIImageFrame** aFrame);
|
||||||
if (!mAnim)
|
|
||||||
return mFrames.SafeObjectAt(0);
|
|
||||||
if (mAnim->lastCompositedFrameIndex == mAnim->currentAnimationFrameIndex)
|
|
||||||
return mAnim->compositingFrame;
|
|
||||||
return mFrames.SafeObjectAt(mAnim->currentAnimationFrameIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Anim* ensureAnimExists() {
|
inline Anim* ensureAnimExists() {
|
||||||
if (!mAnim)
|
if (!mAnim)
|
||||||
|
@ -283,10 +279,15 @@ private:
|
||||||
nsIntSize mSize;
|
nsIntSize mSize;
|
||||||
|
|
||||||
//! All the <gfxIImageFrame>s of the PNG
|
//! All the <gfxIImageFrame>s of the PNG
|
||||||
|
// *** IMPORTANT: if you use mFrames in a method, call RestoreDiscardedData() first to ensure
|
||||||
|
// that the frames actually exist (they may have been discarded to save memory).
|
||||||
nsCOMArray<gfxIImageFrame> mFrames;
|
nsCOMArray<gfxIImageFrame> mFrames;
|
||||||
|
int mNumFrames; /* stored separately from mFrames.Count() to support discarded images */
|
||||||
|
|
||||||
nsCOMPtr<nsIProperties> mProperties;
|
nsCOMPtr<nsIProperties> mProperties;
|
||||||
|
|
||||||
|
// *** IMPORTANT: if you use mAnim in a method, call RestoreDiscardedData() first to ensure
|
||||||
|
// that the frames actually exist (they may have been discarded to save memory).
|
||||||
imgContainer::Anim* mAnim;
|
imgContainer::Anim* mAnim;
|
||||||
|
|
||||||
//! See imgIContainer for mode constants
|
//! See imgIContainer for mode constants
|
||||||
|
@ -297,6 +298,19 @@ private:
|
||||||
|
|
||||||
//! imgIContainerObserver
|
//! imgIContainerObserver
|
||||||
nsWeakPtr mObserver;
|
nsWeakPtr mObserver;
|
||||||
|
|
||||||
|
PRBool mDiscardable;
|
||||||
|
PRBool mDiscarded;
|
||||||
|
nsCString mDiscardableMimeType;
|
||||||
|
|
||||||
|
nsTArray<char> mRestoreData;
|
||||||
|
PRBool mRestoreDataDone;
|
||||||
|
nsCOMPtr<nsITimer> mDiscardTimer;
|
||||||
|
|
||||||
|
nsresult ResetDiscardTimer (void);
|
||||||
|
nsresult RestoreDiscardedData (void);
|
||||||
|
nsresult ReloadImages (void);
|
||||||
|
static void sDiscardTimerCallback (nsITimer *aTimer, void *aClosure);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __imgContainer_h__ */
|
#endif /* __imgContainer_h__ */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче