Krolyxon
Commit f93f4d8

Branches

Refs →

nsxivthumbs.c

407 lines · 8.7 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
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19#include <stdlib.h>
20#include <string.h>
21#include <sys/time.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25
26#include "config.h"
27#include "thumbs.h"
28#include "util.h"
29
30extern Imlib_Image *im_invalid;
31const int thumb_dim = THUMB_SIZE + 10;
32
33int tns_cache_enabled();
34Imlib_Image* tns_cache_load(const char*);
35void tns_cache_write(thumb_t*, Bool);
36
37void tns_init(tns_t *tns, int cnt) {
38 if (!tns)
39 return;
40
41 tns->cnt = tns->first = tns->sel = 0;
42 tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t));
43 memset(tns->thumbs, 0, cnt * sizeof(thumb_t));
44 tns->cap = cnt;
45 tns->dirty = 0;
46}
47
48void tns_free(tns_t *tns, win_t *win) {
49 int i;
50
51 if (!tns || !tns->thumbs)
52 return;
53
54 for (i = 0; i < tns->cnt; ++i) {
55 if (tns->thumbs[i].im) {
56 imlib_context_set_image(tns->thumbs[i].im);
57 imlib_free_image();
58 }
59 }
60
61 free(tns->thumbs);
62 tns->thumbs = NULL;
63}
64
65void tns_load(tns_t *tns, win_t *win, int n, const char *filename) {
66 int w, h;
67 int use_cache, cached = 0;
68 float z, zw, zh;
69 thumb_t *t;
70 Imlib_Image *im;
71
72 if (!tns || !win || !filename)
73 return;
74
75 if (n >= tns->cap)
76 return;
77 else if (n >= tns->cnt)
78 tns->cnt = n + 1;
79
80 t = &tns->thumbs[n];
81
82 if (t->im) {
83 imlib_context_set_image(t->im);
84 imlib_free_image();
85 }
86
87 if ((use_cache = tns_cache_enabled())) {
88 if ((im = tns_cache_load(filename)))
89 cached = 1;
90 }
91
92 if (cached || (im = imlib_load_image(filename)))
93 imlib_context_set_image(im);
94 else
95 imlib_context_set_image(im_invalid);
96
97 w = imlib_image_get_width();
98 h = imlib_image_get_height();
99
100 if (im) {
101 t->filename = filename;
102 zw = (float) THUMB_SIZE / (float) w;
103 zh = (float) THUMB_SIZE / (float) h;
104 z = MIN(zw, zh);
105 } else {
106 t->filename = NULL;
107 z = 1.0;
108 }
109
110 t->w = z * w;
111 t->h = z * h;
112
113 imlib_context_set_anti_alias(1);
114 if (!(t->im = imlib_create_cropped_scaled_image(0, 0, w, h, t->w, t->h)))
115 die("could not allocate memory");
116 if (im)
117 imlib_free_image_and_decache();
118 if (use_cache && !cached)
119 tns_cache_write(t, False);
120
121 tns->dirty = 1;
122}
123
124void tns_check_view(tns_t *tns, Bool scrolled) {
125 int r;
126
127 if (!tns)
128 return;
129
130 tns->first -= tns->first % tns->cols;
131 r = tns->sel % tns->cols;
132
133 if (scrolled) {
134 /* move selection into visible area */
135 if (tns->sel >= tns->first + tns->cols * tns->rows)
136 tns->sel = tns->first + r + tns->cols * (tns->rows - 1);
137 else if (tns->sel < tns->first)
138 tns->sel = tns->first + r;
139 } else {
140 /* scroll to selection */
141 if (tns->first + tns->cols * tns->rows <= tns->sel) {
142 tns->first = tns->sel - r - tns->cols * (tns->rows - 1);
143 tns->dirty = 1;
144 } else if (tns->first > tns->sel) {
145 tns->first = tns->sel - r;
146 tns->dirty = 1;
147 }
148 }
149}
150
151void tns_render(tns_t *tns, win_t *win) {
152 int i, cnt, r, x, y;
153 thumb_t *t;
154
155 if (!tns || !tns->dirty || !win)
156 return;
157
158 win_clear(win);
159 imlib_context_set_drawable(win->pm);
160
161 tns->cols = MAX(1, win->w / thumb_dim);
162 tns->rows = MAX(1, win->h / thumb_dim);
163
164 if (tns->cnt < tns->cols * tns->rows) {
165 tns->first = 0;
166 cnt = tns->cnt;
167 } else {
168 tns_check_view(tns, False);
169 cnt = tns->cols * tns->rows;
170 if ((r = tns->first + cnt - tns->cnt) >= tns->cols)
171 tns->first -= r - r % tns->cols;
172 if (r > 0)
173 cnt -= r % tns->cols;
174 }
175
176 r = cnt % tns->cols ? 1 : 0;
177 tns->x = x = (win->w - MIN(cnt, tns->cols) * thumb_dim) / 2 + 5;
178 tns->y = y = (win->h - (cnt / tns->cols + r) * thumb_dim) / 2 + 5;
179
180 for (i = 0; i < cnt; ++i) {
181 t = &tns->thumbs[tns->first + i];
182 t->x = x + (THUMB_SIZE - t->w) / 2;
183 t->y = y + (THUMB_SIZE - t->h) / 2;
184 imlib_context_set_image(t->im);
185 imlib_render_image_part_on_drawable_at_size(0, 0, t->w, t->h,
186 t->x, t->y, t->w, t->h);
187 if ((i + 1) % tns->cols == 0) {
188 x = tns->x;
189 y += thumb_dim;
190 } else {
191 x += thumb_dim;
192 }
193 }
194
195 tns->dirty = 0;
196 tns_highlight(tns, win, tns->sel, True);
197}
198
199void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) {
200 thumb_t *t;
201 unsigned long col;
202
203 if (!tns || !win)
204 return;
205
206 if (n >= 0 && n < tns->cnt) {
207 t = &tns->thumbs[n];
208
209 if (hl)
210 col = win->selcol;
211 else if (win->fullscreen)
212 col = win->black;
213 else
214 col = win->bgcol;
215
216 win_draw_rect(win, win->pm, t->x - 2, t->y - 2, t->w + 4, t->h + 4,
217 False, 2, col);
218 }
219
220 win_draw(win);
221}
222
223int tns_move_selection(tns_t *tns, win_t *win, tnsdir_t dir) {
224 int old;
225
226 if (!tns || !win)
227 return 0;
228
229 old = tns->sel;
230
231 switch (dir) {
232 case TNS_LEFT:
233 if (tns->sel > 0)
234 --tns->sel;
235 break;
236 case TNS_RIGHT:
237 if (tns->sel < tns->cnt - 1)
238 ++tns->sel;
239 break;
240 case TNS_UP:
241 if (tns->sel >= tns->cols)
242 tns->sel -= tns->cols;
243 break;
244 case TNS_DOWN:
245 if (tns->sel + tns->cols < tns->cnt)
246 tns->sel += tns->cols;
247 break;
248 }
249
250 if (tns->sel != old) {
251 tns_highlight(tns, win, old, False);
252 tns_check_view(tns, False);
253 if (!tns->dirty)
254 tns_highlight(tns, win, tns->sel, True);
255 }
256
257 return tns->sel != old;
258}
259
260int tns_scroll(tns_t *tns, tnsdir_t dir) {
261 int old;
262
263 if (!tns)
264 return 0;
265
266 old = tns->first;
267
268 if (dir == TNS_DOWN && tns->first + tns->cols * tns->rows < tns->cnt) {
269 tns->first += tns->cols;
270 tns_check_view(tns, True);
271 tns->dirty = 1;
272 } else if (dir == TNS_UP && tns->first >= tns->cols) {
273 tns->first -= tns->cols;
274 tns_check_view(tns, True);
275 tns->dirty = 1;
276 }
277
278 return tns->first != old;
279}
280
281int tns_translate(tns_t *tns, int x, int y) {
282 int n;
283 thumb_t *t;
284
285 if (!tns || x < tns->x || y < tns->y)
286 return -1;
287
288 n = tns->first + (y - tns->y) / thumb_dim * tns->cols +
289 (x - tns->x) / thumb_dim;
290
291 if (n < tns->cnt) {
292 t = &tns->thumbs[n];
293 if (x >= t->x && x <= t->x + t->w && y >= t->y && y <= t->y + t->h)
294 return n;
295 }
296
297 return -1;
298}
299
300
301/* thumbnail caching */
302
303int tns_cache_enabled() {
304 int len, ret = 0;
305 char *cpath, *homedir;
306 struct stat stats;
307
308 if ((homedir = getenv("HOME"))) {
309 len = strlen(homedir) + 10;
310 cpath = (char*) s_malloc(len * sizeof(char));
311 snprintf(cpath, len, "%s/.sxiv", homedir);
312 ret = !stat(cpath, &stats) && S_ISDIR(stats.st_mode) &&
313 !access(cpath, W_OK);
314 free(cpath);
315 }
316
317 return ret;
318}
319
320char* tns_cache_filename(const char *filename) {
321 size_t len;
322 int i;
323 char *cfile, *abspath, *homedir;
324
325 if (!filename)
326 return NULL;
327 if (!(homedir = getenv("HOME")))
328 return NULL;
329
330 if (*filename != '/') {
331 if (!(abspath = absolute_path(filename)))
332 return NULL;
333 } else {
334 abspath = (char*) s_malloc(strlen(filename) + 1);
335 strcpy(abspath, filename);
336 }
337
338 len = strlen(abspath);
339 for (i = 0; i < len; ++i) {
340 if (abspath[i] == '/')
341 abspath[i] = '%';
342 }
343
344 len += strlen(homedir) + 15;
345 cfile = (char*) s_malloc(len);
346 snprintf(cfile, len, "%s/.sxiv/%s.png", homedir, abspath + 1);
347
348 free(abspath);
349
350 return cfile;
351}
352
353Imlib_Image* tns_cache_load(const char *filename) {
354 char *cfile;
355 struct stat cstats, fstats;
356 Imlib_Image *im = NULL;
357
358 if (!filename || stat(filename, &fstats))
359 return NULL;
360
361 if ((cfile = tns_cache_filename(filename))) {
362 if (!stat(cfile, &cstats) &&
363 cstats.st_mtim.tv_sec == fstats.st_mtim.tv_sec &&
364 cstats.st_mtim.tv_nsec == fstats.st_mtim.tv_nsec)
365 {
366 printf("cache hit: %s\n", filename);
367 im = imlib_load_image(cfile);
368 } else
369 printf("cache MISS: %s\n", filename);
370 free(cfile);
371 }
372
373 return im;
374}
375
376void tns_cache_write(thumb_t *t, Bool force) {
377 char *cfile;
378 struct stat cstats, fstats;
379 struct timeval times[2];
380 Imlib_Load_Error err;
381
382 if (!t || !t->im || !t->filename)
383 return;
384 if (stat(t->filename, &fstats))
385 return;
386
387 if ((cfile = tns_cache_filename(t->filename))) {
388 if (force || stat(cfile, &cstats) ||
389 cstats.st_mtim.tv_sec != fstats.st_mtim.tv_sec ||
390 cstats.st_mtim.tv_nsec != fstats.st_mtim.tv_nsec)
391 {
392 imlib_context_set_image(t->im);
393 imlib_image_set_format("png");
394 imlib_save_image_with_error_return(cfile, &err);
395
396 if (err) {
397 warn("could not cache thumbnail: %s", t->filename);
398 } else {
399 TIMESPEC_TO_TIMEVAL(&times[0], &fstats.st_atim);
400 TIMESPEC_TO_TIMEVAL(&times[1], &fstats.st_mtim);
401 utimes(cfile, times);
402 printf("thumbnail cache file written: %s\n", t->filename);
403 }
404 }
405 free(cfile);
406 }
407}