Mercurial > geeqie
diff src/exif-common.c @ 449:115db540bd0c
read color profiles from jpeg also with Exiv2
author | nadvornik |
---|---|
date | Sun, 20 Apr 2008 21:35:03 +0000 |
parents | 4b2d7f9af171 |
children | 48c8e49b571c |
line wrap: on
line diff
--- 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; +}