X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=images%2Fio-libjpeg.c;h=1d3d37c5029e046212bee97f484179b50bea9391;hb=0e5c5828c3f3e1abd14bd012827e6b467075290b;hp=876c1720131e0d36c07bcc387d18885c1464bb41;hpb=92ba4a64f2141f83341ee9f0b664324940f0c563;p=libucw.git diff --git a/images/io-libjpeg.c b/images/io-libjpeg.c index 876c1720..1d3d37c5 100644 --- a/images/io-libjpeg.c +++ b/images/io-libjpeg.c @@ -13,10 +13,14 @@ #include "lib/mempool.h" #include "lib/fastbuf.h" #include "images/images.h" +#include "images/error.h" +#include "images/color.h" #include "images/io-main.h" + #include #include #include +#include #include struct libjpeg_err { @@ -48,7 +52,7 @@ libjpeg_read_error_exit(j_common_ptr cinfo) struct libjpeg_err *e = (struct libjpeg_err *)cinfo->err; byte buf[JMSG_LENGTH_MAX]; e->pub.format_message(cinfo, buf); - image_thread_err_dup(e->io->thread, IMAGE_ERR_READ_FAILED, buf); + IMAGE_ERROR(e->io->context, IMAGE_ERROR_READ_FAILED, "%s", buf); longjmp(e->setjmp_buf, 1); } @@ -59,7 +63,7 @@ libjpeg_write_error_exit(j_common_ptr cinfo) struct libjpeg_err *e = (struct libjpeg_err *)cinfo->err; byte buf[JMSG_LENGTH_MAX]; e->pub.format_message(cinfo, buf); - image_thread_err_dup(e->io->thread, IMAGE_ERR_WRITE_FAILED, buf); + IMAGE_ERROR(e->io->context, IMAGE_ERROR_WRITE_FAILED, "%s", buf); longjmp(e->setjmp_buf, 1); } @@ -131,7 +135,11 @@ libjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { num_bytes -= i->src.bytes_in_buffer; libjpeg_fastbuf_read_commit(i); - bskip(i->fastbuf, num_bytes); + if (!bskip(i->fastbuf, num_bytes)) + { + IMAGE_ERROR(i->err.io->context, IMAGE_ERROR_READ_FAILED, "Incomplete JPEG file"); + longjmp(i->err.setjmp_buf, 1); + } libjpeg_fastbuf_read_prepare(i); } } @@ -147,7 +155,7 @@ libjpeg_fastbuf_write_prepare(struct libjpeg_write_internals *i) i->dest.free_in_buffer = len; if (!len) { - image_thread_err(i->err.io->thread, IMAGE_ERR_WRITE_FAILED, "Unexpected end of stream"); + IMAGE_ERROR(i->err.io->context, IMAGE_ERROR_WRITE_FAILED, "Unexpected end of stream"); longjmp(i->err.setjmp_buf, 1); } } @@ -177,6 +185,68 @@ libjpeg_empty_output_buffer(j_compress_ptr cinfo) return TRUE; } +static inline uns +libjpeg_read_byte(struct libjpeg_read_internals *i) +{ + DBG("libjpeg_read_byte()"); + if (!i->src.bytes_in_buffer) + if (!libjpeg_fill_input_buffer(&i->cinfo)) + ERREXIT(&i->cinfo, JERR_CANT_SUSPEND); + i->src.bytes_in_buffer--; + return *i->src.next_input_byte++; +} + +static inline void +libjpeg_read_buf(struct libjpeg_read_internals *i, byte *buf, uns len) +{ + DBG("libjpeg_read_buf(len=%u)", len); + while (len) + { + if (!i->src.bytes_in_buffer) + if (!libjpeg_fill_input_buffer(&i->cinfo)) + ERREXIT(&i->cinfo, JERR_CANT_SUSPEND); + uns buf_size = i->src.bytes_in_buffer; + uns read_size = MIN(buf_size, len); + memcpy(buf, i->src.next_input_byte, read_size); + i->src.bytes_in_buffer -= read_size; + i->src.next_input_byte += read_size; + len -= read_size; + } +} + +static byte libjpeg_exif_header[6] = { 'E', 'x', 'i', 'f', 0, 0 }; + +static boolean +libjpeg_app1_preprocessor(j_decompress_ptr cinfo) +{ + struct libjpeg_read_internals *i = (struct libjpeg_read_internals *)cinfo; + struct image_io *io = i->err.io; + uns len = libjpeg_read_byte(i) << 8; + len += libjpeg_read_byte(i); + DBG("Found APP1 marker, len=%u", len); + if (len < 2) + return TRUE; + len -= 2; + if (len < 7 /*|| io->exif_size*/) + { + libjpeg_skip_input_data(cinfo, len); + return TRUE; + } + byte header[6]; + libjpeg_read_buf(i, header, 6); + if (memcmp(header, libjpeg_exif_header, 6)) + { + libjpeg_skip_input_data(cinfo, len - 6); + return TRUE; + } + io->exif_size = len; + io->exif_data = mp_alloc(io->internal_pool, len); + memcpy(io->exif_data, header, 6); + libjpeg_read_buf(i, io->exif_data + 6, len - 6); + DBG("Parsed EXIF of length %u", len); + return TRUE; +} + static void libjpeg_read_cancel(struct image_io *io) { @@ -214,6 +284,9 @@ libjpeg_read_header(struct image_io *io) i->src.resync_to_restart = jpeg_resync_to_restart; i->src.term_source = libjpeg_term_source; + if (io->flags & IMAGE_IO_WANT_EXIF) + jpeg_set_marker_processor(&i->cinfo, JPEG_APP0 + 1, libjpeg_app1_preprocessor); + /* Read JPEG header and setup decompression options */ DBG("Reading image header"); jpeg_read_header(&i->cinfo, TRUE); @@ -253,7 +326,7 @@ libjpeg_read_data(struct image_io *io) break; default: jpeg_destroy_decompress(&i->cinfo); - image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported color space."); + IMAGE_ERROR(io->context, IMAGE_ERROR_INVALID_PIXEL_FORMAT, "Unsupported color space."); return 0; } @@ -395,15 +468,28 @@ libjpeg_write(struct image_io *io) break; default: jpeg_destroy_compress(&i.cinfo); - image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported pixel format."); + IMAGE_ERROR(io->context, IMAGE_ERROR_INVALID_PIXEL_FORMAT, "Unsupported pixel format."); return 0; } jpeg_set_defaults(&i.cinfo); if (io->jpeg_quality) jpeg_set_quality(&i.cinfo, MIN(io->jpeg_quality, 100), 1); + if (io->exif_size) + { + /* According to the Exif specification, the Exif APP1 marker has to follow immediately after the SOI, + * just as the JFIF specification requires the same for the JFIF APP0 marker! + * Therefore a JPEG file cannot legally be both Exif and JFIF. */ + i.cinfo.write_JFIF_header = FALSE; + i.cinfo.write_Adobe_marker = FALSE; + } /* Compress the image */ jpeg_start_compress(&i.cinfo, TRUE); + if (io->exif_size) + { + DBG("Writing EXIF"); + jpeg_write_marker(&i.cinfo, JPEG_APP0 + 1, io->exif_data, io->exif_size); + } switch (img->pixel_size) { /* grayscale or RGB */