From: John Ellis Date: Mon, 27 Nov 2006 06:37:48 +0000 (+0000) Subject: Mon Nov 27 01:23:23 2006 John Ellis X-Git-Tag: v1.0.0~1689 X-Git-Url: http://geeqie.org/cgi-bin/gitweb.cgi?p=geeqie.git;a=commitdiff_plain;h=6f694c071ce7ec62a2a3705311daae40d6cfea21 Mon Nov 27 01:23:23 2006 John Ellis * 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. --- diff --git a/ChangeLog b/ChangeLog index 8c91a336..3204673b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +Mon Nov 27 01:23:23 2006 John Ellis + + * 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 * configure.in: Add test for lcms (little cms). diff --git a/TODO b/TODO index ded5ffb7..4a525bbf 100644 --- 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 --- diff --git a/src/bar_exif.c b/src/bar_exif.c index 041c3cab..00a3e9b2 100644 --- a/src/bar_exif.c +++ b/src/bar_exif.c @@ -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) { diff --git a/src/cache-loader.c b/src/cache-loader.c index 1fb2fcb0..b95bd2c6 100644 --- a/src/cache-loader.c +++ b/src/cache-loader.c @@ -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; diff --git a/src/color-man.c b/src/color-man.c index 5c524570..a023725d 100644 --- a/src/color-man.c +++ b/src/color-man.c @@ -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; diff --git a/src/color-man.h b/src/color-man.h index b5d3f7c2..e4f63803 100644 --- a/src/color-man.h +++ b/src/color-man.h @@ -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); diff --git a/src/exif.c b/src/exif.c index 62e8d02d..4143192d 100644 --- a/src/exif.c +++ b/src/exif.c @@ -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; } diff --git a/src/exif.h b/src/exif.h index 2cee4b37..1d1bcec8 100644 --- a/src/exif.h +++ b/src/exif.h @@ -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); diff --git a/src/format_raw.c b/src/format_raw.c index 47f382fd..64e82ce7 100644 --- a/src/format_raw.c +++ b/src/format_raw.c @@ -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); diff --git a/src/image.c b/src/image.c index 086e2b0a..88811439 100644 --- a/src/image.c +++ b/src/image.c @@ -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) diff --git a/src/layout.c b/src/layout.c index 09980a0e..906ac62e 100644 --- a/src/layout.c +++ b/src/layout.c @@ -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); diff --git a/src/pan-view.c b/src/pan-view.c index aebe616b..484dcfe2 100644 --- a/src/pan-view.c +++ b/src/pan-view.c @@ -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);