summaryrefslogtreecommitdiff
path: root/jpeg.c
diff options
context:
space:
mode:
Diffstat (limited to 'jpeg.c')
-rw-r--r--jpeg.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/jpeg.c b/jpeg.c
new file mode 100644
index 0000000..7676eae
--- /dev/null
+++ b/jpeg.c
@@ -0,0 +1,398 @@
+/* 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;
+};
+
+static struct jerr_struct *global_jerr;
+static struct jpeg_decompress_struct *global_cinfo;
+static int mesg_unsup_emitted; /* Defaults to zero at program startup and once
+ * set is never reset back to zero */
+
+
+METHODDEF(void) my_error_exit(j_common_ptr cinfo)
+{
+ 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
+ =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;
+ return;
+ }
+ 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)
+{
+ 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("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("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=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;
+
+ 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){
+ fprintf(stderr,
+ "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=sRGB_gamma;
+ cimg->green_gamma=sRGB_gamma;
+ cimg->blue_gamma=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;
+
+ 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
+ +(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;
+
+ 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;
+
+ case 5:
+ /* jpeg_finish_decompress */
+ if (FALSE==jpeg_finish_decompress(global_cinfo))
+ break;
+decoder_ended:
+ img_end(cimg);
+ }
+}
+
+void add_jpeg_version(unsigned char **s, int *l)
+{
+ add_to_str(s, l, "JPEG (");
+#if defined(JPEG_LIB_VERSION_MAJOR) && defined(JPEG_LIB_VERSION_MINOR)
+ add_num_to_str(s, l, JPEG_LIB_VERSION_MAJOR);
+ add_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_to_str(s, l, ".");
+ add_num_to_str(s, l, JPEG_LIB_VERSION % 10);
+#endif
+ add_to_str(s, l, ")");
+}
+
+#endif /* #ifdef HAVE_JPEG */
+
+#endif /* #ifdef G */
+