diff --git a/configure.in.in b/configure.in.in index 968b37b..1f06795 100644 --- a/configure.in.in +++ b/configure.in.in @@ -67,6 +67,11 @@ XDT_CHECK_PACKAGE([XFCONF], [libxfconf-0], [4.8.0]) XDT_CHECK_PACKAGE([CAIRO], [cairo], [1.8.0]) +XDT_CHECK_OPTIONAL_PACKAGE([LCMS2], + [lcms2], + [2.0], [lcms2], + [Little Color Management System]) + XDT_CHECK_LIBX11() AC_CHECK_LIBM diff --git a/src/Makefile.am b/src/Makefile.am index c5e810e..db7069b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,7 @@ ristretto_SOURCES = \ file.c file.h \ privacy_dialog.h privacy_dialog.c \ util.c util.h \ + color_man.c color_man.h \ main.c ristretto_CFLAGS = \ @@ -34,6 +35,7 @@ ristretto_CFLAGS = \ $(LIBXFCE4UTIL_CFLAGS) \ $(LIBXFCE4UI_CFLAGS) \ $(LIBX11_CFLAGS) \ + $(LCMS2_CFLAGS) \ -DDOCDIR=\"$(DESTDIR)$(docdir)\" \ -DDATADIR=\"$(datadir)\" \ -DSRCDIR=\"$(top_srcdir)\" \ @@ -51,6 +53,7 @@ ristretto_LDADD = \ $(LIBXFCE4UTIL_LIBS) \ $(LIBXFCE4UI_LIBS) \ $(LIBX11_LIBS) \ + $(LCMS2_LIBS) \ $(LIBM) INCLUDES = \ diff --git a/src/color_man.c b/src/color_man.c new file mode 100644 index 0000000..b0748e8 --- /dev/null +++ b/src/color_man.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) Stephan Arts 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#ifdef HAVE_LCMS2 +#include +#endif /* HAVE_LCMS2 */ + +#include "color_man.h" + +#ifdef HAVE_LCMS2 + +static void +cb_log_color_man_error ( + cmsContext context_id, + cmsUInt32Number error_code, + const char *text ); + +void +rstto_color_man_init ( void ) +{ + cmsSetLogErrorHandler (cb_log_color_man_error); +} + +void +rstto_color_transform ( + cmsHPROFILE in_profile, + cmsHPROFILE out_profile, + GdkPixbuf *pixbuf) +{ + cmsHTRANSFORM hTransform; + guchar *pixels, *buf; + gint rs; + gint i; + gint height = gdk_pixbuf_get_height (pixbuf); + gint width = gdk_pixbuf_get_width (pixbuf); + + hTransform = cmsCreateTransform( + in_profile, + TYPE_BGR_8, + out_profile, + TYPE_BGR_8, + INTENT_PERCEPTUAL, 0); + if ( hTransform ) + { + + pixels = gdk_pixbuf_get_pixels (pixbuf); + rs = gdk_pixbuf_get_rowstride (pixbuf); + + for (i = 0; i < height; ++i) + { + buf = pixels + (rs*i); + + cmsDoTransform (hTransform, buf, buf, (cmsUInt32Number)width); + } + } + +} + +#endif /* HAVE_LCMS2 */ + + +static void +cb_log_color_man_error ( + cmsContext context_id, + cmsUInt32Number error_code, + const char *text ) +{ + g_printf("LCMS Err: %u: '%s'\n", error_code, text); +} diff --git a/src/color_man.h b/src/color_man.h new file mode 100644 index 0000000..0fb3e3c --- /dev/null +++ b/src/color_man.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Stephan Arts 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __RISTRETTO_COLOR_MAN_H__ +#define __RISTRETTO_COLOR_MAN_H__ + +G_BEGIN_DECLS + +#ifdef HAVE_LCMS2 +#include + +void +rstto_color_man_init ( void ); + +void +rstto_color_transform ( + cmsHPROFILE in_profile, + cmsHPROFILE out_profile, + GdkPixbuf *pixbuf); + +#endif /* HAVE_LCMS2 */ + +G_END_DECLS + +#endif /* __RSTTO_COLOR_MAN_H__ */ diff --git a/src/file.c b/src/file.c index b36ee43..e0c18c4 100644 --- a/src/file.c +++ b/src/file.c @@ -20,12 +20,18 @@ #include #include +#include #include #include #include "util.h" + +#ifdef HAVE_LCMS2 +#include "color_man.h" +#endif /* HAVE_LCMS2 */ + #include "file.h" static void @@ -100,6 +106,11 @@ struct _RsttoFilePriv gchar *uri; gchar *path; +#ifdef HAVE_LCMS2 + guchar *color_profile; + RsttoColorProfile profile_type; +#endif /* HAVE_LCMS2 */ + ExifData *exif_data; RsttoImageOrientation orientation; }; @@ -407,3 +418,96 @@ rstto_file_has_exif ( RsttoFile *file ) } return TRUE; } + +#ifdef HAVE_LCMS2 +RsttoColorProfile +rstto_file_get_color_profile ( + RsttoFile *file, + guchar **color_profile, + gsize *len ) +{ + ExifEntry *exif_entry = NULL; + if ( color_profile != NULL ) + { + g_return_val_if_fail ((*color_profile) == NULL, COLOR_PROFILE_NONE); + } + + file->priv->profile_type = COLOR_PROFILE_NONE; + + if ( NULL == file->priv->color_profile ) + { + exif_entry = rstto_file_get_exif ( + file, + EXIF_TAG_INTER_COLOR_PROFILE); + if (NULL != exif_entry) + { + file->priv->color_profile = g_new0 (guchar, exif_entry->size + 1); + memcpy (file->priv->color_profile, + exif_entry->data, + exif_entry->size); + file->priv->profile_type = COLOR_PROFILE_MEM; + } + else + { + exif_entry = rstto_file_get_exif ( + file, + EXIF_TAG_INTEROPERABILITY_INDEX); + if (NULL != exif_entry && NULL != exif_entry->data) + { + if (0 == strcmp (exif_entry->data, "R98")) + { + file->priv->profile_type = COLOR_PROFILE_SRGB; + } + else if (0 == strcmp (exif_entry->data, "R03")) + { + file->priv->profile_type = COLOR_PROFILE_ADOBERGB; + } + else + { + file->priv->profile_type = COLOR_PROFILE_NONE; + g_print ("%s", exif_entry->data); + } + + } + else + { + exif_entry = rstto_file_get_exif ( + file, + EXIF_TAG_COLOR_SPACE); + if (NULL != exif_entry) + { + g_print ("color-space found"); + switch (exif_get_long ( + exif_entry->data, + exif_data_get_byte_order (exif_entry->parent->parent))) + { + case 1: /* COLOR_PROFILE_SRGB */ + file->priv->profile_type = COLOR_PROFILE_SRGB; + break; + case 2: /* COLOR_PROFILE_ADOBERGB */ + file->priv->profile_type = COLOR_PROFILE_ADOBERGB; + break; + default: + file->priv->profile_type = COLOR_PROFILE_NONE; + g_print ("Color profile %d unsupported", + exif_get_long ( + exif_entry->data, + exif_data_get_byte_order(exif_entry->parent->parent))); + } + } + } + } + } + + if ( color_profile != NULL ) + { + *color_profile = file->priv->color_profile; + } + else + { + g_warning ("color_profile == NULL, internal profile can not be returned."); + } + + return file->priv->profile_type; +} +#endif /* HAVE_LCMS2 */ diff --git a/src/file.h b/src/file.h index 467813a..16bcba3 100644 --- a/src/file.h +++ b/src/file.h @@ -84,7 +84,7 @@ const gchar * rstto_file_get_content_type ( RsttoFile * ); guint64 -rstto_file_get_modified_time ( RsttoFile *); +rstto_file_get_modified_time ( RsttoFile * ); ExifEntry * rstto_file_get_exif ( RsttoFile *, ExifTag ); @@ -100,6 +100,10 @@ rstto_file_set_orientation ( gboolean rstto_file_has_exif ( RsttoFile * ); +#ifdef HAVE_LCMS2 +RsttoColorProfile +rstto_file_get_color_profile ( RsttoFile * , guchar **, gsize *len); +#endif /* HAVE_LCMS2 */ G_END_DECLS diff --git a/src/image_viewer.c b/src/image_viewer.c index 557e1e3..7db9cd1 100644 --- a/src/image_viewer.c +++ b/src/image_viewer.c @@ -27,6 +27,10 @@ #include "util.h" +#ifdef HAVE_LCMS2 +#include "color_man.h" +#endif /* HAVE_LCMS2 */ + #include "file.h" #include "image_viewer.h" #include "settings.h" @@ -100,6 +104,10 @@ struct _RsttoImageViewerPriv gint image_width; gint image_height; +#ifdef HAVE_LCMS2 + cmsHPROFILE screen_profile; +#endif /* HAVE_LCMS2 */ + struct { gdouble x; @@ -265,6 +273,10 @@ static void rstto_image_viewer_init ( GObject *object ) { RsttoImageViewer *viewer = RSTTO_IMAGE_VIEWER (object); + GdkAtom type = GDK_NONE; + gint format = 0; + guchar *color_profile = NULL; + gint color_profile_len; if (default_screen == NULL) { @@ -279,6 +291,26 @@ rstto_image_viewer_init ( GObject *object ) viewer->priv->visual = gdk_rgb_get_visual(); viewer->priv->colormap = gdk_colormap_new (viewer->priv->visual, TRUE); + if (gdk_property_get(gdk_screen_get_root_window(default_screen), + gdk_atom_intern ("_ICC_PROFILE", FALSE), + GDK_NONE, + 0, 64 * 1024 * 1024, FALSE, + &type, &format, &color_profile_len, &color_profile)) + { + if (color_profile_len > 0) + { + viewer->priv->screen_profile = cmsOpenProfileFromMem ( + color_profile, + color_profile_len ); + } + } + + if (NULL == viewer->priv->screen_profile) + { + viewer->priv->screen_profile = cmsCreate_sRGBProfile(); + } + + viewer->priv->icon_theme = gtk_icon_theme_get_default (); viewer->priv->bg_icon = gtk_icon_theme_load_icon ( viewer->priv->icon_theme, @@ -1721,8 +1753,13 @@ cb_rstto_image_viewer_read_input_stream_ready ( static void cb_rstto_image_loader_area_prepared (GdkPixbufLoader *loader, RsttoImageViewerTransaction *transaction) { - gint timeout = 0; RsttoImageViewer *viewer = transaction->viewer; + gint timeout = 0; + const gchar *pixbuf_option = NULL; + guchar *color_profile = NULL; + gsize color_profile_len = 0; + cmsHPROFILE *input_profile; + RsttoColorProfile profile_type = COLOR_PROFILE_NONE; if (viewer->priv->transaction == transaction) { @@ -1761,6 +1798,69 @@ cb_rstto_image_loader_area_prepared (GdkPixbufLoader *loader, RsttoImageViewerTr /* This is a single-frame image, there is no need to copy the pixbuf since it won't change. */ viewer->priv->pixbuf = gdk_pixbuf_animation_iter_get_pixbuf (viewer->priv->iter); + +#ifdef HAVE_LCMS2 + /* TODO: Determine color-profile for image */ + pixbuf_option = gdk_pixbuf_get_option ( + viewer->priv->pixbuf, + "icc-profile"); + if ( NULL != pixbuf_option ) + { + color_profile = g_base64_decode ( + pixbuf_option, + &color_profile_len); + profile_type = COLOR_PROFILE_MEM; + } + else + { + profile_type = rstto_file_get_color_profile ( + viewer->priv->file, + &color_profile, + &color_profile_len); + } + + switch (profile_type) + { + case COLOR_PROFILE_NONE: + case COLOR_PROFILE_SRGB: + g_debug("using sRGB profile"); + input_profile = cmsCreate_sRGBProfile(); + break; + case COLOR_PROFILE_ADOBERGB: + /* This is not the right color-profile, + * but i'll get around to supporting AdobeRGB + * sometime in the future + */ + input_profile = cmsCreate_sRGBProfile(); + g_warning ("AdobeRGB is not supported, falling back to sRGB"); + break; + case COLOR_PROFILE_MEM: + if ( NULL != color_profile ) + { + input_profile = cmsOpenProfileFromMem ( + color_profile, + color_profile_len ); + } + else + { + /* This should not happen */ + g_critical ( + "COLOR_PROFILE_MEM selected," + " but no color_profile in memory,\n" + "Falling back to sRGB."); + input_profile = cmsCreate_sRGBProfile(); + profile_type = COLOR_PROFILE_SRGB; + } + break; + + } + + rstto_color_transform ( + input_profile, + viewer->priv->screen_profile, + viewer->priv->pixbuf); + +#endif /* HAVE_LCMS2 */ g_object_ref (viewer->priv->pixbuf); } } diff --git a/src/main.c b/src/main.c index d59560f..ce7152b 100644 --- a/src/main.c +++ b/src/main.c @@ -97,6 +97,8 @@ main(int argc, char **argv) g_thread_init(NULL); gdk_threads_init(); + rstto_color_man_init (); + if(!gtk_init_with_args(&argc, &argv, "", entries, PACKAGE, &cli_error)) { if (cli_error != NULL) diff --git a/src/util.h b/src/util.h index c8d76f9..88d2955 100644 --- a/src/util.h +++ b/src/util.h @@ -47,6 +47,13 @@ typedef enum { SORT_TYPE_COUNT, } RsttoSortType; +typedef enum { + COLOR_PROFILE_NONE = 0, + COLOR_PROFILE_SRGB, + COLOR_PROFILE_ADOBERGB, + COLOR_PROFILE_MEM +} RsttoColorProfile; + gboolean rstto_launch_help (void);