Mercurial > geeqie
changeset 449:115db540bd0c
read color profiles from jpeg also with Exiv2
author | nadvornik |
---|---|
date | Sun, 20 Apr 2008 21:35:03 +0000 |
parents | a73cc0fa14d0 |
children | d643890c48d0 |
files | src/bar_exif.c src/bar_info.c src/cache-loader.c src/exif-common.c src/exif.c src/exif.h src/exiv2.cc src/image-overlay.c src/image.c src/pan-view.c |
diffstat | 10 files changed, 248 insertions(+), 168 deletions(-) [+] |
line wrap: on
line diff
--- a/src/bar_exif.c Sun Apr 20 21:30:36 2008 +0000 +++ b/src/bar_exif.c Sun Apr 20 21:35:03 2008 +0000 @@ -176,7 +176,7 @@ ExifData *exif; gint i; - exif = exif_read_fd(eb->fd, FALSE); + exif = exif_read_fd(eb->fd); if (!exif) {
--- a/src/bar_info.c Sun Apr 20 21:30:36 2008 +0000 +++ b/src/bar_info.c Sun Apr 20 21:35:03 2008 +0000 @@ -249,7 +249,7 @@ static gint comment_xmp_read(FileData *fd, GList **keywords, gchar **comment) { - ExifData *exif = exif_read_fd(fd, FALSE); + ExifData *exif = exif_read_fd(fd); gint success; if (!exif) return FALSE; @@ -286,7 +286,7 @@ { gint success = FALSE; GList *work = keywords; - ExifData *exif = exif_read_fd(fd, FALSE); + ExifData *exif = exif_read_fd(fd); if (!exif) return FALSE; ExifItem *item = exif_get_item(exif, comment_key);
--- a/src/cache-loader.c Sun Apr 20 21:30:36 2008 +0000 +++ b/src/cache-loader.c Sun Apr 20 21:35:03 2008 +0000 @@ -125,7 +125,7 @@ time_t date = -1; ExifData *exif; - exif = exif_read_fd(cl->fd, FALSE); + exif = exif_read_fd(cl->fd); if (exif) { gchar *text;
--- a/src/exif-common.c Sun Apr 20 21:30:36 2008 +0000 +++ b/src/exif-common.c Sun Apr 20 21:35:03 2008 +0000 @@ -387,8 +387,10 @@ { const gchar *name = ""; const gchar *source = ""; - ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile"); - if (!prof_item) + unsigned char *profile_data; + guint profile_len; + profile_data = exif_get_color_profile(exif, &profile_len); + if (!profile_data) { gint cs; gchar *interop_index; @@ -410,28 +412,21 @@ g_free(interop_index); } - - if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED) + else { source = _("embedded"); #ifdef HAVE_LCMS { - unsigned char *data; - guint data_len; cmsHPROFILE profile; - data = (unsigned char *) exif_item_get_data(prof_item, &data_len); - if (data) + profile = cmsOpenProfileFromMem(profile_data, profile_len); + if (profile) { - profile = cmsOpenProfileFromMem(data, data_len); - if (profile) - { - name = cmsTakeProductName(profile); - cmsCloseProfile(profile); - } - g_free(data); + name = cmsTakeProductName(profile); + cmsCloseProfile(profile); } + g_free(profile_data); } #endif } @@ -492,7 +487,7 @@ return NULL; } -ExifData *exif_read_fd(FileData *fd, gint parse_color_profile) +ExifData *exif_read_fd(FileData *fd) { GList *work; gchar *sidecar_path = NULL; @@ -517,5 +512,142 @@ // FIXME: some caching would be nice - return exif_read(fd->path, sidecar_path, parse_color_profile); + return exif_read(fd->path, sidecar_path); +} + + + +/* embedded icc in jpeg */ + + +#define JPEG_MARKER 0xFF +#define JPEG_MARKER_SOI 0xD8 +#define JPEG_MARKER_EOI 0xD9 +#define JPEG_MARKER_APP1 0xE1 +#define JPEG_MARKER_APP2 0xE2 + +/* jpeg container format: + all data markers start with 0XFF + 2 byte long file start and end markers: 0xFFD8(SOI) and 0XFFD9(EOI) + 4 byte long data segment markers in format: 0xFFTTSSSSNNN... + FF: 1 byte standard marker identifier + TT: 1 byte data type + SSSS: 2 bytes in Motorola byte alignment for length of the data. + This value includes these 2 bytes in the count, making actual + length of NN... == SSSS - 2. + NNN.: the data in this segment + */ + +gint exif_jpeg_segment_find(unsigned char *data, guint size, + guchar app_marker, const gchar *magic, guint magic_len, + guint *seg_offset, guint *seg_length) +{ + guchar marker = 0; + guint offset = 0; + guint length = 0; + + while (marker != app_marker && + marker != JPEG_MARKER_EOI) + { + offset += length; + length = 2; + + if (offset + 2 >= size || + data[offset] != JPEG_MARKER) return FALSE; + + marker = data[offset + 1]; + if (marker != JPEG_MARKER_SOI && + marker != JPEG_MARKER_EOI) + { + if (offset + 4 >= size) return FALSE; + length += ((guint)data[offset + 2] << 8) + data[offset + 3]; + } + } + + if (marker == app_marker && + offset + length < size && + length >= 4 + magic_len && + memcmp(data + offset + 4, magic, magic_len) == 0) + { + *seg_offset = offset + 4; + *seg_length = length - 4; + return TRUE; + } + + return FALSE; } + +gint exif_jpeg_parse_color(ExifData *exif, unsigned char *data, guint size) +{ + guint seg_offset = 0; + guint seg_length = 0; + guint chunk_offset[255]; + guint chunk_length[255]; + guint chunk_count = 0; + + /* For jpeg/jfif, ICC color profile data can be in more than one segment. + the data is in APP2 data segments that start with "ICC_PROFILE\x00\xNN\xTT" + NN = segment number for data + TT = total number of ICC segments (TT in each ICC segment should match) + */ + + while (exif_jpeg_segment_find(data + seg_offset + seg_length, + size - seg_offset - seg_length, + JPEG_MARKER_APP2, + "ICC_PROFILE\x00", 12, + &seg_offset, &seg_length)) + { + guchar chunk_num; + guchar chunk_tot; + + if (seg_length < 14) return FALSE; + + chunk_num = data[seg_offset + 12]; + chunk_tot = data[seg_offset + 13]; + + if (chunk_num == 0 || chunk_tot == 0) return FALSE; + + if (chunk_count == 0) + { + guint i; + + chunk_count = (guint)chunk_tot; + for (i = 0; i < chunk_count; i++) chunk_offset[i] = 0; + for (i = 0; i < chunk_count; i++) chunk_length[i] = 0; + } + + if (chunk_tot != chunk_count || + chunk_num > chunk_count) return FALSE; + + chunk_num--; + chunk_offset[chunk_num] = seg_offset + 14; + chunk_length[chunk_num] = seg_length - 14; + } + + if (chunk_count > 0) + { + unsigned char *cp_data; + guint cp_length = 0; + guint i; + + for (i = 0; i < chunk_count; i++) cp_length += chunk_length[i]; + cp_data = g_malloc(cp_length); + + for (i = 0; i < chunk_count; i++) + { + if (chunk_offset[i] == 0) + { + /* error, we never saw this chunk */ + g_free(cp_data); + return FALSE; + } + memcpy(cp_data, data + chunk_offset[i], chunk_length[i]); + } + if (debug) printf("Found embedded icc profile in jpeg\n"); + exif_add_jpeg_color_profile(exif, cp_data, cp_length); + + return TRUE; + } + + return FALSE; +}
--- a/src/exif.c Sun Apr 20 21:30:36 2008 +0000 +++ b/src/exif.c Sun Apr 20 21:35:03 2008 +0000 @@ -992,6 +992,7 @@ return exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, list); } + /* *------------------------------------------------------------------- * jpeg marker utils @@ -1015,133 +1016,23 @@ length of NN... == SSSS - 2. NNN.: the data in this segment */ - -static gint exif_jpeg_segment_find(unsigned char *data, guint size, - guchar app_marker, const gchar *magic, guint magic_len, - guint *seg_offset, guint *seg_length) -{ - guchar marker = 0; - guint offset = 0; - guint length = 0; - - while (marker != app_marker && - marker != JPEG_MARKER_EOI) - { - offset += length; - length = 2; - - if (offset + 2 >= size || - data[offset] != JPEG_MARKER) return FALSE; - - marker = data[offset + 1]; - if (marker != JPEG_MARKER_SOI && - marker != JPEG_MARKER_EOI) - { - if (offset + 4 >= size) return FALSE; - length += exif_byte_get_int16(data + offset + 2, EXIF_BYTE_ORDER_MOTOROLA); - } - } - - if (marker == app_marker && - offset + length < size && - length >= 4 + magic_len && - memcmp(data + offset + 4, magic, magic_len) == 0) - { - *seg_offset = offset + 4; - *seg_length = length - 4; - return TRUE; - } - - return FALSE; -} - static ExifMarker jpeg_color_marker = { 0x8773, EXIF_FORMAT_UNDEFINED, -1, "Exif.Image.InterColorProfile", NULL, NULL }; -static gint exif_jpeg_parse_color(ExifData *exif, unsigned char *data, guint size) +void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length) { - guint seg_offset = 0; - guint seg_length = 0; - guint chunk_offset[255]; - guint chunk_length[255]; - guint chunk_count = 0; - - /* For jpeg/jfif, ICC color profile data can be in more than one segment. - the data is in APP2 data segments that start with "ICC_PROFILE\x00\xNN\xTT" - NN = segment number for data - TT = total number of ICC segments (TT in each ICC segment should match) - */ - - while (exif_jpeg_segment_find(data + seg_offset + seg_length, - size - seg_offset - seg_length, - JPEG_MARKER_APP2, - "ICC_PROFILE\x00", 12, - &seg_offset, &seg_length)) - { - guchar chunk_num; - guchar chunk_tot; - - if (seg_length < 14) return FALSE; - - chunk_num = data[seg_offset + 12]; - chunk_tot = data[seg_offset + 13]; - - if (chunk_num == 0 || chunk_tot == 0) return FALSE; - - if (chunk_count == 0) - { - guint i; - - chunk_count = (guint)chunk_tot; - for (i = 0; i < chunk_count; i++) chunk_offset[i] = 0; - for (i = 0; i < chunk_count; i++) chunk_length[i] = 0; - } + ExifItem *item = exif_item_new(jpeg_color_marker.format, jpeg_color_marker.tag, 1, + &jpeg_color_marker); + g_free(item->data); + item->data = cp_data; + item->elements = cp_length; + item->data_len = cp_length; + exif->items = g_list_prepend(exif->items, item); - if (chunk_tot != chunk_count || - chunk_num > chunk_count) return FALSE; - - chunk_num--; - chunk_offset[chunk_num] = seg_offset + 14; - chunk_length[chunk_num] = seg_length - 14; - } - - if (chunk_count > 0) - { - ExifItem *item; - unsigned char *cp_data; - guint cp_length = 0; - guint i; - - for (i = 0; i < chunk_count; i++) cp_length += chunk_length[i]; - cp_data = g_malloc(cp_length); - - for (i = 0; i < chunk_count; i++) - { - if (chunk_offset[i] == 0) - { - /* error, we never saw this chunk */ - g_free(cp_data); - return FALSE; - } - memcpy(cp_data, data + chunk_offset[i], chunk_length[i]); - } - - item = exif_item_new(jpeg_color_marker.format, jpeg_color_marker.tag, 1, - &jpeg_color_marker); - g_free(item->data); - item->data = cp_data; - item->elements = cp_length; - item->data_len = cp_length; - exif->items = g_list_prepend(exif->items, item); - - return TRUE; - } - - return FALSE; } static gint exif_jpeg_parse(ExifData *exif, unsigned char *data, guint size, - ExifMarker *list, gint parse_color) + ExifMarker *list) { guint seg_offset = 0; guint seg_length = 0; @@ -1160,8 +1051,7 @@ res = exif_tiff_parse(exif, data + seg_offset + 6, seg_length - 6, list); } - if (parse_color && - exif_jpeg_parse_color(exif, data, size)) + if (exif_jpeg_parse_color(exif, data, size)) { res = 0; } @@ -1169,6 +1059,14 @@ return res; } +unsigned char *exif_get_color_profile(ExifData *exif, guint *data_len) +{ + ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile"); + if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED) + return (unsigned char*) exif_item_get_data(prof_item, data_len); + return NULL; +} + /* *------------------------------------------------------------------- @@ -1200,8 +1098,6 @@ return NULL; } - - static gint map_file(const gchar *path, void **mapping, int *size) { int fd; @@ -1262,7 +1158,7 @@ g_free(exif); } -ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile) +ExifData *exif_read(gchar *path, gchar *sidecar_path) { ExifData *exif; void *f; @@ -1284,8 +1180,7 @@ exif->current = NULL; if ((res = exif_jpeg_parse(exif, (unsigned char *)f, size, - ExifKnownMarkersList, - parse_color_profile)) == -2) + ExifKnownMarkersList)) == -2) { res = exif_tiff_parse(exif, (unsigned char *)f, size, ExifKnownMarkersList); } @@ -1308,7 +1203,7 @@ break; case FORMAT_RAW_EXIF_JPEG: res = exif_jpeg_parse(exif, (unsigned char*)f + offset, size - offset, - ExifKnownMarkersList, FALSE); + ExifKnownMarkersList); break; case FORMAT_RAW_EXIF_IFD_II: case FORMAT_RAW_EXIF_IFD_MM:
--- a/src/exif.h Sun Apr 20 21:30:36 2008 +0000 +++ b/src/exif.h Sun Apr 20 21:35:03 2008 +0000 @@ -105,9 +105,9 @@ *----------------------------------------------------------------------------- */ -ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile); +ExifData *exif_read(gchar *path, gchar *sidecar_path); -ExifData *exif_read_fd(FileData *fd, gint parse_color_profile); +ExifData *exif_read_fd(FileData *fd); int exif_write(ExifData *exif); @@ -146,10 +146,23 @@ int exif_item_delete(ExifData *exif, ExifItem *item); int exif_item_set_string(ExifItem *item, const char *str); +unsigned char *exif_get_color_profile(ExifData *exif, guint *data_len); +/* jpeg embedded icc support */ + +void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length); + + +gint exif_jpeg_segment_find(unsigned char *data, guint size, + guchar app_marker, const gchar *magic, guint magic_len, + guint *seg_offset, guint *seg_length); +gint exif_jpeg_parse_color(ExifData *exif, unsigned char *data, guint size); + +/*raw support */ gint format_raw_img_exif_offsets_fd(int fd, const gchar *path, unsigned char *header_data, const guint header_len, guint *image_offset, guint *exif_offset); + #endif
--- a/src/exiv2.cc Sun Apr 20 21:30:36 2008 +0000 +++ b/src/exiv2.cc Sun Apr 20 21:35:03 2008 +0000 @@ -57,10 +57,15 @@ #endif bool have_sidecar; + /* the icc profile in jpeg is not technically exif - store it here */ + unsigned char *cp_data; + guint cp_length; - _ExifData(gchar *path, gchar *sidecar_path, gint parse_color_profile) + _ExifData(gchar *path, gchar *sidecar_path) { have_sidecar = false; + cp_data = NULL; + cp_length = 0; image = Exiv2::ImageFactory::open(path); // g_assert (image.get() != 0); image->readMetadata(); @@ -74,10 +79,28 @@ have_sidecar = sidecar->good(); if (debug >= 2) printf("sidecar xmp count %li\n", sidecar->xmpData().count()); } + + if (image->mimeType() == std::string("image/jpeg")) + { + /* try to get jpeg color profile */ + Exiv2::BasicIo &io = image->io(); + gint open = io.isopen(); + if (!open) io.open(); + unsigned char *mapped = (unsigned char*)io.mmap(); + if (mapped) exif_jpeg_parse_color(this, mapped, io.size()); + io.munmap(); + if (!open) io.close(); + } + #endif } + ~_ExifData() + { + if (cp_data) g_free(cp_data); + } + void writeMetadata() { if (have_sidecar) sidecar->writeMetadata(); @@ -105,11 +128,11 @@ extern "C" { -ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile) +ExifData *exif_read(gchar *path, gchar *sidecar_path) { if (debug) printf("exif read %s, sidecar: %s\n", path, sidecar_path ? sidecar_path : "-"); try { - return new ExifData(path, sidecar_path, parse_color_profile); + return new ExifData(path, sidecar_path); } catch (Exiv2::AnyError& e) { std::cout << "Caught Exiv2 exception '" << e << "'\n"; @@ -526,6 +549,26 @@ } } +void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length) +{ + if (exif->cp_data) g_free(exif->cp_data); + exif->cp_data = cp_data; + exif->cp_length =cp_length; +} + +unsigned char *exif_get_color_profile(ExifData *exif, guint *data_len) +{ + if (exif->cp_data) + { + if (data_len) *data_len = exif->cp_length; + return (unsigned char *) g_memdup(exif->cp_data, exif->cp_length); + } + ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile"); + if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED) + return (unsigned char *) exif_item_get_data(prof_item, data_len); + return NULL; +} + }
--- a/src/image-overlay.c Sun Apr 20 21:30:36 2008 +0000 +++ b/src/image-overlay.c Sun Apr 20 21:35:03 2008 +0000 @@ -137,7 +137,7 @@ new = g_string_new(str); - exif = exif_read_fd(imd->image_fd, FALSE); + exif = exif_read_fd(imd->image_fd); prev = 0; last = FALSE;
--- a/src/image.c Sun Apr 20 21:30:36 2008 +0000 +++ b/src/image.c Sun Apr 20 21:35:03 2008 +0000 @@ -335,7 +335,8 @@ ColorManProfileType screen_type; const gchar *input_file; const gchar *screen_file; - ExifItem *item = NULL; + unsigned char *profile = NULL; + guint profile_len; if (imd->cm) return FALSE; @@ -380,8 +381,8 @@ if (imd->color_profile_use_image && exif) { - item = exif_get_item(exif, "Exif.Image.InterColorProfile"); - if (!item) + profile = exif_get_color_profile(exif, &profile_len); + if (!profile) { gint cs; gchar *interop_index; @@ -411,19 +412,15 @@ } } - if (item && exif_item_get_format_id(item) == EXIF_FORMAT_UNDEFINED) + if (profile) { - unsigned char *data; - guint data_len; if (debug) printf("Found embedded color profile\n"); imd->color_profile_from_image = COLOR_PROFILE_MEM; - data = (unsigned char *) exif_item_get_data(item, &data_len); - cm = color_man_new_embedded(run_in_bg ? imd : NULL, NULL, - data, data_len, + profile, profile_len, screen_type, screen_file); - g_free(data); + g_free(profile); } else { @@ -462,7 +459,7 @@ if (options->image.exif_rotate_enable || (imd->color_profile_enable && imd->color_profile_use_image) ) { - exif = exif_read_fd(imd->image_fd, (imd->color_profile_enable && imd->color_profile_use_image)); + exif = exif_read_fd(imd->image_fd); } if (options->image.exif_rotate_enable && exif) { @@ -731,7 +728,7 @@ { ExifData *exif = NULL; - if (imd->color_profile_use_image) exif = exif_read_fd(imd->image_fd, TRUE); + if (imd->color_profile_use_image) exif = exif_read_fd(imd->image_fd); // image_post_process_color(imd, imd->prev_color_row, exif, TRUE); exif_free(exif); } @@ -1315,7 +1312,7 @@ { gint orientation; - exif = exif_read_fd(imd->image_fd, read_exif_for_color_profile); + exif = exif_read_fd(imd->image_fd); if (exif && read_exif_for_orientation) {