Krolyxon
Commit 79bd86d

Branches

Refs →

nsxivimage.c

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