/* 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 #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 */ 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; ascanlines[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 */