From: Pavel Charvat Date: Sun, 10 Sep 2006 16:24:34 +0000 (+0200) Subject: Added support for EXIFs in JPEG files. X-Git-Tag: holmes-import~566 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=35a15c48e83fb6efba353c44a44f4c565a4d5e01;p=libucw.git Added support for EXIFs in JPEG files. Some small changes in images build system... --- diff --git a/images/Makefile b/images/Makefile index 89c6c169..58d9df21 100644 --- a/images/Makefile +++ b/images/Makefile @@ -2,11 +2,18 @@ DIRS+=images -PROGS+=$(addprefix $(o)/images/,image-tool image-dup-test image-sim-test) - +PROGS+=$(o)/images/image-tool CONFIGS+=images +LIBIMAGES_MODS=math config image scale color alpha io-main object -LIBIMAGES_MODS=math config image scale color alpha io-main dup-init dup-cmp sig-dump sig-init sig-seg sig-txt sig-cmp object +ifdef CONFIG_IMAGES_DUP +PROGS+=$(o)/images/image-dup-test +LIBIMAGES_MODS+=dup-init dup-cmp +endif +ifdef CONFIG_IMAGES_SIM +PROGS+=$(o)/images/image-sim-test +LIBIMAGES_MODS+=sig-dump sig-init sig-seg sig-txt sig-cmp +endif LIBIMAGES_LIBS=-lm -lpthread @@ -44,11 +51,15 @@ $(o)/images/libimages.so: $(addsuffix .oo,$(addprefix $(o)/images/,$(LIBIMAGES_M $(o)/images/image-tool: $(o)/images/image-tool.o $(LIBIMAGES) $(LIBUCW) $(o)/images/image-tool: LIBS+=$(LIBIMAGES_LIBS) +ifdef CONFIG_IMAGES_DUP $(o)/images/image-dup-test: $(o)/images/image-dup-test.o $(LIBIMAGES) $(LIBUCW) $(o)/images/image-dup-test: LIBS+=$(LIBIMAGES_LIBS) +endif +ifdef CONFIG_IMAGES_SIM $(o)/images/image-sim-test: $(o)/images/image-sim-test.o $(LIBIMAGES) $(LIBUCW) $(o)/images/image-sim-test: LIBS+=$(LIBIMAGES_LIBS) +endif TESTS+=$(o)/images/image-test.test $(o)/images/image-test: $(o)/images/image-test.o $(LIBIMAGES) $(LIBUCW) diff --git a/images/image-tool.c b/images/image-tool.c index eb2a3ec2..66f36811 100644 --- a/images/image-tool.c +++ b/images/image-tool.c @@ -33,12 +33,15 @@ Usage: image-tool [options] infile [outfile]\n\ -Q --jpeg-quality JPEG quality (1..100)\n\ -g --background background color (hexadecimal RRGGBB)\n\ -G --default-background background applied only if the image contains no background info (RRGGBB, default=FFFFFF)\n\ --a --remove-alpha remove alpha channel\n\ -", stderr); +-a --remove-alpha remove alpha channel\n" +#ifdef CONFIG_IMAGES_EXIF +"-e --exif reads Exif data\n" +#endif +, stderr); exit(1); } -static char *shortopts = "qf:F:s:b:c:Q:g:G:a"; +static char *shortopts = "qf:F:s:b:c:Q:g:G:ae"; static struct option longopts[] = { { "quiet", 0, 0, 'q' }, @@ -51,9 +54,12 @@ static struct option longopts[] = { "background", 0, 0, 'g' }, { "default-background", 0, 0, 'G' }, { "remove-alpha", 0, 0, 'a' }, +#ifdef CONFIG_IMAGES_EXIF + { "exif", 0, 0, 'e' }, +#endif { NULL, 0, 0, 0 } }; - + static uns verbose = 1; static byte *input_file_name; static enum image_format input_format; @@ -67,6 +73,9 @@ static uns jpeg_quality; static struct color background_color; static struct color default_background_color; static uns remove_alpha; +#ifdef CONFIG_IMAGES_EXIF +static uns exif; +#endif static void parse_color(struct color *color, byte *s) @@ -142,6 +151,11 @@ main(int argc, char **argv) case 'a': remove_alpha++; break; +#ifdef CONFIG_IMAGES_EXIF + case 'e': + exif++; + break; +#endif default: usage(); } @@ -151,7 +165,7 @@ main(int argc, char **argv) input_file_name = argv[optind++]; if (argc > optind) output_file_name = argv[optind]; - + #define TRY(x) do{ if (!(x)) die("Error: %s", it.err_msg); }while(0) MSG("Initializing image library"); struct image_thread it; @@ -163,6 +177,9 @@ main(int argc, char **argv) MSG("Reading %s", input_file_name); io.fastbuf = bopen(input_file_name, O_RDONLY, 1 << 18); io.format = input_format ? : image_file_name_to_format(input_file_name); +#ifdef CONFIG_IMAGES_EXIF + io.flags |= IMAGE_IO_WANT_EXIF; +#endif TRY(image_io_read_header(&io)); if (!output_file_name) { @@ -177,6 +194,10 @@ main(int argc, char **argv) color_put_rgb(rgb, &io.background_color); printf("Background: %02x%02x%02x\n", rgb[0], rgb[1], rgb[2]); } +#ifdef CONFIG_IMAGES_EXIF + if (io.exif_size) + printf("ExifSize: %u\n", io.exif_size); +#endif } else { @@ -214,7 +235,7 @@ main(int argc, char **argv) TRY(image_io_write(&io)); bclose(io.fastbuf); } - + image_io_cleanup(&io); image_thread_cleanup(&it); MSG("Done."); diff --git a/images/images.h b/images/images.h index 236b0446..460f5881 100644 --- a/images/images.h +++ b/images/images.h @@ -147,6 +147,10 @@ struct image_io { u32 jpeg_quality; /* [ W] - JPEG compression quality (1..100) */ u32 number_of_colors; /* [ H ] - number of image colors */ struct color background_color; /* [ HI ] - background color, zero if undefined */ +#ifdef CONFIG_IMAGES_EXIF + u32 exif_size; /* [ H W] - EXIF size in bytes (zero if not present) */ + byte *exif_data; /* [ H W] - EXIF data */ +#endif /* internals */ struct image_thread *thread; @@ -160,6 +164,9 @@ enum image_io_flags { IMAGE_IO_NEED_DESTROY = 0x10000, /* [ O ] - enables automatic call of image_destroy */ IMAGE_IO_HAS_PALETTE = 0x20000, /* [ H ] - true for image with indexed colors */ IMAGE_IO_USE_BACKGROUND = 0x40000, /* [ I ] - merge transparent pixels with background_color */ +#ifdef CONFIG_IMAGES_EXIF + IMAGE_IO_WANT_EXIF = 0x80000, /* [R ] - read EXIF data if present */ +#endif }; int image_io_init(struct image_thread *it, struct image_io *io); diff --git a/images/io-libjpeg.c b/images/io-libjpeg.c index 876c1720..392b19ca 100644 --- a/images/io-libjpeg.c +++ b/images/io-libjpeg.c @@ -17,6 +17,7 @@ #include #include #include +#include #include struct libjpeg_err { @@ -177,6 +178,76 @@ libjpeg_empty_output_buffer(j_compress_ptr cinfo) return TRUE; } +#ifdef CONFIG_IMAGES_EXIF + +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) + 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]; + for (uns j = 0; j < 6; j++) + { + header[j] = libjpeg_read_byte(i); + DBG("0x%02x", header[j]); + } + //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; +} + +#endif + static void libjpeg_read_cancel(struct image_io *io) { @@ -214,6 +285,11 @@ libjpeg_read_header(struct image_io *io) i->src.resync_to_restart = jpeg_resync_to_restart; i->src.term_source = libjpeg_term_source; +#ifdef CONFIG_IMAGES_EXIF + if (io->flags & IMAGE_IO_WANT_EXIF) + jpeg_set_marker_processor(&i->cinfo, JPEG_APP0 + 1, libjpeg_app1_preprocessor); +#endif + /* Read JPEG header and setup decompression options */ DBG("Reading image header"); jpeg_read_header(&i->cinfo, TRUE); @@ -401,9 +477,26 @@ libjpeg_write(struct image_io *io) jpeg_set_defaults(&i.cinfo); if (io->jpeg_quality) jpeg_set_quality(&i.cinfo, MIN(io->jpeg_quality, 100), 1); +#ifdef CONFIG_IMAGES_EXIF + 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; + } +#endif /* Compress the image */ jpeg_start_compress(&i.cinfo, TRUE); +#ifdef CONFIG_IMAGES_EXIF + if (io->exif_size) + { + DBG("Writing EXIF"); + jpeg_write_marker(&i.cinfo, JPEG_APP0 + 1, io->exif_data, io->exif_size); + } +#endif switch (img->pixel_size) { /* grayscale or RGB */