read color profiles from jpeg also with Exiv2
authorVladimir Nadvornik <nadvornik@suse.cz>
Sun, 20 Apr 2008 21:35:03 +0000 (21:35 +0000)
committerVladimir Nadvornik <nadvornik@suse.cz>
Sun, 20 Apr 2008 21:35:03 +0000 (21:35 +0000)
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

index a02faa7..7019e15 100644 (file)
@@ -176,7 +176,7 @@ static void bar_exif_update(ExifBar *eb)
        ExifData *exif;
        gint i;
 
-       exif = exif_read_fd(eb->fd, FALSE);
+       exif = exif_read_fd(eb->fd);
 
        if (!exif)
                {
index f3014e5..a4676a3 100644 (file)
@@ -249,7 +249,7 @@ const gchar *keyword_key = "Xmp.dc.subject";
 
 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 @@ static gint comment_xmp_write(FileData *fd, GList *keywords, const gchar *commen
 {
        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);
index 476ef73..1580d03 100644 (file)
@@ -125,7 +125,7 @@ static gboolean cache_loader_process(CacheLoader *cl)
                time_t date = -1;
                ExifData *exif;
 
-               exif = exif_read_fd(cl->fd, FALSE);
+               exif = exif_read_fd(cl->fd);
                if (exif)
                        {
                        gchar *text;
index be6c491..50e5a3e 100644 (file)
@@ -387,8 +387,10 @@ do {                                    \
                {
                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 @@ do {                                    \
 
                        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 @@ gchar *exif_get_data_as_text(ExifData *exif, const gchar *key)
        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 @@ ExifData *exif_read_fd(FileData *fd, gint parse_color_profile)
 
 
        // 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;
 }
index 63f5a4f..51be577 100644 (file)
@@ -992,6 +992,7 @@ gint exif_tiff_parse(ExifData *exif, unsigned char *tiff, guint size, ExifMarker
        return exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, list);
 }
 
+
 /*
  *-------------------------------------------------------------------
  * jpeg marker utils
@@ -1015,133 +1016,23 @@ gint exif_tiff_parse(ExifData *exif, unsigned char *tiff, guint size, ExifMarker
             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;
-                       }
-
-               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,
+       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);
-
-               return TRUE;
-               }
+       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 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 @@ static gint exif_jpeg_parse(ExifData *exif,
                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 @@ static gint exif_jpeg_parse(ExifData *exif,
        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 @@ ExifItem *exif_get_next_item(ExifData *exif)
        return NULL;
 }
 
-
-
 static gint map_file(const gchar *path, void **mapping, int *size)
 {
        int fd;
@@ -1262,7 +1158,7 @@ void exif_free(ExifData *exif)
        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 @@ ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile)
        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 @@ ExifData *exif_read(gchar *path, gchar *sidecar_path, gint parse_color_profile)
                                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:
index 7b362be..000a0ac 100644 (file)
@@ -105,9 +105,9 @@ typedef enum {
  *-----------------------------------------------------------------------------
  */
 
-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 @@ gchar *exif_get_formatted_by_key(ExifData *exif, const gchar *key, gint *key_val
 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
index e8b8633..08d5360 100644 (file)
@@ -57,10 +57,15 @@ struct _ExifData
 #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 @@ struct _ExifData
                        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 @@ struct _ExifData
 
 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 @@ int exif_item_delete(ExifData *exif, ExifItem *item)
        }
 }
 
+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;
+}
+
 
 
 }
index 8854d31..eef321b 100644 (file)
@@ -137,7 +137,7 @@ static gchar *image_osd_mkinfo(const gchar *str, ImageWindow *imd, GHashTable *v
 
        new = g_string_new(str);
 
-       exif = exif_read_fd(imd->image_fd, FALSE);
+       exif = exif_read_fd(imd->image_fd);
        prev = 0;
        last = FALSE;
 
index 9581019..4fc2afe 100644 (file)
@@ -335,7 +335,8 @@ static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData
        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 @@ static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData
 
        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 @@ static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData
                        }
                }
 
-       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 @@ static void image_post_process(ImageWindow *imd, gint clamp)
        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 @@ static gint image_post_buffer_get(ImageWindow *imd)
                        {
                        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 @@ void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
                {
                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)
                        {
index 37317c3..72271ca 100644 (file)
@@ -1433,7 +1433,7 @@ static void pan_info_add_exif(PanTextAlignment *ta, FileData *fd)
        gint i;
 
        if (!fd) return;
-       exif = exif_read_fd(fd, FALSE);
+       exif = exif_read_fd(fd);
        if (!exif) return;
 
        pan_text_alignment_add(ta, NULL, NULL);