gecko-dev/modules/libimg/jpgcom/jpeg.cpp

1013 строки
32 KiB
C++
Исходник Ответственный История

Этот файл содержит невидимые символы Юникода!

Этот файл содержит невидимые символы Юникода, которые могут быть отображены не так, как показано ниже. Если это намеренно, можете спокойно проигнорировать это предупреждение. Используйте кнопку Экранировать, чтобы показать скрытые символы.

/* -*- 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.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
/*
* jpeg.c --- Glue code to Independent JPEG Group decoder library
* $Id: jpeg.cpp,v 1.28 2001/05/15 06:18:18 kandrot%netscape.com Exp $
*/
#include "nsIImgDecoder.h" // include if_struct.h Needs to be first
#include "nsIImgDCallbk.h"
#include "nsCRT.h"
#include "nsJPGDecoder.h"
#include "jpeg.h"
#include "ilErrors.h"
#include "il.h"
#include <ctype.h> /* to declare isprint() */
#ifndef XP_MAC
# include <sys/types.h>
#endif
/* This is a lame hack to get around a problem with boolean on Solaris 2.3 */
#if defined(__sun) && defined(__sparc) && defined(__svr4__) && (OSMINOR == 3)
#define HAVE_BOOLEAN
#undef MUST_UNDEF_HAVE_BOOLEAN_AFTER_INCLUDES
#endif
PR_BEGIN_EXTERN_C
#include "jpeglib.h"
#include "jerror.h"
PR_END_EXTERN_C
#ifdef XP_OS2
/* 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 (((PRUint32)1 << 16) - 1)
#ifdef DEBUG
static int il_debug_jpg = 0;
static PRLogModuleInfo *il_log_module_jpg = NULL;
#define ILTRACE(l,t) { if(il_debug_jpg>l) {PR_LOG(il_log_module_jpg, 1, t);} }
#else
#define ILTRACE(l,t) {}
#endif
/*
* 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 */
JPEG_ERROR
} 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 */
PRUint32 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 0
/*ptn fix later */
if (il_debug >= 1) {
char buffer[JMSG_LENGTH_MAX];
/* Create the message */
(*cinfo->err->format_message) (cinfo, buffer);
ILTRACE(1,("%s\n", buffer));
}
#endif
#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);
}
static 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
*---------------------------------------------------------------------------*/
static boolean PR_CALLBACK
fill_input_buffer (j_decompress_ptr jd)
{
il_source_mgr *src = (il_source_mgr *)jd->src;
enum data_source_state src_state = src->state;
PRUint32 bytesToSkip, new_backtrack_buflen, new_buflen, roundup_buflen;
unsigned char *new_buffer;
ILTRACE(5,("il:jpeg: fill, state=%d, nib=0x%x, bib=%d", src_state,
src->pub.next_input_byte, src->pub.bytes_in_buffer));
switch (src_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 *)PR_REALLOC(src->backtrack_buffer, roundup_buflen);
} else {
src->backtrack_buffer = (JOCTET*)PR_MALLOC(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. */
nsCRT::memmove(src->backtrack_buffer + src->backtrack_buflen,
src->pub.next_input_byte,
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:
PR_ASSERT(0);
return FALSE;
}
suspend:
ILTRACE(5,(" Suspending, bib=%d", src->pub.bytes_in_buffer));
return FALSE;
}
static 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.
*/
static 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.
*---------------------------------------------------------------------------*/
PRBool
setup_jpeg_src (j_decompress_ptr jd, jpeg_struct *js)
{
il_source_mgr *src;
if (jd->src == NULL) {
src = PR_NEWZAP(il_source_mgr);
if (!src) {
ILTRACE(1,("il:jpeg: src manager memory lossage"));
return PR_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 PR_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)
{
PRUintn 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);
if (length < 2) {
cinfo->err->msg_code = JERR_BAD_LENGTH;
il_error_exit((j_common_ptr)cinfo);
}
length -= 2; /* discount the length word itself */
PR_FREEIF(ic->comment);
comment = ic->comment = (char *)PR_MALLOC(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;
}
PRBool
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 = PR_NEWZAP(jpeg_struct);
if (!js) {
ILTRACE(1,("il:jpeg: jpeg_struct memory lossage"));
return PR_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 PR_FALSE;
}
#ifdef DEBUG
#if 0
if (il_debug > 20) {
jd->err->trace_level = 99;
}
#endif
#endif
jpeg_create_decompress(jd);
/* Setup jpeg data source object */
if (!setup_jpeg_src(jd, js)) {
ILTRACE(1,("il:jpeg: jpeg source memory lossage"));
/* Free up all the data structures */
il_jpeg_abort(ic);
return PR_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 PR_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
PRUintn 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];
}
ic->imgdcb->ImgDCBHaveRow( 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 150000
/* Timeout durations, in milliseconds */
/* Delay between displaying chunks of pixels for the first scan. */
#define JPEG_TIMEOUT_INITIAL_DELAY 32
/* Delay between displaying chunks of pixels for subsequent scans */
#define JPEG_TIMEOUT_DELAY 250
static void
jpeg_timeout_callback(void *closure)
{
PRUint32 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 = js->ic->imgdcb->ImgDCBSetTimeout(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;
/* If this js == NULL, chances are the netlib
continued to send data after the image stream was closed. */
if (!js) {
#ifdef DEBUG
ILTRACE(1,("Netlib sent data after the image stream was closed\n"));
#endif
return MK_IMAGE_LOSSAGE;
}
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 */
/* Return here if there is a fatal error. */
if ((error_code = setjmp(js->jerr.setjmp_buffer)) != 0) {
/* 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 = (PRUint32)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 = ic->imgdcb->ImgDCBImageSize()) != 0) {
ILTRACE(1,("il:jpeg: MEM il_size"));
return status;
}
ic->imgdcb->ImgDCBSetupColorspaceConverter(); /* XXXM12N Should check
return code. */
if(!img_header->widthBytes)
return JPEG_ERROR;
else
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) {
PRUint32 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 = ic->imgdcb->ImgDCBSetTimeout(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) {
ic->imgdcb->ImgDCBClearTimeout(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:
PR_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) {
PR_FREEIF(src->backtrack_buffer);
src->backtrack_buffer = NULL;
}
PR_FREEIF(src);
js->jd.src = NULL;
}
/* Clear any pending timeouts */
if (js->timeout) {
ic->imgdcb->ImgDCBClearTimeout(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. */
PR_FREEIF(js);
ic->ds = NULL;
}
}
void
il_jpeg_complete(il_container *ic)
{
il_jpeg_abort(ic);
ic->imgdcb->ImgDCBHaveImageAll();
}
#ifdef PROFILE
# pragma profile off
#endif