Krolyxon
Commit 79d780a

Branches

Refs →

nsxivthumbs.c

461 lines · 9.6 KB · C

1/* sxiv: thumbs.c
2 * Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#define _POSIX_C_SOURCE 200112L
20
21#include <stdlib.h>
22#include <string.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <unistd.h>
26#include <utime.h>
27
28#include "thumbs.h"
29#include "util.h"
30
31#define _THUMBS_CONFIG
32#include "config.h"
33
34#ifdef EXIF_SUPPORT
35void exif_auto_orientate(const fileinfo_t*);
36#endif
37
38const int thumb_dim = THUMB_SIZE + 10;
39char *cache_dir = NULL;
40
41int tns_cache_enabled() {
42 struct stat stats;
43
44 return cache_dir && !stat(cache_dir, &stats) && S_ISDIR(stats.st_mode) &&
45 !access(cache_dir, W_OK);
46}
47
48char* tns_cache_filepath(const char *filepath) {
49 size_t len;
50 char *cfile = NULL;
51
52 if (!cache_dir || !filepath || *filepath != '/')
53 return NULL;
54
55 if (strncmp(filepath, cache_dir, strlen(cache_dir))) {
56 len = strlen(cache_dir) + strlen(filepath) + 6;
57 cfile = (char*) s_malloc(len);
58 snprintf(cfile, len, "%s/%s.png", cache_dir, filepath + 1);
59 }
60
61 return cfile;
62}
63
64Imlib_Image* tns_cache_load(const char *filepath) {
65 char *cfile;
66 struct stat cstats, fstats;
67 Imlib_Image *im = NULL;
68
69 if (!filepath)
70 return NULL;
71
72 if (stat(filepath, &fstats))
73 return NULL;
74
75 if ((cfile = tns_cache_filepath(filepath))) {
76 if (!stat(cfile, &cstats) && cstats.st_mtime == fstats.st_mtime)
77 im = imlib_load_image(cfile);
78 free(cfile);
79 }
80
81 return im;
82}
83
84void tns_cache_write(thumb_t *t, Bool force) {
85 char *cfile, *dirend;
86 struct stat cstats, fstats;
87 struct utimbuf times;
88 Imlib_Load_Error err = 0;
89
90 if (!t || !t->im || !t->file || !t->file->name || !t->file->path)
91 return;
92
93 if (stat(t->file->path, &fstats))
94 return;
95
96 if ((cfile = tns_cache_filepath(t->file->path))) {
97 if (force || stat(cfile, &cstats) || cstats.st_mtime != fstats.st_mtime) {
98 if ((dirend = strrchr(cfile, '/'))) {
99 *dirend = '\0';
100 err = r_mkdir(cfile);
101 *dirend = '/';
102 }
103
104 if (!err) {
105 imlib_context_set_image(t->im);
106 imlib_image_set_format("png");
107 imlib_save_image_with_error_return(cfile, &err);
108 }
109
110 if (err) {
111 warn("could not cache thumbnail: %s", t->file->name);
112 } else {
113 times.actime = fstats.st_atime;
114 times.modtime = fstats.st_mtime;
115 utime(cfile, &times);
116 }
117 }
118 free(cfile);
119 }
120}
121
122void tns_clean_cache(tns_t *tns) {
123 int dirlen, delete;
124 char *cfile, *filename, *tpos;
125 r_dir_t dir;
126
127 if (!cache_dir)
128 return;
129
130 if (r_opendir(&dir, cache_dir)) {
131 warn("could not open thumbnail cache directory: %s", cache_dir);
132 return;
133 }
134
135 dirlen = strlen(cache_dir);
136
137 while ((cfile = r_readdir(&dir))) {
138 filename = cfile + dirlen;
139 delete = 0;
140
141 if ((tpos = strrchr(filename, '.'))) {
142 *tpos = '\0';
143 delete = access(filename, F_OK);
144 *tpos = '.';
145 }
146
147 if (delete && unlink(cfile))
148 warn("could not delete cache file: %s", cfile);
149
150 free(cfile);
151 }
152
153 r_closedir(&dir);
154}
155
156
157void tns_init(tns_t *tns, int cnt) {
158 int len;
159 char *homedir;
160
161 if (!tns)
162 return;
163
164 if (cnt) {
165 tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t));
166 memset(tns->thumbs, 0, cnt * sizeof(thumb_t));
167 } else {
168 tns->thumbs = NULL;
169 }
170
171 tns->cnt = tns->first = tns->sel = 0;
172 tns->cap = cnt;
173 tns->alpha = 1;
174 tns->dirty = 0;
175
176 if ((homedir = getenv("HOME"))) {
177 if (cache_dir)
178 free(cache_dir);
179 len = strlen(homedir) + 10;
180 cache_dir = (char*) s_malloc(len * sizeof(char));
181 snprintf(cache_dir, len, "%s/.sxiv", homedir);
182 } else {
183 warn("could not locate thumbnail cache directory");
184 }
185}
186
187void tns_free(tns_t *tns) {
188 int i;
189
190 if (!tns)
191 return;
192
193 if (tns->thumbs) {
194 for (i = 0; i < tns->cnt; i++) {
195 if (tns->thumbs[i].im) {
196 imlib_context_set_image(tns->thumbs[i].im);
197 imlib_free_image();
198 }
199 }
200 free(tns->thumbs);
201 tns->thumbs = NULL;
202 }
203
204 if (cache_dir) {
205 free(cache_dir);
206 cache_dir = NULL;
207 }
208}
209
210int tns_load(tns_t *tns, int n, const fileinfo_t *file,
211 Bool force, Bool silent)
212{
213 int w, h;
214 int use_cache, cache_hit = 0;
215 float z, zw, zh;
216 thumb_t *t;
217 Imlib_Image *im;
218 const char *fmt;
219
220 if (!tns || !tns->thumbs || !file || !file->name || !file->path)
221 return 0;
222
223 if (n < 0 || n >= tns->cap)
224 return 0;
225
226 t = &tns->thumbs[n];
227 t->file = file;
228
229 if (t->im) {
230 imlib_context_set_image(t->im);
231 imlib_free_image();
232 }
233
234 if ((use_cache = tns_cache_enabled())) {
235 if (!force && (im = tns_cache_load(file->path)))
236 cache_hit = 1;
237 }
238
239 if (!cache_hit &&
240 (access(file->path, R_OK) || !(im = imlib_load_image(file->path))))
241 {
242 if (!silent)
243 warn("could not open image: %s", file->name);
244 return 0;
245 }
246
247 imlib_context_set_image(im);
248 imlib_context_set_anti_alias(1);
249
250 fmt = imlib_image_format();
251 /* avoid unused-but-set-variable warning */
252 (void) fmt;
253
254#ifdef EXIF_SUPPORT
255 if (!cache_hit && !strcmp(fmt, "jpeg"))
256 exif_auto_orientate(file);
257#endif
258
259 w = imlib_image_get_width();
260 h = imlib_image_get_height();
261 zw = (float) THUMB_SIZE / (float) w;
262 zh = (float) THUMB_SIZE / (float) h;
263 z = MIN(zw, zh);
264 t->w = z * w;
265 t->h = z * h;
266
267 if (!(t->im = imlib_create_cropped_scaled_image(0, 0, w, h, t->w, t->h)))
268 die("could not allocate memory");
269
270 imlib_free_image_and_decache();
271
272 if (use_cache && !cache_hit)
273 tns_cache_write(t, False);
274
275 tns->dirty = 1;
276 return 1;
277}
278
279void tns_check_view(tns_t *tns, Bool scrolled) {
280 int r;
281
282 if (!tns)
283 return;
284
285 tns->first -= tns->first % tns->cols;
286 r = tns->sel % tns->cols;
287
288 if (scrolled) {
289 /* move selection into visible area */
290 if (tns->sel >= tns->first + tns->cols * tns->rows)
291 tns->sel = tns->first + r + tns->cols * (tns->rows - 1);
292 else if (tns->sel < tns->first)
293 tns->sel = tns->first + r;
294 } else {
295 /* scroll to selection */
296 if (tns->first + tns->cols * tns->rows <= tns->sel) {
297 tns->first = tns->sel - r - tns->cols * (tns->rows - 1);
298 tns->dirty = 1;
299 } else if (tns->first > tns->sel) {
300 tns->first = tns->sel - r;
301 tns->dirty = 1;
302 }
303 }
304}
305
306void tns_render(tns_t *tns, win_t *win) {
307 int i, cnt, r, x, y;
308 thumb_t *t;
309
310 if (!tns || !tns->thumbs || !win)
311 return;
312
313 if (!tns->dirty)
314 return;
315
316 win_clear(win);
317 imlib_context_set_drawable(win->pm);
318
319 tns->cols = MAX(1, win->w / thumb_dim);
320 tns->rows = MAX(1, win->h / thumb_dim);
321
322 if (tns->cnt < tns->cols * tns->rows) {
323 tns->first = 0;
324 cnt = tns->cnt;
325 } else {
326 tns_check_view(tns, False);
327 cnt = tns->cols * tns->rows;
328 if ((r = tns->first + cnt - tns->cnt) >= tns->cols)
329 tns->first -= r - r % tns->cols;
330 if (r > 0)
331 cnt -= r % tns->cols;
332 }
333
334 r = cnt % tns->cols ? 1 : 0;
335 tns->x = x = (win->w - MIN(cnt, tns->cols) * thumb_dim) / 2 + 5;
336 tns->y = y = (win->h - (cnt / tns->cols + r) * thumb_dim) / 2 + 5;
337
338 for (i = 0; i < cnt; i++) {
339 t = &tns->thumbs[tns->first + i];
340 t->x = x + (THUMB_SIZE - t->w) / 2;
341 t->y = y + (THUMB_SIZE - t->h) / 2;
342 imlib_context_set_image(t->im);
343
344 if (imlib_image_has_alpha() && !tns->alpha)
345 win_draw_rect(win, win->pm, t->x, t->y, t->w, t->h, True, 0, win->white);
346
347 imlib_render_image_part_on_drawable_at_size(0, 0, t->w, t->h,
348 t->x, t->y, t->w, t->h);
349 if ((i + 1) % tns->cols == 0) {
350 x = tns->x;
351 y += thumb_dim;
352 } else {
353 x += thumb_dim;
354 }
355 }
356
357 tns->dirty = 0;
358 tns_highlight(tns, win, tns->sel, True);
359}
360
361void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) {
362 thumb_t *t;
363 int x, y;
364 unsigned long col;
365
366 if (!tns || !tns->thumbs || !win)
367 return;
368
369 if (n >= 0 && n < tns->cnt) {
370 t = &tns->thumbs[n];
371
372 if (hl)
373 col = win->selcol;
374 else if (win->fullscreen)
375 col = win->black;
376 else
377 col = win->bgcol;
378
379 x = t->x - (THUMB_SIZE - t->w) / 2;
380 y = t->y - (THUMB_SIZE - t->h) / 2;
381 win_draw_rect(win, win->pm, x - 3, y - 3, THUMB_SIZE + 6, THUMB_SIZE + 6,
382 False, 2, col);
383 }
384
385 win_draw(win);
386}
387
388int tns_move_selection(tns_t *tns, win_t *win, direction_t dir) {
389 int old;
390
391 if (!tns || !tns->thumbs || !win)
392 return 0;
393
394 old = tns->sel;
395
396 switch (dir) {
397 case DIR_LEFT:
398 if (tns->sel > 0)
399 tns->sel--;
400 break;
401 case DIR_RIGHT:
402 if (tns->sel < tns->cnt - 1)
403 tns->sel++;
404 break;
405 case DIR_UP:
406 if (tns->sel >= tns->cols)
407 tns->sel -= tns->cols;
408 break;
409 case DIR_DOWN:
410 if (tns->sel + tns->cols < tns->cnt)
411 tns->sel += tns->cols;
412 break;
413 }
414
415 if (tns->sel != old) {
416 tns_highlight(tns, win, old, False);
417 tns_check_view(tns, False);
418 if (!tns->dirty)
419 tns_highlight(tns, win, tns->sel, True);
420 }
421
422 return tns->sel != old;
423}
424
425int tns_scroll(tns_t *tns, direction_t dir) {
426 int old;
427
428 if (!tns)
429 return 0;
430
431 old = tns->first;
432
433 if (dir == DIR_DOWN && tns->first + tns->cols * tns->rows < tns->cnt) {
434 tns->first += tns->cols;
435 tns_check_view(tns, True);
436 tns->dirty = 1;
437 } else if (dir == DIR_UP && tns->first >= tns->cols) {
438 tns->first -= tns->cols;
439 tns_check_view(tns, True);
440 tns->dirty = 1;
441 }
442
443 return tns->first != old;
444}
445
446int tns_translate(tns_t *tns, int x, int y) {
447 int n;
448
449 if (!tns || !tns->thumbs)
450 return -1;
451
452 if (x < tns->x || y < tns->y)
453 return -1;
454
455 n = tns->first + (y - tns->y) / thumb_dim * tns->cols +
456 (x - tns->x) / thumb_dim;
457 if (n >= tns->cnt)
458 n = -1;
459
460 return n;
461}