Krolyxon
Commit 86a6f00

Branches

Refs →

nsxivimage.c

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