home *** CD-ROM | disk | FTP | other *** search
- /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.0 (the "NPL"); you may not use this file except in
- * compliance with the NPL. You may obtain a copy of the NPL at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the NPL is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
- * for the specific language governing rights and limitations under the
- * NPL.
- *
- * The Initial Developer of this code under the NPL is Netscape
- * Communications Corporation. Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All Rights
- * Reserved.
- */
-
- /*
- * jpeg.c --- Glue code to Independent JPEG Group decoder library
- * $Id: jpeg.c,v 3.1 1998/03/28 03:35:03 ltabb Exp $
- */
-
-
- /*
- #include "xp.h"
- */
-
- #include "if.h"
-
- #include "xp_core.h"
- #include "merrors.h"
-
- #include "il.h"
-
- #include <ctype.h> /* to declare isprint() */
- #ifndef XP_MAC
- # include <sys/types.h>
- #endif
-
- #include "jinclude.h"
- #include "jpeglib.h"
- #include "jerror.h"
-
- extern int MK_OUT_OF_MEMORY;
-
- #ifdef XP_OS2_HACK
- /* IBM-MAS: We removed setjmp.h from XP_core.h, now we need it here. */
- /* We need to see if we can fix hwthreads/XP_CORE correctly.. */
- #include <setjmp.h>
- #endif
-
- #ifdef PROFILE
- # pragma profile on
- #endif
-
-
- /* Normal JFIF markers can't have more bytes than this. */
- #define MAX_JPEG_MARKER_LENGTH (((uint32)1 << 16) - 1)
-
- /*
- * States that the jpeg decoder might be in
- */
- typedef enum {
- JPEG_HEADER, /* Reading JFIF headers */
- JPEG_START_DECOMPRESS,
- JPEG_DECOMPRESS_PROGRESSIVE, /* Output progressive pixels */
- JPEG_DECOMPRESS_SEQUENTIAL, /* Output sequential pixels */
- JPEG_FINAL_PROGRESSIVE_SCAN_OUTPUT,
- JPEG_DONE,
- JPEG_SINK_NON_JPEG_TRAILER /* Some image files have a */
- /* non-JPEG trailer */
- } jstate;
-
- typedef struct {
- struct jpeg_error_mgr pub; /* "public" fields for IJG library*/
- jmp_buf setjmp_buffer; /* For handling catastropic errors */
- } il_error_mgr;
-
- /*
- * Structure used to manage the JPEG decoder stream.
- */
- typedef struct jpeg_struct {
- jstate state; /* Decoder FSM state */
- int pass_num;
- int completed_output_passes;
-
- int rows_per_chunk;
- void *timeout;
-
- /* One scanline's worth of post-processed sample data */
- JSAMPARRAY samples;
- JSAMPARRAY samples3;
- /* IJG JPEG library decompressor state */
- struct jpeg_decompress_struct jd;
- il_error_mgr jerr;
- il_container *ic;
- } jpeg_struct;
-
-
-
-
- /* Possible states for JPEG source manager */
- enum data_source_state {
- dss_consuming_backtrack_buffer = 0, /* Must be zero for init purposes */
- dss_consuming_netlib_buffer
- };
-
- /*
- * Implementation of a JPEG src object that understands our state machine
- */
- typedef struct {
- /* public fields; must be first in this struct! */
- struct jpeg_source_mgr pub;
-
- jpeg_struct *js; /* pointer to netlib stream object */
-
- int bytes_to_skip; /* remaining bytes to skip */
-
- enum data_source_state state;
-
- JOCTET *netlib_buffer; /* next buffer for fill_input_buffer */
- uint32 netlib_buflen;
-
- /*
- * Buffer of "remaining" characters left over after a call to
- * fill_input_buffer(), when no additional data is available.
- */
- JOCTET *backtrack_buffer;
- size_t backtrack_buffer_size; /* Allocated size of backtrack_buffer */
- size_t backtrack_buflen; /* Offset of end of active backtrack data */
- size_t backtrack_num_unread_bytes; /* Length of active backtrack data */
- } il_source_mgr;
-
-
- /* Override the standard error method in the IJG JPEG decoder code. */
- void PR_CALLBACK
- il_error_exit (j_common_ptr cinfo)
- {
- int error_code;
- il_error_mgr *err = (il_error_mgr *) cinfo->err;
-
- #ifdef DEBUG
- if (il_debug >= 1) {
- char buffer[JMSG_LENGTH_MAX];
-
- /* Create the message */
- (*cinfo->err->format_message) (cinfo, buffer);
-
- XP_TRACE(("%s\n", buffer));
- }
- #endif
-
- /* Convert error to a browser error code */
- if (cinfo->err->msg_code == JERR_OUT_OF_MEMORY)
- error_code = MK_OUT_OF_MEMORY;
- else
- error_code = MK_IMAGE_LOSSAGE;
-
- /* Return control to the setjmp point. */
- longjmp(err->setjmp_buffer, error_code);
- }
-
-
- void PR_CALLBACK
- init_source (j_decompress_ptr jd)
- {
- }
-
- /*-----------------------------------------------------------------------------
- * This is the callback routine from the IJG JPEG library used to supply new
- * data to the decompressor when its input buffer is exhausted. It juggles
- * multiple buffers in an attempt to avoid unnecessary copying of input data.
- *
- * (A simpler scheme is possible: It's much easier to use only a single
- * buffer; when fill_input_buffer() is called, move any unconsumed data
- * (beyond the current pointer/count) down to the beginning of this buffer and
- * then load new data into the remaining buffer space. This approach requires
- * a little more data copying but is far easier to get right.)
- *
- * At any one time, the JPEG decompressor is either reading from the netlib
- * input buffer, which is volatile across top-level calls to the IJG library,
- * or the "backtrack" buffer. The backtrack buffer contains the remaining
- * unconsumed data from the netlib buffer after parsing was suspended due
- * to insufficient data in some previous call to the IJG library.
- *
- * When suspending, the decompressor will back up to a convenient restart
- * point (typically the start of the current MCU). The variables
- * next_input_byte & bytes_in_buffer indicate where the restart point will be
- * if the current call returns FALSE. Data beyond this point must be
- * rescanned after resumption, so it must be preserved in case the decompressor
- * decides to backtrack.
- *
- * Returns:
- * TRUE if additional data is available, FALSE if no data present and
- * the JPEG library should therefore suspend processing of input stream
- *---------------------------------------------------------------------------*/
- boolean PR_CALLBACK
- fill_input_buffer (j_decompress_ptr jd)
- {
- il_source_mgr *src = (il_source_mgr *)jd->src;
- enum data_source_state data_source_state = src->state;
- uint32 bytesToSkip, new_backtrack_buflen, new_buflen, roundup_buflen;
- unsigned char *new_buffer;
-
- ILTRACE(5,("il:jpeg: fill, state=%d, nib=0x%x, bib=%d", data_source_state,
- src->pub.next_input_byte, src->pub.bytes_in_buffer));
-
- switch (data_source_state) {
-
- /* Decompressor reached end of backtrack buffer. Return netlib buffer.*/
- case dss_consuming_backtrack_buffer:
- new_buffer = (unsigned char *)src->netlib_buffer;
- new_buflen = src->netlib_buflen;
- if ((new_buffer == NULL) || (new_buflen == 0))
- goto suspend;
-
- /*
- * Clear, so that repeated calls to fill_input_buffer() do not
- * deliver the same netlib buffer more than once.
- */
- src->netlib_buflen = 0;
-
- /* Discard data if asked by skip_input_data(). */
- bytesToSkip = src->bytes_to_skip;
- if (bytesToSkip) {
- if (bytesToSkip < new_buflen) {
- /* All done skipping bytes; Return what's left. */
- new_buffer += bytesToSkip;
- new_buflen -= bytesToSkip;
- src->bytes_to_skip = 0;
- } else {
- /* Still need to skip some more data in the future */
- src->bytes_to_skip -= (size_t)new_buflen;
- goto suspend;
- }
- }
-
- /* Save old backtrack buffer parameters, in case the decompressor
- * backtracks and we're forced to restore its contents.
- */
- src->backtrack_num_unread_bytes = src->pub.bytes_in_buffer;
-
- src->pub.next_input_byte = new_buffer;
- src->pub.bytes_in_buffer = (size_t)new_buflen;
- src->state = dss_consuming_netlib_buffer;
- return TRUE;
-
- /* Reached end of netlib buffer. Suspend */
- case dss_consuming_netlib_buffer:
- if (src->pub.next_input_byte != src->netlib_buffer) {
- /* Backtrack data has been permanently consumed. */
- src->backtrack_num_unread_bytes = 0;
- src->backtrack_buflen = 0;
- }
-
- /* Save remainder of netlib buffer in backtrack buffer */
- new_backtrack_buflen = src->pub.bytes_in_buffer + src->backtrack_buflen;
-
- /* Make sure backtrack buffer is big enough to hold new data. */
- if (src->backtrack_buffer_size < new_backtrack_buflen) {
-
- /* Round up to multiple of 16 bytes. */
- roundup_buflen = ((new_backtrack_buflen + 15) >> 4) << 4;
- if (src->backtrack_buffer_size) {
- src->backtrack_buffer =
- (JOCTET *)XP_REALLOC(src->backtrack_buffer, roundup_buflen);
- } else {
- src->backtrack_buffer = (JOCTET*)XP_ALLOC(roundup_buflen);
- }
-
- /* Check for OOM */
- if (! src->backtrack_buffer) {
- j_common_ptr cinfo = (j_common_ptr)(&src->js->jd);
- cinfo->err->msg_code = JERR_OUT_OF_MEMORY;
- il_error_exit(cinfo);
- }
-
- src->backtrack_buffer_size = (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. */
- XP_BCOPY (src->pub.next_input_byte,
- src->backtrack_buffer + src->backtrack_buflen,
- src->pub.bytes_in_buffer);
-
- /* Point to start of data to be rescanned. */
- src->pub.next_input_byte = src->backtrack_buffer +
- src->backtrack_buflen - src->backtrack_num_unread_bytes;
- src->pub.bytes_in_buffer += src->backtrack_num_unread_bytes;
- src->backtrack_buflen = (size_t)new_backtrack_buflen;
-
- src->state = dss_consuming_backtrack_buffer;
- goto suspend;
-
- default:
- XP_ASSERT(0);
- return FALSE;
- }
-
- suspend:
- ILTRACE(5,(" Suspending, bib=%d", src->pub.bytes_in_buffer));
- return FALSE;
- }
-
- void PR_CALLBACK
- skip_input_data (j_decompress_ptr jd, long num_bytes)
- {
- il_source_mgr *src = (il_source_mgr *)jd->src;
-
- #ifdef DEBUG
- ILTRACE(5, ("il:jpeg: skip_input_data js->buf=0x%x js->buflen=%d skip=%d",
- src->netlib_buffer, src->netlib_buflen,
- num_bytes));
- #endif
-
- if (num_bytes > (long)src->pub.bytes_in_buffer) {
- /*
- * Can't skip it all right now until we get more data from
- * network stream. Set things up so that fill_input_buffer
- * will skip remaining amount.
- */
- src->bytes_to_skip = (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;
- } else {
- /* Simple case. Just advance buffer pointer */
- src->pub.bytes_in_buffer -= (size_t)num_bytes;
- src->pub.next_input_byte += num_bytes;
- }
- }
-
-
- /*
- * Terminate source --- called by jpeg_finish_decompress() after all
- * data has been read to clean up JPEG source manager.
- */
- void PR_CALLBACK
- term_source (j_decompress_ptr jd)
- {
- /* No work necessary here */
- }
-
-
- /*-----------------------------------------------------------------------------
- * Setup a JPEG source object for streaming data in a demand-driven
- * fashion into the IJG JPEG decompression library. A JPEG source
- * object consists of a set of callback functions which the
- * decompressor library calls when it needs more data or to seek ahead
- * in the input stream.
- *
- * Returns:
- * TRUE if setup succeeds, FALSE otherwise.
- *---------------------------------------------------------------------------*/
- static int
- setup_jpeg_src (j_decompress_ptr jd, jpeg_struct *js)
- {
- il_source_mgr *src;
-
- if (jd->src == NULL) {
- src = XP_NEW_ZAP(il_source_mgr);
- if (!src) {
- XP_TRACE(("il:jpeg: src manager memory lossage"));
- return FALSE;
- }
- jd->src = (struct jpeg_source_mgr *) src;
- }
-
- src = (il_source_mgr *)jd->src;
- src->js = js;
-
- /* Setup callback functions. */
- src->pub.init_source = init_source;
- src->pub.fill_input_buffer = fill_input_buffer;
- src->pub.skip_input_data = skip_input_data;
- src->pub.resync_to_restart = jpeg_resync_to_restart;
- src->pub.term_source = term_source;
-
- return TRUE;
- }
-
-
-
- /*
- * Macros for fetching data from the data source module.
- *
- * At all times, cinfo->src->next_input_byte and ->bytes_in_buffer reflect
- * the current restart point; we update them only when we have reached a
- * suitable place to restart if a suspension occurs.
- */
-
- /* Declare and initialize local copies of input pointer/count */
- #define INPUT_VARS(cinfo) \
- struct jpeg_source_mgr * datasrc = (cinfo)->src; \
- const JOCTET * next_input_byte = datasrc->next_input_byte; \
- size_t bytes_in_buffer = datasrc->bytes_in_buffer
-
- /* Unload the local copies --- do this only at a restart boundary */
- #define INPUT_SYNC(cinfo) \
- ( datasrc->next_input_byte = next_input_byte, \
- datasrc->bytes_in_buffer = bytes_in_buffer )
-
- /* Reload the local copies --- seldom used except in MAKE_BYTE_AVAIL */
- #define INPUT_RELOAD(cinfo) \
- ( next_input_byte = datasrc->next_input_byte, \
- bytes_in_buffer = datasrc->bytes_in_buffer )
-
- /* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available.
- * Note we do *not* do INPUT_SYNC before calling fill_input_buffer,
- * but we must reload the local copies after a successful fill.
- */
- #define MAKE_BYTE_AVAIL(cinfo,action) \
- if (bytes_in_buffer == 0) { \
- if (! (*datasrc->fill_input_buffer) (cinfo)) \
- { action; } \
- INPUT_RELOAD(cinfo); \
- } \
- bytes_in_buffer--
-
- /* Read a byte into variable V.
- * If must suspend, take the specified action (typically "return FALSE").
- */
- #define INPUT_BYTE(cinfo,V,action) \
- MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \
- V = GETJOCTET(*next_input_byte++); )
-
- /* As above, but read two bytes interpreted as an unsigned 16-bit integer.
- * V should be declared unsigned int or perhaps INT32.
- */
- #define INPUT_2BYTES(cinfo,V,action) \
- MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \
- V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 8; \
- MAKE_BYTE_AVAIL(cinfo,action); \
- V += GETJOCTET(*next_input_byte++); )
-
-
- /* Process the COM marker segment, which contains human-readable comments. */
- boolean PR_CALLBACK
- il_jpeg_COM_handler (j_decompress_ptr cinfo)
- {
- uint length;
- char *comment;
- unsigned int ch;
-
- il_source_mgr *src = (il_source_mgr*) cinfo->src;
- il_container *ic = src->js->ic;
- static unsigned int lastch = 0;
-
- INPUT_VARS(cinfo);
-
- /* Get 16-bit comment length word. */
- INPUT_2BYTES(cinfo, length, return FALSE);
- length -= 2; /* discount the length word itself */
-
- if (ic->comment)
- XP_FREE(ic->comment);
- comment = ic->comment = (char *)XP_ALLOC(length + 1);
- ic->comment_length = length;
-
- if (!ic->comment) {
- skip_input_data(cinfo, length + 2);
- return TRUE;
- }
-
- /* Emit the character in a readable form.
- * Newlines in CR, CR/LF, or LF form will be printed as one newline.
- */
- while (length-- > 0) {
- INPUT_BYTE(cinfo, ch, return FALSE);
-
- if (ch == '\r') {
- *comment++ = '\n';
- } else if (ch == '\n') {
- if (lastch != '\r')
- *comment++ = '\n';
- } else if (isprint(ch) || !ch) {
- *comment++ = ch;
- }
- lastch = ch;
- }
-
- *comment = 0;
-
- INPUT_SYNC(cinfo);
-
- return TRUE;
- }
-
- int il_jpeg_init(il_container *ic)
- {
- jpeg_struct *js;
- j_decompress_ptr jd;
- NI_ColorSpace *src_color_space = ic->src_header->color_space;
- NI_RGBBits *rgb = &src_color_space->bit_alloc.rgb;
-
- js = XP_NEW_ZAP(jpeg_struct);
- if (!js) {
- XP_TRACE(("il:jpeg: jpeg_struct memory lossage"));
- return FALSE;
- }
-
- /* Init jpeg_struct */
- ic->ds = js;
- js->state = JPEG_HEADER;
- js->samples = NULL;
- js->samples3 = NULL;
- js->ic = ic;
-
- jd = &js->jd;
-
- /* Install our error handler because the default is to exit(). */
- jd->err = jpeg_std_error(&js->jerr.pub);
- js->jerr.pub.error_exit = il_error_exit;
-
- /* Control returns here if an error occurs before setup completes. */
- if(setjmp(js->jerr.setjmp_buffer)) {
- /* Free up all the data structures */
- il_jpeg_abort(ic);
- return FALSE;
- }
-
- #ifdef DEBUG
- if (il_debug > 20) {
- jd->err->trace_level = 99;
- }
- #endif
-
-
- jpeg_create_decompress(jd);
-
- /* Setup jpeg data source object */
- if (!setup_jpeg_src(jd, js)) {
- XP_TRACE(("il:jpeg: jpeg source memory lossage"));
- /* Free up all the data structures */
- il_jpeg_abort(ic);
- return FALSE;
- }
-
- /* Insert custom COM comment marker processor. */
- jpeg_set_marker_processor(jd, JPEG_COM, il_jpeg_COM_handler);
-
-
- /* Initialize the container's source image header. */
- src_color_space->type = NI_TrueColor;
- src_color_space->pixmap_depth = 24;
- rgb->red_bits = 8;
- rgb->red_shift = 16;
- rgb->green_bits = 8;
- rgb->green_shift = 8;
- rgb->blue_bits = 8;
- rgb->blue_shift = 0;
-
- return TRUE;
- }
-
-
- /*-----------------------------------------------------------------------------
- * Calling this routine sends scanlines to the front-end for display.
- * Scanlines will be emitted until the entire scan has been displayed or
- * until insufficient input data is available to continue output.
- *
- * The maximum number of scanlines to be output is controlled by the
- * `num_scanlines' parameter. Set num_scanlines to -1 to output scanlines
- * until input data is exhausted.
- *
- * Returns:
- * TRUE if output was discontinued due to lack of input data
- * FALSE, otherwise
- *---------------------------------------------------------------------------*/
- int
- output_jpeg_scanlines(il_container *ic, int num_scanlines)
- {
- jpeg_struct *js = (jpeg_struct *)ic->ds;
- j_decompress_ptr jd = &js->jd;
- int input_exhausted;
- int pass;
-
- #ifdef DEBUG
- uint start_scanline = jd->output_scanline;
- #endif
-
- if (js->state == JPEG_FINAL_PROGRESSIVE_SCAN_OUTPUT)
- pass = IL_FINAL_PASS;
- else
- pass = js->completed_output_passes + 1;
-
- while ((jd->output_scanline < jd->output_height) && num_scanlines--) {
- JSAMPROW samples;
-
- /* Request one scanline. Returns 0 or 1 scanlines. */
- int ns = jpeg_read_scanlines(jd, js->samples, 1);
- ILTRACE(15,("il:jpeg: scanline %d, ns = %d",
- jd->output_scanline, ns));
- if (ns != 1) {
- ILTRACE(5,("il:jpeg: suspending scanline"));
- input_exhausted = TRUE;
- goto done;
- }
-
- /* If grayscale image ... */
- if (jd->output_components == 1) {
- JSAMPLE j, *j1, *j1end, *j3;
-
- /* Convert from grayscale to RGB. */
- j1 = js->samples[0];
- j1end = j1 + jd->output_width;
- j3 = js->samples3[0];
- while (j1 < j1end) {
- j = *j1++;
- j3[0] = j;
- j3[1] = j;
- j3[2] = j;
- j3 += 3;
- }
- samples = js->samples3[0];
- } else { /* 24-bit color image */
- samples = js->samples[0];
- }
-
- il_emit_row(ic, 0, samples, 0, jd->output_width, jd->output_scanline-1,
- 1, ilErase, pass);
- }
-
- input_exhausted = FALSE;
-
- done:
-
- #ifdef DEBUG
- if (start_scanline != jd->output_scanline)
- ILTRACE(4, ("il: jpeg: Input pass=%2d, next input scanline=%3d,"
- " emitted %3d - %3d\n",
- jd->input_scan_number, jd->input_iMCU_row * 16,
- start_scanline, jd->output_scanline - 1));
-
- #endif
-
- return input_exhausted;
- }
-
- #define JPEG_OUTPUT_CHUNK_SIZE 50000
-
- /* Timeout durations, in milliseconds */
-
- /* Delay between displaying chunks of pixels for the first scan. */
- #define JPEG_TIMEOUT_INITIAL_DELAY 100
-
- /* Delay between displaying chunks of pixels for subsequent scans */
- #define JPEG_TIMEOUT_DELAY 200
-
- static void
- jpeg_timeout_callback(void *closure)
- {
- uint32 delay;
- jpeg_struct *js = (jpeg_struct *)closure;
- j_decompress_ptr jd = &js->jd;
-
- if (jd->input_scan_number == 1)
- delay = JPEG_TIMEOUT_INITIAL_DELAY;
- else
- delay = JPEG_TIMEOUT_DELAY;
-
- /*
- * Perform incremental display of progressive scans,
- * except don't display unless enough time has elapsed
- * since the previous scan was displayed.
- */
-
- if (js->pass_num != js->completed_output_passes + 1) {
- if (! jpeg_start_output(jd, jd->input_scan_number)) {
- ILTRACE(1, ("il: jpeg: jpeg_start_output returned"
- "FALSE!\n"));
- goto done;
- }
- js->pass_num = js->completed_output_passes + 1;
- }
-
- js->timeout = NULL;
-
- /* If there's no more data to process for this scan,
- wait until jpeg_write() wakes us up by scheduling a
- new timeout */
- if (output_jpeg_scanlines(js->ic, js->rows_per_chunk))
- return;
-
- /* If we're at the end of this progressive scan ... */
- if (jd->output_scanline == jd->output_height) {
- if (jpeg_finish_output(jd))
- js->completed_output_passes++;
- }
-
- done:
- js->timeout = FE_SetTimeout(jpeg_timeout_callback, js, delay);
- }
-
- /*
- * Force the display of scans, even if no data has entered the netlib
- * buffer since jpeg_timeout_callback() was run. This will flush
- * pixels to the screen during a long pause in a bursty data source.
- */
- #if 0
- static void
- jpeg_idle_callback(void *closure)
- {
- il_container *ic = (il_container *)closure;
- il_jpeg_write(ic, NULL, 0);
- }
- #endif
-
- int
- il_jpeg_write(il_container *ic, const unsigned char *buf, int32 len)
- {
- int row_stride, status;
- int input_exhausted;
- int error_code;
-
- jpeg_struct *js = (jpeg_struct *)ic->ds;
- j_decompress_ptr jd = &js->jd;
- il_source_mgr *src = (il_source_mgr*) js->jd.src;
- NI_PixmapHeader *img_header = &ic->image->header;
- #ifndef M12N /* XXXM12N Get rid of this */
- NI_PixmapHeader *src_header = ic->src_header;
- NI_ColorMap *cmap = &src_header->color_space->cmap;
- #endif /* M12N */
-
- /* If this assert fires, chances are the netlib
- continued to send data after the image stream was closed. */
- XP_ASSERT(js);
- if (!js) {
- #ifdef DEBUG
- XP_TRACE(("Netlib sent data after the image stream was closed\n"));
- #endif
- return MK_IMAGE_LOSSAGE;
- }
-
- /* Return here if there is a fatal error. */
- if ((error_code = setjmp(js->jerr.setjmp_buffer))) {
- /* Free up all the data structures */
- il_jpeg_abort(ic);
- return error_code;
- }
-
- /* Register new buffer contents with data source manager. */
- src->netlib_buffer = (JOCTET*)buf;
- src->netlib_buflen = (uint32)len;
-
- input_exhausted = 0;
- while (! input_exhausted) {
- ILTRACE(5,("il:jpeg: write, state=%d, buf=0x%x, len=%d",
- js->state, buf, len));
-
- switch (js->state) {
- case JPEG_HEADER:
- if (jpeg_read_header(jd, TRUE) != JPEG_SUSPENDED) {
- #ifndef M12N /* XXXM12N Get rid of this */
- cmap->map = 0;
- cmap->num_colors = ic->cs->default_map_size;
- #endif /* M12N */
- ic->src_header->width = jd->image_width;
- ic->src_header->height = jd->image_height;
- if ((status = il_size(ic))) {
- XP_TRACE(("il:jpeg: MEM il_size"));
- return status;
- }
-
- il_setup_color_space_converter(ic); /* XXXM12N Should check
- return code. */
-
- js->rows_per_chunk =
- JPEG_OUTPUT_CHUNK_SIZE / img_header->widthBytes;
-
- /* FIXME -- Should reset dct_method and dither mode
- * for final pass of progressive JPEG
- */
- jd->dct_method = JDCT_FASTEST;
- jd->dither_mode = JDITHER_ORDERED;
- jd->do_fancy_upsampling = FALSE;
- jd->enable_2pass_quant = FALSE;
- jd->do_block_smoothing = TRUE;
-
- /*
- * Don't allocate a giant and superfluous memory buffer
- * when the image is a sequential JPEG.
- */
- jd->buffered_image = jpeg_has_multiple_scans(jd);
-
- /* Used to set up image size so arrays can be allocated */
- jpeg_calc_output_dimensions(jd);
-
- /*
- * Make a one-row-high sample array that will go away
- * when done with image. Always make it big enough to
- * hold an RGB row. Since this uses the IJG memory
- * manager, it must be allocated before the call to
- * jpeg_start_compress().
- */
- row_stride = jd->output_width * jd->output_components;
- js->samples = (*jd->mem->alloc_sarray)((j_common_ptr) jd,
- JPOOL_IMAGE,
- row_stride, 1);
-
- /* Allocate RGB buffer for conversion from greyscale. */
- if (jd->output_components != 3) {
- row_stride = jd->output_width * 3;
- js->samples3 = (*jd->mem->alloc_sarray)((j_common_ptr) jd,
- JPOOL_IMAGE,
- row_stride, 1);
- }
- js->state = JPEG_START_DECOMPRESS;
-
- } else {
- ILTRACE(5,("il:jpeg: suspending header"));
- input_exhausted = TRUE;
- }
- break;
-
- case JPEG_START_DECOMPRESS:
- if (jpeg_start_decompress(jd)) {
-
- /* If this is a progressive JPEG ... */
- if (jd->buffered_image) {
- js->state = JPEG_DECOMPRESS_PROGRESSIVE;
- } else {
- js->state = JPEG_DECOMPRESS_SEQUENTIAL;
- }
- }
-
- break;
-
- case JPEG_DECOMPRESS_SEQUENTIAL:
- input_exhausted = output_jpeg_scanlines(ic, -1);
-
- /* If we've completed image output ... */
- if (jd->output_scanline == jd->output_height)
- js->state = JPEG_DONE;
-
- break;
-
- case JPEG_DECOMPRESS_PROGRESSIVE:
-
- /*
- * Set a timeout to trigger display of the next progressive scan.
- * Any scans which arrive in the intervening time will be displayed
- * instead. Thus, the decoder adapts to the data arrival rate.
- */
- if (js->timeout == NULL) {
- uint32 delay;
-
- /*
- * First time around, display the scan a little
- * quicker than in subsequent scans.
- */
- if (jd->input_scan_number == 1)
- delay = JPEG_TIMEOUT_INITIAL_DELAY;
- else {
- delay = JPEG_TIMEOUT_DELAY;
- }
-
- js->timeout = FE_SetTimeout(jpeg_timeout_callback, js, delay);
-
- }
-
- /* Eat all the available input data in the netlib buffer. */
- do {
- status = jpeg_consume_input(jd);
- } while (!((status == JPEG_SUSPENDED) ||
- (status == JPEG_REACHED_EOI)));
-
- /* If we've parsed the whole input, do final display immediately. */
- if (status == JPEG_REACHED_EOI) {
- js->state = JPEG_FINAL_PROGRESSIVE_SCAN_OUTPUT;
- break;
- }
-
- input_exhausted = TRUE;
-
- break;
-
- case JPEG_FINAL_PROGRESSIVE_SCAN_OUTPUT:
-
- if ((jd->input_scan_number == jd->output_scan_number) &&
- (js->pass_num == js->completed_output_passes + 1)) {
- output_jpeg_scanlines(ic, -1);
- jpeg_finish_output(jd);
- } else {
-
- /* Abort the last output scan.
- * We need to redraw the whole image.
- */
- if (js->pass_num == js->completed_output_passes + 1)
- jpeg_finish_output(jd);
-
- jpeg_start_output(jd, jd->input_scan_number);
- output_jpeg_scanlines(ic, -1);
- jpeg_finish_output(jd);
- }
-
- js->state = JPEG_DONE;
-
- /* Fall through ... */
-
- case JPEG_DONE:
- status = jpeg_finish_decompress(jd);
-
- /* Clear any pending timeouts */
- if (js->timeout) {
- FE_ClearTimeout(js->timeout);
- js->timeout = NULL;
- }
-
- input_exhausted = TRUE;
- js->state = JPEG_SINK_NON_JPEG_TRAILER; /* Be prepared for */
- /* non-JPEG data after */
- /* EOI marker. */
- break;
-
- case JPEG_SINK_NON_JPEG_TRAILER: /* Ignore non-JPEG trailer, if any */
- input_exhausted = TRUE;
- break;
-
- default:
- XP_ASSERT(0);
- break;
- }
- }
-
- return 0;
- }
-
- void
- il_jpeg_abort(il_container *ic)
- {
- jpeg_struct *js = (jpeg_struct *)ic->ds;
-
- if (js) {
- il_source_mgr *src = (il_source_mgr*) js->jd.src;
-
- /*
- * Free up the memory that we allocated ourselves (not memory we
- * allocated using the IJG memory manager - it will be freed in
- * a moment.)
- */
- if (src) {
- if (src->backtrack_buffer) {
- XP_FREE(src->backtrack_buffer);
- src->backtrack_buffer = NULL;
- }
- XP_FREE(src);
- js->jd.src = NULL;
- }
-
- /* Clear any pending timeouts */
- if (js->timeout) {
- FE_ClearTimeout(js->timeout);
- js->timeout = NULL;
- }
-
- /*
- * Free all the ancillary memory used during JPEG decoding by the
- * IJG JPEG library. This has the side effect of freeing up js->samples
- * and js->samples3 which were allocated using the IJG memory manager.
- */
- jpeg_destroy_decompress(&js->jd);
- js->samples = NULL;
- js->samples3 = NULL;
-
- /* Finally, free up our private decoder structure. */
- XP_FREE(js);
- ic->ds = NULL;
- }
- }
-
- void
- il_jpeg_complete(il_container *ic)
- {
- il_jpeg_abort(ic);
- il_image_complete(ic);
- il_frame_complete_notify(ic);
- }
-
- #ifdef PROFILE
- # pragma profile off
- #endif
-
-