]> git.street.me.uk Git - andy/viking.git/blobdiff - src/thumbnails.c
Merge pull request #32 from davidedelvento/installnotes
[andy/viking.git] / src / thumbnails.c
index 6d42bd5f8b4f0ee5b7f0001045f0d3787d27dba3..2eca0e8482afbd1da2911195ade67e4b3c5db2f5 100644 (file)
 #endif
 
 #include <stdlib.h>
-#include <sys/stat.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 #include <errno.h>
 #include <string.h>
+#include <glib.h>
+#include <glib/gstdio.h>
 #include "viking.h"
 #include "thumbnails.h"
-#include "thumbnails_pixbuf.h"
+#include "icons/icons.h"
+#include "md5_hash.h"
 
 #ifdef __CYGWIN__
 #ifdef __CYGWIN_USE_BIG_TYPES__
 #define ST_SIZE_FMT "%ld"
 #endif
 
-#undef MIN /* quit yer whining, gcc */
-#undef MAX
-#include <sys/param.h> /* for realpath() */
-#ifndef MAX
-/* We need MAX macro and some system does not offer it */
-#define MAX(a,b) (((a)>(b))?(a):(b))
-#endif
+static gchar* thumb_dir = NULL;
 
 #ifdef WINDOWS
-#define HOME_DIR "C:\\VIKING"
-#define THUMB_DIR "\\THUMBNAILS\\" /* viking maps default viking\maps */
-#define THUMB_SUB_DIR "normal\\"
-#define mkdir(a,b) mkdir(a)
-#define realpath(X,Y) _fullpath(Y,X,MAX_PATH)
-
+static void set_thumb_dir ()
+{
+  thumb_dir = g_strconcat ( g_get_home_dir(), "\\THUMBNAILS\\normal\\", NULL ) ;
+}
 #else
-#define HOME_DIR g_get_home_dir()
-#define THUMB_DIR "/.thumbnails/"
-#define THUMB_SUB_DIR "normal/"
+static void set_thumb_dir ()
+{
+  const gchar *xdg_cache_home = g_getenv("XDG_CACHE_HOME");
+  if ( xdg_cache_home && g_strcmp0 (xdg_cache_home, "") != 0 )
+    thumb_dir = g_strconcat ( xdg_cache_home, "/thumbnails/normal/", NULL );
+  else
+    thumb_dir = g_strconcat ( g_get_home_dir(), "/.cache/thumbnails/normal/", NULL );
+}
 #endif
 
 #define PIXMAP_THUMB_SIZE  128
 
-#ifndef MAXPATHLEN
-#define MAXPATHLEN 1024
-#endif
-
-static char *md5_hash(const char *message);
-static char *pathdup(const char *path);
 static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full);
 static GdkPixbuf *child_create_thumbnail(const gchar *path);
 
@@ -100,7 +90,7 @@ gboolean a_thumbnails_exists ( const gchar *filename )
 
 GdkPixbuf *a_thumbnails_get_default ()
 {
-  return gdk_pixbuf_from_pixdata ( &tnnyl_pixbuf, FALSE, NULL );
+  return gdk_pixbuf_from_pixdata ( &thumbnails_pixbuf, FALSE, NULL );
 }
 
 /* filename must be absolute. you could have a function to make sure it exists and absolutize it */
@@ -111,7 +101,7 @@ void a_thumbnails_create(const gchar *filename)
 
   if ( ! pixbuf )
     pixbuf = child_create_thumbnail(filename);
-  
+
   if ( pixbuf )
     g_object_unref (  G_OBJECT ( pixbuf ) );
 }
@@ -125,7 +115,7 @@ GdkPixbuf *a_thumbnails_scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
 
        if (w <= max_w && h <= max_h)
        {
-               gdk_pixbuf_ref(src);
+               g_object_ref ( G_OBJECT ( src ) );
                return src;
        }
        else
@@ -145,14 +135,20 @@ GdkPixbuf *a_thumbnails_scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
 
 static GdkPixbuf *child_create_thumbnail(const gchar *path)
 {
-       GdkPixbuf *image;
+       GdkPixbuf *image, *tmpbuf;
 
        image = gdk_pixbuf_new_from_file(path, NULL);
+       if (!image)
+               return NULL;
+
+       tmpbuf = gdk_pixbuf_apply_embedded_orientation(image);
+       g_object_unref(G_OBJECT(image));
+       image = tmpbuf;
 
        if (image)
-        {
+       {
                GdkPixbuf *thumb = save_thumbnail(path, image);
-               gdk_pixbuf_unref ( image );
+               g_object_unref ( G_OBJECT ( image ) );
                return thumb;
        }
 
@@ -164,6 +160,7 @@ static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full)
        struct stat info;
        gchar *path;
        int original_width, original_height;
+       const gchar* orientation;
        GString *to;
        char *md5, *swidth, *sheight, *ssize, *smtime, *uri;
        mode_t old_mask;
@@ -175,6 +172,8 @@ static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full)
 
        thumb = a_thumbnails_scale_pixbuf(full, PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE);
 
+       orientation = gdk_pixbuf_get_option (full, "orientation");
+
        original_width = gdk_pixbuf_get_width(full);
        original_height = gdk_pixbuf_get_height(full);
 
@@ -184,19 +183,14 @@ static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full)
        ssize = g_strdup_printf(ST_SIZE_FMT, info.st_size);
        smtime = g_strdup_printf("%ld", (long) info.st_mtime);
 
-       path = pathdup(pathname);
+       path = file_realpath_dup(pathname);
        uri = g_strconcat("file://", path, NULL);
        md5 = md5_hash(uri);
        g_free(path);
-               
-       to = g_string_new(HOME_DIR);
-#ifndef WINDOWS
-       mkdir(to->str, 0700);
-#endif
-       g_string_append(to, THUMB_DIR);
-       mkdir(to->str, 0700);
-       g_string_append(to, THUMB_SUB_DIR);
-       mkdir(to->str, 0700);
+
+       to = g_string_new ( thumb_dir );
+       if ( g_mkdir_with_parents(to->str, 0700) != 0 )
+               g_warning ("%s: Failed to mkdir %s", __FUNCTION__, to->str );
        g_string_append(to, md5);
        name_len = to->len + 4; /* Truncate to this length when renaming */
 #ifdef WINDOWS
@@ -207,18 +201,38 @@ static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full)
 
        g_free(md5);
 
+       // Thumb::URI must be in ISO-8859-1 encoding otherwise gdk_pixbuf_save() will fail
+       // - e.g. if characters such as 'ě' are encountered
+       // Also see http://en.wikipedia.org/wiki/ISO/IEC_8859-1
+       // ATM GLIB Manual doesn't specify in which version this function became available
+       //  find out that it's fairly recent so may break builds without this test
+#if GLIB_CHECK_VERSION(2,40,0)
+       char *thumb_uri = g_str_to_ascii ( uri, NULL );
+#else
+       char *thumb_uri = g_strdup ( uri );
+#endif
        old_mask = umask(0077);
-       gdk_pixbuf_save(thumb, to->str, "png", NULL,
-                       "tEXt::Thumb::Image::Width", swidth,
-                       "tEXt::Thumb::Image::Height", sheight,
-                       "tEXt::Thumb::Size", ssize,
-                       "tEXt::Thumb::MTime", smtime,
-                       "tEXt::Thumb::URI", uri,
-                       "tEXt::Software", PROJECT,
-                       NULL);
+       GError *error = NULL;
+       gdk_pixbuf_save(thumb, to->str, "png", &error,
+                       "tEXt::Thumb::Image::Width", swidth,
+                       "tEXt::Thumb::Image::Height", sheight,
+                       "tEXt::Thumb::Size", ssize,
+                       "tEXt::Thumb::MTime", smtime,
+                       "tEXt::Thumb::URI", thumb_uri,
+                       "tEXt::Software", PROJECT,
+                       "tEXt::Software::Orientation", orientation ? orientation : "0",
+                       NULL);
        umask(old_mask);
+       g_free(thumb_uri);
 
-       /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
+       if (error) {
+               g_warning ( "%s::%s", __FUNCTION__, error->message );
+               g_error_free ( error );
+               g_object_unref ( G_OBJECT(thumb) );
+               thumb = NULL; /* return NULL */
+       }
+       else
+       /* We create the file ###.png.Viking-PID and rename it to avoid
         * a race condition if two programs create the same thumb at
         * once.
         */
@@ -255,12 +269,12 @@ GdkPixbuf *a_thumbnails_get(const gchar *pathname)
        const char *ssize, *smtime;
        struct stat info;
 
-       path = pathdup(pathname);
+       path = file_realpath_dup(pathname);
        uri = g_strconcat("file://", path, NULL);
        md5 = md5_hash(uri);
        g_free(uri);
-       
-       thumb_path = g_strdup_printf("%s%s%s%s.png", HOME_DIR, THUMB_DIR, THUMB_SUB_DIR, md5);
+
+       thumb_path = g_strdup_printf("%s%s.png", thumb_dir, md5);
 
        g_free(md5);
 
@@ -286,7 +300,7 @@ GdkPixbuf *a_thumbnails_get(const gchar *pathname)
        goto out;
 err:
        if (thumb)
-               gdk_pixbuf_unref(thumb);
+               g_object_unref ( G_OBJECT ( thumb ) );
        thumb = NULL;
 out:
        g_free(path);
@@ -294,268 +308,16 @@ out:
        return thumb;
 }
 
-/* pathdup() stuff */
-
-static char *pathdup(const char *path)
-{
-       char real[MAXPATHLEN];
-
-       g_return_val_if_fail(path != NULL, NULL);
-
-       if (realpath(path, real))
-               return g_strdup(real);
-
-       return g_strdup(path);
-}
-
 /*
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest. The original code was
- * written by Colin Plumb in 1993, and put in the public domain.
- * 
- * Modified to use glib datatypes. Put under GPL to simplify
- * licensing for ROX-Filer. Taken from Debian's dpkg package.
- *
+ * Startup and finish routines
  */
 
-#define md5byte unsigned char
-
-typedef struct _MD5Context MD5Context;
-
-struct _MD5Context {
-       guint32 buf[4];
-       guint32 bytes[2];
-       guint32 in[16];
-};
-
-static void MD5Init(MD5Context *ctx);
-static void MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len);
-static char *MD5Final(MD5Context *ctx);
-static void MD5Transform(guint32 buf[4], guint32 const in[16]);
-
-#if G_BYTE_ORDER == G_BIG_ENDIAN
-static void byteSwap(guint32 *buf, unsigned words)
+void a_thumbnails_init ()
 {
-       md5byte *p = (md5byte *)buf;
-
-       do {
-               *buf++ = (guint32)((unsigned)p[3] << 8 | p[2]) << 16 |
-                       ((unsigned)p[1] << 8 | p[0]);
-               p += 4;
-       } while (--words);
+  set_thumb_dir ();
 }
-#else
-#define byteSwap(buf,words)
-#endif
-
-/*
- * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
- * initialization constants.
- */
-static void MD5Init(MD5Context *ctx)
-{
-       ctx->buf[0] = 0x67452301;
-       ctx->buf[1] = 0xefcdab89;
-       ctx->buf[2] = 0x98badcfe;
-       ctx->buf[3] = 0x10325476;
 
-       ctx->bytes[0] = 0;
-       ctx->bytes[1] = 0;
-}
-
-/*
- * Update context to reflect the concatenation of another buffer full
- * of bytes.
- */
-static void MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len)
+void a_thumbnails_uninit ()
 {
-       guint32 t;
-
-       /* Update byte count */
-
-       t = ctx->bytes[0];
-       if ((ctx->bytes[0] = t + len) < t)
-               ctx->bytes[1]++;        /* Carry from low to high */
-
-       t = 64 - (t & 0x3f);    /* Space available in ctx->in (at least 1) */
-       if (t > len) {
-               memcpy((md5byte *)ctx->in + 64 - t, buf, len);
-               return;
-       }
-       /* First chunk is an odd size */
-       memcpy((md5byte *)ctx->in + 64 - t, buf, t);
-       byteSwap(ctx->in, 16);
-       MD5Transform(ctx->buf, ctx->in);
-       buf += t;
-       len -= t;
-
-       /* Process data in 64-byte chunks */
-       while (len >= 64) {
-               memcpy(ctx->in, buf, 64);
-               byteSwap(ctx->in, 16);
-               MD5Transform(ctx->buf, ctx->in);
-               buf += 64;
-               len -= 64;
-       }
-
-       /* Handle any remaining bytes of data. */
-       memcpy(ctx->in, buf, len);
-}
-
-/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern 
- * 1 0* (64-bit count of bits processed, MSB-first)
- * Returns the newly allocated string of the hash.
- */
-static char *MD5Final(MD5Context *ctx)
-{
-       char *retval;
-       int i;
-       int count = ctx->bytes[0] & 0x3f;       /* Number of bytes in ctx->in */
-       md5byte *p = (md5byte *)ctx->in + count;
-       guint8  *bytes;
-
-       /* Set the first char of padding to 0x80.  There is always room. */
-       *p++ = 0x80;
-
-       /* Bytes of padding needed to make 56 bytes (-8..55) */
-       count = 56 - 1 - count;
-
-       if (count < 0) {        /* Padding forces an extra block */
-               memset(p, 0, count + 8);
-               byteSwap(ctx->in, 16);
-               MD5Transform(ctx->buf, ctx->in);
-               p = (md5byte *)ctx->in;
-               count = 56;
-       }
-       memset(p, 0, count);
-       byteSwap(ctx->in, 14);
-
-       /* Append length in bits and transform */
-       ctx->in[14] = ctx->bytes[0] << 3;
-       ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
-       MD5Transform(ctx->buf, ctx->in);
-
-       byteSwap(ctx->buf, 4);
-
-       retval = g_malloc(33);
-       bytes = (guint8 *) ctx->buf;
-       for (i = 0; i < 16; i++)
-               sprintf(retval + (i * 2), "%02x", bytes[i]);
-       retval[32] = '\0';
-       
-       return retval;
-}
-
-# ifndef ASM_MD5
-
-/* The four core functions - F1 is optimized somewhat */
-
-/* #define F1(x, y, z) (x & y | ~x & z) */
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-/* This is the central step in the MD5 algorithm. */
-#define MD5STEP(f,w,x,y,z,in,s) \
-        (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
-
-/*
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data.  MD5Update blocks
- * the data and converts bytes into longwords for this routine.
- */
-static void MD5Transform(guint32 buf[4], guint32 const in[16])
-{
-       register guint32 a, b, c, d;
-
-       a = buf[0];
-       b = buf[1];
-       c = buf[2];
-       d = buf[3];
-
-       MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
-       MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
-       MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
-       MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
-       MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
-       MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
-       MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
-       MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
-       MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
-       MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
-       MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
-       MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
-       MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
-       MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
-       MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
-       MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
-       MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
-       MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
-       MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
-       MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
-       MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
-       MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
-       MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
-       MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
-       MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
-       MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
-       MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
-       MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
-       MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
-       MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
-       MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
-       MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
-       MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
-       MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
-       MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
-       MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
-       MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
-       MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
-       MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
-       MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
-       MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
-       MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
-       MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
-       MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
-       MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
-       MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
-       MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
-       MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
-       MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
-       MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
-       MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
-       MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
-       MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
-       MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
-       MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
-       MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
-       MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
-       MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
-       MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
-       MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
-       MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
-       MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
-       MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
-       MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-
-       buf[0] += a;
-       buf[1] += b;
-       buf[2] += c;
-       buf[3] += d;
-}
-
-# endif /* ASM_MD5 */
-
-static char *md5_hash(const char *message)
-{
-       MD5Context ctx;
-
-       MD5Init(&ctx);
-       MD5Update(&ctx, message, strlen(message));
-       return MD5Final(&ctx);
+  g_free ( thumb_dir );
 }