diff --git a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp index 84e275861e79..986814a3695a 100644 --- a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp +++ b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp @@ -58,6 +58,13 @@ void PR_CALLBACK il_error_exit (j_common_ptr cinfo); /* Normal JFIF markers can't have more bytes than this. */ #define MAX_JPEG_MARKER_LENGTH (((PRUint32)1 << 16) - 1) + +/* Possible states for JPEG source manager */ +enum data_source_state { + READING_BACK = 0, /* Must be zero for init purposes */ + READING_NEW +}; + /* * Implementation of a JPEG src object that understands our state machine */ @@ -75,8 +82,7 @@ nsJPEGDecoder::nsJPEGDecoder() NS_INIT_ISUPPORTS(); mState = JPEG_HEADER; - - mDataLen = 0; + mFillState = READING_BACK; mSamples = nsnull; mSamples3 = nsnull; @@ -87,11 +93,21 @@ nsJPEGDecoder::nsJPEGDecoder() mRGBPadRow = nsnull; mCompletedPasses = 0; + + mBuffer = nsnull; + mBufferLen = mBufferSize = 0; + + mBackBuffer = nsnull; + mBackBufferLen = mBackBufferSize = mBackBufferUnreadLen = 0; + } nsJPEGDecoder::~nsJPEGDecoder() { - + if (mBuffer) + PR_Free(mBuffer); + if (mBackBuffer) + PR_Free(mBackBuffer); } @@ -105,11 +121,6 @@ NS_IMETHODIMP nsJPEGDecoder::Init(imgIRequest *aRequest) aRequest->GetImage(getter_AddRefs(mImage)); - - NS_NewPipe(getter_AddRefs(mInStream), - getter_AddRefs(mOutStream), - 10240); // this could be a lot smaller (like 3-6k?) - /* Step 1: allocate and initialize JPEG decompression object */ mInfo.err = jpeg_std_error(&mErr.pub); jpeg_create_decompress(&mInfo); @@ -136,7 +147,6 @@ NS_IMETHODIMP nsJPEGDecoder::Init(imgIRequest *aRequest) src->decoder = this; - #if 0 /* We set up the normal JPEG error routines, then override error_exit. */ mInfo.err = jpeg_std_error(&jerr.pub); @@ -192,11 +202,11 @@ NS_IMETHODIMP nsJPEGDecoder::Close() /* void flush (); */ NS_IMETHODIMP nsJPEGDecoder::Flush() { - PR_LOG(gJPEGlog, PR_LOG_DEBUG, - ("[this=%p] nsJPEGDecoder::Flush\n", this)); + LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Flush"); PRUint32 ret; - this->WriteFrom(nsnull, 0, &ret); + if (mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER) + return this->WriteFrom(nsnull, 0, &ret); return NS_OK; } @@ -218,16 +228,31 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR */ // XXX above what is this? - if (inStr) { - mOutStream->WriteFrom(inStr, count, _retval); - mDataLen += *_retval; - } - /* Register new buffer contents with data source manager. */ + if (inStr) { + if (!mBuffer) { + mBuffer = (JOCTET *)PR_Malloc(count); + mBufferSize = count; + } else if (count > mBufferSize) { + mBuffer = (JOCTET *)PR_Realloc(mBuffer, count); + mBufferSize = count; + } + + nsresult rv = inStr->Read((char*)mBuffer, count, &mBufferLen); + *_retval = mBufferLen; + + //nsresult rv = mOutStream->WriteFrom(inStr, count, _retval); + + NS_ASSERTION(NS_SUCCEEDED(rv), "nsJPEGDecoder::WriteFrom -- mOutStream->WriteFrom failed"); + } + // else no input stream.. Flush() ? + + + PR_LOG(gJPEGlog, PR_LOG_DEBUG, + ("[this=%p] nsJPEGDecoder::WriteFrom -- processing JPEG data\n", this)); decoder_source_mgr *src = NS_REINTERPRET_CAST(decoder_source_mgr *, mInfo.src); - int status; switch (mState) { case JPEG_HEADER: { @@ -269,11 +294,11 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR mSamples = (*mInfo.mem->alloc_sarray)((j_common_ptr) &mInfo, JPOOL_IMAGE, row_stride, 1); - + mRGBPadRow = (PRUint8*) PR_MALLOC(mInfo.output_width * 4); memset(mRGBPadRow, 0, mInfo.output_width * 4); - - + + /* Allocate RGB buffer for conversion from greyscale. */ if (mInfo.output_components != 3) { @@ -299,48 +324,75 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR mInfo.enable_2pass_quant = FALSE; mInfo.do_block_smoothing = TRUE; - /* In this example, we don't need to change any of the defaults set by - * jpeg_read_header(), so we do nothing here. - */ - /* Step 5: Start decompressor */ if (jpeg_start_decompress(&mInfo) == FALSE) return NS_OK; /* I/O suspension */ - mState = JPEG_DECOMPRESS_PROGRESSIVE; + /* If this is a progressive JPEG ... */ + if (mInfo.buffered_image) { + mState = JPEG_DECOMPRESS_PROGRESSIVE; + } else { + mState = JPEG_DECOMPRESS_SEQUENTIAL; + } + } - case JPEG_DECOMPRESS_PROGRESSIVE: + + case JPEG_DECOMPRESS_SEQUENTIAL: { - LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- JPEG_DECOMPRESS_PROGRESSIVE case"); + if (mState == JPEG_DECOMPRESS_SEQUENTIAL) + { + LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- JPEG_DECOMPRESS_SEQUENTIAL case"); - do { - status = jpeg_consume_input(&mInfo); - } while (!((status == JPEG_SUSPENDED) || - (status == JPEG_REACHED_EOI))); + OutputScanlines(-1); - switch (status) { - case JPEG_REACHED_EOI: - // End of image - mState = JPEG_FINAL_PROGRESSIVE_SCAN_OUTPUT; - break; - case JPEG_SUSPENDED: - default: - PR_LOG(gJPEGlog, PR_LOG_DEBUG, - ("[this=%p] nsJPEGDecoder::WriteFrom -- suspending\n", this)); - - return NS_OK; + /* If we've completed image output ... */ + if (mInfo.output_scanline == mInfo.output_height) + mState = JPEG_DONE; } } + + + case JPEG_DECOMPRESS_PROGRESSIVE: + { + if (mState == JPEG_DECOMPRESS_PROGRESSIVE) + { + LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- JPEG_DECOMPRESS_PROGRESSIVE case"); + + int status; + do { + status = jpeg_consume_input(&mInfo); + } while (!((status == JPEG_SUSPENDED) || + (status == JPEG_REACHED_EOI))); + + switch (status) { + case JPEG_REACHED_EOI: + // End of image + mState = JPEG_FINAL_PROGRESSIVE_SCAN_OUTPUT; + break; + case JPEG_SUSPENDED: + PR_LOG(gJPEGlog, PR_LOG_DEBUG, + ("[this=%p] nsJPEGDecoder::WriteFrom -- suspending\n", this)); + + return NS_OK; /* I/O suspension */ + default: + printf("got someo other state!?\n"); + } + } + } + case JPEG_FINAL_PROGRESSIVE_SCAN_OUTPUT: { - LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_FINAL_PROGRESSIVE_SCAN_OUTPUT case"); + if (mState == JPEG_FINAL_PROGRESSIVE_SCAN_OUTPUT) + { + LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_FINAL_PROGRESSIVE_SCAN_OUTPUT case"); - // XXX progressive? ;) - // not really progressive according to the state machine... -saari - jpeg_start_output(&mInfo, mInfo.input_scan_number); - OutputScanlines(-1); - jpeg_finish_output(&mInfo); - mState = JPEG_DONE; + // XXX progressive? ;) + // not really progressive according to the state machine... -saari + jpeg_start_output(&mInfo, mInfo.input_scan_number); + OutputScanlines(-1); + jpeg_finish_output(&mInfo); + mState = JPEG_DONE; + } } case JPEG_DONE: @@ -592,40 +644,6 @@ il_error_exit (j_common_ptr cinfo) * TRUE if additional data is available, FALSE if no data present and * the JPEG library should therefore suspend processing of input stream *---------------------------------------------------------------------------*/ -static NS_METHOD ReadDataOut(nsIInputStream* in, - void* closure, - const char* fromRawSegment, - PRUint32 toOffset, - PRUint32 count, - PRUint32 *writeCount) -{ - j_decompress_ptr jd = NS_STATIC_CAST(j_decompress_ptr, closure); - decoder_source_mgr *src = NS_REINTERPRET_CAST(decoder_source_mgr *, jd->src); - - src->pub.next_input_byte = NS_REINTERPRET_CAST(const unsigned char *, fromRawSegment); - src->pub.bytes_in_buffer = count; - - *writeCount = 0; // pretend we didn't really read anything - - return NS_ERROR_FAILURE; // return error so that we can point to this buffer and exit the ReadSegments loop -} - -/******************************************************************************/ -static NS_METHOD DiscardData(nsIInputStream* in, - void* closure, - const char* fromRawSegment, - PRUint32 toOffset, - PRUint32 count, - PRUint32 *writeCount) -{ - j_decompress_ptr jd = NS_STATIC_CAST(j_decompress_ptr, closure); - decoder_source_mgr *src = NS_REINTERPRET_CAST(decoder_source_mgr *, jd->src); - - *writeCount = count; - - return NS_OK; -} - /******************************************************************************/ /* data source manager method @@ -666,12 +684,6 @@ skip_input_data (j_decompress_ptr jd, long num_bytes) * network stream. Set things up so that fill_input_buffer * will skip remaining amount. */ - - PRUint32 _retval; - src->decoder->mInStream->ReadSegments(DiscardData, NS_STATIC_CAST(void*, jd), - src->pub.bytes_in_buffer, &_retval); - src->decoder->mDataLen -= _retval; - src->decoder->mBytesToSkip = (size_t)num_bytes - src->pub.bytes_in_buffer; src->pub.next_input_byte += src->pub.bytes_in_buffer; src->pub.bytes_in_buffer = 0; @@ -679,13 +691,6 @@ skip_input_data (j_decompress_ptr jd, long num_bytes) } else { /* Simple case. Just advance buffer pointer */ - PRUint32 _retval; - src->decoder->mInStream->ReadSegments(DiscardData, NS_STATIC_CAST(void*, jd), - num_bytes, &_retval); - // XXX not sure we should be managing this buffer here - //src->decoder->mDataLen -= _retval; - //src->decoder->mBytesToSkip = 0; - src->pub.bytes_in_buffer -= (size_t)num_bytes; src->pub.next_input_byte += num_bytes; } @@ -709,39 +714,106 @@ fill_input_buffer (j_decompress_ptr jd) { decoder_source_mgr *src = (decoder_source_mgr *)jd->src; - PRUint32 _retval; + unsigned char *new_buffer = (unsigned char *)src->decoder->mBuffer; + PRUint32 new_buflen = src->decoder->mBufferLen; + PRUint32 bytesToSkip = src->decoder->mBytesToSkip; - if (src->decoder->mBytesToSkip != 0) { - if (src->decoder->mBytesToSkip > src->decoder->mDataLen){ - src->decoder->mInStream->ReadSegments(DiscardData, NS_STATIC_CAST(void*, jd), - src->decoder->mDataLen, &_retval); + switch(src->decoder->mFillState) { + case READING_BACK: + { + if (!new_buffer || new_buflen == 0) + return PR_FALSE; /* suspend */ - src->decoder->mBytesToSkip -= _retval; - src->decoder->mDataLen -= _retval; + src->decoder->mBufferLen = 0; - if (src->decoder->mDataLen > 0) - return PR_FALSE; + if (bytesToSkip != 0) { + if (bytesToSkip < new_buflen) { + /* All done skipping bytes; Return what's left. */ + new_buffer += bytesToSkip; + new_buflen -= bytesToSkip; + src->decoder->mBytesToSkip = 0; + } else { + /* Still need to skip some more data in the future */ + src->decoder->mBytesToSkip -= (size_t)new_buflen; + return PR_FALSE; /* suspend */ + } + } - } else { - src->decoder->mInStream->ReadSegments(DiscardData, NS_STATIC_CAST(void*, jd), - src->decoder->mBytesToSkip, &_retval); + src->decoder->mBackBufferUnreadLen = src->pub.bytes_in_buffer; - src->decoder->mBytesToSkip -= _retval; - src->decoder->mDataLen -= _retval; + src->pub.next_input_byte = new_buffer; + src->pub.bytes_in_buffer = (size_t)new_buflen; + src->decoder->mFillState = READING_NEW; + return PR_TRUE; } + break; + case READING_NEW: + { + if (src->pub.next_input_byte != src->decoder->mBuffer) { + /* Backtrack data has been permanently consumed. */ + src->decoder->mBackBufferUnreadLen = 0; + src->decoder->mBackBufferLen = 0; + } + + /* Save remainder of netlib buffer in backtrack buffer */ + PRUint32 new_backtrack_buflen = src->pub.bytes_in_buffer + src->decoder->mBackBufferLen; + + + /* Make sure backtrack buffer is big enough to hold new data. */ + if (src->decoder->mBackBufferSize < new_backtrack_buflen) { + + /* Round up to multiple of 16 bytes. */ + PRUint32 roundup_buflen = ((new_backtrack_buflen + 15) >> 4) << 4; + if (src->decoder->mBackBufferSize) { + src->decoder->mBackBuffer = + (JOCTET *)PR_REALLOC(src->decoder->mBackBuffer, roundup_buflen); + } else { + src->decoder->mBackBuffer = (JOCTET*)PR_MALLOC(roundup_buflen); + } + + /* Check for OOM */ + if (!src->decoder->mBackBuffer) { +#if 0 + j_common_ptr cinfo = (j_common_ptr)(&src->js->jd); + cinfo->err->msg_code = JERR_OUT_OF_MEMORY; + il_error_exit(cinfo); +#endif + } + + src->decoder->mBackBufferSize = (size_t)roundup_buflen; + + /* Check for malformed MARKER segment lengths. */ + if (new_backtrack_buflen > MAX_JPEG_MARKER_LENGTH) { + // il_error_exit((j_common_ptr)(&src->js->jd)); + } + } + + + /* Copy remainder of netlib buffer into backtrack buffer. */ + nsCRT::memmove(src->decoder->mBackBuffer + src->decoder->mBackBufferLen, + src->pub.next_input_byte, + src->pub.bytes_in_buffer); + + + /* Point to start of data to be rescanned. */ + src->pub.next_input_byte = src->decoder->mBackBuffer + src->decoder->mBackBufferLen - src->decoder->mBackBufferUnreadLen; + src->pub.bytes_in_buffer += src->decoder->mBackBufferUnreadLen; + src->decoder->mBackBufferLen = (size_t)new_backtrack_buflen; + + src->decoder->mFillState = READING_BACK; +#if 0 + if (src->pub.bytes_in_buffer != 0) + return PR_TRUE; + else +#endif + return PR_FALSE; + } + break; } - if (src->decoder->mDataLen != 0) { - src->decoder->mInStream->ReadSegments(ReadDataOut, NS_STATIC_CAST(void*, jd), - src->decoder->mDataLen, &_retval); - src->decoder->mDataLen -= src->pub.bytes_in_buffer; - return PR_TRUE; - } else { - return PR_FALSE; - } - + return PR_FALSE; } /******************************************************************************/ diff --git a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h index bc00d0b896ac..364545069732 100644 --- a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h +++ b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h @@ -93,17 +93,26 @@ public: decoder_error_mgr mErr; jstate mState; - nsCOMPtr mInStream; - nsCOMPtr mOutStream; - - PRUint32 mDataLen; - JSAMPARRAY mSamples; JSAMPARRAY mSamples3; PRUint8* mRGBPadRow; PRInt32 mCompletedPasses; PRInt32 mPasses; + + int mFillState; + + JOCTET *mBuffer; + PRUint32 mBufferLen; // amount of data currently in mBuffer + PRUint32 mBufferSize; // size in bytes what mBuffer was created with + + JOCTET *mBackBuffer; + PRUint32 mBackBufferLen; // Offset of end of active backtrack data + PRUint32 mBackBufferSize; // size in bytes what mBackBuffer was created with + PRUint32 mBackBufferUnreadLen; // amount of data currently in mBackBuffer + + + }; #endif // nsJPEGDecoder_h__