Index: evolution-2.6.0/mail/em-icon-stream.c =================================================================== --- evolution-2.6.0/mail/em-icon-stream.c (revision 33722) +++ evolution-2.6.0/mail/em-icon-stream.c (working copy) @@ -36,6 +36,9 @@ #include #include "em-icon-stream.h" +#include +#include + #include "libedataserver/e-msgport.h" #define d(x) @@ -47,6 +50,7 @@ EMCacheNode node; GdkPixbuf *pixbuf; + cmsHPROFILE profile; }; static void em_icon_stream_class_init (EMIconStreamClass *klass); @@ -115,6 +119,16 @@ emis->loader = NULL; } + if (emis->exif_loader) { + exif_loader_unref (emis->exif_loader); + emis->exif_loader = NULL; + } + + if (emis->profile) { + cmsCloseProfile (emis->profile); + emis->profile = NULL; + } + if (emis->destroy_id) { g_signal_handler_disconnect(emis->image, emis->destroy_id); emis->destroy_id = 0; @@ -135,6 +149,87 @@ emis_cleanup(emis); } +static void +emis_update_profile (EMIconStream *emis, ExifData *exif_data) +{ + if (exif_data) { + ExifEntry *entry; + const ExifByteOrder o = exif_data_get_byte_order (exif_data); + + entry = exif_content_get_entry (exif_data->ifd [EXIF_IFD_EXIF], EXIF_TAG_COLOR_SPACE); + if (entry) { + + if (exif_get_short (entry->data, o) == 1) { + emis->profile = cmsCreate_sRGBProfile (); + printf ("profile is sRGB\n"); + } else if (exif_get_short (entry->data, o) == 2) { + /* TODO: create Adobe RGB profile */ + emis->profile = cmsOpenProfileFromFile ("/usr/share/color/icc/Adobe ICC Profiles/RGB Profiles/AdobeRGB1998.icc", "r"); + printf ("profile is Adobe RGB\n"); + } else if (exif_get_short (entry->data, o) == 0xFFFF) { + double gammaValue; + cmsCIExyY whitepoint; + cmsCIExyYTRIPLE primaries; + LPGAMMATABLE gamma[3]; + + const int offset = exif_format_get_size (EXIF_FORMAT_RATIONAL); + ExifRational r; + + entry = exif_content_get_entry (exif_data->ifd [EXIF_IFD_0], EXIF_TAG_WHITE_POINT); + if (entry && entry->components == 2) { + r = exif_get_rational (entry->data, o); + whitepoint.x = (double)r.numerator/r.denominator; + r = exif_get_rational (entry->data + offset, o); + whitepoint.y = (double)r.numerator/r.denominator; + whitepoint.Y = 1.0; + } else { + printf ("No whitepoint found\n"); + // return; + } + + entry = exif_content_get_entry (exif_data->ifd [EXIF_IFD_0], EXIF_TAG_PRIMARY_CHROMATICITIES); + if (entry && entry->components == 6) { + r = exif_get_rational (entry->data + 0 * offset, o); + primaries.Red.x = (double)r.numerator/r.denominator; + r = exif_get_rational (entry->data + 1 * offset, o); + primaries.Red.y = (double)r.numerator/r.denominator; + + r = exif_get_rational (entry->data + 2 * offset, o); + primaries.Green.x = (double)r.numerator/r.denominator; + r = exif_get_rational (entry->data + 3 * offset, o); + primaries.Green.y = (double)r.numerator/r.denominator; + + r = exif_get_rational (entry->data + 4 * offset, o); + primaries.Blue.x = (double)r.numerator/r.denominator; + r = exif_get_rational (entry->data + 5 * offset, o); + primaries.Blue.y = (double)r.numerator/r.denominator; + + primaries.Red.Y = primaries.Green.Y = primaries.Blue.Y = 1.0; + } else { + printf ("No primary chromaticities found\n"); + // return; + } + + entry = exif_content_get_entry (exif_data->ifd [EXIF_IFD_EXIF], EXIF_TAG_GAMMA); + if (entry) { + r = exif_get_rational (entry->data, o); + gammaValue = (double)r.numerator/r.denominator; + } else { + /* Assume 2.2 */ + printf ("No gamma found\n"); + gammaValue = 2.2; + } + + gamma[0] = gamma[1] = gamma[2] = cmsBuildGamma(256, gammaValue); + + emis->profile = cmsCreateRGBProfile(&whitepoint, &primaries, gamma); + printf ("JPEG is calibrated\n"); + cmsFreeGamma(gamma[0]); + } + } + } +} + static ssize_t emis_sync_write(CamelStream *stream, const char *buffer, size_t n) { @@ -143,6 +238,15 @@ if (emis->loader == NULL) return -1; + if (emis->exif_loader && exif_loader_write (emis->exif_loader, buffer, n)) { + ExifData *exif_data = exif_loader_get_data (emis->exif_loader); + printf ("exif loader ends with data: %p\n", exif_data); + exif_loader_unref (emis->exif_loader); + emis->exif_loader = NULL; + emis_update_profile (emis, exif_data); + exif_data_unref (exif_data); + } + if (!gdk_pixbuf_loader_write(emis->loader, buffer, n, NULL)) { emis_cleanup(emis); return -1; @@ -195,6 +299,7 @@ { EMIconStream *emis = (EMIconStream *)stream; GdkPixbuf *pixbuf, *mini; + cmsHPROFILE profile; struct _emis_cache_node *node; char *scalekey; int scale; @@ -212,11 +317,13 @@ } mini = emis_fit(pixbuf, emis->width, emis->height, &scale); - gtk_image_set_from_pixbuf(emis->image, mini?mini:pixbuf); + em_gtk_image_set_from_pixbuf_cms(emis->image, mini?mini:pixbuf, emis->profile); if (emis->keep) { node = (struct _emis_cache_node *)em_cache_node_new(emis_cache, emis->key); node->pixbuf = g_object_ref(pixbuf); + node->profile = emis->profile; + emis->profile = NULL; em_cache_add(emis_cache, (EMCacheNode *)node); } @@ -225,12 +332,22 @@ sprintf(scalekey, "%s.%x", emis->key, scale); node = (struct _emis_cache_node *)em_cache_node_new(emis_cache, scalekey); node->pixbuf = mini?mini:g_object_ref(pixbuf); + node->profile = emis->profile; em_cache_add(emis_cache, (EMCacheNode *)node); } g_object_unref(emis->loader); emis->loader = NULL; + if (emis->exif_loader) { + ExifData *exif_data = exif_loader_get_data (emis->exif_loader); + printf ("exif loader ends with data: %p\n", exif_data); + exif_loader_unref (emis->exif_loader); + emis->exif_loader = NULL; + emis_update_profile (emis, exif_data); + exif_data_unref (exif_data); + } + g_signal_handler_disconnect(emis->image, emis->destroy_id); emis->destroy_id = 0; @@ -255,13 +372,15 @@ new->keep = keep; new->destroy_id = g_signal_connect(image, "destroy", G_CALLBACK(emis_image_destroy), new); new->loader = gdk_pixbuf_loader_new(); + new->exif_loader = exif_loader_new (); + new->profile = NULL; new->key = g_strdup(key); return (CamelStream *)new; } GdkPixbuf * -em_icon_stream_get_image(const char *key, unsigned int maxwidth, unsigned int maxheight) +em_icon_stream_get_image(const char *key, unsigned int maxwidth, unsigned int maxheight, cmsHPROFILE *profile) { struct _emis_cache_node *node; GdkPixbuf *pb = NULL; @@ -274,6 +393,7 @@ int width, height; pb = node->pixbuf; + *profile = node->profile; g_object_ref(pb); em_cache_node_unref(emis_cache, (EMCacheNode *)node); @@ -296,6 +416,7 @@ if (node) { g_object_unref(pb); pb = node->pixbuf; + *profile = node->profile; g_object_ref(pb); em_cache_node_unref(emis_cache, (EMCacheNode *)node); } else { @@ -305,6 +426,7 @@ pb = mini; node = (struct _emis_cache_node *)em_cache_node_new(emis_cache, realkey); node->pixbuf = pb; + node->profile = *profile; g_object_ref(pb); em_cache_add(emis_cache, (EMCacheNode *)node); } Index: evolution-2.6.0/mail/em-icon-stream.h =================================================================== --- evolution-2.6.0/mail/em-icon-stream.h (revision 33722) +++ evolution-2.6.0/mail/em-icon-stream.h (working copy) @@ -33,6 +33,8 @@ #define EM_ICON_STREAM_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), EM_ICON_STREAM_TYPE, EMIconStreamClass)) #define EM_IS_ICON_STREAM(o) (CAMEL_CHECK_TYPE((o), EM_ICON_STREAM_TYPE)) +#include + struct _GtkHTML; struct _GtkIconStream; @@ -44,6 +46,9 @@ unsigned int width, height; guint destroy_id; struct _GdkPixbufLoader *loader; + struct _ExifLoader *exif_loader; + cmsHPROFILE profile; + struct _GtkImage *image; char *key; @@ -57,7 +62,7 @@ CamelType em_icon_stream_get_type (void); CamelStream *em_icon_stream_new(GtkImage *image, const char *key, unsigned int maxwidth, unsigned int maxheight, int keep); -struct _GdkPixbuf *em_icon_stream_get_image(const char *key, unsigned int maxwidth, unsigned int maxheight); +struct _GdkPixbuf *em_icon_stream_get_image(const char *key, unsigned int maxwidth, unsigned int maxheight, cmsHPROFILE* profile); int em_icon_stream_is_resized(const char *key, unsigned int maxwidth, unsigned int maxheight); void em_icon_stream_clear_cache(void); Index: evolution-2.6.0/mail/em-format-html-display.c =================================================================== --- evolution-2.6.0/mail/em-format-html-display.c (revision 33722) +++ evolution-2.6.0/mail/em-format-html-display.c (working copy) @@ -30,6 +30,8 @@ #include #include +#include + #ifdef G_OS_WIN32 /* Work around 'DATADIR' and 'interface' lossage in */ #define DATADIR crap_DATADIR @@ -1222,22 +1224,102 @@ efhd_attachment_show(NULL, NULL, data); } +void +em_gtk_image_set_from_pixbuf_cms (GtkImage *image, GdkPixbuf *pixbuf, cmsHPROFILE profile) +{ + static gboolean sRGB_initialized = FALSE; + static gboolean do_cms = FALSE; + static cmsHPROFILE inProfile; + static cmsHPROFILE outProfile; + static cmsHTRANSFORM cmssRGBTransformRGB, cmssRGBTransformRGBA; + + printf ("em_gtk_image_set_from_pixbuf_cms with profile %p\n", profile); + + if (!sRGB_initialized) { + GdkAtom type; + int format, length; + guchar *data; + + if (gdk_property_get (gdk_screen_get_root_window (gtk_widget_get_screen (GTK_WIDGET (image))), + gdk_atom_intern ("_ICC_PROFILE", TRUE), + GDK_NONE, + 0, G_MAXLONG, FALSE, + &type, &format, &length, &data)) { + inProfile = cmsCreate_sRGBProfile (); + outProfile = cmsOpenProfileFromMem (data, length); + cmssRGBTransformRGB = cmsCreateTransform (inProfile, + TYPE_RGB_8, + outProfile, + TYPE_RGB_8, + INTENT_PERCEPTUAL, 0); + cmssRGBTransformRGBA = cmsCreateTransform (inProfile, + TYPE_RGBA_8, + outProfile, + TYPE_RGBA_8, + INTENT_PERCEPTUAL, 0); + do_cms = TRUE; + printf ("got the profile, length: %d\n", length); + } + sRGB_initialized = TRUE; + } + + if (do_cms) { + int y, width, height, stride; + gboolean has_alpha; + guchar* pixels; + cmsHTRANSFORM transform; + + pixbuf = gdk_pixbuf_copy (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + width = gdk_pixbuf_get_width (pixbuf); + stride = gdk_pixbuf_get_rowstride (pixbuf); + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + pixels = gdk_pixbuf_get_pixels (pixbuf); + + if (profile) { + transform = cmsCreateTransform (profile, + has_alpha ? TYPE_RGBA_8 : TYPE_RGB_8, + outProfile, + has_alpha ? TYPE_RGBA_8 : TYPE_RGB_8, + INTENT_PERCEPTUAL, 0); + } else + transform = has_alpha ? cmssRGBTransformRGBA : cmssRGBTransformRGB; + + for (y = 0; y < height; y ++, pixels += stride) + cmsDoTransform (transform, pixels, pixels, width); + + if (profile) + cmsDeleteTransform (transform); + } + + gtk_image_set_from_pixbuf (image, pixbuf); + + if (do_cms) + gdk_pixbuf_unref (pixbuf); +} + static void efhd_image_fit(EPopup *ep, EPopupItem *item, void *data) { struct _attach_puri *info = data; + cmsHPROFILE profile; + GdkPixbuf *pixbuf; info->fit_width = ((GtkWidget *)((EMFormatHTML *)info->puri.format)->html)->allocation.width - 12; - gtk_image_set_from_pixbuf(info->image, em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height)); + pixbuf = em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height, &profile); + em_gtk_image_set_from_pixbuf_cms(info->image, pixbuf, profile); } static void efhd_image_unfit(EPopup *ep, EPopupItem *item, void *data) { struct _attach_puri *info = data; + cmsHPROFILE profile; + GdkPixbuf *pixbuf; info->fit_width = 0; - gtk_image_set_from_pixbuf((GtkImage *)info->image, em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height)); + pixbuf = em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height, &profile); + em_gtk_image_set_from_pixbuf_cms((GtkImage *)info->image, pixbuf, profile); } static EPopupItem efhd_menu_items[] = { @@ -1422,6 +1504,7 @@ { GdkPixbuf *pb; int width; + cmsHPROFILE profile; if (info->fit_width == 0) return; @@ -1430,8 +1513,8 @@ if (info->fit_width == width) return; info->fit_width = width; - pb = em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height); - gtk_image_set_from_pixbuf(info->image, pb); + pb = em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height, &profile); + em_gtk_image_set_from_pixbuf_cms(info->image, pb, profile); g_object_unref(pb); } @@ -1451,6 +1534,8 @@ efhd_image_fit_width(GtkWidget *widget, GdkEventButton *event, struct _attach_puri *info) { int width; + cmsHPROFILE profile; + GdkPixbuf *pixbuf; width = ((GtkWidget *)((EMFormatHTML *)info->puri.format)->html)->allocation.width - 12; @@ -1471,7 +1556,8 @@ } } - gtk_image_set_from_pixbuf(info->image, em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height)); + pixbuf = em_icon_stream_get_image(info->puri.cid, info->fit_width, info->fit_height, &profile); + em_gtk_image_set_from_pixbuf_cms(info->image, pixbuf, profile); } @@ -1487,13 +1573,14 @@ { "text/uri-list", 0, 1 }, }; char *simple_type; + cmsHPROFILE profile; info = (struct _attach_puri *)em_format_find_puri((EMFormat *)efh, pobject->classid); info->image = (GtkImage *)gtk_image_new(); - pixbuf = em_icon_stream_get_image(pobject->classid, info->fit_width, info->fit_height); + pixbuf = em_icon_stream_get_image(pobject->classid, info->fit_width, info->fit_height, &profile); if (pixbuf) { - gtk_image_set_from_pixbuf(info->image, pixbuf); + em_gtk_image_set_from_pixbuf_cms(info->image, pixbuf, profile); g_object_unref(pixbuf); } else { job = em_format_html_job_new(efh, efhd_write_icon_job, pobject); @@ -1628,12 +1715,13 @@ EMFormatHTMLJob *job; GdkPixbuf *mini; char *key; + cmsHPROFILE profile; key = pobject->classid; - mini = em_icon_stream_get_image(key, 24, 24); + mini = em_icon_stream_get_image(key, 24, 24, &profile); if (mini) { d(printf("got image from cache '%s'\n", key)); - gtk_image_set_from_pixbuf((GtkImage *)w, mini); + em_gtk_image_set_from_pixbuf_cms((GtkImage *)w, mini, profile); g_object_unref(mini); } else { d(printf("need to create icon image '%s'\n", key)); @@ -1646,7 +1734,7 @@ if ((pixbuf = e_icon_for_mime_type (simple_type, 24))) { if ((mini = gdk_pixbuf_scale_simple (pixbuf, 24, 24, GDK_INTERP_BILINEAR))) { - gtk_image_set_from_pixbuf ((GtkImage *) w, mini); + em_gtk_image_set_from_pixbuf_cms ((GtkImage *) w, mini, NULL); g_object_unref (mini); } g_object_unref (pixbuf); Index: evolution-2.6.0/mail/em-format-html-display.h =================================================================== --- evolution-2.6.0/mail/em-format-html-display.h (revision 33722) +++ evolution-2.6.0/mail/em-format-html-display.h (working copy) @@ -6,6 +6,8 @@ #ifndef _EM_FORMAT_HTML_DISPLAY_H #define _EM_FORMAT_HTML_DISPLAY_H +#include + #include "mail/em-format-html.h" #include "widgets/misc/e-attachment-bar.h" @@ -67,4 +69,9 @@ struct _EPopupExtension; void em_format_html_display_set_popup(EMFormatHTMLDisplay *, struct _EPopupExtension *); +struct _GtkImage; +struct _GdkPixbuf; + +void em_gtk_image_set_from_pixbuf_cms (struct _GtkImage *image, struct _GdkPixbuf *pixbuf, cmsHPROFILE profile); + #endif /* !_EM_FORMAT_HTML_DISPLAY_H */