]> git.street.me.uk Git - andy/viking.git/blame - src/thumbnails.c
Remove definition of a non existant function
[andy/viking.git] / src / thumbnails.c
CommitLineData
50a14534
EB
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 *
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.
10 *
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.
15 *
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
19 *
20 */
21
22/*
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).
26 *
27 */
28
45acf79e
MA
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
50a14534 33#include <stdlib.h>
45acf79e
MA
34#ifdef HAVE_UNISTD_H
35#include <unistd.h>
36#endif
50a14534
EB
37#include <errno.h>
38#include <string.h>
f83131b9
MA
39#include <glib.h>
40#include <glib/gstdio.h>
50a14534
EB
41#include "viking.h"
42#include "thumbnails.h"
5bfafde9 43#include "icons/icons.h"
bd27baa4 44#include "md5_hash.h"
50a14534
EB
45
46#ifdef __CYGWIN__
47#ifdef __CYGWIN_USE_BIG_TYPES__
48#define ST_SIZE_FMT "%lld"
49#else
50#define ST_SIZE_FMT "%ld"
51#endif
52#else
53/* FIXME -- on some systems this may need to me "lld", see ROX-Filer code */
54#define ST_SIZE_FMT "%ld"
55#endif
56
ac4b23cb 57static gchar* thumb_dir = NULL;
118b901b 58
50a14534 59#ifdef WINDOWS
ac4b23cb
RN
60static void set_thumb_dir ()
61{
62 thumb_dir = g_strconcat ( g_get_home_dir(), "\\THUMBNAILS\\normal\\", NULL ) ;
63}
50a14534 64#else
ac4b23cb
RN
65static void set_thumb_dir ()
66{
67 const gchar *xdg_cache_home = g_getenv("XDG_CACHE_HOME");
68 if ( xdg_cache_home && g_strcmp0 (xdg_cache_home, "") != 0 )
69 thumb_dir = g_strconcat ( xdg_cache_home, "/thumbnails/normal/", NULL );
70 else
71 thumb_dir = g_strconcat ( g_get_home_dir(), "/.cache/thumbnails/normal/", NULL );
72}
50a14534
EB
73#endif
74
75#define PIXMAP_THUMB_SIZE 128
76
50a14534
EB
77static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full);
78static GdkPixbuf *child_create_thumbnail(const gchar *path);
79
80gboolean a_thumbnails_exists ( const gchar *filename )
81{
82 GdkPixbuf *pixbuf = a_thumbnails_get(filename);
83 if ( pixbuf )
84 {
85 g_object_unref ( G_OBJECT ( pixbuf ) );
86 return TRUE;
87 }
88 return FALSE;
89}
90
91GdkPixbuf *a_thumbnails_get_default ()
92{
5bfafde9 93 return gdk_pixbuf_from_pixdata ( &thumbnails_pixbuf, FALSE, NULL );
50a14534
EB
94}
95
96/* filename must be absolute. you could have a function to make sure it exists and absolutize it */
97
98void a_thumbnails_create(const gchar *filename)
99{
100 GdkPixbuf *pixbuf = a_thumbnails_get(filename);
101
102 if ( ! pixbuf )
103 pixbuf = child_create_thumbnail(filename);
19096e42 104
50a14534
EB
105 if ( pixbuf )
106 g_object_unref ( G_OBJECT ( pixbuf ) );
107}
108
109GdkPixbuf *a_thumbnails_scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
110{
111 int w, h;
112
113 w = gdk_pixbuf_get_width(src);
114 h = gdk_pixbuf_get_height(src);
115
116 if (w <= max_w && h <= max_h)
117 {
f2fcc2d2 118 g_object_ref ( G_OBJECT ( src ) );
50a14534
EB
119 return src;
120 }
121 else
122 {
123 float scale_x = ((float) w) / max_w;
124 float scale_y = ((float) h) / max_h;
125 float scale = MAX(scale_x, scale_y);
126 int dest_w = w / scale;
127 int dest_h = h / scale;
128
129 return gdk_pixbuf_scale_simple(src,
130 MAX(dest_w, 1),
131 MAX(dest_h, 1),
132 GDK_INTERP_BILINEAR);
133 }
134}
135
136static GdkPixbuf *child_create_thumbnail(const gchar *path)
137{
6d0927b1 138 GdkPixbuf *image, *tmpbuf;
50a14534
EB
139
140 image = gdk_pixbuf_new_from_file(path, NULL);
778e43b4 141 if (!image)
19096e42 142 return NULL;
778e43b4 143
6d0927b1
GK
144 tmpbuf = gdk_pixbuf_apply_embedded_orientation(image);
145 g_object_unref(G_OBJECT(image));
146 image = tmpbuf;
50a14534
EB
147
148 if (image)
19096e42 149 {
50a14534 150 GdkPixbuf *thumb = save_thumbnail(path, image);
f2fcc2d2 151 g_object_unref ( G_OBJECT ( image ) );
50a14534
EB
152 return thumb;
153 }
154
155 return NULL;
156}
157
158static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full)
159{
160 struct stat info;
161 gchar *path;
162 int original_width, original_height;
6d0927b1 163 const gchar* orientation;
50a14534
EB
164 GString *to;
165 char *md5, *swidth, *sheight, *ssize, *smtime, *uri;
166 mode_t old_mask;
167 int name_len;
168 GdkPixbuf *thumb;
169
170 if (stat(pathname, &info) != 0)
171 return NULL;
172
173 thumb = a_thumbnails_scale_pixbuf(full, PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE);
174
6d0927b1
GK
175 orientation = gdk_pixbuf_get_option (full, "orientation");
176
50a14534
EB
177 original_width = gdk_pixbuf_get_width(full);
178 original_height = gdk_pixbuf_get_height(full);
179
180
181 swidth = g_strdup_printf("%d", original_width);
182 sheight = g_strdup_printf("%d", original_height);
183 ssize = g_strdup_printf(ST_SIZE_FMT, info.st_size);
184 smtime = g_strdup_printf("%ld", (long) info.st_mtime);
185
1b14d0d2 186 path = file_realpath_dup(pathname);
50a14534
EB
187 uri = g_strconcat("file://", path, NULL);
188 md5 = md5_hash(uri);
189 g_free(path);
19096e42 190
ac4b23cb 191 to = g_string_new ( thumb_dir );
5e4cce8f
RN
192 if ( g_mkdir_with_parents(to->str, 0700) != 0 )
193 g_warning ("%s: Failed to mkdir %s", __FUNCTION__, to->str );
50a14534
EB
194 g_string_append(to, md5);
195 name_len = to->len + 4; /* Truncate to this length when renaming */
196#ifdef WINDOWS
197 g_string_append_printf(to, ".png.Viking");
198#else
199 g_string_append_printf(to, ".png.Viking-%ld", (long) getpid());
200#endif
201
202 g_free(md5);
203
d31be35e
RN
204 // Thumb::URI must be in ISO-8859-1 encoding otherwise gdk_pixbuf_save() will fail
205 // - e.g. if characters such as 'ě' are encountered
206 // Also see http://en.wikipedia.org/wiki/ISO/IEC_8859-1
86571e94
RN
207 // ATM GLIB Manual doesn't specify in which version this function became available
208 // find out that it's fairly recent so may break builds without this test
209#if GLIB_CHECK_VERSION(2,40,0)
d31be35e 210 char *thumb_uri = g_str_to_ascii ( uri, NULL );
86571e94
RN
211#else
212 char *thumb_uri = g_strdup ( uri );
213#endif
50a14534 214 old_mask = umask(0077);
56f6a97c
RN
215 GError *error = NULL;
216 gdk_pixbuf_save(thumb, to->str, "png", &error,
19096e42
RN
217 "tEXt::Thumb::Image::Width", swidth,
218 "tEXt::Thumb::Image::Height", sheight,
219 "tEXt::Thumb::Size", ssize,
220 "tEXt::Thumb::MTime", smtime,
d31be35e 221 "tEXt::Thumb::URI", thumb_uri,
19096e42
RN
222 "tEXt::Software", PROJECT,
223 "tEXt::Software::Orientation", orientation ? orientation : "0",
224 NULL);
50a14534 225 umask(old_mask);
d31be35e 226 g_free(thumb_uri);
50a14534 227
56f6a97c
RN
228 if (error) {
229 g_warning ( "%s::%s", __FUNCTION__, error->message );
230 g_error_free ( error );
231 g_object_unref ( G_OBJECT(thumb) );
232 thumb = NULL; /* return NULL */
233 }
234 else
235 /* We create the file ###.png.Viking-PID and rename it to avoid
50a14534
EB
236 * a race condition if two programs create the same thumb at
237 * once.
238 */
239 {
240 gchar *final;
241
242 final = g_strndup(to->str, name_len);
243 if (rename(to->str, final))
244 {
245 g_warning("Failed to rename '%s' to '%s': %s",
246 to->str, final, g_strerror(errno));
247 g_object_unref ( G_OBJECT(thumb) );
248 thumb = NULL; /* return NULL */
249 }
250
251 g_free(final);
252 }
253
254 g_string_free(to, TRUE);
255 g_free(swidth);
256 g_free(sheight);
257 g_free(ssize);
258 g_free(smtime);
259 g_free(uri);
260
261 return thumb;
262}
263
264
265GdkPixbuf *a_thumbnails_get(const gchar *pathname)
266{
267 GdkPixbuf *thumb = NULL;
268 char *thumb_path, *md5, *uri, *path;
269 const char *ssize, *smtime;
270 struct stat info;
271
1b14d0d2 272 path = file_realpath_dup(pathname);
50a14534
EB
273 uri = g_strconcat("file://", path, NULL);
274 md5 = md5_hash(uri);
275 g_free(uri);
19096e42 276
ac4b23cb 277 thumb_path = g_strdup_printf("%s%s.png", thumb_dir, md5);
50a14534
EB
278
279 g_free(md5);
280
281 thumb = gdk_pixbuf_new_from_file(thumb_path, NULL);
282 if (!thumb)
283 goto err;
284
285 /* Note that these don't need freeing... */
286 ssize = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::Size");
287 if (!ssize)
288 goto err;
289
290 smtime = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::MTime");
291 if (!smtime)
292 goto err;
293
294 if (stat(path, &info) != 0)
295 goto err;
296
297 if (info.st_mtime != atol(smtime) || info.st_size != atol(ssize))
298 goto err;
299
300 goto out;
301err:
302 if (thumb)
f2fcc2d2 303 g_object_unref ( G_OBJECT ( thumb ) );
50a14534
EB
304 thumb = NULL;
305out:
306 g_free(path);
307 g_free(thumb_path);
308 return thumb;
309}
ac4b23cb
RN
310
311/*
312 * Startup and finish routines
313 */
314
315void a_thumbnails_init ()
316{
317 set_thumb_dir ();
318}
319
320void a_thumbnails_uninit ()
321{
322 g_free ( thumb_dir );
323}