Krolyxon
Commit b2a2a62

Branches

Refs →

nsxivimage.c

708 lines · 15.0 KB · C

1/* sxiv: image.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#define _IMAGE_CONFIG
21
22#include <string.h>
23#include <unistd.h>
24
25#ifdef EXIF_SUPPORT
26#include <libexif/exif-data.h>
27#endif
28
29#ifdef GIF_SUPPORT
30#include <stdlib.h>
31#include <sys/types.h>
32#include <gif_lib.h>
33#endif
34
35#include "image.h"
36#include "options.h"
37#include "util.h"
38#include "config.h"
39
40#define ZOOMDIFF(z1,z2) ((z1) - (z2) > 0.001 || (z1) - (z2) < -0.001)
41
42enum { MIN_GIF_DELAY = 50 };
43
44float zoom_min;
45float zoom_max;
46
47void img_init(img_t *img, win_t *win) {
48 zoom_min = zoom_levels[0] / 100.0;
49 zoom_max = zoom_levels[ARRLEN(zoom_levels) - 1] / 100.0;
50
51 if (img) {
52 img->im = NULL;
53 img->multi.cap = img->multi.cnt = 0;
54 img->multi.animate = false;
55 img->zoom = options->zoom;
56 img->zoom = MAX(img->zoom, zoom_min);
57 img->zoom = MIN(img->zoom, zoom_max);
58 img->checkpan = false;
59 img->dirty = false;
60 img->aa = options->aa;
61 img->alpha = true;
62 img->slideshow = false;
63 img->ss_delay = SLIDESHOW_DELAY * 1000;
64 }
65
66 if (win) {
67 imlib_context_set_display(win->env.dpy);
68 imlib_context_set_visual(win->env.vis);
69 imlib_context_set_colormap(win->env.cmap);
70 }
71}
72
73#ifdef EXIF_SUPPORT
74void exif_auto_orientate(const fileinfo_t *file) {
75 ExifData *ed;
76 ExifEntry *entry;
77 int byte_order, orientation;
78
79 if (!(ed = exif_data_new_from_file(file->path)))
80 return;
81 entry = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION);
82 if (entry) {
83 byte_order = exif_data_get_byte_order(ed);
84 orientation = exif_get_short(entry->data, byte_order);
85 }
86 exif_data_unref(ed);
87 if (!entry)
88 return;
89
90 switch (orientation) {
91 case 5:
92 imlib_image_orientate(1);
93 case 2:
94 imlib_image_flip_vertical();
95 break;
96
97 case 3:
98 imlib_image_orientate(2);
99 break;
100
101 case 7:
102 imlib_image_orientate(1);
103 case 4:
104 imlib_image_flip_horizontal();
105 break;
106
107 case 6:
108 imlib_image_orientate(1);
109 break;
110
111 case 8:
112 imlib_image_orientate(270);
113 break;
114 }
115}
116#endif /* EXIF_SUPPORT */
117
118#ifdef GIF_SUPPORT
119/* Originally based on, but in its current form merely inspired by Imlib2's
120 * src/modules/loaders/loader_gif.c:load(), written by Carsten Haitzler.
121 */
122bool img_load_gif(img_t *img, const fileinfo_t *file) {
123 GifFileType *gif;
124 GifRowType *rows = NULL;
125 GifRecordType rec;
126 ColorMapObject *cmap;
127 DATA32 bgpixel, *data, *ptr;
128 DATA32 *prev_frame = NULL;
129 Imlib_Image *im;
130 int i, j, bg, r, g, b;
131 int x, y, w, h, sw, sh;
132 int intoffset[] = { 0, 4, 2, 1 };
133 int intjump[] = { 8, 8, 4, 2 };
134 int transp = -1;
135 unsigned int delay = 0;
136 bool err = false;
137
138 if (img->multi.cap == 0) {
139 img->multi.cap = 8;
140 img->multi.frames = (img_frame_t*)
141 s_malloc(sizeof(img_frame_t) * img->multi.cap);
142 }
143 img->multi.cnt = 0;
144 img->multi.sel = 0;
145
146 gif = DGifOpenFileName(file->path);
147 if (!gif) {
148 warn("could not open gif file: %s", file->name);
149 return false;
150 }
151 bg = gif->SBackGroundColor;
152 sw = gif->SWidth;
153 sh = gif->SHeight;
154
155 do {
156 if (DGifGetRecordType(gif, &rec) == GIF_ERROR) {
157 err = true;
158 break;
159 }
160 if (rec == EXTENSION_RECORD_TYPE) {
161 int ext_code;
162 GifByteType *ext = NULL;
163
164 DGifGetExtension(gif, &ext_code, &ext);
165 while (ext) {
166 if (ext_code == 0xf9) {
167 if (ext[1] & 1)
168 transp = (int) ext[4];
169 else
170 transp = -1;
171
172 delay = 10 * ((unsigned int) ext[3] << 8 | (unsigned int) ext[2]);
173 if (delay)
174 delay = MAX(delay, MIN_GIF_DELAY);
175 }
176 ext = NULL;
177 DGifGetExtensionNext(gif, &ext);
178 }
179 } else if (rec == IMAGE_DESC_RECORD_TYPE) {
180 if (DGifGetImageDesc(gif) == GIF_ERROR) {
181 err = true;
182 break;
183 }
184 x = gif->Image.Left;
185 y = gif->Image.Top;
186 w = gif->Image.Width;
187 h = gif->Image.Height;
188
189 rows = (GifRowType*) s_malloc(h * sizeof(GifRowType));
190 for (i = 0; i < h; i++)
191 rows[i] = (GifRowType) s_malloc(w * sizeof(GifPixelType));
192 if (gif->Image.Interlace) {
193 for (i = 0; i < 4; i++) {
194 for (j = intoffset[i]; j < h; j += intjump[i])
195 DGifGetLine(gif, rows[j], w);
196 }
197 } else {
198 for (i = 0; i < h; i++)
199 DGifGetLine(gif, rows[i], w);
200 }
201
202 ptr = data = (DATA32*) s_malloc(sizeof(DATA32) * sw * sh);
203 cmap = gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
204 r = cmap->Colors[bg].Red;
205 g = cmap->Colors[bg].Green;
206 b = cmap->Colors[bg].Blue;
207 bgpixel = 0x00ffffff & (r << 16 | g << 8 | b);
208
209 for (i = 0; i < sh; i++) {
210 for (j = 0; j < sw; j++) {
211 if (i < y || i >= y + h || j < x || j >= x + w) {
212 if (transp >= 0 && prev_frame)
213 *ptr = prev_frame[i * sw + j];
214 else
215 *ptr = bgpixel;
216 } else if (rows[i-y][j-x] == transp) {
217 if (prev_frame)
218 *ptr = prev_frame[i * sw + j];
219 else
220 *ptr = bgpixel;
221 } else {
222 r = cmap->Colors[rows[i-y][j-x]].Red;
223 g = cmap->Colors[rows[i-y][j-x]].Green;
224 b = cmap->Colors[rows[i-y][j-x]].Blue;
225 *ptr = 0xff << 24 | r << 16 | g << 8 | b;
226 }
227 ptr++;
228 }
229 }
230
231 im = imlib_create_image_using_copied_data(sw, sh, data);
232
233 for (i = 0; i < h; i++)
234 free(rows[i]);
235 free(rows);
236 free(data);
237
238 if (!im) {
239 err = true;
240 break;
241 }
242
243 imlib_context_set_image(im);
244 prev_frame = imlib_image_get_data_for_reading_only();
245
246 imlib_image_set_format("gif");
247 if (transp >= 0)
248 imlib_image_set_has_alpha(1);
249
250 if (img->multi.cnt == img->multi.cap) {
251 img->multi.cap *= 2;
252 img->multi.frames = (img_frame_t*)
253 s_realloc(img->multi.frames,
254 img->multi.cap * sizeof(img_frame_t));
255 }
256 img->multi.frames[img->multi.cnt].im = im;
257 img->multi.frames[img->multi.cnt].delay = delay ? delay : GIF_DELAY;
258 img->multi.cnt++;
259 }
260 } while (rec != TERMINATE_RECORD_TYPE);
261
262 DGifCloseFile(gif);
263
264 if (err && !file->loaded)
265 warn("corrupted gif file: %s", file->name);
266
267 if (img->multi.cnt > 1) {
268 imlib_context_set_image(img->im);
269 imlib_free_image();
270 img->im = img->multi.frames[0].im;
271 img->multi.animate = GIF_AUTOPLAY;
272 } else if (img->multi.cnt == 1) {
273 imlib_context_set_image(img->multi.frames[0].im);
274 imlib_free_image();
275 img->multi.cnt = 0;
276 img->multi.animate = false;
277 }
278
279 imlib_context_set_image(img->im);
280
281 return !err;
282}
283#endif /* GIF_SUPPORT */
284
285bool img_load(img_t *img, const fileinfo_t *file) {
286 const char *fmt;
287
288 if (!img || !file || !file->name || !file->path)
289 return false;
290
291 if (access(file->path, R_OK) || !(img->im = imlib_load_image(file->path))) {
292 warn("could not open image: %s", file->name);
293 return false;
294 }
295
296 imlib_context_set_image(img->im);
297 imlib_image_set_changes_on_disk();
298 imlib_context_set_anti_alias(img->aa);
299
300 fmt = imlib_image_format();
301 /* avoid unused-but-set-variable warning */
302 (void) fmt;
303
304#ifdef EXIF_SUPPORT
305 if (!strcmp(fmt, "jpeg"))
306 exif_auto_orientate(file);
307#endif
308#ifdef GIF_SUPPORT
309 if (!strcmp(fmt, "gif"))
310 img_load_gif(img, file);
311#endif
312
313 img->scalemode = options->scalemode;
314 img->re = false;
315 img->checkpan = false;
316 img->dirty = true;
317
318 img->w = imlib_image_get_width();
319 img->h = imlib_image_get_height();
320
321 return true;
322}
323
324void img_close(img_t *img, bool decache) {
325 int i;
326
327 if (!img)
328 return;
329
330 if (img->multi.cnt) {
331 for (i = 0; i < img->multi.cnt; i++) {
332 imlib_context_set_image(img->multi.frames[i].im);
333 imlib_free_image();
334 }
335 img->multi.cnt = 0;
336 img->im = NULL;
337 } else if (img->im) {
338 imlib_context_set_image(img->im);
339 if (decache)
340 imlib_free_image_and_decache();
341 else
342 imlib_free_image();
343 img->im = NULL;
344 }
345}
346
347void img_check_pan(img_t *img, win_t *win, bool moved) {
348 int ox, oy;
349
350 if (!img || !win)
351 return;
352
353 ox = img->x;
354 oy = img->y;
355
356 if (img->w * img->zoom > win->w) {
357 if (img->x > 0 && img->x + img->w * img->zoom > win->w)
358 img->x = 0;
359 if (img->x < 0 && img->x + img->w * img->zoom < win->w)
360 img->x = win->w - img->w * img->zoom;
361 } else {
362 img->x = (win->w - img->w * img->zoom) / 2;
363 }
364 if (img->h * img->zoom > win->h) {
365 if (img->y > 0 && img->y + img->h * img->zoom > win->h)
366 img->y = 0;
367 if (img->y < 0 && img->y + img->h * img->zoom < win->h)
368 img->y = win->h - img->h * img->zoom;
369 } else {
370 img->y = (win->h - img->h * img->zoom) / 2;
371 }
372
373 if (!moved && (ox != img->x || oy != img->y))
374 img->dirty = true;
375}
376
377bool img_fit(img_t *img, win_t *win) {
378 float z, zmax, zw, zh;
379
380 if (!img || !win || img->scalemode == SCALE_ZOOM)
381 return false;
382
383 zmax = img->scalemode == SCALE_DOWN ? 1.0 : zoom_max;
384 zw = (float) win->w / (float) img->w;
385 zh = (float) win->h / (float) img->h;
386
387 z = MIN(zw, zh);
388 z = MAX(z, zoom_min);
389 z = MIN(z, zmax);
390
391 if (ZOOMDIFF(z, img->zoom)) {
392 img->zoom = z;
393 img->dirty = true;
394 return true;
395 } else {
396 return false;
397 }
398}
399
400void img_render(img_t *img, win_t *win) {
401 int sx, sy, sw, sh;
402 int dx, dy, dw, dh;
403
404 if (!img || !img->im || !win)
405 return;
406
407 img_fit(img, win);
408
409 if (!img->re) {
410 /* rendered for the first time */
411 img->re = true;
412 if (img->zoom * img->w <= win->w)
413 img->x = (win->w - img->w * img->zoom) / 2;
414 else
415 img->x = 0;
416 if (img->zoom * img->h <= win->h)
417 img->y = (win->h - img->h * img->zoom) / 2;
418 else
419 img->y = 0;
420 }
421
422 if (img->checkpan) {
423 img_check_pan(img, win, false);
424 img->checkpan = false;
425 }
426
427 if (!img->dirty)
428 return;
429
430 /* calculate source and destination offsets */
431 if (img->x < 0) {
432 sx = -img->x / img->zoom;
433 sw = win->w / img->zoom;
434 dx = 0;
435 dw = win->w;
436 } else {
437 sx = 0;
438 sw = img->w;
439 dx = img->x;
440 dw = img->w * img->zoom;
441 }
442 if (img->y < 0) {
443 sy = -img->y / img->zoom;
444 sh = win->h / img->zoom;
445 dy = 0;
446 dh = win->h;
447 } else {
448 sy = 0;
449 sh = img->h;
450 dy = img->y;
451 dh = img->h * img->zoom;
452 }
453
454 win_clear(win);
455
456 imlib_context_set_image(img->im);
457
458 if (imlib_image_has_alpha() && !img->alpha)
459 win_draw_rect(win, win->pm, dx, dy, dw, dh, True, 0, win->white);
460
461 imlib_context_set_drawable(win->pm);
462 imlib_render_image_part_on_drawable_at_size(sx, sy, sw, sh, dx, dy, dw, dh);
463
464 win_draw(win);
465
466 img->dirty = false;
467}
468
469bool img_fit_win(img_t *img, win_t *win) {
470 if (!img || !img->im || !win)
471 return false;
472
473 img->scalemode = SCALE_FIT;
474 return img_fit(img, win);
475}
476
477bool img_center(img_t *img, win_t *win) {
478 int ox, oy;
479
480 if (!img || !win)
481 return false;
482
483 ox = img->x;
484 oy = img->y;
485
486 img->x = (win->w - img->w * img->zoom) / 2;
487 img->y = (win->h - img->h * img->zoom) / 2;
488
489 if (ox != img->x || oy != img->y) {
490 img->dirty = true;
491 return true;
492 } else {
493 return false;
494 }
495}
496
497bool img_zoom(img_t *img, win_t *win, float z) {
498 if (!img || !img->im || !win)
499 return false;
500
501 z = MAX(z, zoom_min);
502 z = MIN(z, zoom_max);
503
504 img->scalemode = SCALE_ZOOM;
505
506 if (ZOOMDIFF(z, img->zoom)) {
507 img->x = win->w / 2 - (win->w / 2 - img->x) * z / img->zoom;
508 img->y = win->h / 2 - (win->h / 2 - img->y) * z / img->zoom;
509 img->zoom = z;
510 img->checkpan = true;
511 img->dirty = true;
512 return true;
513 } else {
514 return false;
515 }
516}
517
518bool img_zoom_in(img_t *img, win_t *win) {
519 int i;
520
521 if (!img || !img->im || !win)
522 return false;
523
524 for (i = 1; i < ARRLEN(zoom_levels); i++) {
525 if (zoom_levels[i] > img->zoom * 100.0)
526 return img_zoom(img, win, zoom_levels[i] / 100.0);
527 }
528 return false;
529}
530
531bool img_zoom_out(img_t *img, win_t *win) {
532 int i;
533
534 if (!img || !img->im || !win)
535 return false;
536
537 for (i = ARRLEN(zoom_levels) - 2; i >= 0; i--) {
538 if (zoom_levels[i] < img->zoom * 100.0)
539 return img_zoom(img, win, zoom_levels[i] / 100.0);
540 }
541 return false;
542}
543
544bool img_move(img_t *img, win_t *win, int dx, int dy) {
545 int ox, oy;
546
547 if (!img || !img->im || !win)
548 return false;
549
550 ox = img->x;
551 oy = img->y;
552
553 img->x += dx;
554 img->y += dy;
555
556 img_check_pan(img, win, true);
557
558 if (ox != img->x || oy != img->y) {
559 img->dirty = true;
560 return true;
561 } else {
562 return false;
563 }
564}
565
566bool img_pan(img_t *img, win_t *win, direction_t dir, bool screen) {
567 if (!img || !img->im || !win)
568 return false;
569
570 switch (dir) {
571 case DIR_LEFT:
572 return img_move(img, win, win->w / (screen ? 1 : 5), 0);
573 case DIR_RIGHT:
574 return img_move(img, win, win->w / (screen ? 1 : 5) * -1, 0);
575 case DIR_UP:
576 return img_move(img, win, 0, win->h / (screen ? 1 : 5));
577 case DIR_DOWN:
578 return img_move(img, win, 0, win->h / (screen ? 1 : 5) * -1);
579 }
580 return false;
581}
582
583bool img_pan_edge(img_t *img, win_t *win, direction_t dir) {
584 int ox, oy;
585
586 if (!img || !img->im || !win)
587 return false;
588
589 ox = img->x;
590 oy = img->y;
591
592 switch (dir) {
593 case DIR_LEFT:
594 img->x = 0;
595 break;
596 case DIR_RIGHT:
597 img->x = win->w - img->w * img->zoom;
598 break;
599 case DIR_UP:
600 img->y = 0;
601 break;
602 case DIR_DOWN:
603 img->y = win->h - img->h * img->zoom;
604 break;
605 }
606
607 img_check_pan(img, win, true);
608
609 if (ox != img->x || oy != img->y) {
610 img->dirty = true;
611 return true;
612 } else {
613 return false;
614 }
615}
616
617void img_rotate(img_t *img, win_t *win, int d) {
618 int ox, oy, tmp;
619
620 if (!img || !img->im || !win)
621 return;
622
623 ox = d == 1 ? img->x : win->w - img->x - img->w * img->zoom;
624 oy = d == 3 ? img->y : win->h - img->y - img->h * img->zoom;
625
626 imlib_context_set_image(img->im);
627 imlib_image_orientate(d);
628
629 img->x = oy + (win->w - win->h) / 2;
630 img->y = ox + (win->h - win->w) / 2;
631
632 tmp = img->w;
633 img->w = img->h;
634 img->h = tmp;
635
636 img->checkpan = true;
637 img->dirty = true;
638}
639
640void img_rotate_left(img_t *img, win_t *win) {
641 img_rotate(img, win, 3);
642}
643
644void img_rotate_right(img_t *img, win_t *win) {
645 img_rotate(img, win, 1);
646}
647
648void img_toggle_antialias(img_t *img) {
649 if (!img || !img->im)
650 return;
651
652 img->aa = !img->aa;
653 imlib_context_set_image(img->im);
654 imlib_context_set_anti_alias(img->aa);
655 img->dirty = true;
656}
657
658bool img_frame_goto(img_t *img, int n) {
659 if (!img || n < 0 || n >= img->multi.cnt)
660 return false;
661
662 if (n == img->multi.sel)
663 return false;
664
665 img->multi.sel = n;
666 img->im = img->multi.frames[n].im;
667
668 imlib_context_set_image(img->im);
669 img->w = imlib_image_get_width();
670 img->h = imlib_image_get_height();
671 img->checkpan = true;
672 img->dirty = true;
673
674 return true;
675}
676
677bool img_frame_navigate(img_t *img, int d) {
678 if (!img || !img->multi.cnt || !d)
679 return false;
680
681 d += img->multi.sel;
682 if (d < 0)
683 d = 0;
684 else if (d >= img->multi.cnt)
685 d = img->multi.cnt - 1;
686
687 return img_frame_goto(img, d);
688}
689
690bool img_frame_animate(img_t *img, bool restart) {
691 if (!img || !img->multi.cnt)
692 return false;
693
694 if (img->multi.sel + 1 >= img->multi.cnt) {
695 if (restart || (GIF_LOOP && !img->slideshow)) {
696 img_frame_goto(img, 0);
697 } else {
698 img->multi.animate = false;
699 return false;
700 }
701 } else if (!restart) {
702 img_frame_goto(img, img->multi.sel + 1);
703 }
704 img->multi.animate = true;
705 img->dirty = true;
706
707 return true;
708}