Krolyxon
Commit 8b3ae50

Branches

Refs →

nsxivimage.c

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