A Simple X Image Viewer
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 
 

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