chimera2/image/jpeg.c

571 lines
15 KiB
C

/*
* jpeg.c:
*
* (c) 1995 Erik Corry ehcorry@inet.uni-c.dk
*
* modelled on xbm.c.
*
* If you want to understand this read the following files from libjpeg:
*
* libjpeg.doc
* jdatasrc.c
* jpeglib.h
* example.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "port_before.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "port_after.h"
#include "common.h"
#include "imagep.h"
#include "jpegp.h"
static void jpegDestroy _ArgProto((void *));
/*
* jpegGetImage - called through vector
*/
static Image *
jpegGetImage(void *pointer)
{
jpegState *jpeg = (jpegState *)pointer;
return jpeg->image;
}
/*
* jpegDestroy - called through vector
*/
static void
jpegDestroy(pointer)
void *pointer;
{
jpegState *jpeg = (jpegState *)pointer;
if (jpeg) {
if(setjmp(jpeg->error_state.jump_buffer))
{
/*
* Oh shit, an error while clearing up...
*/
fprintf(stderr, "jpegDestroy: failed\n");
return;
}
if (jpeg->image)
freeImage(jpeg->image);
jpeg->image = 0;
if (jpeg->destroy_jpeg)
jpeg_destroy_decompress(&jpeg->cinfo);
jpeg->destroy_jpeg = false;
free_mem(jpeg);
}
}
/*
* these routines form a suspending "data source manager" for libjpeg.
*/
static void
lf_init_source_callback(
struct jpeg_decompress_struct *cinfo)
{
}
static boolean
lf_fill_input_buffer_callback(
struct jpeg_decompress_struct *cinfo)
{
struct jpeg_chimera_input_state *input_state =
(struct jpeg_chimera_input_state *)(cinfo->src);
if (! input_state->at_eof)
return FALSE; /* force suspension until more data arrives */
/* If libjpeg demands more data beyond the EOF,
* pacify it by supplying EOI marker(s).
* This lets us produce some kind of image from a trucated or corrupted file.
*/
WARNMS(cinfo, JWRN_JPEG_EOF);
input_state->fake_eoi[0] = (JOCTET) 0xFF;
input_state->fake_eoi[1] = (JOCTET) JPEG_EOI;
input_state->pub.next_input_byte = input_state->fake_eoi;
input_state->pub.bytes_in_buffer = 2;
input_state->faked_eoi = true;
return TRUE;
}
static void
lf_skip_input_data_callback(
struct jpeg_decompress_struct *cinfo,
long num_bytes)
{
struct jpeg_chimera_input_state *input_state =
(struct jpeg_chimera_input_state *)(cinfo->src);
if (num_bytes <= 0) return;
if (num_bytes > (long) input_state->pub.bytes_in_buffer)
{
/* Tricky code here: we advance next_input_byte beyond the end of the
* buffer. libjpeg will suspend because we set bytes_in_buffer to 0,
* and then jpegAddData's bookkeeping will do the right thing.
* This could fail on sufficiently weird architectures, because
* pointers aren't guaranteed by the C standard to be able to point
* past the end of allocated memory; but in practice it should be fine.
*/
input_state->pub.next_input_byte += num_bytes;
input_state->pub.bytes_in_buffer = 0;
}
else
{
input_state->pub.next_input_byte += num_bytes;
input_state->pub.bytes_in_buffer -= num_bytes;
}
}
static void
lf_term_source_callback(
struct jpeg_decompress_struct *cinfo)
{
}
/*
* this routine overrides libjpeg's default fatal-error handler.
*/
static void
lf_error_exit_callback(
j_common_ptr cinfo)
{
struct jpeg_chimera_error_state *error_state =
(struct jpeg_chimera_error_state *)(cinfo->err);
/*
* Display message. At the moment I use the builtin stderr handler,
* but we could install something smarter, or just keep quiet about it.
* Note that if we did want to keep quiet, we'd have to override
* output_message as well as error_exit, so as to suppress warning msgs.
*/
(error_state->pub.output_message)(cinfo);
/* Return control to the setjmp point */
longjmp(error_state->jump_buffer, 1);
}
/*
* These routines are the elements of a state machine for reading JPEGs.
* We use a distinct state for each point at which we may have to suspend
* processing.
*/
static int
lf_read_header(jpegState *jpeg)
{
int status = jpeg_read_header(&jpeg->cinfo, TRUE);
if (status == JPEG_SUSPENDED) return CH_JPEG_NEED_DATA;
/*
* Done reading header; set decompression parameters.
* Probably ought to have some user-settable preferences here.
*/
/* These are needed for progressive rendering */
if (jpeg_has_multiple_scans(&jpeg->cinfo))
jpeg->cinfo.buffered_image = TRUE;
jpeg->cinfo.do_block_smoothing = TRUE;
/* These are optional tradeoffs of quality for speed */
/* if (force-grayscale) jpeg->cinfo.out_color_space = JCS_GRAYSCALE; */
jpeg->cinfo.dct_method = JDCT_FASTEST;
jpeg->cinfo.do_fancy_upsampling = FALSE;
/* Advance to next state */
jpeg->state = CH_JPEG_START_DECOMPRESS;
return CH_JPEG_SUCCESS;
}
static int
lf_start_decompress(jpegState *jpeg)
{
int status = jpeg_start_decompress(&jpeg->cinfo);
if (status == FALSE) return CH_JPEG_NEED_DATA;
/*
* start_decompress done; get image parameters
*/
if (jpeg->cinfo.out_color_space == JCS_GRAYSCALE &&
jpeg->cinfo.output_components == 1)
{
/* we represent grayscale as RGBI with a gray-ramp palette */
int i;
jpeg->image = newRGBImage(jpeg->cinfo.output_width,
jpeg->cinfo.output_height, 8);
if (!jpeg->image)
return CH_JPEG_ERROR;
jpeg->image->type = IGRAY;
jpeg->image->rgb.red = jpeg->cmap;
jpeg->image->rgb.green = jpeg->cmap;
jpeg->image->rgb.blue = jpeg->cmap;
jpeg->image->rgb.used = 256;
for (i = 0; i < 256; i++)
jpeg->cmap[i] = i | (i << 8);
}
else if (jpeg->cinfo.out_color_space == JCS_RGB &&
jpeg->cinfo.output_components == 3)
{
jpeg->image = newTrueImage(jpeg->cinfo.output_width,
jpeg->cinfo.output_height);
if (!jpeg->image)
return CH_JPEG_ERROR;
}
else
{
fprintf(stderr, "Unsupported JPEG colorspace\n");
return CH_JPEG_ERROR;
}
/* Advance to next state */
jpeg->state = CH_JPEG_START_OUTPUT;
return CH_JPEG_SUCCESS;
}
static int
lf_start_output(jpegState *jpeg)
{
if (jpeg->cinfo.buffered_image)
{
/* Eat all the available data before issuing start_output.
* This ensures we have an up-to-date target scan number
* and prevents executing unnecessary output passes.
* Once we get a SUSPENDED return (or possibly an EOI),
* we can proceed with display.
*/
int status;
for (;;) {
status = jpeg_consume_input(&jpeg->cinfo);
if (status == JPEG_SUSPENDED || status == JPEG_REACHED_EOI)
break;
}
status = jpeg_start_output(&jpeg->cinfo, jpeg->cinfo.input_scan_number);
if (status == FALSE) return CH_JPEG_NEED_DATA;
/* printf("scan %d\n", jpeg->cinfo.input_scan_number); */
}
jpeg->ypos = 0;
/* Advance to next state */
jpeg->state = CH_JPEG_READ_IMAGE;
return CH_JPEG_SUCCESS;
}
static int
lf_read_image(jpegState *jpeg)
{
int scanline_count, max_scanlines;
int i;
unsigned char *scanline_pointers[4];
unsigned char *output_addr;
if (jpeg->cinfo.buffered_image)
{
/* In progressive mode, consume all available input right away.
* NOTE: maybe ought to be less greedy if whole file is available???
*/
for (;;) {
int status = jpeg_consume_input(&jpeg->cinfo);
if (status == JPEG_SUSPENDED || status == JPEG_REACHED_EOI)
break;
}
}
while (jpeg->ypos < jpeg->cinfo.output_height)
{
/* Somewhat optimistically, we assume that libjpeg may be able to
* give us as many as four scanlines per call.
*/
max_scanlines = MIN(4, jpeg->cinfo.output_height - jpeg->ypos);
output_addr =
jpeg->image->data + jpeg->image->bytes_per_line * jpeg->ypos;
for (i = 0; i < max_scanlines; i++)
{
scanline_pointers[i] = output_addr;
output_addr += jpeg->image->bytes_per_line;
}
scanline_count =
jpeg_read_scanlines(&jpeg->cinfo, scanline_pointers, max_scanlines);
if (scanline_count == 0) return CH_JPEG_NEED_DATA;
if (jpeg->lineProc != NULL)
(jpeg->lineProc)(jpeg->lineClosure, jpeg->ypos,
jpeg->ypos + scanline_count - 1);
jpeg->ypos += scanline_count;
}
/* Advance to next state */
jpeg->state = CH_JPEG_FINISH_OUTPUT;
return CH_JPEG_SUCCESS;
}
static int
lf_finish_output(jpegState *jpeg)
{
if (jpeg->cinfo.buffered_image)
{
int status = jpeg_finish_output(&jpeg->cinfo);
if (status == FALSE) return CH_JPEG_NEED_DATA;
/* We need another output scan unless the input file is all read
* and the just-completed output scan was started after the final
* input scan began arriving.
*/
if (! jpeg_input_complete(&jpeg->cinfo) ||
jpeg->cinfo.output_scan_number < jpeg->cinfo.input_scan_number)
{
jpeg->state = CH_JPEG_START_OUTPUT;
return CH_JPEG_SUCCESS;
}
}
/* Advance to next state */
jpeg->state = CH_JPEG_FINISH_DECOMPRESS;
return CH_JPEG_SUCCESS;
}
static int
lf_finish_decompress(jpegState *jpeg)
{
int status = jpeg_finish_decompress(&jpeg->cinfo);
if (status == FALSE) return CH_JPEG_NEED_DATA;
/* Advance to next state */
jpeg->state = CH_JPEG_FINISHED;
return CH_JPEG_SUCCESS;
}
/*
* jpegAddData - called through vector
*
* Returns:
* 0 success
* 1 need more data
* -1 error
*
* Assumes data is the address of the beginning of the jpeg data and len
* is the total length.
*/
static int
jpegAddData(pointer, data, len, data_ended)
void *pointer;
byte *data;
int len;
bool data_ended;
{
jpegState *jpeg = (jpegState *)pointer;
int rval;
/*
fprintf(stderr, "jpegAddData %p: buf %p %d %d\n",
&jpeg->cinfo, data, len, data_ended);
*/
if (setjmp(jpeg->error_state.jump_buffer))
{
/* Failed. Assume we will get a jpegDestroy call */
return -1;
}
/*
* Can't progress unless more data is available (or EOI is reached).
* Note that len < bytes_consumed is entirely valid, if libjpeg has
* commanded a skip beyond the data so far received.
*/
if (len <= jpeg->input_state.bytes_consumed)
{
if (! data_ended) return 1; /* suspend */
/* If reached EOF, terminate any incomplete skip. */
jpeg->input_state.bytes_consumed = len;
}
/* Update libjpeg's input pointers */
jpeg->input_state.pub.next_input_byte =
((JOCTET*) data) + jpeg->input_state.bytes_consumed;
jpeg->input_state.pub.bytes_in_buffer =
len - jpeg->input_state.bytes_consumed;
jpeg->input_state.faked_eoi = false; /* next_input_byte is valid */
if (data_ended)
jpeg->input_state.at_eof = true;
/*
* Don't bother to parse less than a few hundred bytes at a time;
* this prevents excess cycling in libjpeg. (Normal TCP connections
* will deliver a packet at a time, so this test will probably never
* trigger...)
*/
if (!data_ended &&
jpeg->input_state.pub.bytes_in_buffer < 200)
return 1;
/*
* Cycle the state machine until done or forced to suspend by lack of input.
* Note that we are able to progress through multiple states per call
* as long as the input is there.
*/
for ( ; ; )
{
switch (jpeg->state)
{
case CH_JPEG_READ_HEADER:
rval = lf_read_header(jpeg);
break;
case CH_JPEG_START_DECOMPRESS:
rval = lf_start_decompress(jpeg);
break;
case CH_JPEG_START_OUTPUT:
rval = lf_start_output(jpeg);
break;
case CH_JPEG_READ_IMAGE:
rval = lf_read_image(jpeg);
break;
case CH_JPEG_FINISH_OUTPUT:
rval = lf_finish_output(jpeg);
break;
case CH_JPEG_FINISH_DECOMPRESS:
rval = lf_finish_decompress(jpeg);
break;
case CH_JPEG_FINISHED:
rval = CH_JPEG_FINISHED;
break;
default:
fprintf(stderr, "jpegAddData: bogus state %d\n", jpeg->state);
rval = CH_JPEG_ERROR;
break;
}
/* exit loop for FINISHED, NEED_DATA, or ERROR returns */
if (rval != CH_JPEG_SUCCESS)
break;
}
/*
* Record how much data libjpeg consumed.
*/
if (jpeg->input_state.faked_eoi)
{
/* next_input_bytes is invalid, just say we ate it all. */
jpeg->input_state.bytes_consumed = len;
}
else
{
jpeg->input_state.bytes_consumed =
jpeg->input_state.pub.next_input_byte - ((JOCTET*) data);
}
if (rval == CH_JPEG_NEED_DATA)
return 1;
if (rval == CH_JPEG_FINISHED)
return 0;
return(-1);
}
/*
* jpegInit
*
* Initialize JPEG reader state
*/
void
jpegInit(lineProc, lineClosure, if_vector)
void (*lineProc)();
void *lineClosure;
struct ifs_vector *if_vector;
{
jpegState *jpeg;
/*
* Initialise vector for methods Chimera needs
*/
if_vector->initProc = &jpegInit;
if_vector->destroyProc = &jpegDestroy;
if_vector->addDataProc = &jpegAddData;
if_vector->getImageProc = &jpegGetImage;
/* allocate my workspace */
jpeg = (jpegState *)alloc_mem(sizeof(jpegState));
if_vector->image_format_closure = (void *)jpeg;
if (jpeg == NULL)
return;
memset(jpeg, 0, sizeof(jpegState));
jpeg->state = CH_JPEG_READ_HEADER;
jpeg->lineProc = lineProc;
jpeg->lineClosure = lineClosure;
/*
* Initialise error handler.
* We set up the normal JPEG error routines, then override error_exit.
*/
jpeg->cinfo.err = jpeg_std_error(&jpeg->error_state.pub);
jpeg->error_state.pub.error_exit = &lf_error_exit_callback;
if (setjmp(jpeg->error_state.jump_buffer))
{
/*
* We get here after a failure in initialisation. Lets just assume that
* people are careful about the order in which things are done, and that
* a destroy will work here.
*/
jpegDestroy((void *)jpeg);
/*
* It would be nice if init could indicate failure...
*/
return;
}
/*
* Initialise jpeg library
*/
jpeg->destroy_jpeg = true; /* jpeg_destroy is OK even if create fails */
jpeg_create_decompress(&jpeg->cinfo);
/*
* Initialise our input reader.
*/
jpeg->cinfo.src = &jpeg->input_state.pub;
jpeg->input_state.pub.init_source = &lf_init_source_callback;
jpeg->input_state.pub.fill_input_buffer = &lf_fill_input_buffer_callback;
jpeg->input_state.pub.skip_input_data = &lf_skip_input_data_callback;
jpeg->input_state.pub.resync_to_restart = &jpeg_resync_to_restart;
jpeg->input_state.pub.term_source = &lf_term_source_callback;
jpeg->input_state.pub.bytes_in_buffer = 0;
jpeg->input_state.pub.next_input_byte = NULL;
jpeg->input_state.bytes_consumed = 0;
jpeg->input_state.at_eof = false;
}