Mon Nov 27 01:23:23 2006 John Ellis <johne@verizon.net>
authorJohn Ellis <johne@verizon.net>
Mon, 27 Nov 2006 06:37:48 +0000 (06:37 +0000)
committerJohn Ellis <johne@verizon.net>
Mon, 27 Nov 2006 06:37:48 +0000 (06:37 +0000)
        * bar_exif.c, cache-loader.c, pan-view.c: Pass new arg for exif_read().
        * color-man.[ch]: Add color_man_new_embedded for using in-memory color
        profiles.
        * exif.[ch]: Add support for extracting color profiles embedded in jpeg
        and tiff images. This resulted in a rewrite of the jpeg parser; both
        to allow searching for any marker type, and to make the code readable.
        * format_raw.c: Add color profile tag to the debug code.
        * image.c, layout.c: Use embedded color profiles when found and
        enabled, also add toggle for the option in color profile menu.

12 files changed:
ChangeLog
TODO
src/bar_exif.c
src/cache-loader.c
src/color-man.c
src/color-man.h
src/exif.c
src/exif.h
src/format_raw.c
src/image.c
src/layout.c
src/pan-view.c

index 8c91a33..3204673 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+Mon Nov 27 01:23:23 2006  John Ellis  <johne@verizon.net>
+
+       * bar_exif.c, cache-loader.c, pan-view.c: Pass new arg for exif_read().
+       * color-man.[ch]: Add color_man_new_embedded for using in-memory color
+       profiles.
+       * exif.[ch]: Add support for extracting color profiles embedded in jpeg
+       and tiff images. This resulted in a rewrite of the jpeg parser; both
+       to allow searching for any marker type, and to make the code readable.
+       * format_raw.c: Add color profile tag to the debug code.
+       * image.c, layout.c: Use embedded color profiles when found and
+       enabled, also add toggle for the option in color profile menu.
+
 Fri Nov 24 21:37:01 2006  John Ellis  <johne@verizon.net>
 
        * configure.in: Add test for lcms (little cms).
diff --git a/TODO b/TODO
index ded5ffb..4a525bb 100644 (file)
--- a/TODO
+++ b/TODO
@@ -22,7 +22,8 @@ d> figure out if crash when expanding a folder in the folder tree view when pess
    > should honor enable_thumbnails setting
 
  > color profiles:
-   > support profiles embedded in images
+  d> support profiles embedded in images
+   > check if clamp arg is handled correct in post_process_*()
    > add support in img-view.c
 
   ---
index 041c3ca..00a3e9b 100644 (file)
@@ -171,7 +171,7 @@ static void bar_exif_update(ExifBar *eb)
        ExifData *exif;
        gint len, i;
 
-       exif = exif_read(eb->path);
+       exif = exif_read(eb->path, FALSE);
 
        if (!exif)
                {
index 1fb2fcb..b95bd2c 100644 (file)
@@ -123,7 +123,7 @@ static gboolean cache_loader_process(CacheLoader *cl)
                time_t date = -1;
                ExifData *exif;
 
-               exif = exif_read(cl->path);
+               exif = exif_read(cl->path, FALSE);
                if (exif)
                        {
                        gchar *text;
index 5c52457..a023725 100644 (file)
@@ -93,7 +93,8 @@ static void color_man_cache_unref(ColorManCache *cc)
                }
 }
 
-static cmsHPROFILE color_man_cache_load_profile(ColorManProfileType type, const gchar *file)
+static cmsHPROFILE color_man_cache_load_profile(ColorManProfileType type, const gchar *file,
+                                               unsigned char *data, guint data_len)
 {
        cmsHPROFILE profile = NULL;
 
@@ -112,6 +113,12 @@ static cmsHPROFILE color_man_cache_load_profile(ColorManProfileType type, const
                case COLOR_PROFILE_SRGB:
                        profile = cmsCreate_sRGBProfile();
                        break;
+               case COLOR_PROFILE_MEM:
+                       if (data)
+                               {
+                               profile = cmsOpenProfileFromMem(data, data_len);
+                               }
+                       break;
                case COLOR_PROFILE_NONE:
                default:
                        break;
@@ -121,6 +128,7 @@ static cmsHPROFILE color_man_cache_load_profile(ColorManProfileType type, const
 }
 
 static ColorManCache *color_man_cache_new(ColorManProfileType in_type, const gchar *in_file,
+                                         unsigned char *in_data, guint in_data_len,
                                          ColorManProfileType out_type, const gchar *out_file,
                                          gint has_alpha)
 {
@@ -139,8 +147,10 @@ static ColorManCache *color_man_cache_new(ColorManProfileType in_type, const gch
 
        cc->has_alpha = has_alpha;
 
-       cc->profile_in = color_man_cache_load_profile(cc->profile_in_type, cc->profile_in_file);
-       cc->profile_out = color_man_cache_load_profile(cc->profile_out_type, cc->profile_out_file);
+       cc->profile_in = color_man_cache_load_profile(cc->profile_in_type, cc->profile_in_file,
+                                                     in_data, in_data_len);
+       cc->profile_out = color_man_cache_load_profile(cc->profile_out_type, cc->profile_out_file,
+                                                      NULL, 0);
 
        if (!cc->profile_in || !cc->profile_out)
                {
@@ -167,7 +177,11 @@ static ColorManCache *color_man_cache_new(ColorManProfileType in_type, const gch
                return NULL;
                }
 
-       cm_cache_list = g_list_append(cm_cache_list, cc);
+       if (cc->profile_in_type != COLOR_PROFILE_MEM)
+               {
+               cm_cache_list = g_list_append(cm_cache_list, cc);
+               color_man_cache_ref(cc);
+               }
 
        return cc;
 }
@@ -231,19 +245,21 @@ static ColorManCache *color_man_cache_find(ColorManProfileType in_type, const gc
 }
 
 static ColorManCache *color_man_cache_get(ColorManProfileType in_type, const gchar *in_file,
+                                         unsigned char *in_data, guint in_data_len,
                                          ColorManProfileType out_type, const gchar *out_file,
                                          gint has_alpha)
 {
        ColorManCache *cc;
 
        cc = color_man_cache_find(in_type, in_file, out_type, out_file, has_alpha);
-
-       if (!cc)
+       if (cc)
                {
-               cc = color_man_cache_new(in_type, in_file, out_type, out_file, has_alpha);
+               color_man_cache_ref(cc);
+               return cc;
                }
 
-       return cc;
+       return color_man_cache_new(in_type, in_file, in_data, in_data_len,
+                                  out_type, out_file, has_alpha);
 }
 
 
@@ -319,17 +335,17 @@ static gint color_man_idle_cb(gpointer data)
        return TRUE;
 }
 
-ColorMan *color_man_new(ImageWindow *imd,
-                       ColorManProfileType input_type, const gchar *input_file,
-                       ColorManProfileType screen_type, const gchar *screen_file,
-                       ColorManDoneFunc done_func, gpointer done_data)
+static ColorMan *color_man_new_real(ImageWindow *imd,
+                                   ColorManProfileType input_type, const gchar *input_file,
+                                   unsigned char *input_data, guint input_data_len,
+                                   ColorManProfileType screen_type, const gchar *screen_file,
+                                   ColorManDoneFunc done_func, gpointer done_data)
 {
        ColorMan *cm;
        GdkPixbuf *pixbuf;
        gint has_alpha;
 
        if (!imd) return NULL;
-       if (input_type == COLOR_PROFILE_NONE || screen_type == COLOR_PROFILE_NONE) return NULL;
 
        pixbuf = image_get_pixbuf(imd);
        if (!pixbuf) return NULL;
@@ -344,20 +360,41 @@ ColorMan *color_man_new(ImageWindow *imd,
        cm->func_done_data = done_data;
 
        has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);
-       cm->profile = color_man_cache_get(input_type, input_file, screen_type, screen_file, has_alpha);
+       cm->profile = color_man_cache_get(input_type, input_file, input_data, input_data_len,
+                                         screen_type, screen_file, has_alpha);
        if (!cm->profile)
                {
                color_man_free(cm);
                return NULL;
                }
 
-       color_man_cache_ref(cm->profile);
-
        cm->idle_id = g_idle_add(color_man_idle_cb, cm);
 
        return cm;
 }
 
+ColorMan *color_man_new(ImageWindow *imd,
+                       ColorManProfileType input_type, const gchar *input_file,
+                       ColorManProfileType screen_type, const gchar *screen_file,
+                       ColorManDoneFunc done_func, gpointer done_data)
+{
+       return color_man_new_real(imd,
+                                 input_type, input_file, NULL, 0,
+                                 screen_type, screen_file,
+                                 done_func, done_data);
+}
+
+ColorMan *color_man_new_embedded(ImageWindow *imd,
+                                unsigned char *input_data, guint input_data_len,
+                                ColorManProfileType screen_type, const gchar *screen_file,
+                                ColorManDoneFunc done_func, gpointer done_data)
+{
+       return color_man_new_real(imd,
+                                 COLOR_PROFILE_MEM, NULL, input_data, input_data_len,
+                                 screen_type, screen_file,
+                                 done_func, done_data);
+}
+
 void color_man_free(ColorMan *cm)
 {
        if (!cm) return;
index b5d3f7c..e4f6380 100644 (file)
@@ -17,6 +17,7 @@ typedef enum {
        COLOR_PROFILE_NONE = 0,
        COLOR_PROFILE_FILE,
        COLOR_PROFILE_SRGB,
+       COLOR_PROFILE_MEM
 } ColorManProfileType;
 
 typedef enum {
@@ -47,6 +48,10 @@ ColorMan *color_man_new(ImageWindow *imd,
                        ColorManProfileType input_type, const gchar *input_file,
                        ColorManProfileType screen_type, const gchar *screen_file,
                        ColorManDoneFunc done_func, gpointer done_data);
+ColorMan *color_man_new_embedded(ImageWindow *imd,
+                                unsigned char *input_data, guint input_data_len,
+                                ColorManProfileType screen_type, const gchar *screen_file,
+                                ColorManDoneFunc done_func, gpointer done_data);
 void color_man_free(ColorMan *cm);
 
 void color_man_update(void);
index 62e8d02..4143192 100644 (file)
@@ -377,7 +377,7 @@ ExifMarker ExifKnownMarkersList[] = {
 { 0x828e, EXIF_FORMAT_BYTE_UNSIGNED, -1,       "CFAPattern",           NULL, NULL },
 { 0x828f, EXIF_FORMAT_RATIONAL_UNSIGNED, 1,    "BatteryLevel",         NULL, NULL },
 { 0x83bb, EXIF_FORMAT_LONG_UNSIGNED, -1,       "IPTC/NAA",             NULL, NULL },
-{ 0x8773, EXIF_FORMAT_UNDEFINED, -1,           "InterColorProfile",    NULL, NULL },
+{ 0x8773, EXIF_FORMAT_UNDEFINED, -1,           "ColorProfile",         NULL, NULL },
 { 0x8825, EXIF_FORMAT_LONG_UNSIGNED, 1,                "GPSInfo",              "SubIFD GPS offset", NULL },
 { 0x8829, EXIF_FORMAT_SHORT_UNSIGNED, 1,       "Interlace",            NULL, NULL },
 { 0x882a, EXIF_FORMAT_SHORT, 1,                        "TimeZoneOffset",       NULL, NULL },
@@ -523,21 +523,18 @@ const char *exif_item_get_format_name(ExifItem *item, gint brief)
        return (brief) ? ExifFormatList[item->format].short_name : ExifFormatList[item->format].description;
 }
 
-
-#define UNDEFINED_TEXT_BYTE_COUNT 16
-
 static GString *string_append_raw_bytes(GString *string, gpointer data, gint ne)
 {
        gint i;
 
-       for (i = 0 ; i < ne && i < UNDEFINED_TEXT_BYTE_COUNT; i++)
+       for (i = 0 ; i < ne; i++)
                {
                unsigned char c = ((char *)data)[i];
                if (c < 32 || c > 127) c = '.';
                g_string_append_printf(string, "%c", c);
                }
        string = g_string_append(string, " : ");
-       for (i = 0 ; i < ne && i < UNDEFINED_TEXT_BYTE_COUNT; i++)
+       for (i = 0 ; i < ne; i++)
                {
                const gchar *spacer;
                if (i > 0)
@@ -557,7 +554,6 @@ static GString *string_append_raw_bytes(GString *string, gpointer data, gint ne)
                        }
                g_string_append_printf(string, "%s%02x", spacer, ((char *)data)[i]);
                }
-       if (i >= UNDEFINED_TEXT_BYTE_COUNT) g_string_append_printf(string, " (%d bytes)", ne);
 
        return string;
 }
@@ -947,83 +943,178 @@ gint exif_tiff_parse(ExifData *exif, unsigned char *tiff, guint size, ExifMarker
  *-------------------------------------------------------------------
  */
 
-#define MARKER_UNKNOWN         0x00
-#define MARKER_SOI             0xD8
-#define MARKER_APP1            0xE1
+#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
+ */
 
-static gint jpeg_get_marker_size(unsigned char *data)
+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)
 {
-       /* Size is always in Motorola byte order */
-       return exif_byte_get_int16(data + 2, EXIF_BYTE_ORDER_MOTOROLA);
-}
+       guchar marker = 0;
+       guint offset = 0;
+       guint length = 0;
 
-static gint jpeg_goto_next_marker(unsigned char **data, gint *size, gint *marker)
-{
-       gint marker_size = 2;
+       while (marker != app_marker &&
+              marker != JPEG_MARKER_EOI)
+               {
+               offset += length;
+               length = 2;
 
-       *marker = MARKER_UNKNOWN;
+               if (offset + 2 >= size ||
+                   data[offset] != JPEG_MARKER) return FALSE;
 
-       /* It is safe to access the marker and its size since we have checked
-        * the SOI and this function guaranties the whole next marker is
-        * available
-        */
-       if (*(*data + 1) != MARKER_SOI)
+               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)
                {
-               marker_size += jpeg_get_marker_size(*data);
+               *seg_offset = offset + 4;
+               *seg_length = length - 4;
+               return TRUE;
                }
 
-       *size -= marker_size;
+       return FALSE;
+}
 
-       /* size should be at least 4, so we can read the marker and its size
-        * and check data are actually available
-        */
-       if (*size < 4) return -1;
+static ExifMarker jpeg_color_marker = { 0x8773, EXIF_FORMAT_UNDEFINED, -1, "ColorProfile", NULL, NULL };
 
-       /* Jump to the next marker and be sure it begins with 0xFF
+static 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)
         */
-       *data += marker_size;
-       if (**data != 0xFF) return -1;
 
-       if (jpeg_get_marker_size(*data) + 2 > *size) return -1;
+       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;
 
-       *marker = *(*data + 1);
+               if (seg_length < 14) return FALSE;
 
-       return 0;
-}
+               chunk_num = data[seg_offset + 12];
+               chunk_tot = data[seg_offset + 13];
 
+               if (chunk_num == 0 || chunk_tot == 0) return FALSE;
 
-static gint exif_parse_JPEG(ExifData *exif, unsigned char *data, gint size, ExifMarker *list)
-{
-       gint marker;
-       gint marker_size;
+               if (chunk_count == 0)
+                       {
+                       guint i;
 
-       if (size < 4 || *data != 0xFF || *(data + 1) != MARKER_SOI)
-               {
-               return -2;
+                       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;
                }
 
-       do {
-               if (jpeg_goto_next_marker(&data, &size, &marker) == -1)
+       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++)
                        {
-                       break;
+                       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]);
                        }
-       } while (marker != MARKER_APP1);
 
-       if (marker != MARKER_APP1)
+               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)
+{
+       guint seg_offset = 0;
+       guint seg_length = 0;
+       gint res = -1;
+
+       if (size < 4 ||
+           memcmp(data, "\xFF\xD8", 2) != 0)
                {
                return -2;
                }
 
-       marker_size = jpeg_get_marker_size(data) - 2;
-               
-       if (marker_size < 6 || memcmp(data + 4, "Exif\x00\x00", 6) != 0)
+       if (exif_jpeg_segment_find(data, size, JPEG_MARKER_APP1,
+                                  "Exif\x00\x00", 6,
+                                  &seg_offset, &seg_length))
                {
-               return -2;
+               res = exif_tiff_parse(exif, data + seg_offset + 6, seg_length - 6, list);
+               }
+
+       if (parse_color &&
+           exif_jpeg_parse_color(exif, data, size))
+               {
+               res = 0;
                }
 
-       return exif_tiff_parse(exif, data + 10, marker_size - 6, list);
+       return res;
 }
 
+
 /*
  *-------------------------------------------------------------------
  * misc
@@ -1090,7 +1181,7 @@ void exif_free(ExifData *exif)
        g_free(exif);
 }
 
-ExifData *exif_read(const gchar *path)
+ExifData *exif_read(const gchar *path, gint parse_color_profile)
 {
        ExifData *exif;
        void *f;
@@ -1110,7 +1201,9 @@ ExifData *exif_read(const gchar *path)
        exif = g_new0(ExifData, 1);
        exif->items = NULL;
 
-       if ((res = exif_parse_JPEG(exif, (unsigned char *)f, size, ExifKnownMarkersList)) == -2)
+       if ((res = exif_jpeg_parse(exif, (unsigned char *)f, size,
+                                  ExifKnownMarkersList,
+                                  parse_color_profile)) == -2)
                {
                res = exif_tiff_parse(exif, (unsigned char *)f, size, ExifKnownMarkersList);
                }
@@ -1132,8 +1225,8 @@ ExifData *exif_read(const gchar *path)
                                                      ExifKnownMarkersList);
                                break;
                        case FORMAT_RAW_EXIF_JPEG:
-                               res = exif_parse_JPEG(exif, (unsigned char*)f + offset, size - offset,
-                                                     ExifKnownMarkersList);
+                               res = exif_jpeg_parse(exif, (unsigned char*)f + offset, size - offset,
+                                                     ExifKnownMarkersList, FALSE);
                                break;
                        case FORMAT_RAW_EXIF_IFD_II:
                        case FORMAT_RAW_EXIF_IFD_MM:
@@ -1187,6 +1280,8 @@ ExifItem *exif_get_item(ExifData *exif, const gchar *key)
        return NULL;
 }
 
+#define EXIF_DATA_AS_TEXT_MAX_COUNT 16
+
 gchar *exif_item_get_data_as_text(ExifItem *item)
 {
        const ExifMarker *marker;
@@ -1203,6 +1298,7 @@ gchar *exif_item_get_data_as_text(ExifItem *item)
 
        data = item->data;
        ne = item->elements;
+       if (ne > EXIF_DATA_AS_TEXT_MAX_COUNT) ne = EXIF_DATA_AS_TEXT_MAX_COUNT;
        string = g_string_new("");
        switch (item->format)
                {
@@ -1310,8 +1406,14 @@ gchar *exif_item_get_data_as_text(ExifItem *item)
                        break;
                }
 
-       text = g_strdup(string->str);
-       g_string_free(string, TRUE);
+       if (item->elements > EXIF_DATA_AS_TEXT_MAX_COUNT &&
+           item->format != EXIF_FORMAT_STRING)
+               {
+               g_string_append(string, " ...");
+               }
+
+       text = string->str;
+       g_string_free(string, FALSE);
 
        return text;
 }
index 2cee4b3..1d1bcec 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  GQView
- *  (C) 2004 John Ellis
+ *  (C) 2006 John Ellis
  *
  *  Authors:
  *    Support for Exif file format, originally written by Eric Swalens.    
@@ -176,7 +176,7 @@ extern ExifFormattedText ExifFormattedList[];
  *-----------------------------------------------------------------------------
  */
 
-ExifData *exif_read(const gchar *path);
+ExifData *exif_read(const gchar *path, gint parse_color_profile);
 void exif_free(ExifData *exif);
 
 gchar *exif_get_data_as_text(ExifData *exif, const gchar *key);
index 47f382f..64e82ce 100644 (file)
@@ -481,6 +481,10 @@ static void format_debug_tiff_entry(unsigned char *data, const guint len, guint
                        format_debug_tiff_table(data, len, subset, bo, level + 1);
                        }
                }
+       else if (tag == 0x8773 && type == EXIF_FORMAT_UNDEFINED)
+               {
+               printf("%*s~~~ found ICC color profile at offset %d, length %d\n", level, "", segment, seg_len);
+               }
        else if (tag == 0x201 && (type == EXIF_FORMAT_LONG_UNSIGNED || type == EXIF_FORMAT_LONG))
                {
                guint subset = exif_byte_get_int32(data + segment, bo);
index 086e2b0..8881143 100644 (file)
@@ -165,15 +165,17 @@ static void image_update_title(ImageWindow *imd)
  *-------------------------------------------------------------------
  */
 
-static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp, gint exif_rotated)
+static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
 {
        PixbufRenderer *pr;
        GdkPixbuf *new = NULL;
+       gint exif_rotate;
        gint x, y;
        gint t;
 
        pr = (PixbufRenderer *)imd->pr;
 
+       exif_rotate = (imd->delay_alter_type != ALTER_NONE && (imd->state & IMAGE_STATE_ROTATE_AUTO));
        imd->delay_alter_type = ALTER_NONE;
 
        if (!pr->pixbuf) return;
@@ -226,7 +228,7 @@ static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp, gint
 
        if (clamp && pr->zoom != 0.0 && pr->scale != 0.0)
                {
-               if (exif_rotated)
+               if (exif_rotate)
                        {
                        switch (pr->scroll_reset)
                                {
@@ -249,6 +251,14 @@ static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp, gint
                }
 }
 
+static void image_post_process_alter(ImageWindow *imd, gint clamp)
+{
+       if (imd->delay_alter_type != ALTER_NONE)
+               {
+               image_alter_real(imd, imd->delay_alter_type, clamp);
+               }
+}
+
 static void image_post_process_color_cb(ColorMan *cm, ColorManReturnType type, gpointer data)
 {
        ImageWindow *imd = data;
@@ -259,17 +269,18 @@ static void image_post_process_color_cb(ColorMan *cm, ColorManReturnType type, g
 
        if (type != COLOR_RETURN_IMAGE_CHANGED)
                {
-               image_post_process(imd, FALSE);
+               image_post_process_alter(imd, FALSE);
                }
 }
 
-static gint image_post_process_color(ImageWindow *imd, gint start_row)
+static gint image_post_process_color(ImageWindow *imd, gint start_row, ExifData *exif)
 {
        ColorMan *cm;
        ColorManProfileType input_type;
        ColorManProfileType screen_type;
        const gchar *input_file;
        const gchar *screen_file;
+       ExifItem *item = NULL;
 
        if (imd->cm) return FALSE;
 
@@ -310,10 +321,27 @@ static gint image_post_process_color(ImageWindow *imd, gint start_row)
                return FALSE;
                }
 
-       cm = color_man_new(imd,
-                          input_type, input_file,
-                          screen_type, screen_file,
-                          image_post_process_color_cb, imd);
+       if (imd->color_profile_use_image && exif)
+               {
+               item = exif_get_item(exif, "ColorProfile");
+               }
+       if (item && item->format == EXIF_FORMAT_UNDEFINED)
+               {
+               if (debug) printf("Found embedded color profile\n");
+
+               cm = color_man_new_embedded(imd,
+                                           item->data, item->data_len,
+                                           screen_type, screen_file,
+                                           image_post_process_color_cb, imd);
+               }
+       else
+               {
+               cm = color_man_new(imd,
+                                  input_type, input_file,
+                                  screen_type, screen_file,
+                                  image_post_process_color_cb, imd);
+               }
+
        if (cm)
                {
                if (start_row > 0) cm->row = start_row;
@@ -327,25 +355,24 @@ static gint image_post_process_color(ImageWindow *imd, gint start_row)
 
 static void image_post_process(ImageWindow *imd, gint clamp)
 {
-       gint exif_rotated = FALSE;
+       ExifData *exif = NULL;
 
-       if (imd->color_profile_enable &&
-           !(imd->state & IMAGE_STATE_COLOR_ADJ))
-               {
-               if (image_post_process_color(imd, 0)) return;
+       if (!image_get_pixbuf(imd)) return;
 
-               /* fixme: note error to user */
-               imd->state |= IMAGE_STATE_COLOR_ADJ;
+       if (exif_rotate_enable ||
+           (imd->color_profile_enable && imd->color_profile_use_image) )
+               {
+               exif = exif_read(imd->image_path, (imd->color_profile_enable && imd->color_profile_use_image));
                }
 
-       if (exif_rotate_enable && image_get_pixbuf(imd))
+       if (exif_rotate_enable && exif)
                {
-               ExifData *ed;
                gint orientation;
 
-               ed = exif_read(imd->image_path);
-               if (ed && exif_get_integer(ed, "Orientation", &orientation))
+               if (exif_get_integer(exif, "Orientation", &orientation))
                        {
+                       gint rotate = TRUE;
+
                        /* see http://jpegclub.org/exif_orientation.html 
                          1        2       3      4         5            6           7          8
 
@@ -355,55 +382,62 @@ static void image_post_process(ImageWindow *imd, gint clamp)
                        88          88      88  88
                        88          88  888888  888888
                        */
-
                        switch (orientation)
                                {
                                case EXIF_ORIENTATION_TOP_LEFT:
                                        /* normal -- nothing to do */
+                                       rotate = FALSE;
                                        break;
                                case EXIF_ORIENTATION_TOP_RIGHT:
                                        /* mirrored */
                                        imd->delay_alter_type = ALTER_MIRROR;
-                                       exif_rotated = TRUE;
                                        break;
                                case EXIF_ORIENTATION_BOTTOM_RIGHT:
                                        /* upside down */
                                        imd->delay_alter_type = ALTER_ROTATE_180;
-                                       exif_rotated = TRUE;
                                        break;
                                case EXIF_ORIENTATION_BOTTOM_LEFT:
                                        /* flipped */
                                        imd->delay_alter_type = ALTER_FLIP;
-                                       exif_rotated = TRUE;
                                        break;
                                case EXIF_ORIENTATION_LEFT_TOP:
                                        /* not implemented -- too wacky to fix in one step */
+                                       rotate = FALSE;
                                        break;
                                case EXIF_ORIENTATION_RIGHT_TOP:
                                        /* rotated -90 (270) */
                                        imd->delay_alter_type = ALTER_ROTATE_90;
-                                       exif_rotated = TRUE;
                                        break;
                                case EXIF_ORIENTATION_RIGHT_BOTTOM:
                                        /* not implemented -- too wacky to fix in one step */
+                                       rotate = FALSE;
                                        break;
                                case EXIF_ORIENTATION_LEFT_BOTTOM:
                                        /* rotated 90 */
                                        imd->delay_alter_type = ALTER_ROTATE_90_CC;
-                                       exif_rotated = TRUE;
                                        break;
                                default:
                                        /* The other values are out of range */
+                                       rotate = FALSE;
                                        break;
                                }
+
+                       if (rotate) imd->state |= IMAGE_STATE_COLOR_ADJ;
                        }
-               exif_free(ed);
                }
 
-       if (imd->delay_alter_type != ALTER_NONE)
+       if (imd->color_profile_enable)
                {
-               image_alter_real(imd, imd->delay_alter_type, clamp, exif_rotated);
+               if (!image_post_process_color(imd, 0, exif))
+                       {
+                       /* fixme: note error to user */
+                       imd->state |= IMAGE_STATE_COLOR_ADJ;
+                       }
                }
+
+       if (!imd->cm) image_post_process_alter(imd, clamp);
+
+       exif_free(exif);
 }
 
 /*
@@ -525,7 +559,11 @@ static gint image_post_buffer_get(ImageWindow *imd)
                image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
                if (imd->prev_color_row >= 0)
                        {
-                       image_post_process_color(imd, imd->prev_color_row);
+                       ExifData *exif = NULL;
+
+                       if (imd->color_profile_use_image) exif = exif_read(imd->image_path, TRUE);
+                       image_post_process_color(imd, imd->prev_color_row, exif);
+                       exif_free(exif);
                        }
                success = TRUE;
                }
@@ -1228,10 +1266,16 @@ void image_alter(ImageWindow *imd, AlterType type)
                {
                /* still loading, wait till done */
                imd->delay_alter_type = type;
+               imd->state |= IMAGE_STATE_ROTATE_USER;
+
+               if (imd->cm && (imd->state & IMAGE_STATE_ROTATE_AUTO))
+                       {
+                       imd->state &= ~IMAGE_STATE_ROTATE_AUTO;
+                       }
                return;
                }
 
-       image_alter_real(imd, type, TRUE, FALSE);
+       image_alter_real(imd, type, TRUE);
 }
 
 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
index 09980a0..906ac62 100644 (file)
@@ -316,6 +316,16 @@ static void layout_color_menu_enable_cb(GtkWidget *widget, gpointer data)
        layout_image_refresh(lw);
 }
 
+static void layout_color_menu_use_image_cb(GtkWidget *widget, gpointer data)
+{
+       LayoutWindow *lw = data;
+       gint input, screen, use_image;
+
+       if (!layout_image_color_profile_get(lw, &input, &screen, &use_image)) return;
+       layout_image_color_profile_set(lw, input, screen, !use_image);
+       layout_image_refresh(lw);
+}
+
 #define COLOR_MENU_KEY "color_menu_key"
 
 static void layout_color_menu_input_cb(GtkWidget *widget, gpointer data)
@@ -402,6 +412,10 @@ static void layout_color_button_press_cb(GtkWidget *widget, gpointer data)
 
        menu_item_add_divider(menu);
 
+       item = menu_item_add_check(menu, _("Use profile from _image"), use_image,
+                           G_CALLBACK(layout_color_menu_use_image_cb), lw);
+       gtk_widget_set_sensitive(item, active);
+
        front = g_strdup_printf(_("Input _%d:"), 0);
        buf = g_strdup_printf("%s %s", front, "sRGB");
        g_free(front);
index aebe616..484dcfe 100644 (file)
@@ -1423,7 +1423,7 @@ static void pan_info_add_exif(PanTextAlignment *ta, FileData *fd)
        gint i;
 
        if (!fd) return;
-       exif = exif_read(fd->path);
+       exif = exif_read(fd->path, FALSE);
        if (!exif) return;
 
        pan_text_alignment_add(ta, NULL, NULL);