2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * Large (and important) sections of this file were adapted from
24 * ROX-Filer source code, Copyright (C) 2003, the ROX-Filer team,
25 * originally licensed under the GPL v2 or greater (as above).
34 #include "thumbnails.h"
35 #include "thumbnails_pixbuf.h"
38 #ifdef __CYGWIN_USE_BIG_TYPES__
39 #define ST_SIZE_FMT "%lld"
41 #define ST_SIZE_FMT "%ld"
44 /* FIXME -- on some systems this may need to me "lld", see ROX-Filer code */
45 #define ST_SIZE_FMT "%ld"
48 #undef MIN /* quit yer whining, gcc */
50 #include <sys/param.h> /* for realpath() */
53 #define HOME_DIR "C:\\VIKING"
54 #define THUMB_DIR "\\THUMBNAILS\\" /* viking maps default viking\maps */
55 #define THUMB_SUB_DIR "normal\\"
56 #define mkdir(a,b) mkdir(a)
57 #define MAX(a,b) (((a)>(b))?(a):(b))
58 #define realpath(X,Y) _fullpath(Y,X,MAX_PATH)
61 #define HOME_DIR g_get_home_dir()
62 #define THUMB_DIR "/.thumbnails/"
63 #define THUMB_SUB_DIR "normal/"
66 #define PIXMAP_THUMB_SIZE 128
68 static char *md5_hash(const char *message
);
69 static char *pathdup(const char *path
);
70 static GdkPixbuf
*save_thumbnail(const char *pathname
, GdkPixbuf
*full
);
71 static GdkPixbuf
*child_create_thumbnail(const gchar
*path
);
73 gboolean
a_thumbnails_exists ( const gchar
*filename
)
75 GdkPixbuf
*pixbuf
= a_thumbnails_get(filename
);
78 g_object_unref ( G_OBJECT ( pixbuf
) );
84 GdkPixbuf
*a_thumbnails_get_default ()
86 return gdk_pixbuf_from_pixdata ( &tnnyl_pixbuf
, FALSE
, NULL
);
89 /* filename must be absolute. you could have a function to make sure it exists and absolutize it */
91 void a_thumbnails_create(const gchar
*filename
)
93 GdkPixbuf
*pixbuf
= a_thumbnails_get(filename
);
96 pixbuf
= child_create_thumbnail(filename
);
99 g_object_unref ( G_OBJECT ( pixbuf
) );
102 GdkPixbuf
*a_thumbnails_scale_pixbuf(GdkPixbuf
*src
, int max_w
, int max_h
)
106 w
= gdk_pixbuf_get_width(src
);
107 h
= gdk_pixbuf_get_height(src
);
109 if (w
<= max_w
&& h
<= max_h
)
116 float scale_x
= ((float) w
) / max_w
;
117 float scale_y
= ((float) h
) / max_h
;
118 float scale
= MAX(scale_x
, scale_y
);
119 int dest_w
= w
/ scale
;
120 int dest_h
= h
/ scale
;
122 return gdk_pixbuf_scale_simple(src
,
125 GDK_INTERP_BILINEAR
);
129 static GdkPixbuf
*child_create_thumbnail(const gchar
*path
)
133 image
= gdk_pixbuf_new_from_file(path
, NULL
);
137 GdkPixbuf
*thumb
= save_thumbnail(path
, image
);
138 gdk_pixbuf_unref ( image
);
145 static GdkPixbuf
*save_thumbnail(const char *pathname
, GdkPixbuf
*full
)
149 int original_width
, original_height
;
151 char *md5
, *swidth
, *sheight
, *ssize
, *smtime
, *uri
;
156 if (stat(pathname
, &info
) != 0)
159 thumb
= a_thumbnails_scale_pixbuf(full
, PIXMAP_THUMB_SIZE
, PIXMAP_THUMB_SIZE
);
161 original_width
= gdk_pixbuf_get_width(full
);
162 original_height
= gdk_pixbuf_get_height(full
);
165 swidth
= g_strdup_printf("%d", original_width
);
166 sheight
= g_strdup_printf("%d", original_height
);
167 ssize
= g_strdup_printf(ST_SIZE_FMT
, info
.st_size
);
168 smtime
= g_strdup_printf("%ld", (long) info
.st_mtime
);
170 path
= pathdup(pathname
);
171 uri
= g_strconcat("file://", path
, NULL
);
175 to
= g_string_new(HOME_DIR
);
177 mkdir(to
->str
, 0700);
179 g_string_append(to
, THUMB_DIR
);
180 mkdir(to
->str
, 0700);
181 g_string_append(to
, THUMB_SUB_DIR
);
182 mkdir(to
->str
, 0700);
183 g_string_append(to
, md5
);
184 name_len
= to
->len
+ 4; /* Truncate to this length when renaming */
186 g_string_append_printf(to
, ".png.Viking");
188 g_string_append_printf(to
, ".png.Viking-%ld", (long) getpid());
193 old_mask
= umask(0077);
194 gdk_pixbuf_save(thumb
, to
->str
, "png", NULL
,
195 "tEXt::Thumb::Image::Width", swidth
,
196 "tEXt::Thumb::Image::Height", sheight
,
197 "tEXt::Thumb::Size", ssize
,
198 "tEXt::Thumb::MTime", smtime
,
199 "tEXt::Thumb::URI", uri
,
200 "tEXt::Software", PROJECT
,
204 /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
205 * a race condition if two programs create the same thumb at
211 final
= g_strndup(to
->str
, name_len
);
212 if (rename(to
->str
, final
))
214 g_warning("Failed to rename '%s' to '%s': %s",
215 to
->str
, final
, g_strerror(errno
));
216 g_object_unref ( G_OBJECT(thumb
) );
217 thumb
= NULL
; /* return NULL */
223 g_string_free(to
, TRUE
);
234 GdkPixbuf
*a_thumbnails_get(const gchar
*pathname
)
236 GdkPixbuf
*thumb
= NULL
;
237 char *thumb_path
, *md5
, *uri
, *path
;
238 const char *ssize
, *smtime
;
241 path
= pathdup(pathname
);
242 uri
= g_strconcat("file://", path
, NULL
);
246 thumb_path
= g_strdup_printf("%s%s%s%s.png", HOME_DIR
, THUMB_DIR
, THUMB_SUB_DIR
, md5
);
250 thumb
= gdk_pixbuf_new_from_file(thumb_path
, NULL
);
254 /* Note that these don't need freeing... */
255 ssize
= gdk_pixbuf_get_option(thumb
, "tEXt::Thumb::Size");
259 smtime
= gdk_pixbuf_get_option(thumb
, "tEXt::Thumb::MTime");
263 if (stat(path
, &info
) != 0)
266 if (info
.st_mtime
!= atol(smtime
) || info
.st_size
!= atol(ssize
))
272 gdk_pixbuf_unref(thumb
);
280 /* pathdup() stuff */
282 static char *pathdup(const char *path
)
284 char real
[MAXPATHLEN
];
286 g_return_val_if_fail(path
!= NULL
, NULL
);
288 if (realpath(path
, real
))
289 return g_strdup(real
);
291 return g_strdup(path
);
295 * This code implements the MD5 message-digest algorithm.
296 * The algorithm is due to Ron Rivest. The original code was
297 * written by Colin Plumb in 1993, and put in the public domain.
299 * Modified to use glib datatypes. Put under GPL to simplify
300 * licensing for ROX-Filer. Taken from Debian's dpkg package.
304 #define md5byte unsigned char
306 typedef struct _MD5Context MD5Context
;
314 static void MD5Init(MD5Context
*ctx
);
315 static void MD5Update(MD5Context
*ctx
, md5byte
const *buf
, unsigned len
);
316 static char *MD5Final(MD5Context
*ctx
);
317 static void MD5Transform(guint32 buf
[4], guint32
const in
[16]);
319 #if G_BYTE_ORDER == G_BIG_ENDIAN
320 static void byteSwap(guint32
*buf
, unsigned words
)
322 md5byte
*p
= (md5byte
*)buf
;
325 *buf
++ = (guint32
)((unsigned)p
[3] << 8 | p
[2]) << 16 |
326 ((unsigned)p
[1] << 8 | p
[0]);
331 #define byteSwap(buf,words)
335 * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
336 * initialization constants.
338 static void MD5Init(MD5Context
*ctx
)
340 ctx
->buf
[0] = 0x67452301;
341 ctx
->buf
[1] = 0xefcdab89;
342 ctx
->buf
[2] = 0x98badcfe;
343 ctx
->buf
[3] = 0x10325476;
350 * Update context to reflect the concatenation of another buffer full
353 static void MD5Update(MD5Context
*ctx
, md5byte
const *buf
, unsigned len
)
357 /* Update byte count */
360 if ((ctx
->bytes
[0] = t
+ len
) < t
)
361 ctx
->bytes
[1]++; /* Carry from low to high */
363 t
= 64 - (t
& 0x3f); /* Space available in ctx->in (at least 1) */
365 memcpy((md5byte
*)ctx
->in
+ 64 - t
, buf
, len
);
368 /* First chunk is an odd size */
369 memcpy((md5byte
*)ctx
->in
+ 64 - t
, buf
, t
);
370 byteSwap(ctx
->in
, 16);
371 MD5Transform(ctx
->buf
, ctx
->in
);
375 /* Process data in 64-byte chunks */
377 memcpy(ctx
->in
, buf
, 64);
378 byteSwap(ctx
->in
, 16);
379 MD5Transform(ctx
->buf
, ctx
->in
);
384 /* Handle any remaining bytes of data. */
385 memcpy(ctx
->in
, buf
, len
);
389 * Final wrapup - pad to 64-byte boundary with the bit pattern
390 * 1 0* (64-bit count of bits processed, MSB-first)
391 * Returns the newly allocated string of the hash.
393 static char *MD5Final(MD5Context
*ctx
)
397 int count
= ctx
->bytes
[0] & 0x3f; /* Number of bytes in ctx->in */
398 md5byte
*p
= (md5byte
*)ctx
->in
+ count
;
401 /* Set the first char of padding to 0x80. There is always room. */
404 /* Bytes of padding needed to make 56 bytes (-8..55) */
405 count
= 56 - 1 - count
;
407 if (count
< 0) { /* Padding forces an extra block */
408 memset(p
, 0, count
+ 8);
409 byteSwap(ctx
->in
, 16);
410 MD5Transform(ctx
->buf
, ctx
->in
);
411 p
= (md5byte
*)ctx
->in
;
415 byteSwap(ctx
->in
, 14);
417 /* Append length in bits and transform */
418 ctx
->in
[14] = ctx
->bytes
[0] << 3;
419 ctx
->in
[15] = ctx
->bytes
[1] << 3 | ctx
->bytes
[0] >> 29;
420 MD5Transform(ctx
->buf
, ctx
->in
);
422 byteSwap(ctx
->buf
, 4);
424 retval
= g_malloc(33);
425 bytes
= (guint8
*) ctx
->buf
;
426 for (i
= 0; i
< 16; i
++)
427 sprintf(retval
+ (i
* 2), "%02x", bytes
[i
]);
435 /* The four core functions - F1 is optimized somewhat */
437 /* #define F1(x, y, z) (x & y | ~x & z) */
438 #define F1(x, y, z) (z ^ (x & (y ^ z)))
439 #define F2(x, y, z) F1(z, x, y)
440 #define F3(x, y, z) (x ^ y ^ z)
441 #define F4(x, y, z) (y ^ (x | ~z))
443 /* This is the central step in the MD5 algorithm. */
444 #define MD5STEP(f,w,x,y,z,in,s) \
445 (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
448 * The core of the MD5 algorithm, this alters an existing MD5 hash to
449 * reflect the addition of 16 longwords of new data. MD5Update blocks
450 * the data and converts bytes into longwords for this routine.
452 static void MD5Transform(guint32 buf
[4], guint32
const in
[16])
454 register guint32 a
, b
, c
, d
;
461 MD5STEP(F1
, a
, b
, c
, d
, in
[0] + 0xd76aa478, 7);
462 MD5STEP(F1
, d
, a
, b
, c
, in
[1] + 0xe8c7b756, 12);
463 MD5STEP(F1
, c
, d
, a
, b
, in
[2] + 0x242070db, 17);
464 MD5STEP(F1
, b
, c
, d
, a
, in
[3] + 0xc1bdceee, 22);
465 MD5STEP(F1
, a
, b
, c
, d
, in
[4] + 0xf57c0faf, 7);
466 MD5STEP(F1
, d
, a
, b
, c
, in
[5] + 0x4787c62a, 12);
467 MD5STEP(F1
, c
, d
, a
, b
, in
[6] + 0xa8304613, 17);
468 MD5STEP(F1
, b
, c
, d
, a
, in
[7] + 0xfd469501, 22);
469 MD5STEP(F1
, a
, b
, c
, d
, in
[8] + 0x698098d8, 7);
470 MD5STEP(F1
, d
, a
, b
, c
, in
[9] + 0x8b44f7af, 12);
471 MD5STEP(F1
, c
, d
, a
, b
, in
[10] + 0xffff5bb1, 17);
472 MD5STEP(F1
, b
, c
, d
, a
, in
[11] + 0x895cd7be, 22);
473 MD5STEP(F1
, a
, b
, c
, d
, in
[12] + 0x6b901122, 7);
474 MD5STEP(F1
, d
, a
, b
, c
, in
[13] + 0xfd987193, 12);
475 MD5STEP(F1
, c
, d
, a
, b
, in
[14] + 0xa679438e, 17);
476 MD5STEP(F1
, b
, c
, d
, a
, in
[15] + 0x49b40821, 22);
478 MD5STEP(F2
, a
, b
, c
, d
, in
[1] + 0xf61e2562, 5);
479 MD5STEP(F2
, d
, a
, b
, c
, in
[6] + 0xc040b340, 9);
480 MD5STEP(F2
, c
, d
, a
, b
, in
[11] + 0x265e5a51, 14);
481 MD5STEP(F2
, b
, c
, d
, a
, in
[0] + 0xe9b6c7aa, 20);
482 MD5STEP(F2
, a
, b
, c
, d
, in
[5] + 0xd62f105d, 5);
483 MD5STEP(F2
, d
, a
, b
, c
, in
[10] + 0x02441453, 9);
484 MD5STEP(F2
, c
, d
, a
, b
, in
[15] + 0xd8a1e681, 14);
485 MD5STEP(F2
, b
, c
, d
, a
, in
[4] + 0xe7d3fbc8, 20);
486 MD5STEP(F2
, a
, b
, c
, d
, in
[9] + 0x21e1cde6, 5);
487 MD5STEP(F2
, d
, a
, b
, c
, in
[14] + 0xc33707d6, 9);
488 MD5STEP(F2
, c
, d
, a
, b
, in
[3] + 0xf4d50d87, 14);
489 MD5STEP(F2
, b
, c
, d
, a
, in
[8] + 0x455a14ed, 20);
490 MD5STEP(F2
, a
, b
, c
, d
, in
[13] + 0xa9e3e905, 5);
491 MD5STEP(F2
, d
, a
, b
, c
, in
[2] + 0xfcefa3f8, 9);
492 MD5STEP(F2
, c
, d
, a
, b
, in
[7] + 0x676f02d9, 14);
493 MD5STEP(F2
, b
, c
, d
, a
, in
[12] + 0x8d2a4c8a, 20);
495 MD5STEP(F3
, a
, b
, c
, d
, in
[5] + 0xfffa3942, 4);
496 MD5STEP(F3
, d
, a
, b
, c
, in
[8] + 0x8771f681, 11);
497 MD5STEP(F3
, c
, d
, a
, b
, in
[11] + 0x6d9d6122, 16);
498 MD5STEP(F3
, b
, c
, d
, a
, in
[14] + 0xfde5380c, 23);
499 MD5STEP(F3
, a
, b
, c
, d
, in
[1] + 0xa4beea44, 4);
500 MD5STEP(F3
, d
, a
, b
, c
, in
[4] + 0x4bdecfa9, 11);
501 MD5STEP(F3
, c
, d
, a
, b
, in
[7] + 0xf6bb4b60, 16);
502 MD5STEP(F3
, b
, c
, d
, a
, in
[10] + 0xbebfbc70, 23);
503 MD5STEP(F3
, a
, b
, c
, d
, in
[13] + 0x289b7ec6, 4);
504 MD5STEP(F3
, d
, a
, b
, c
, in
[0] + 0xeaa127fa, 11);
505 MD5STEP(F3
, c
, d
, a
, b
, in
[3] + 0xd4ef3085, 16);
506 MD5STEP(F3
, b
, c
, d
, a
, in
[6] + 0x04881d05, 23);
507 MD5STEP(F3
, a
, b
, c
, d
, in
[9] + 0xd9d4d039, 4);
508 MD5STEP(F3
, d
, a
, b
, c
, in
[12] + 0xe6db99e5, 11);
509 MD5STEP(F3
, c
, d
, a
, b
, in
[15] + 0x1fa27cf8, 16);
510 MD5STEP(F3
, b
, c
, d
, a
, in
[2] + 0xc4ac5665, 23);
512 MD5STEP(F4
, a
, b
, c
, d
, in
[0] + 0xf4292244, 6);
513 MD5STEP(F4
, d
, a
, b
, c
, in
[7] + 0x432aff97, 10);
514 MD5STEP(F4
, c
, d
, a
, b
, in
[14] + 0xab9423a7, 15);
515 MD5STEP(F4
, b
, c
, d
, a
, in
[5] + 0xfc93a039, 21);
516 MD5STEP(F4
, a
, b
, c
, d
, in
[12] + 0x655b59c3, 6);
517 MD5STEP(F4
, d
, a
, b
, c
, in
[3] + 0x8f0ccc92, 10);
518 MD5STEP(F4
, c
, d
, a
, b
, in
[10] + 0xffeff47d, 15);
519 MD5STEP(F4
, b
, c
, d
, a
, in
[1] + 0x85845dd1, 21);
520 MD5STEP(F4
, a
, b
, c
, d
, in
[8] + 0x6fa87e4f, 6);
521 MD5STEP(F4
, d
, a
, b
, c
, in
[15] + 0xfe2ce6e0, 10);
522 MD5STEP(F4
, c
, d
, a
, b
, in
[6] + 0xa3014314, 15);
523 MD5STEP(F4
, b
, c
, d
, a
, in
[13] + 0x4e0811a1, 21);
524 MD5STEP(F4
, a
, b
, c
, d
, in
[4] + 0xf7537e82, 6);
525 MD5STEP(F4
, d
, a
, b
, c
, in
[11] + 0xbd3af235, 10);
526 MD5STEP(F4
, c
, d
, a
, b
, in
[2] + 0x2ad7d2bb, 15);
527 MD5STEP(F4
, b
, c
, d
, a
, in
[9] + 0xeb86d391, 21);
535 # endif /* ASM_MD5 */
537 static char *md5_hash(const char *message
)
542 MD5Update(&ctx
, message
, strlen(message
));
543 return MD5Final(&ctx
);