Bug 411718 - "Speed up JPEG decoding by 30% by skipping buffer" [p=alfredkayser@gmail.com (Alfred Kayser) r=stuart sr=tor a=blocking1.9+]

This commit is contained in:
reed@reedloden.com 2008-01-24 07:59:32 -08:00
parent 624db2f135
commit 0240f79a6f
2 changed files with 82 additions and 95 deletions

View File

@ -85,14 +85,15 @@ nsJPEGDecoder::nsJPEGDecoder()
{
mState = JPEG_HEADER;
mReading = PR_TRUE;
mError = NS_OK;
mBytesToSkip = 0;
memset(&mInfo, 0, sizeof(jpeg_decompress_struct));
memset(&mSourceMgr, 0, sizeof(mSourceMgr));
mInfo.client_data = (void*)this;
mBuffer = nsnull;
mBufferLen = mBufferSize = 0;
mSegment = nsnull;
mSegmentLen = 0;
mBackBuffer = nsnull;
mBackBufferLen = mBackBufferSize = mBackBufferUnreadLen = 0;
@ -107,7 +108,6 @@ nsJPEGDecoder::nsJPEGDecoder()
nsJPEGDecoder::~nsJPEGDecoder()
{
PR_FREEIF(mBuffer);
PR_FREEIF(mBackBuffer);
if (mTransform)
cmsDeleteTransform(mTransform);
@ -196,14 +196,17 @@ NS_IMETHODIMP nsJPEGDecoder::Close()
PR_LOG(gJPEGlog, PR_LOG_DEBUG,
("[this=%p] nsJPEGDecoder::Close\n", this));
if (mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER)
NS_WARNING("Never finished decoding the JPEG.");
/* Step 8: Release JPEG decompression object */
mInfo.src = nsnull;
jpeg_destroy_decompress(&mInfo);
if (mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER) {
NS_WARNING("Never finished decoding the JPEG.");
/* Tell imgLoader that image decoding has failed */
return NS_ERROR_FAILURE;
}
return NS_OK;
}
@ -214,58 +217,57 @@ NS_IMETHODIMP nsJPEGDecoder::Flush()
PRUint32 ret;
if (mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER && mState != JPEG_ERROR)
return this->WriteFrom(nsnull, 0, &ret);
return this->ProcessData(nsnull, 0, &ret);
return NS_OK;
}
/* unsigned long writeFrom (in nsIInputStream inStr, in unsigned long count); */
NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
static NS_METHOD ReadDataOut(nsIInputStream* in,
void* closure,
const char* fromRawSegment,
PRUint32 toOffset,
PRUint32 count,
PRUint32 *writeCount)
{
LOG_SCOPE_WITH_PARAM(gJPEGlog, "nsJPEGDecoder::WriteFrom", "count", count);
nsJPEGDecoder *decoder = static_cast<nsJPEGDecoder*>(closure);
nsresult rv = decoder->ProcessData(fromRawSegment, count, writeCount);
if (NS_FAILED(rv)) {
/* Tell imgLoader that image decoding has failed */
decoder->mError = rv;
*writeCount = 0;
}
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));
return NS_OK;
}
if (inStr) {
if (!mBuffer) {
mBuffer = (JOCTET *)PR_Malloc(count);
if (!mBuffer) {
mState = JPEG_ERROR;
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
("} (out of memory allocating buffer)"));
return NS_ERROR_OUT_OF_MEMORY;
}
mBufferSize = count;
} else if (count > mBufferSize) {
JOCTET *buf = (JOCTET *)PR_Realloc(mBuffer, count);
if (!buf) {
mState = JPEG_ERROR;
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
("} (out of memory resizing buffer)"));
return NS_ERROR_OUT_OF_MEMORY;
}
mBuffer = buf;
mBufferSize = count;
}
nsresult rv = inStr->Read((char*)mBuffer, count, &mBufferLen);
NS_ASSERTION(NS_SUCCEEDED(rv), "nsJPEGDecoder::WriteFrom -- inStr->Read failed");
/* unsigned long writeFrom (in nsIInputStream inStr, in unsigned long count); */
NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *writeCount)
{
NS_ENSURE_ARG_POINTER(inStr);
NS_ENSURE_ARG_POINTER(writeCount);
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;
/* necko doesn't propagate the errors from ReadDataOut */
nsresult rv = inStr->ReadSegments(ReadDataOut, this, count, writeCount);
if (NS_FAILED(mError)) {
/* Tell imgLoader that image decoding has failed */
rv = NS_ERROR_FAILURE;
}
nsresult result = mImage->AddRestoreData((char *) mBuffer, count);
return rv;
}
//******************************************************************************
nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 *writeCount)
{
LOG_SCOPE_WITH_PARAM(gJPEGlog, "nsJPEGDecoder::ProcessData", "count", count);
mSegment = (const JOCTET *)data;
mSegmentLen = count;
*writeCount = count;
if (data && count) {
nsresult result = mImage->AddRestoreData((char *) data, count);
if (NS_FAILED(result)) {
mState = JPEG_ERROR;
@ -301,12 +303,12 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
}
PR_LOG(gJPEGlog, PR_LOG_DEBUG,
("[this=%p] nsJPEGDecoder::WriteFrom -- processing JPEG data\n", this));
("[this=%p] nsJPEGDecoder::ProcessData -- processing JPEG data\n", this));
switch (mState) {
case JPEG_HEADER:
{
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_HEADER case");
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- entering JPEG_HEADER case");
/* Step 3: read file parameters with jpeg_read_header() */
if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) {
@ -494,7 +496,7 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
mImage->AppendFrame(mFrame);
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
(" JPEGDecoderAccounting: nsJPEGDecoder::WriteFrom -- created image frame with %ux%u pixels",
(" JPEGDecoderAccounting: nsJPEGDecoder::ProcessData -- created image frame with %ux%u pixels",
mInfo.image_width, mInfo.image_height));
}
@ -504,7 +506,7 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
case JPEG_START_DECOMPRESS:
{
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_START_DECOMPRESS case");
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- entering JPEG_START_DECOMPRESS case");
/* Step 4: set parameters for decompression */
/* FIXME -- Should reset dct_method and dither mode
@ -535,7 +537,7 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
{
if (mState == JPEG_DECOMPRESS_SEQUENTIAL)
{
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- JPEG_DECOMPRESS_SEQUENTIAL case");
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- JPEG_DECOMPRESS_SEQUENTIAL case");
if (!OutputScanlines()) {
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
@ -553,7 +555,7 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
{
if (mState == JPEG_DECOMPRESS_PROGRESSIVE)
{
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- JPEG_DECOMPRESS_PROGRESSIVE case");
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- JPEG_DECOMPRESS_PROGRESSIVE case");
int status;
do {
@ -618,7 +620,7 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
{
nsresult result;
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_DONE case");
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- entering JPEG_DONE case");
/* Step 7: Finish decompression */
@ -643,13 +645,13 @@ NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PR
}
case JPEG_SINK_NON_JPEG_TRAILER:
PR_LOG(gJPEGlog, PR_LOG_DEBUG,
("[this=%p] nsJPEGDecoder::WriteFrom -- entering JPEG_SINK_NON_JPEG_TRAILER case\n", this));
("[this=%p] nsJPEGDecoder::ProcessData -- entering JPEG_SINK_NON_JPEG_TRAILER case\n", this));
break;
case JPEG_ERROR:
PR_LOG(gJPEGlog, PR_LOG_DEBUG,
("[this=%p] nsJPEGDecoder::WriteFrom -- entering JPEG_ERROR case\n", this));
("[this=%p] nsJPEGDecoder::ProcessData -- entering JPEG_ERROR case\n", this));
break;
}
@ -674,7 +676,6 @@ nsJPEGDecoder::OutputScanlines()
mFrame->GetImageData(&imageData, &imageDataLength);
while ((mInfo.output_scanline < mInfo.output_height)) {
/* Request one scanline. Returns 0 or 1 scanlines. */
PRUint32 *imageRow = ((PRUint32*)imageData) +
(mInfo.output_scanline * mInfo.output_width);
@ -686,6 +687,7 @@ nsJPEGDecoder::OutputScanlines()
sampleRow += mInfo.output_width;
}
/* Request one scanline. Returns 0 or 1 scanlines. */
if (jpeg_read_scanlines(&mInfo, &sampleRow, 1) != 1) {
rv = PR_FALSE; /* suspend */
break;
@ -878,13 +880,13 @@ fill_input_buffer (j_decompress_ptr jd)
nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data);
if (decoder->mReading) {
unsigned char *new_buffer = (unsigned char *)decoder->mBuffer;
PRUint32 new_buflen = decoder->mBufferLen;
const JOCTET *new_buffer = decoder->mSegment;
PRUint32 new_buflen = decoder->mSegmentLen;
if (!new_buffer || new_buflen == 0)
return PR_FALSE; /* suspend */
decoder->mBufferLen = 0;
decoder->mSegmentLen = 0;
if (decoder->mBytesToSkip) {
if (decoder->mBytesToSkip < new_buflen) {
@ -908,7 +910,7 @@ fill_input_buffer (j_decompress_ptr jd)
return PR_TRUE;
}
if (src->next_input_byte != decoder->mBuffer) {
if (src->next_input_byte != decoder->mSegment) {
/* Backtrack data has been permanently consumed. */
decoder->mBackBufferUnreadLen = 0;
decoder->mBackBufferLen = 0;
@ -919,37 +921,24 @@ fill_input_buffer (j_decompress_ptr jd)
/* Make sure backtrack buffer is big enough to hold new data. */
if (decoder->mBackBufferSize < new_backtrack_buflen) {
/* Round up to multiple of 256 bytes. */
const PRUint32 roundup_buflen = ((new_backtrack_buflen + 255) >> 8) << 8;
if (decoder->mBackBuffer) {
JOCTET *buf = (JOCTET *)PR_REALLOC(decoder->mBackBuffer, roundup_buflen);
/* Check for OOM */
if (!buf) {
decoder->mInfo.err->msg_code = JERR_OUT_OF_MEMORY;
my_error_exit((j_common_ptr)(&decoder->mInfo));
}
decoder->mBackBuffer = buf;
} else {
decoder->mBackBuffer = (JOCTET*)PR_MALLOC(roundup_buflen);
/* Check for OOM */
if (!decoder->mBackBuffer) {
decoder->mInfo.err->msg_code = JERR_OUT_OF_MEMORY;
my_error_exit((j_common_ptr)(&decoder->mInfo));
}
}
decoder->mBackBufferSize = (size_t)roundup_buflen;
/* Check for malformed MARKER segment lengths. */
/* Check for malformed MARKER segment lengths, before allocating space for it */
if (new_backtrack_buflen > MAX_JPEG_MARKER_LENGTH) {
my_error_exit((j_common_ptr)(&decoder->mInfo));
}
/* Round up to multiple of 256 bytes. */
const size_t roundup_buflen = ((new_backtrack_buflen + 255) >> 8) << 8;
JOCTET *buf = (JOCTET *)PR_REALLOC(decoder->mBackBuffer, roundup_buflen);
/* Check for OOM */
if (!buf) {
decoder->mInfo.err->msg_code = JERR_OUT_OF_MEMORY;
my_error_exit((j_common_ptr)(&decoder->mInfo));
}
decoder->mBackBuffer = buf;
decoder->mBackBufferSize = roundup_buflen;
}
/* Copy remainder of netlib buffer into backtrack buffer. */
/* Copy remainder of netlib segment into backtrack buffer. */
memmove(decoder->mBackBuffer + decoder->mBackBufferLen,
src->next_input_byte,
src->bytes_in_buffer);

View File

@ -91,6 +91,8 @@ public:
nsJPEGDecoder();
virtual ~nsJPEGDecoder();
nsresult ProcessData(const char *data, PRUint32 count, PRUint32 *writeCount);
protected:
PRBool OutputScanlines();
@ -105,12 +107,12 @@ public:
struct jpeg_source_mgr mSourceMgr;
decoder_error_mgr mErr;
jstate mState;
nsresult mError;
PRUint32 mBytesToSkip;
JOCTET *mBuffer;
PRUint32 mBufferLen; // amount of data currently in mBuffer
PRUint32 mBufferSize; // size in bytes what mBuffer was created with
const JOCTET *mSegment; // The current segment we are decoding from
PRUint32 mSegmentLen; // amount of data in mSegment
JOCTET *mBackBuffer;
PRUint32 mBackBufferLen; // Offset of end of active backtrack data
@ -124,10 +126,6 @@ public:
cmsHTRANSFORM mTransform;
PRPackedBool mReading;
private:
nsresult AddToTmpAccumulateBuffer(JOCTET *src, PRUint32 len);
};
#endif // nsJPEGDecoder_h__