Include a Other Software section in Help file
[geeqie.git] / src / thumb_standard.c
index c23329f..3ac2b4b 100644 (file)
@@ -1,19 +1,28 @@
 /*
- * Geeqie
- * (C) 2006 John Ellis
- * Copyright (C) 2008 - 2010 The Geeqie Team
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
  *
  * Author: John Ellis
  *
- * This software is released under the GNU General Public License (GNU GPL).
- * Please read the included file COPYING for more information.
- * This software comes with no warranty of any kind, use at your own risk!
+ * 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 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-
 #include "main.h"
 #include "thumb_standard.h"
 
+#include "cache.h"
 #include "image-load.h"
 #include "md5-util.h"
 #include "pixbuf_util.h"
 #include "filedata.h"
 #include "exif.h"
 #include "metadata.h"
+#include "color-man.h"
 
 
-/*
+/**
+ * @file
+ * 
  * This thumbnail caching implementation attempts to conform
  * to the Thumbnail Managing Standard proposed on freedesktop.org
- * The standard is documented here:
- *   http://triq.net/~jens/thumbnail-spec/index.html
+ * The standard is documented here: \n
+ *   http://triq.net/~jens/thumbnail-spec/index.html \n
  *  (why isn't it actually hosted on freedesktop.org?)
  *
  * This code attempts to conform to version 0.7.0 of the standard.
  *
  * Notes:
  *   > Validation of the thumb's embedded uri is a simple strcmp between our
- *     version of the escaped uri and the thumb's escaped uri. But not all uri
- *     escape functions escape the same set of chars, comparing the unescaped
- *     versions may be more accurate.
+ *   > version of the escaped uri and the thumb's escaped uri. But not all uri
+ *   > escape functions escape the same set of chars, comparing the unescaped
+ *   > versions may be more accurate. \n
  *   > Only Thumb::URI and Thumb::MTime are stored in a thumb at this time.
  *     Storing the Size, Width, Height should probably be implemented.
  */
@@ -148,7 +160,8 @@ static gchar *thumb_std_cache_path(const gchar *path, const gchar *uri, gboolean
                }
        else
                {
-               result = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, cache_subfolder, name, NULL);
+               result = g_build_filename(get_thumbnails_standard_cache_dir(),
+                                                                                                       cache_subfolder, name, NULL);
                }
 
        g_free(name);
@@ -338,8 +351,7 @@ static void thumb_loader_std_save(ThumbLoaderStd *tl, GdkPixbuf *pixbuf)
                mark_uri = (tl->cache_local) ? tl->local_uri :tl->thumb_uri;
 
                mark_app = g_strdup_printf("%s %s", GQ_APPNAME, VERSION);
-               mark_mtime = g_strdup_printf("%lu", tl->source_mtime);
-
+               mark_mtime = g_strdup_printf("%llu", (unsigned long long)tl->source_mtime);
                pathl = path_from_utf8(tmp_path);
                success = gdk_pixbuf_save(pixbuf, pathl, "png", NULL,
                                          THUMB_MARKER_URI, mark_uri,
@@ -375,6 +387,104 @@ static void thumb_loader_std_set_fallback(ThumbLoaderStd *tl)
        tl->fd->thumb_pixbuf = pixbuf_fallback(tl->fd, tl->requested_width, tl->requested_height);
 }
 
+
+void thumb_loader_std_calibrate_pixbuf(FileData *fd, GdkPixbuf *pixbuf) {
+       ColorMan *cm = NULL;
+       ExifData *exif = NULL;
+       gint color_profile_from_image = COLOR_PROFILE_NONE;
+       ColorManProfileType input_type = COLOR_PROFILE_MEM;
+       ColorManProfileType screen_type;
+       const gchar *input_file = NULL;
+       guchar *profile = NULL;
+       guint profile_len;
+       gint sw, sh;
+
+       if (!options->thumbnails.use_color_management)
+               {
+               return;
+               }
+
+       sw = gdk_pixbuf_get_width(pixbuf);
+       sh = gdk_pixbuf_get_height(pixbuf);
+
+       exif = exif_read_fd(fd);
+
+       if (exif)
+               {
+               profile = exif_get_color_profile(exif, &profile_len);
+               if (profile)
+                       {
+                       DEBUG_1("Found embedded color profile");
+                       color_profile_from_image = COLOR_PROFILE_MEM;
+                       }
+               else
+                       {
+                       gchar *interop_index = exif_get_data_as_text(exif, "Exif.Iop.InteroperabilityIndex");
+
+                       if (interop_index)
+                               {
+                               /* Exif 2.21 specification */
+                               if (!strcmp(interop_index, "R98"))
+                                       {
+                                       color_profile_from_image = COLOR_PROFILE_SRGB;
+                                       DEBUG_1("Found EXIF 2.21 ColorSpace of sRGB");
+                                       }
+                               else if (!strcmp(interop_index, "R03"))
+                                       {
+                                       color_profile_from_image = COLOR_PROFILE_ADOBERGB;
+                                       DEBUG_1("Found EXIF 2.21 ColorSpace of AdobeRGB");
+                                       }
+                               g_free(interop_index);
+                               }
+                       else
+                               {
+                               gint cs;
+
+                               /* ColorSpace == 1 specifies sRGB per EXIF 2.2 */
+                               if (!exif_get_integer(exif, "Exif.Photo.ColorSpace", &cs)) cs = 0;
+                               if (cs == 1)
+                                       {
+                                       color_profile_from_image = COLOR_PROFILE_SRGB;
+                                       DEBUG_1("Found EXIF 2.2 ColorSpace of sRGB");
+                                       }
+                               else if (cs == 2)
+                                       {
+                                       /* non-standard way of specifying AdobeRGB (used by some software) */
+                                       color_profile_from_image = COLOR_PROFILE_ADOBERGB;
+                                       DEBUG_1("Found EXIF 2.2 ColorSpace of AdobeRGB");
+                                       }
+                               }
+                       }
+
+               if(color_profile_from_image != COLOR_PROFILE_NONE)
+                       {
+                               // transform image, we always use sRGB as target for thumbnails
+                               screen_type = COLOR_PROFILE_SRGB;
+
+                               if (profile)
+                                       {
+                                       cm = color_man_new_embedded(NULL, pixbuf,
+                                                                       profile, profile_len,
+                                                                       screen_type, NULL, NULL, 0);
+                                       g_free(profile);
+                                       }
+                               else
+                                       {
+                                       cm = color_man_new(NULL, pixbuf,
+                                                       input_type, input_file,
+                                                       screen_type, NULL, NULL, 0);
+                                       }
+
+                               if(cm) {
+                                       color_man_correct_region(cm, cm->pixbuf, 0, 0, sw, sh);
+                                       g_free(cm);
+                               }
+
+                       }
+               exif_free_fd(fd, exif);
+               }
+}
+
 static GdkPixbuf *thumb_loader_std_finish(ThumbLoaderStd *tl, GdkPixbuf *pixbuf, gboolean shrunk)
 {
        GdkPixbuf *pixbuf_thumb = NULL;
@@ -389,7 +499,7 @@ static GdkPixbuf *thumb_loader_std_finish(ThumbLoaderStd *tl, GdkPixbuf *pixbuf,
                        {
                        tl->fd->exif_orientation = metadata_read_int(tl->fd, ORIENTATION_KEY, EXIF_ORIENTATION_TOP_LEFT);
                        }
-               
+
                if (tl->fd->exif_orientation != EXIF_ORIENTATION_TOP_LEFT)
                        {
                        rotated = pixbuf_apply_orientation(pixbuf, tl->fd->exif_orientation);
@@ -434,7 +544,7 @@ static GdkPixbuf *thumb_loader_std_finish(ThumbLoaderStd *tl, GdkPixbuf *pixbuf,
 
                                /* do not save the thumbnail if the source file has changed meanwhile -
                                   the thumbnail is most probably broken */
-                               if (stat_utf8(tl->fd->path, &st) && 
+                               if (stat_utf8(tl->fd->path, &st) &&
                                    tl->source_mtime == st.st_mtime &&
                                    tl->source_size == st.st_size)
                                        {
@@ -488,6 +598,9 @@ static GdkPixbuf *thumb_loader_std_finish(ThumbLoaderStd *tl, GdkPixbuf *pixbuf,
                        }
                }
 
+       // apply color correction, if required
+       thumb_loader_std_calibrate_pixbuf(tl->fd, result);
+
        if (pixbuf_thumb) g_object_unref(pixbuf_thumb);
        if (rotated) g_object_unref(rotated);
 
@@ -581,14 +694,14 @@ static void thumb_loader_std_error_cb(ImageLoader *il, gpointer data)
                thumb_loader_std_done_cb(il, data);
                return;
                }
-       
+
        DEBUG_1("thumb image error: %s", tl->fd->path);
        DEBUG_1("             from: %s", image_loader_get_fd(tl->il)->path);
 
        if (thumb_loader_std_next_source(tl, TRUE)) return;
 
        thumb_loader_std_set_fallback(tl);
-       
+
        if (tl->func_error) tl->func_error(tl, tl->data);
 }
 
@@ -658,7 +771,7 @@ gboolean thumb_loader_std_start(ThumbLoaderStd *tl, FileData *fd)
 
 
        tl->fd = file_data_ref(fd);
-       if (!stat_utf8(fd->path, &st))
+       if (!stat_utf8(fd->path, &st) || (tl->fd->format_class != FORMAT_CLASS_IMAGE && tl->fd->format_class != FORMAT_CLASS_RAWIMAGE && tl->fd->format_class != FORMAT_CLASS_VIDEO && tl->fd->format_class != FORMAT_CLASS_COLLECTION && tl->fd->format_class != FORMAT_CLASS_DOCUMENT && !options->file_filter.disable))
                {
                thumb_loader_std_set_fallback(tl);
                return FALSE;
@@ -667,7 +780,11 @@ gboolean thumb_loader_std_start(ThumbLoaderStd *tl, FileData *fd)
        tl->source_size = st.st_size;
        tl->source_mode = st.st_mode;
 
-       if (!thumb_cache) thumb_cache = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, NULL);
+       if (!thumb_cache)
+               {
+               thumb_cache = g_strdup(get_thumbnails_standard_cache_dir());
+               }
+
        if (strncmp(tl->fd->path, thumb_cache, strlen(thumb_cache)) != 0)
                {
                gchar *pathl;
@@ -792,9 +909,9 @@ static void thumb_loader_std_thumb_file_validate_done_cb(ThumbLoaderStd *tl, gpo
        GdkPixbuf *pixbuf;
        gboolean valid = FALSE;
 
-       /* get the original thumbnail pixbuf (unrotated, with original options) 
+       /* get the original thumbnail pixbuf (unrotated, with original options)
           this is called from image_loader done callback, so tv->tl->il must exist*/
-       pixbuf = image_loader_get_pixbuf(tv->tl->il); 
+       pixbuf = image_loader_get_pixbuf(tv->tl->il);
        if (pixbuf)
                {
                const gchar *uri;
@@ -959,9 +1076,9 @@ static void thumb_std_maint_move_validate_cb(const gchar *path, gboolean valid,
        TMaintMove *tm = data;
        GdkPixbuf *pixbuf;
 
-       /* get the original thumbnail pixbuf (unrotated, with original options) 
+       /* get the original thumbnail pixbuf (unrotated, with original options)
           this is called from image_loader done callback, so tm->tl->il must exist*/
-       pixbuf = image_loader_get_pixbuf(tm->tl->il); 
+       pixbuf = image_loader_get_pixbuf(tm->tl->il);
        if (pixbuf)
                {
                const gchar *uri;
@@ -1068,8 +1185,8 @@ static gboolean thumb_std_maint_move_idle(gpointer data)
  *
  * The thumbnails are processed when the app is idle. If the app
  * exits early well too bad - they can simply be regenerated from scratch.
- *
- * This does not manage local thumbnails (fixme ?)
+ */
+/** @FIXME This does not manage local thumbnails (fixme ?)
  */
 void thumb_std_maint_moved(const gchar *source, const gchar *dest)
 {