From d4eec46179107fcca413ed915e52e85f3d004525 Mon Sep 17 00:00:00 2001 From: "reed@reedloden.com" Date: Mon, 21 Jan 2008 00:18:19 -0800 Subject: [PATCH] Bug 411718 - "Speed up JPEG decoding by 30% by skipping buffer" (try without the realloc optimization) [p=alfredkayser@gmail.com (Alfred Kayser) r=stuart sr=tor a=blocking1.9+] --- .../libpr0n/decoders/jpeg/nsJPEGDecoder.cpp | 138 +++++++++--------- modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h | 13 +- 2 files changed, 72 insertions(+), 79 deletions(-) diff --git a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp index b1242b42367..658a158b642 100644 --- a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp +++ b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp @@ -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); @@ -125,7 +125,6 @@ nsJPEGDecoder::~nsJPEGDecoder() /* void init (in imgILoad aLoad); */ NS_IMETHODIMP nsJPEGDecoder::Init(imgILoad *aLoad) { - mImageLoad = aLoad; mObserver = do_QueryInterface(aLoad); /* We set up the normal JPEG error routines, then override error_exit. */ @@ -169,14 +168,14 @@ NS_IMETHODIMP nsJPEGDecoder::Init(imgILoad *aLoad) * If we have a mismatch in width/height for the container later on we will * generate an error. */ - mImageLoad->GetImage(getter_AddRefs(mImage)); + aLoad->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); + aLoad->SetImage(mImage); nsresult result = mImage->SetDiscardable("image/jpeg"); if (NS_FAILED(result)) { mState = JPEG_ERROR; @@ -196,14 +195,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 +216,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(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 +302,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 +495,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 +505,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 +536,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 +554,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 +619,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 +644,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 +675,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 +686,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 +879,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 +909,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; @@ -949,7 +950,7 @@ fill_input_buffer (j_decompress_ptr jd) } } - /* 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); @@ -980,9 +981,4 @@ term_source (j_decompress_ptr jd) decoder->mObserver->OnStopContainer(nsnull, decoder->mImage); decoder->mObserver->OnStopDecode(nsnull, NS_OK, nsnull); } - - PRBool isMutable = PR_FALSE; - if (decoder->mImageLoad) - decoder->mImageLoad->GetIsMultiPartChannel(&isMutable); - decoder->mFrame->SetMutable(isMutable); } diff --git a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h index dae586fe4c5..f51bae364a1 100644 --- a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h +++ b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h @@ -91,12 +91,13 @@ public: nsJPEGDecoder(); virtual ~nsJPEGDecoder(); + nsresult ProcessData(const char *data, PRUint32 count, PRUint32 *writeCount); + protected: PRBool OutputScanlines(); public: nsCOMPtr mImage; - nsCOMPtr mImageLoad; nsCOMPtr mFrame; nsCOMPtr mObserver; @@ -105,12 +106,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 +125,6 @@ public: cmsHTRANSFORM mTransform; PRPackedBool mReading; - -private: - - nsresult AddToTmpAccumulateBuffer(JOCTET *src, PRUint32 len); }; #endif // nsJPEGDecoder_h__