A Simple X Image Viewer
 
 
 
 
 
 

835 lines
17 KiB

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