Back to index

lightning-sunbird  0.9+nobinonly
nsJPEGDecoder.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Stuart Parmenter <pavlov@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsJPEGDecoder.h"
00041 
00042 #include "imgIContainerObserver.h"
00043 
00044 #include "nsIComponentManager.h"
00045 #include "nsIInputStream.h"
00046 
00047 #include "nspr.h"
00048 #include "nsCRT.h"
00049 #include "ImageLogging.h"
00050 
00051 #include "jerror.h"
00052 
00053 NS_IMPL_ISUPPORTS1(nsJPEGDecoder, imgIDecoder)
00054 
00055 #if defined(PR_LOGGING)
00056 PRLogModuleInfo *gJPEGlog = PR_NewLogModule("JPEGDecoder");
00057 #else
00058 #define gJPEGlog
00059 #endif
00060 
00061 
00062 METHODDEF(void) init_source (j_decompress_ptr jd);
00063 METHODDEF(boolean) fill_input_buffer (j_decompress_ptr jd);
00064 METHODDEF(void) skip_input_data (j_decompress_ptr jd, long num_bytes);
00065 METHODDEF(void) term_source (j_decompress_ptr jd);
00066 METHODDEF(void) my_error_exit (j_common_ptr cinfo);
00067 
00068 /* Normal JFIF markers can't have more bytes than this. */
00069 #define MAX_JPEG_MARKER_LENGTH  (((PRUint32)1 << 16) - 1)
00070 
00071 
00072 /* Possible states for JPEG source manager */
00073 enum data_source_state {
00074     READING_BACK = 0, /* Must be zero for init purposes */
00075     READING_NEW
00076 };
00077 
00078 /*
00079  *  Implementation of a JPEG src object that understands our state machine
00080  */
00081 typedef struct {
00082   /* public fields; must be first in this struct! */
00083   struct jpeg_source_mgr pub;
00084 
00085   nsJPEGDecoder *decoder;
00086 
00087 } decoder_source_mgr;
00088 
00089 
00090 nsJPEGDecoder::nsJPEGDecoder()
00091 {
00092   mState = JPEG_HEADER;
00093   mFillState = READING_BACK;
00094 
00095   mSamples = nsnull;
00096   mRGBRow = nsnull;
00097 
00098   mBytesToSkip = 0;
00099   
00100   memset(&mInfo, 0, sizeof(jpeg_decompress_struct));
00101 
00102   mCompletedPasses = 0;
00103 
00104   mBuffer = nsnull;
00105   mBufferLen = mBufferSize = 0;
00106 
00107   mBackBuffer = nsnull;
00108   mBackBufferLen = mBackBufferSize = mBackBufferUnreadLen = 0;
00109 
00110 }
00111 
00112 nsJPEGDecoder::~nsJPEGDecoder()
00113 {
00114   if (mBuffer)
00115     PR_Free(mBuffer);
00116   if (mBackBuffer)
00117     PR_Free(mBackBuffer);
00118   if (mRGBRow)
00119     PR_Free(mRGBRow);
00120 }
00121 
00122 
00125 /* void init (in imgILoad aLoad); */
00126 NS_IMETHODIMP nsJPEGDecoder::Init(imgILoad *aLoad)
00127 {
00128   mImageLoad = aLoad;
00129   mObserver = do_QueryInterface(aLoad);
00130 
00131   /* We set up the normal JPEG error routines, then override error_exit. */
00132   mInfo.err = jpeg_std_error(&mErr.pub);
00133   /*   mInfo.err = jpeg_std_error(&mErr.pub); */
00134   mErr.pub.error_exit = my_error_exit;
00135   /* Establish the setjmp return context for my_error_exit to use. */
00136   if (setjmp(mErr.setjmp_buffer)) {
00137     /* If we get here, the JPEG code has signaled an error.
00138      * We need to clean up the JPEG object, close the input file, and return.
00139      */
00140     return NS_ERROR_FAILURE;
00141   }
00142 
00143   /* Step 1: allocate and initialize JPEG decompression object */
00144   jpeg_create_decompress(&mInfo);
00145   
00146   decoder_source_mgr *src;
00147   if (mInfo.src == NULL) {
00148     src = PR_NEWZAP(decoder_source_mgr);
00149     if (!src) {
00150       mState = JPEG_ERROR;
00151       return NS_ERROR_OUT_OF_MEMORY;
00152     }
00153 
00154     mInfo.src = NS_REINTERPRET_CAST(struct jpeg_source_mgr *, src);
00155   }
00156 
00157   /* Step 2: specify data source (eg, a file) */
00158 
00159   /* Setup callback functions. */
00160   src->pub.init_source = init_source;
00161   src->pub.fill_input_buffer = fill_input_buffer;
00162   src->pub.skip_input_data = skip_input_data;
00163   src->pub.resync_to_restart = jpeg_resync_to_restart;
00164   src->pub.term_source = term_source;
00165 
00166   src->decoder = this;
00167 
00168   return NS_OK;
00169 }
00170 
00171 
00172 /* void close (); */
00173 NS_IMETHODIMP nsJPEGDecoder::Close()
00174 {
00175   PR_LOG(gJPEGlog, PR_LOG_DEBUG,
00176          ("[this=%p] nsJPEGDecoder::Close\n", this));
00177 
00178   if (mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER)
00179     NS_WARNING("Never finished decoding the JPEG.");
00180 
00181   /* Step 8: Release JPEG decompression object */
00182   decoder_source_mgr *src = NS_REINTERPRET_CAST(decoder_source_mgr *, mInfo.src);
00183   PR_FREEIF(src);
00184   mInfo.src = nsnull;
00185 
00186   jpeg_destroy_decompress(&mInfo);
00187 
00188   return NS_OK;
00189 }
00190 
00191 /* void flush (); */
00192 NS_IMETHODIMP nsJPEGDecoder::Flush()
00193 {
00194   LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Flush");
00195 
00196   PRUint32 ret;
00197   if (mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER && mState != JPEG_ERROR)
00198     return this->WriteFrom(nsnull, 0, &ret);
00199 
00200   return NS_OK;
00201 }
00202 
00203 /* unsigned long writeFrom (in nsIInputStream inStr, in unsigned long count); */
00204 NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
00205 {
00206   LOG_SCOPE_WITH_PARAM(gJPEGlog, "nsJPEGDecoder::WriteFrom", "count", count);
00207 
00208   if (inStr) {
00209     if (!mBuffer) {
00210       mBuffer = (JOCTET *)PR_Malloc(count);
00211       mBufferSize = count;
00212     } else if (count > mBufferSize) {
00213       mBuffer = (JOCTET *)PR_Realloc(mBuffer, count);
00214       mBufferSize = count;
00215     }
00216 
00217     nsresult rv = inStr->Read((char*)mBuffer, count, &mBufferLen);
00218     *_retval = mBufferLen;
00219 
00220     //nsresult rv = mOutStream->WriteFrom(inStr, count, _retval);
00221 
00222     NS_ASSERTION(NS_SUCCEEDED(rv), "nsJPEGDecoder::WriteFrom -- mOutStream->WriteFrom failed");
00223   }
00224   // else no input stream.. Flush() ?
00225 
00226   /* Return here if there is a fatal error. */
00227   nsresult error_code;
00228   if ((error_code = setjmp(mErr.setjmp_buffer)) != 0) {
00229     mState = JPEG_SINK_NON_JPEG_TRAILER;
00230     if (error_code == NS_ERROR_FAILURE) {
00231       /* Error due to corrupt stream - return NS_OK so that libpr0n
00232          doesn't throw away a partial image load */
00233       return NS_OK;
00234     } else {
00235       /* Error due to reasons external to the stream (probably out of
00236          memory) - let libpr0n attempt to clean up, even though
00237          mozilla is seconds away from falling flat on its face. */
00238       return error_code;
00239     }
00240   }
00241 
00242   PR_LOG(gJPEGlog, PR_LOG_DEBUG,
00243          ("[this=%p] nsJPEGDecoder::WriteFrom -- processing JPEG data\n", this));
00244 
00245   switch (mState) {
00246   case JPEG_HEADER:
00247   {
00248     LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_HEADER case");
00249 
00250     /* Step 3: read file parameters with jpeg_read_header() */
00251     if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED)
00252       return NS_OK; /* I/O suspension */
00253 
00254     /* let libjpeg take care of gray->RGB and YCbCr->RGB conversions */
00255     switch (mInfo.jpeg_color_space) {
00256       case JCS_GRAYSCALE:
00257       case JCS_RGB:
00258       case JCS_YCbCr:
00259         mInfo.out_color_space = JCS_RGB;
00260         break;
00261       case JCS_CMYK:
00262       case JCS_YCCK:
00263       default:
00264         mState = JPEG_ERROR;
00265         return NS_ERROR_UNEXPECTED;
00266     }
00267 
00268     /*
00269      * Don't allocate a giant and superfluous memory buffer
00270      * when the image is a sequential JPEG.
00271      */
00272     mInfo.buffered_image = jpeg_has_multiple_scans(&mInfo);
00273 
00274     /* Used to set up image size so arrays can be allocated */
00275     jpeg_calc_output_dimensions(&mInfo);
00276 
00277     mObserver->OnStartDecode(nsnull);
00278 
00279     /* Check if the request already has an image container.
00280        this is the case when multipart/x-mixed-replace is being downloaded
00281        if we already have one and it has the same width and height, reuse it.
00282      */
00283     mImageLoad->GetImage(getter_AddRefs(mImage));
00284     if (mImage) {
00285       PRInt32 width, height;
00286       mImage->GetWidth(&width);
00287       mImage->GetHeight(&height);
00288       if ((width != (PRInt32)mInfo.image_width) ||
00289           (height != (PRInt32)mInfo.image_height)) {
00290         mImage = nsnull;
00291       }
00292     }
00293 
00294     if (!mImage) {
00295       mImage = do_CreateInstance("@mozilla.org/image/container;1");
00296       if (!mImage) {
00297         mState = JPEG_ERROR;
00298         return NS_ERROR_OUT_OF_MEMORY;
00299       }
00300       mImageLoad->SetImage(mImage);
00301       mImage->Init(mInfo.image_width, mInfo.image_height, mObserver);
00302     }
00303 
00304     mObserver->OnStartContainer(nsnull, mImage);
00305 
00306     mImage->GetFrameAt(0, getter_AddRefs(mFrame));
00307 
00308     PRBool createNewFrame = PR_TRUE;
00309 
00310     if (mFrame) {
00311       PRInt32 width, height;
00312       mFrame->GetWidth(&width);
00313       mFrame->GetHeight(&height);
00314 
00315       if ((width == (PRInt32)mInfo.image_width) &&
00316           (height == (PRInt32)mInfo.image_height)) {
00317         createNewFrame = PR_FALSE;
00318       } else {
00319         mImage->Clear();
00320       }
00321     }
00322 
00323     if (createNewFrame) {
00324       mFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2");
00325       if (!mFrame) {
00326         mState = JPEG_ERROR;
00327         return NS_ERROR_OUT_OF_MEMORY;
00328       }
00329 
00330       gfx_format format = gfxIFormats::RGB;
00331 #if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
00332       format = gfxIFormats::BGR;
00333 #endif
00334 
00335       if (NS_FAILED(mFrame->Init(0, 0, mInfo.image_width, mInfo.image_height, format, 24))) {
00336         mState = JPEG_ERROR;
00337         return NS_ERROR_OUT_OF_MEMORY;
00338       }
00339 
00340       mImage->AppendFrame(mFrame);
00341     }      
00342 
00343     mObserver->OnStartFrame(nsnull, mFrame);
00344 
00345     /*
00346      * Make a one-row-high sample array that will go away
00347      * when done with image. Always make it big enough to
00348      * hold an RGB row.  Since this uses the IJG memory
00349      * manager, it must be allocated before the call to
00350      * jpeg_start_compress().
00351      */
00352     int row_stride;
00353 
00354     // Note! row_stride here must match the row_stride in
00355     // nsJPEGDecoder::OutputScanlines
00356 #if defined(XP_MAC) || defined(XP_MACOSX)
00357     row_stride = mInfo.output_width * 4;
00358 #else
00359     row_stride = mInfo.output_width * 3;
00360 #endif
00361 
00362     mSamples = (*mInfo.mem->alloc_sarray)((j_common_ptr) &mInfo,
00363                                            JPOOL_IMAGE,
00364                                            row_stride, 1);
00365 
00366 #if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(XP_MAC) || defined(XP_MACOSX) || defined(MOZ_WIDGET_PHOTON)
00367     // allocate buffer to do byte flipping / padding
00368     mRGBRow = (PRUint8*) PR_MALLOC(row_stride);
00369 #endif
00370 
00371     mState = JPEG_START_DECOMPRESS;
00372   }
00373 
00374   case JPEG_START_DECOMPRESS:
00375   {
00376     LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_START_DECOMPRESS case");
00377     /* Step 4: set parameters for decompression */
00378 
00379     /* FIXME -- Should reset dct_method and dither mode
00380      * for final pass of progressive JPEG
00381      */
00382     mInfo.dct_method =  JDCT_ISLOW;
00383     mInfo.dither_mode = JDITHER_FS;
00384     mInfo.do_fancy_upsampling = TRUE;
00385     mInfo.enable_2pass_quant = FALSE;
00386     mInfo.do_block_smoothing = TRUE;
00387 
00388     /* Step 5: Start decompressor */
00389     if (jpeg_start_decompress(&mInfo) == FALSE)
00390       return NS_OK; /* I/O suspension */
00391 
00392     /* If this is a progressive JPEG ... */
00393     if (mInfo.buffered_image) {
00394       mState = JPEG_DECOMPRESS_PROGRESSIVE;
00395     } else {
00396       mState = JPEG_DECOMPRESS_SEQUENTIAL;
00397     }
00398   }
00399 
00400   case JPEG_DECOMPRESS_SEQUENTIAL:
00401   {
00402     if (mState == JPEG_DECOMPRESS_SEQUENTIAL)
00403     {
00404       LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- JPEG_DECOMPRESS_SEQUENTIAL case");
00405       
00406       if (OutputScanlines() == PR_FALSE)
00407         return NS_OK; /* I/O suspension */
00408       
00409       /* If we've completed image output ... */
00410       NS_ASSERTION(mInfo.output_scanline == mInfo.output_height, "We didn't process all of the data!");
00411       mState = JPEG_DONE;
00412     }
00413   }
00414 
00415   case JPEG_DECOMPRESS_PROGRESSIVE:
00416   {
00417     if (mState == JPEG_DECOMPRESS_PROGRESSIVE)
00418     {
00419       LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- JPEG_DECOMPRESS_PROGRESSIVE case");
00420 
00421       int status;
00422       do {
00423         status = jpeg_consume_input(&mInfo);
00424       } while ((status != JPEG_SUSPENDED) &&
00425                (status != JPEG_REACHED_EOI));
00426 
00427       for (;;) {
00428         if (mInfo.output_scanline == 0) {
00429           int scan = mInfo.input_scan_number;
00430 
00431           /* if we haven't displayed anything yet (output_scan_number==0)
00432              and we have enough data for a complete scan, force output
00433              of the last full scan */
00434           if ((mInfo.output_scan_number == 0) &&
00435               (scan > 1) &&
00436               (status != JPEG_REACHED_EOI))
00437             scan--;
00438 
00439           if (!jpeg_start_output(&mInfo, scan))
00440             return NS_OK; /* I/O suspension */
00441         }
00442 
00443         if (mInfo.output_scanline == 0xffffff)
00444           mInfo.output_scanline = 0;
00445 
00446         if (OutputScanlines() == PR_FALSE) {
00447           if (mInfo.output_scanline == 0) {
00448             /* didn't manage to read any lines - flag so we don't call
00449                jpeg_start_output() multiple times for the same scan */
00450             mInfo.output_scanline = 0xffffff;
00451           }
00452           return NS_OK; /* I/O suspension */
00453         }
00454 
00455         if (mInfo.output_scanline == mInfo.output_height)
00456         {
00457           if (!jpeg_finish_output(&mInfo))
00458             return NS_OK; /* I/O suspension */
00459 
00460           if (jpeg_input_complete(&mInfo) &&
00461               (mInfo.input_scan_number == mInfo.output_scan_number))
00462             break;
00463 
00464           mInfo.output_scanline = 0;
00465         }
00466       }
00467 
00468       mState = JPEG_DONE;
00469     }
00470   }
00471 
00472   case JPEG_DONE:
00473   {
00474     LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::WriteFrom -- entering JPEG_DONE case");
00475 
00476     /* Step 7: Finish decompression */
00477 
00478     if (jpeg_finish_decompress(&mInfo) == FALSE)
00479       return NS_OK; /* I/O suspension */
00480 
00481     mState = JPEG_SINK_NON_JPEG_TRAILER;
00482 
00483     /* we're done dude */
00484     break;
00485   }
00486   case JPEG_SINK_NON_JPEG_TRAILER:
00487     PR_LOG(gJPEGlog, PR_LOG_DEBUG,
00488            ("[this=%p] nsJPEGDecoder::WriteFrom -- entering JPEG_SINK_NON_JPEG_TRAILER case\n", this));
00489 
00490     break;
00491 
00492   case JPEG_ERROR:
00493     PR_LOG(gJPEGlog, PR_LOG_DEBUG,
00494            ("[this=%p] nsJPEGDecoder::WriteFrom -- entering JPEG_ERROR case\n", this));
00495 
00496     break;
00497   }
00498 
00499   return NS_OK;
00500 }
00501 
00502 
00503 int
00504 nsJPEGDecoder::OutputScanlines()
00505 {
00506   PRUint32 top = mInfo.output_scanline;
00507   PRBool rv = PR_TRUE;
00508 
00509   while ((mInfo.output_scanline < mInfo.output_height)) {
00510       JSAMPROW samples;
00511 
00512       /* Request one scanline.  Returns 0 or 1 scanlines. */
00513       int ns = jpeg_read_scanlines(&mInfo, mSamples, 1);
00514       
00515       if (ns != 1) {
00516         rv = PR_FALSE; /* suspend */
00517         break;
00518       }
00519 
00520 #if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON)
00521       PRUint8 *ptrOutputBuf = mRGBRow;
00522 
00523       JSAMPLE *j1 = mSamples[0];
00524       for (PRUint32 i=0;i<mInfo.output_width;++i) {
00525         ptrOutputBuf[2] = *j1++;
00526         ptrOutputBuf[1] = *j1++;
00527         ptrOutputBuf[0] = *j1++;
00528         ptrOutputBuf += 3;
00529       }
00530 
00531       samples = mRGBRow;
00532 #elif defined(XP_MAC) || defined(XP_MACOSX)
00533       PRUint8 *ptrOutputBuf = mRGBRow;
00534 
00535       JSAMPLE *j1 = mSamples[0];
00536       for (PRUint32 i=0;i<mInfo.output_width;++i) {
00537         ptrOutputBuf[0] = 0;
00538         ptrOutputBuf[1] = *j1++;
00539         ptrOutputBuf[2] = *j1++;
00540         ptrOutputBuf[3] = *j1++;
00541         ptrOutputBuf += 4;
00542       }
00543 
00544       samples = mRGBRow;
00545 #else
00546       samples = mSamples[0];
00547 #endif
00548 
00549       // Note! row_stride here must match the row_stride in
00550       // nsJPEGDecoder::WriteFrom
00551 #if defined(XP_MAC) || defined(XP_MACOSX)
00552       int row_stride = mInfo.output_width * 4;
00553 #else
00554       int row_stride = mInfo.output_width * 3;
00555 #endif
00556 
00557       PRUint32 bpr;
00558       mFrame->GetImageBytesPerRow(&bpr);
00559       mFrame->SetImageData(
00560         samples,             // data
00561         row_stride,          // length
00562         (mInfo.output_scanline-1) * bpr); // offset
00563   }
00564 
00565   if (top != mInfo.output_scanline) {
00566       nsIntRect r(0, top, mInfo.output_width, mInfo.output_scanline-top);
00567       mObserver->OnDataAvailable(nsnull, mFrame, &r);
00568   }
00569 
00570   return rv;
00571 }
00572 
00573 
00574 /* Override the standard error method in the IJG JPEG decoder code. */
00575 METHODDEF(void)
00576 my_error_exit (j_common_ptr cinfo)
00577 {
00578   nsresult error_code;
00579   decoder_error_mgr *err = (decoder_error_mgr *) cinfo->err;
00580 
00581   /* Convert error to a browser error code */
00582   switch (cinfo->err->msg_code) {
00583   case JERR_OUT_OF_MEMORY:
00584       error_code = NS_ERROR_OUT_OF_MEMORY;
00585   default:
00586       error_code = NS_ERROR_FAILURE;
00587   }
00588 
00589 #ifdef DEBUG
00590   char buffer[JMSG_LENGTH_MAX];
00591 
00592   /* Create the message */
00593   (*cinfo->err->format_message) (cinfo, buffer);
00594 
00595   fprintf(stderr, "JPEG decoding error:\n%s\n", buffer);
00596 #endif
00597 
00598   /* Return control to the setjmp point. */
00599   longjmp(err->setjmp_buffer, error_code);
00600 }
00601 
00602 /******************************************************************************/
00603 /*-----------------------------------------------------------------------------
00604  * This is the callback routine from the IJG JPEG library used to supply new
00605  * data to the decompressor when its input buffer is exhausted.  It juggles
00606  * multiple buffers in an attempt to avoid unnecessary copying of input data.
00607  *
00608  * (A simpler scheme is possible: It's much easier to use only a single
00609  * buffer; when fill_input_buffer() is called, move any unconsumed data
00610  * (beyond the current pointer/count) down to the beginning of this buffer and
00611  * then load new data into the remaining buffer space.  This approach requires
00612  * a little more data copying but is far easier to get right.)
00613  *
00614  * At any one time, the JPEG decompressor is either reading from the necko
00615  * input buffer, which is volatile across top-level calls to the IJG library,
00616  * or the "backtrack" buffer.  The backtrack buffer contains the remaining
00617  * unconsumed data from the necko buffer after parsing was suspended due
00618  * to insufficient data in some previous call to the IJG library.
00619  *
00620  * When suspending, the decompressor will back up to a convenient restart
00621  * point (typically the start of the current MCU). The variables
00622  * next_input_byte & bytes_in_buffer indicate where the restart point will be
00623  * if the current call returns FALSE.  Data beyond this point must be
00624  * rescanned after resumption, so it must be preserved in case the decompressor
00625  * decides to backtrack.
00626  *
00627  * Returns:
00628  *  TRUE if additional data is available, FALSE if no data present and
00629  *   the JPEG library should therefore suspend processing of input stream
00630  *---------------------------------------------------------------------------*/
00631 
00632 /******************************************************************************/
00633 /* data source manager method                                                 */
00634 /******************************************************************************/
00635 
00636 
00637 /******************************************************************************/
00638 /* data source manager method 
00639         Initialize source.  This is called by jpeg_read_header() before any
00640         data is actually read.  May leave
00641         bytes_in_buffer set to 0 (in which case a fill_input_buffer() call
00642         will occur immediately).
00643 */
00644 METHODDEF(void)
00645 init_source (j_decompress_ptr jd)
00646 {
00647 }
00648 
00649 /******************************************************************************/
00650 /* data source manager method
00651         Skip num_bytes worth of data.  The buffer pointer and count should
00652         be advanced over num_bytes input bytes, refilling the buffer as
00653         needed.  This is used to skip over a potentially large amount of
00654         uninteresting data (such as an APPn marker).  In some applications
00655         it may be possible to optimize away the reading of the skipped data,
00656         but it's not clear that being smart is worth much trouble; large
00657         skips are uncommon.  bytes_in_buffer may be zero on return.
00658         A zero or negative skip count should be treated as a no-op.
00659 */
00660 METHODDEF(void)
00661 skip_input_data (j_decompress_ptr jd, long num_bytes)
00662 {
00663   decoder_source_mgr *src = (decoder_source_mgr *)jd->src;
00664 
00665   if (num_bytes > (long)src->pub.bytes_in_buffer) {
00666     /*
00667      * Can't skip it all right now until we get more data from
00668      * network stream. Set things up so that fill_input_buffer
00669      * will skip remaining amount.
00670      */
00671     src->decoder->mBytesToSkip = (size_t)num_bytes - src->pub.bytes_in_buffer;
00672     src->pub.next_input_byte += src->pub.bytes_in_buffer;
00673     src->pub.bytes_in_buffer = 0;
00674 
00675   } else {
00676     /* Simple case. Just advance buffer pointer */
00677 
00678     src->pub.bytes_in_buffer -= (size_t)num_bytes;
00679     src->pub.next_input_byte += num_bytes;
00680   }
00681 }
00682 
00683 
00684 /******************************************************************************/
00685 /* data source manager method
00686         This is called whenever bytes_in_buffer has reached zero and more
00687         data is wanted.  In typical applications, it should read fresh data
00688         into the buffer (ignoring the current state of next_input_byte and
00689         bytes_in_buffer), reset the pointer & count to the start of the
00690         buffer, and return TRUE indicating that the buffer has been reloaded.
00691         It is not necessary to fill the buffer entirely, only to obtain at
00692         least one more byte.  bytes_in_buffer MUST be set to a positive value
00693         if TRUE is returned.  A FALSE return should only be used when I/O
00694         suspension is desired.
00695 */
00696 METHODDEF(boolean)
00697 fill_input_buffer (j_decompress_ptr jd)
00698 {
00699   decoder_source_mgr *src = (decoder_source_mgr *)jd->src;
00700 
00701   unsigned char *new_buffer = (unsigned char *)src->decoder->mBuffer;
00702   PRUint32 new_buflen = src->decoder->mBufferLen;
00703   PRUint32 bytesToSkip = src->decoder->mBytesToSkip;
00704 
00705   switch(src->decoder->mFillState) {
00706   case READING_BACK:
00707     {
00708       if (!new_buffer || new_buflen == 0)
00709         return PR_FALSE; /* suspend */
00710 
00711       src->decoder->mBufferLen = 0;
00712 
00713       if (bytesToSkip != 0) {
00714         if (bytesToSkip < new_buflen) {
00715           /* All done skipping bytes; Return what's left. */
00716           new_buffer += bytesToSkip;
00717           new_buflen -= bytesToSkip;
00718           src->decoder->mBytesToSkip = 0;
00719         } else {
00720           /* Still need to skip some more data in the future */
00721           src->decoder->mBytesToSkip -= (size_t)new_buflen;
00722           return PR_FALSE; /* suspend */
00723         }
00724       }
00725 
00726       src->decoder->mBackBufferUnreadLen = src->pub.bytes_in_buffer;
00727 
00728       src->pub.next_input_byte = new_buffer;
00729       src->pub.bytes_in_buffer = (size_t)new_buflen;
00730       src->decoder->mFillState = READING_NEW;
00731 
00732       return PR_TRUE;
00733     }
00734     break;
00735 
00736   case READING_NEW:
00737     {
00738       if (src->pub.next_input_byte != src->decoder->mBuffer) {
00739         /* Backtrack data has been permanently consumed. */
00740         src->decoder->mBackBufferUnreadLen = 0;
00741         src->decoder->mBackBufferLen = 0;
00742       }
00743 
00744       /* Save remainder of netlib buffer in backtrack buffer */
00745       PRUint32 new_backtrack_buflen = src->pub.bytes_in_buffer + src->decoder->mBackBufferLen;
00746 
00747 
00748       /* Make sure backtrack buffer is big enough to hold new data. */
00749       if (src->decoder->mBackBufferSize < new_backtrack_buflen) {
00750 
00751         /* Round up to multiple of 16 bytes. */
00752         PRUint32 roundup_buflen = ((new_backtrack_buflen + 15) >> 4) << 4;
00753         if (src->decoder->mBackBufferSize) {
00754             src->decoder->mBackBuffer =
00755           (JOCTET *)PR_REALLOC(src->decoder->mBackBuffer, roundup_buflen);
00756         } else {
00757           src->decoder->mBackBuffer = (JOCTET*)PR_MALLOC(roundup_buflen);
00758         }
00759 
00760         /* Check for OOM */
00761         if (!src->decoder->mBackBuffer) {
00762 #if 0
00763           j_common_ptr cinfo = (j_common_ptr)(&src->js->jd);
00764           cinfo->err->msg_code = JERR_OUT_OF_MEMORY;
00765           my_error_exit(cinfo);
00766 #endif
00767         }
00768           
00769         src->decoder->mBackBufferSize = (size_t)roundup_buflen;
00770 
00771         /* Check for malformed MARKER segment lengths. */
00772         if (new_backtrack_buflen > MAX_JPEG_MARKER_LENGTH) {
00773           my_error_exit((j_common_ptr)(&src->decoder->mInfo));
00774         }
00775       }
00776 
00777 
00778       /* Copy remainder of netlib buffer into backtrack buffer. */
00779       memmove(src->decoder->mBackBuffer + src->decoder->mBackBufferLen,
00780               src->pub.next_input_byte,
00781               src->pub.bytes_in_buffer);
00782 
00783 
00784       /* Point to start of data to be rescanned. */
00785       src->pub.next_input_byte = src->decoder->mBackBuffer + src->decoder->mBackBufferLen - src->decoder->mBackBufferUnreadLen;
00786       src->pub.bytes_in_buffer += src->decoder->mBackBufferUnreadLen;
00787       src->decoder->mBackBufferLen = (size_t)new_backtrack_buflen;
00788     
00789       src->decoder->mFillState = READING_BACK;
00790 
00791       return PR_FALSE;
00792     }
00793     break;
00794   }
00795 
00796   return PR_FALSE;
00797 }
00798 
00799 /******************************************************************************/
00800 /* data source manager method */
00801 /*
00802  * Terminate source --- called by jpeg_finish_decompress() after all
00803  * data has been read to clean up JPEG source manager. NOT called by 
00804  * jpeg_abort() or jpeg_destroy().
00805  */
00806 METHODDEF(void)
00807 term_source (j_decompress_ptr jd)
00808 {
00809   decoder_source_mgr *src = (decoder_source_mgr *)jd->src;
00810 
00811   if (src->decoder->mObserver) {
00812     src->decoder->mObserver->OnStopFrame(nsnull, src->decoder->mFrame);
00813     src->decoder->mObserver->OnStopContainer(nsnull, src->decoder->mImage);
00814     src->decoder->mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
00815   }
00816 
00817   PRBool isMutable = PR_FALSE;
00818   if (src->decoder->mImageLoad) 
00819       src->decoder->mImageLoad->GetIsMultiPartChannel(&isMutable);
00820   src->decoder->mFrame->SetMutable(isMutable);
00821 }