422 lines
11 KiB
C
422 lines
11 KiB
C
/* jpeg.c
|
|
* JPEG decoding
|
|
* (c) 2002 Karel 'Clock' Kulhavy
|
|
* This file is a part of the Links program, released under GPL.
|
|
*/
|
|
|
|
#include "cfg.h"
|
|
|
|
#ifdef G
|
|
#include "links.h"
|
|
|
|
#ifdef HAVE_JPEG
|
|
#include <jpeglib.h>
|
|
|
|
#if BITS_IN_JSAMPLE != 8
|
|
#error "You have a weird jpeglib compiled for 12 bits per sample that is not able to read ordinary JPEG's. \
|
|
See INSTALL for description how to compile Links with jpeglib statically to supply your own \"good\" version \
|
|
of jpeglib or reinstall your system's jpeglib to be a normal one."
|
|
#endif /* #if BITS_IN_JSAMPLE != 8 */
|
|
|
|
struct jerr_struct{
|
|
struct jpeg_error_mgr pub;
|
|
jmp_buf setjmp_buffer;
|
|
};
|
|
|
|
struct jpg_decoder{
|
|
struct jpeg_decompress_struct *cinfo;
|
|
struct jerr_struct *jerr;
|
|
unsigned char state; /* 0: header 1: start 2: scanlines 3: end 4,5: also
|
|
something */
|
|
int skip_bytes;
|
|
unsigned char *jdata;
|
|
unsigned char *scanlines[16];
|
|
};
|
|
|
|
static struct jerr_struct *global_jerr;
|
|
static struct jpeg_decompress_struct *global_cinfo;
|
|
static int mesg_unsup_emitted = 0; /* Defaults to zero at program startup and once
|
|
* set is never reset back to zero */
|
|
|
|
|
|
/*#include <jerror.h>*/
|
|
|
|
METHODDEF(void) my_error_exit(j_common_ptr cinfo)
|
|
{
|
|
/*fprintf(stderr, "jpeg error %d (%d)\n", cinfo->err->msg_code, JERR_OUT_OF_MEMORY);*/
|
|
longjmp(global_jerr->setjmp_buffer,2);
|
|
}
|
|
|
|
METHODDEF(void) /* Only for the sake of libjpeg */
|
|
nop(j_decompress_ptr cinfo)
|
|
{
|
|
}
|
|
|
|
METHODDEF(void)
|
|
my_output_message(j_common_ptr cinfo)
|
|
{
|
|
}
|
|
|
|
METHODDEF(boolean) my_fill_input_buffer(j_decompress_ptr cinfo)
|
|
{
|
|
return FALSE; /* We utilize I/O suspension (or emulsion? ;-) ) */
|
|
}
|
|
|
|
METHODDEF(void) my_skip_input_data(j_decompress_ptr cinfo,long num_bytes)
|
|
{
|
|
if ((unsigned long)num_bytes>cinfo->src->bytes_in_buffer)
|
|
{
|
|
/* We have to enter skipping state */
|
|
cinfo->src->next_input_byte+=cinfo->src->bytes_in_buffer;
|
|
((struct jpg_decoder *)(global_cimg->decoder))->skip_bytes
|
|
=(int)(num_bytes-cinfo->src->bytes_in_buffer);
|
|
cinfo->src->bytes_in_buffer=0;
|
|
}
|
|
else
|
|
{
|
|
/* We only pull out some bytes from buffer. */
|
|
cinfo->src->next_input_byte+=num_bytes;
|
|
cinfo->src->bytes_in_buffer-=num_bytes;
|
|
}
|
|
}
|
|
|
|
void jpeg_start(struct cached_image *cimg)
|
|
{
|
|
struct jpg_decoder *jd;
|
|
|
|
global_cinfo=mem_alloc(sizeof(*global_cinfo));
|
|
global_jerr=mem_alloc(sizeof(*global_jerr));
|
|
global_cinfo->err = jpeg_std_error(&(global_jerr->pub));
|
|
global_jerr->pub.error_exit=my_error_exit;
|
|
global_jerr->pub.output_message=my_output_message;
|
|
if (setjmp(global_jerr->setjmp_buffer)){
|
|
g19_2000:
|
|
mem_free(global_cinfo);
|
|
mem_free(global_jerr);
|
|
img_end(cimg);
|
|
return;
|
|
}
|
|
jpeg_create_decompress(global_cinfo);
|
|
if (setjmp(global_jerr->setjmp_buffer)){
|
|
jpeg_destroy_decompress(global_cinfo);
|
|
goto g19_2000;
|
|
}
|
|
jpeg_stdio_src(global_cinfo,stdin);
|
|
global_cinfo->src->init_source=&nop;
|
|
global_cinfo->src->fill_input_buffer=&my_fill_input_buffer;
|
|
global_cinfo->src->skip_input_data=&my_skip_input_data;
|
|
global_cinfo->src->resync_to_restart=&jpeg_resync_to_restart;
|
|
global_cinfo->src->term_source=nop;
|
|
global_cinfo->src->bytes_in_buffer=0;
|
|
global_cinfo->src->next_input_byte=NULL;
|
|
cimg->decoder=mem_alloc(sizeof(struct jpg_decoder));
|
|
jd=(struct jpg_decoder *)cimg->decoder;
|
|
jd->cinfo=global_cinfo;
|
|
jd->jerr=global_jerr;
|
|
jd->state=0;
|
|
jd->skip_bytes=0;
|
|
jd->jdata=NULL;
|
|
/* Scanlines can be left unititialized */
|
|
}
|
|
|
|
/* This is here because libjpeg doesn't support transformation from CMYK
|
|
* to RGB so that we must do it ourselves.
|
|
*
|
|
* data must be non-NULL. */
|
|
static void cmyk_to_rgb(unsigned char *data, int pixels)
|
|
{
|
|
for (;pixels;pixels--, data+=4)
|
|
{
|
|
/* C -> R */
|
|
data[0]=((data[0])*(data[3])+127)/255;
|
|
|
|
/* M -> G */
|
|
data[1]=((data[1])*(data[3])+127)/255;
|
|
|
|
/* Y -> B */
|
|
data[2]=((data[2])*(data[3])+127)/255;
|
|
|
|
/* Put alpha=1 instead of K */
|
|
data[3]=255;
|
|
}
|
|
}
|
|
|
|
/* data must be non-NULL */
|
|
static void gray_to_rgb(unsigned char *data, int pixels)
|
|
{
|
|
unsigned char *dest;
|
|
|
|
dest=data+(pixels-1)*3;
|
|
data+=pixels-1;
|
|
for(;pixels;pixels--,data--,dest-=3){
|
|
dest[2]=*data;
|
|
dest[1]=*data;
|
|
dest[0]=*data;
|
|
}
|
|
}
|
|
|
|
/* Fixes returned data in case they are CMYK or grayscale. */
|
|
static inline void fix_data( struct jpg_decoder *deco, int lines_read)
|
|
{
|
|
/* ICC bug */
|
|
icc_volatile int a;
|
|
|
|
switch (global_cinfo->output_components){
|
|
case 1:
|
|
for (a=0; a<lines_read; a++)
|
|
gray_to_rgb(deco->scanlines[a], global_cinfo
|
|
->output_width);
|
|
break;
|
|
|
|
case 3:
|
|
break;
|
|
|
|
case 4:
|
|
cmyk_to_rgb(deco->scanlines[0], global_cinfo
|
|
->output_width*lines_read);
|
|
break;
|
|
|
|
default: internal_error("Invalid output_components");
|
|
}
|
|
}
|
|
|
|
void jpeg_restart(struct cached_image *cimg, unsigned char *data, int length)
|
|
{
|
|
struct jpg_decoder *deco;
|
|
|
|
deco=(struct jpg_decoder *)(cimg->decoder);
|
|
#ifdef DEBUG
|
|
if (!deco) internal_error("NULL decoder in jpeg_restart");
|
|
#endif /* #ifdef DEBUG */
|
|
global_cinfo=((struct jpg_decoder *)(cimg->decoder))->cinfo;
|
|
global_jerr=((struct jpg_decoder *)(cimg->decoder))->jerr;
|
|
/* These global variables are here so that we don't have to pass lots
|
|
* of structure pointers into each function. The jpeg decoder is never
|
|
* running twice at the same time so it doesn't matter.
|
|
*/
|
|
|
|
/* If the decoder wants us to skip bytes it's not interested in */
|
|
if (deco->skip_bytes>=length){
|
|
/* If the decoder wants to skip as much as or more bytes than
|
|
* the chunk that has just arrived */
|
|
deco->skip_bytes-=length;
|
|
return;
|
|
}else{
|
|
/* If the decoder wants to skip less bytes than the chunk
|
|
* that has just arrived */
|
|
data+=deco->skip_bytes;
|
|
length-=deco->skip_bytes;
|
|
deco->skip_bytes=0;
|
|
}
|
|
|
|
/* Add the arrived data chunk into the decoder buffer. Sometimes the
|
|
* chunks are so small the decoder can't move on on a single chunk
|
|
* so it has to accumulate more chunks together. This is why the buffer
|
|
* is there. */
|
|
if ((unsigned)global_cinfo->src->bytes_in_buffer + (unsigned)length > MAXINT) overalloc();
|
|
if ((unsigned)global_cinfo->src->bytes_in_buffer + (unsigned)length < (unsigned)length) overalloc();
|
|
if (deco->jdata){
|
|
/* If there is already some decoder buffer, we have to
|
|
* allocate more space */
|
|
memmove(deco->jdata,global_cinfo->src->next_input_byte,
|
|
global_cinfo->src->bytes_in_buffer);
|
|
deco->jdata=mem_realloc(
|
|
deco->jdata, global_cinfo->src->bytes_in_buffer+length);
|
|
}else{
|
|
/* If there is no decoder buffer we'll have to allocate
|
|
* space for a new buffer */
|
|
deco->jdata=mem_alloc(global_cinfo->src->bytes_in_buffer+length);
|
|
}
|
|
|
|
/* Copy the data iself into the decoder buffer */
|
|
memcpy(deco->jdata+global_cinfo->src->bytes_in_buffer
|
|
,data,length);
|
|
|
|
/* Update the next input byte pointer for the decoder to continue at
|
|
* the right position */
|
|
global_cinfo->src->next_input_byte=(void *)deco->jdata;
|
|
|
|
/* ...:::...:..:.:::.:.::::.::.:.:.:.::..::::.::::.:...: */
|
|
/* Update the length of data in the decoder buffer */
|
|
global_cinfo->src->bytes_in_buffer+=length;
|
|
|
|
if (setjmp(global_jerr->setjmp_buffer)) goto decoder_ended;
|
|
switch(deco->state){
|
|
case 0:
|
|
/* jpeg_read_header */
|
|
if (JPEG_SUSPENDED==jpeg_read_header(global_cinfo,TRUE))
|
|
break;
|
|
global_cinfo->buffered_image=TRUE;
|
|
deco->state=1;
|
|
/*-fallthrough*/
|
|
|
|
case 1:
|
|
/* If the scaling is sufficiently brutal we can leave out
|
|
* some DCT coefficients...: */
|
|
/* jpeg_start_decompress */
|
|
if (jpeg_start_decompress(global_cinfo)==FALSE)
|
|
break;
|
|
|
|
cimg->width=global_cinfo->output_width;
|
|
cimg->height=global_cinfo->output_height;
|
|
|
|
switch(cimg->buffer_bytes_per_pixel=
|
|
global_cinfo->output_components)
|
|
{
|
|
case 1:
|
|
/* We'll do the conversion ourselves
|
|
* because libjpeg seems to be buggy */
|
|
cimg->buffer_bytes_per_pixel=3;
|
|
break;
|
|
|
|
|
|
case 3: /* RGB or YCrCb. We will ask libjpeg to
|
|
* possibly convert from YCrCb to RGB. */
|
|
|
|
global_cinfo->out_color_space=JCS_RGB;
|
|
break;
|
|
|
|
case 4:
|
|
/* CMYK or YCCK. We need to enable conversion
|
|
* to CMYK and then convert CMYK data to RGBA
|
|
* with dummy A ourselves.
|
|
* We will ask libjpeg to possibly convert from
|
|
* YCCK to CMYK. */
|
|
global_cinfo->out_color_space=JCS_CMYK;
|
|
break;
|
|
|
|
default:
|
|
/* Let's make a decompression fatal error here */
|
|
|
|
if (!mesg_unsup_emitted){
|
|
error(
|
|
"Unsupported JPEG output components number: %d.\n",
|
|
cimg->buffer_bytes_per_pixel);
|
|
mesg_unsup_emitted=1;
|
|
}
|
|
longjmp(global_jerr->setjmp_buffer,2);
|
|
|
|
/* longjmp() and siglongjmp() make programs hard to
|
|
* understand and maintain. If possible an alternative
|
|
* should be used. Hahaha :) ;-)
|
|
*/
|
|
/* Free will makes people hard to understand
|
|
* and maintain. If possible an alternative should be
|
|
* used.
|
|
*/
|
|
/* With our new LongJump(TM) your jumps will be longer
|
|
* than with ordinary commercially available jumps.
|
|
*/
|
|
}
|
|
cimg->red_gamma=(float)sRGB_gamma;
|
|
cimg->green_gamma=(float)sRGB_gamma;
|
|
cimg->blue_gamma=(float)sRGB_gamma;
|
|
/* This is defined in the JPEG standard somehow that sRGB
|
|
* color space is used. */
|
|
|
|
cimg->strip_optimized=0;
|
|
/* Strip optimization yet waits to be written. This will
|
|
* allow huge jpegs to be processed without consuming
|
|
* Links memory and consuming Xserver memory instead ;-)
|
|
* However strip optimization is already written for PNG's.
|
|
*/
|
|
|
|
if (header_dimensions_known(cimg)) {
|
|
longjmp(global_jerr->setjmp_buffer,2);
|
|
}
|
|
new_scan:
|
|
deco->state=2;
|
|
/*-fallthrough*/
|
|
|
|
case 2:
|
|
/* jpeg_start_output */
|
|
if (FALSE==jpeg_start_output(global_cinfo,global_cinfo->input_scan_number)){
|
|
susp0:
|
|
/* Suspended */
|
|
break;
|
|
}
|
|
deco->state=3;
|
|
|
|
case 3:
|
|
/* jpeg_read_scanlines */
|
|
/* color */
|
|
while (global_cinfo->output_scanline < global_cinfo->output_height) {
|
|
int a, lines;
|
|
|
|
for (a = 0; a < 16; a++) {
|
|
deco->scanlines[a] = cimg->buffer + ((size_t)global_cinfo->output_scanline + a) * global_cinfo->output_width * cimg->buffer_bytes_per_pixel;
|
|
}
|
|
|
|
if ((lines = jpeg_read_scanlines(global_cinfo, deco->scanlines, 1))) {
|
|
/* Some lines were written into cimg buffer */
|
|
cimg->rows_added = 1;
|
|
fix_data(deco, lines);
|
|
} else {
|
|
/* No lines have been written into cimg
|
|
* buffer */
|
|
/* We are suspended and we want more data */
|
|
goto susp0; /* Break the outer
|
|
* switch statement */
|
|
}
|
|
}
|
|
deco->state=4;
|
|
/*-fallthrough*/
|
|
|
|
case 4:
|
|
/* jpeg_finish_output */
|
|
if (FALSE==jpeg_finish_output(global_cinfo))
|
|
{
|
|
/* Suspended */
|
|
break;
|
|
}
|
|
if (!jpeg_input_complete(global_cinfo))
|
|
{
|
|
/* Some more scans awaited... */
|
|
goto new_scan;
|
|
}
|
|
deco->state=5;
|
|
/*-fallthrough*/
|
|
|
|
case 5:
|
|
/* jpeg_finish_decompress */
|
|
if (FALSE==jpeg_finish_decompress(global_cinfo))
|
|
break;
|
|
decoder_ended:
|
|
img_end(cimg);
|
|
}
|
|
}
|
|
|
|
void jpeg_destroy_decoder(struct cached_image *cimg)
|
|
{
|
|
struct jpg_decoder *deco = (struct jpg_decoder *)cimg->decoder;
|
|
jpeg_destroy_decompress(deco->cinfo);
|
|
mem_free(deco->cinfo);
|
|
mem_free(deco->jerr);
|
|
if (deco->jdata) mem_free(deco->jdata);
|
|
}
|
|
|
|
void add_jpeg_version(unsigned char **s, int *l)
|
|
{
|
|
#if defined(LIBJPEG_TURBO_VERSION)
|
|
add_to_str(s, l, cast_uchar "JPEG-TURBO (");
|
|
add_to_str(s, l, cast_uchar stringify(LIBJPEG_TURBO_VERSION));
|
|
add_to_str(s, l, cast_uchar ", ABI ");
|
|
#else
|
|
add_to_str(s, l, cast_uchar "JPEG (");
|
|
#endif
|
|
#if defined(JPEG_LIB_VERSION_MAJOR) && defined(JPEG_LIB_VERSION_MINOR)
|
|
add_num_to_str(s, l, JPEG_LIB_VERSION_MAJOR);
|
|
add_chr_to_str(s, l, '.');
|
|
add_num_to_str(s, l, JPEG_LIB_VERSION_MINOR);
|
|
#else
|
|
add_num_to_str(s, l, JPEG_LIB_VERSION / 10);
|
|
add_chr_to_str(s, l, '.');
|
|
add_num_to_str(s, l, JPEG_LIB_VERSION % 10);
|
|
#endif
|
|
add_chr_to_str(s, l, ')');
|
|
}
|
|
|
|
#endif /* #ifdef HAVE_JPEG */
|
|
|
|
#endif /* #ifdef G */
|
|
|