diff options
Diffstat (limited to 'src/jpeg.c')
-rw-r--r-- | src/jpeg.c | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/src/jpeg.c b/src/jpeg.c new file mode 100644 index 00000000..fbf3eeee --- /dev/null +++ b/src/jpeg.c @@ -0,0 +1,334 @@ +/* + * File: jpeg.c + * + * Copyright (C) 1997 Raph Levien <raph@acm.org> + * Copyright (C) 2000-2006 Jorge Arellano Cid <jcid@dillo.org> + * + * 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 3 of the License, or + * (at your option) any later version. + */ + +/* + * The jpeg decoder for dillo. It is responsible for decoding JPEG data + * and transferring it to the dicache. It uses libjpeg to do the actual + * decoding. + */ + +#include <config.h> +#ifdef ENABLE_JPEG + +#include <stdio.h> +#include <setjmp.h> + +/* avoid a redefinition of HAVE_STDLIB_H with old jpeglib.h */ +#ifdef HAVE_STDLIB_H +# undef HAVE_STDLIB_H +#endif +#include <jpeglib.h> +#ifdef HAVE_STDLIB_H +# undef HAVE_STDLIB_H +#endif + +#include "image.hh" +#include "web.hh" +#include "cache.h" +#include "dicache.h" + +#define DEBUG_LEVEL 6 +#include "debug.h" + +typedef enum { + DILLO_JPEG_INIT, + DILLO_JPEG_STARTING, + DILLO_JPEG_READING, + DILLO_JPEG_DONE, + DILLO_JPEG_ERROR +} DilloJpegState; + +/* An implementation of a suspending source manager */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + struct DilloJpeg *jpeg; /* a pointer back to the jpeg object */ +} my_source_mgr; + +struct my_error_mgr { + struct jpeg_error_mgr pub; /* "public" fields */ + jmp_buf setjmp_buffer; /* for return to caller */ +}; +typedef struct my_error_mgr * my_error_ptr; + +typedef struct DilloJpeg { + DilloImage *Image; + DilloUrl *url; + int version; + + my_source_mgr Src; + + DilloJpegState state; + size_t Start_Ofs, Skip, NewStart; + char *Data; + + uint_t y; + + struct jpeg_decompress_struct cinfo; + struct my_error_mgr jerr; +} DilloJpeg; + +/* + * Forward declarations + */ +static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, int version); +static void Jpeg_callback(int Op, CacheClient_t *Client); +static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize); +static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client); +METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo); + +/* exported function */ +void *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call, + void **Data); + + +/* this is the routine called by libjpeg when it detects an error. */ +METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo) +{ + /* display message and return to setjmp buffer */ + my_error_ptr myerr = (my_error_ptr) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(myerr->setjmp_buffer, 1); +} + +/* + * MIME handler for "image/jpeg" type + * (Sets Jpeg_callback or a_Dicache_callback as the cache-client) + */ +void *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call, + void **Data) +{ + DilloWeb *web = P; + DICacheEntry *DicEntry; + + if (!web->Image) + web->Image = a_Image_new(0, 0, NULL, 0); + + /* Add an extra reference to the Image (for dicache usage) */ + a_Image_ref(web->Image); + + DicEntry = a_Dicache_get_entry(web->url); + if (!DicEntry) { + /* Let's create an entry for this image... */ + DicEntry = a_Dicache_add_entry(web->url); + + /* ... and let the decoder feed it! */ + *Data = Jpeg_new(web->Image, DicEntry->url, DicEntry->version); + *Call = (CA_Callback_t) Jpeg_callback; + } else { + /* Let's feed our client from the dicache */ + a_Dicache_ref(DicEntry->url, DicEntry->version); + *Data = web->Image; + *Call = (CA_Callback_t) a_Dicache_callback; + } + return (web->Image->dw); +} + +/* + * Finish the decoding process + */ +static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client) +{ + a_Dicache_close(jpeg->url, jpeg->version, Client); + + if (jpeg->state != DILLO_JPEG_DONE) { + jpeg_destroy_decompress(&(jpeg->cinfo)); + } + dFree(jpeg); +} + +static void init_source(j_decompress_ptr cinfo) +{ +} + +static boolean fill_input_buffer(j_decompress_ptr cinfo) +{ + DilloJpeg *jpeg = ((my_source_mgr *) cinfo->src)->jpeg; + + DEBUG_MSG(5, "fill_input_buffer\n"); +#if 0 + if (!cinfo->src->bytes_in_buffer) { + DEBUG_MSG(5, "fill_input_buffer: %ld bytes in buffer\n", + (long)cinfo->src->bytes_in_buffer); + + jpeg->Start_Ofs = (ulong_t) jpeg->cinfo.src->next_input_byte - + (ulong_t) jpeg->Data; +#endif + if (jpeg->Skip) { + jpeg->Start_Ofs = jpeg->NewStart + jpeg->Skip - 1; + jpeg->Skip = 0; + } else { + jpeg->Start_Ofs = (ulong_t) jpeg->cinfo.src->next_input_byte - + (ulong_t) jpeg->Data; + } + return FALSE; +#if 0 + } + return TRUE; +#endif +} + +static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + DilloJpeg *jpeg; + + if (num_bytes < 1) + return; + jpeg = ((my_source_mgr *) cinfo->src)->jpeg; + + DEBUG_MSG(5, "skip_input_data: Start_Ofs = %lu, num_bytes = %ld," + " %ld bytes in buffer\n", + (ulong_t)jpeg->Start_Ofs, num_bytes, + (long)cinfo->src->bytes_in_buffer); + + cinfo->src->next_input_byte += num_bytes; + if (num_bytes < (long)cinfo->src->bytes_in_buffer) { + cinfo->src->bytes_in_buffer -= num_bytes; + } else { + jpeg->Skip += num_bytes - cinfo->src->bytes_in_buffer + 1; + cinfo->src->bytes_in_buffer = 0; + } +} + +static void term_source(j_decompress_ptr cinfo) +{ +} + +static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, int version) +{ + my_source_mgr *src; + DilloJpeg *jpeg = dMalloc(sizeof(*jpeg)); + + jpeg->Image = Image; + jpeg->url = url; + jpeg->version = version; + + jpeg->state = DILLO_JPEG_INIT; + jpeg->Start_Ofs = 0; + jpeg->Skip = 0; + + /* decompression step 1 (see libjpeg.doc) */ + jpeg->cinfo.err = jpeg_std_error(&(jpeg->jerr.pub)); + jpeg->jerr.pub.error_exit = Jpeg_errorexit; + + jpeg_create_decompress(&(jpeg->cinfo)); + + /* decompression step 2 (see libjpeg.doc) */ + jpeg->cinfo.src = &jpeg->Src.pub; + src = &jpeg->Src; + 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;/* use default method */ + src->pub.term_source = term_source; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL;/* until buffer loaded */ + + src->jpeg = jpeg; + + /* decompression steps continue in write method */ + return jpeg; +} + +static void Jpeg_callback(int Op, CacheClient_t *Client) +{ + if (Op) + Jpeg_close(Client->CbData, Client); + else + Jpeg_write(Client->CbData, Client->Buf, Client->BufSize); +} + +/* + * Receive and process new chunks of JPEG image data + */ +static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize) +{ + DilloImgType type; + uchar_t *linebuf; + JSAMPLE *array[1]; + int num_read; + + DEBUG_MSG(5, "Jpeg_write: (%p) Bytes in buff: %ld Ofs: %lu\n", jpeg, + (long) BufSize, (ulong_t)jpeg->Start_Ofs); + + /* See if we are supposed to skip ahead. */ + if (BufSize <= jpeg->Start_Ofs) + return; + + /* Concatenate with the partial input, if any. */ + jpeg->cinfo.src->next_input_byte = (uchar_t *)Buf + jpeg->Start_Ofs; + jpeg->cinfo.src->bytes_in_buffer = BufSize - jpeg->Start_Ofs; + jpeg->NewStart = BufSize; + jpeg->Data = Buf; + + if (setjmp(jpeg->jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. */ + jpeg->state = DILLO_JPEG_ERROR; + } + + /* Process the bytes in the input buffer. */ + if (jpeg->state == DILLO_JPEG_INIT) { + + /* decompression step 3 (see libjpeg.doc) */ + if (jpeg_read_header(&(jpeg->cinfo), TRUE) != JPEG_SUSPENDED) { + type = DILLO_IMG_TYPE_GRAY; + if (jpeg->cinfo.num_components == 1) + type = DILLO_IMG_TYPE_GRAY; + else if (jpeg->cinfo.num_components == 3) + type = DILLO_IMG_TYPE_RGB; + else + DEBUG_MSG(5, "jpeg: can't handle %d component images\n", + jpeg->cinfo.num_components); + a_Dicache_set_parms(jpeg->url, jpeg->version, jpeg->Image, + (uint_t)jpeg->cinfo.image_width, + (uint_t)jpeg->cinfo.image_height, + type); + + /* decompression step 4 (see libjpeg.doc) */ + jpeg->state = DILLO_JPEG_STARTING; + } + } + if (jpeg->state == DILLO_JPEG_STARTING) { + /* decompression step 5 (see libjpeg.doc) */ + if (jpeg_start_decompress(&(jpeg->cinfo))) { + jpeg->y = 0; + jpeg->state = DILLO_JPEG_READING; + } + } + if (jpeg->state == DILLO_JPEG_READING) { + linebuf = dMalloc(jpeg->cinfo.image_width * + jpeg->cinfo.num_components); + array[0] = linebuf; + while (jpeg->y < jpeg->cinfo.image_height) { + num_read = jpeg_read_scanlines(&(jpeg->cinfo), array, 1); + if (num_read == 0) + break; + a_Dicache_write(jpeg->Image, jpeg->url, jpeg->version, + linebuf, 0, jpeg->y); + + jpeg->y++; + } + if (jpeg->y == jpeg->cinfo.image_height) { + DEBUG_MSG(5, "height achieved\n"); + + jpeg_destroy_decompress(&(jpeg->cinfo)); + jpeg->state = DILLO_JPEG_DONE; + } + dFree(linebuf); + } + if (jpeg->state == DILLO_JPEG_ERROR) { + jpeg_destroy_decompress(&(jpeg->cinfo)); + jpeg->state = DILLO_JPEG_DONE; + } +} + +#endif /* ENABLE_JPEG */ |