nsxivd26f399
Use thumbnails in EXIF tags; requirement for libexif is back
+107 −221
6 files changed
- M Makefile +4 −4
- M README.md +5 −5
- D exif.c +0 −141
- D exif.h +0 −42
- M image.c +32 −16
- M thumbs.c +66 −13
| @@ -1,15 +1,15 @@ | |||
| 1 | − | VERSION = git-20140531 | |
| 1 | + | VERSION = git-20140609 | |
| 2 | 2 | ||
| 3 | 3 | PREFIX = /usr/local | |
| 4 | 4 | MANPREFIX = $(PREFIX)/share/man | |
| 5 | 5 | ||
| 6 | 6 | CC = gcc | |
| 7 | 7 | CFLAGS = -std=c99 -Wall -pedantic -O2 | |
| 8 | − | CPPFLAGS = -I$(PREFIX)/include -D_XOPEN_SOURCE=500 -DHAVE_GIFLIB | |
| 8 | + | CPPFLAGS = -I$(PREFIX)/include -D_XOPEN_SOURCE=500 -DHAVE_LIBEXIF -DHAVE_GIFLIB | |
| 9 | 9 | LDFLAGS = -L$(PREFIX)/lib | |
| 10 | − | LIBS = -lX11 -lImlib2 -lgif | |
| 10 | + | LIBS = -lX11 -lImlib2 -lexif -lgif | |
| 11 | 11 | ||
| 12 | − | SRC = commands.c exif.c image.c main.c options.c thumbs.c util.c window.c | |
| 12 | + | SRC = commands.c image.c main.c options.c thumbs.c util.c window.c | |
| 13 | 13 | OBJ = $(SRC:.c=.o) | |
| 14 | 14 | ||
| 15 | 15 | all: sxiv | |
| @@ -3,11 +3,11 @@ | |||
| 3 | 3 | **Simple X Image Viewer** | |
| 4 | 4 | ||
| 5 | 5 | sxiv is an alternative to feh and qiv. Its only dependencies besides xlib are | |
| 6 | − | imlib2 and giflib. The primary goal for writing sxiv is to create an image | |
| 7 | − | viewer, which only has the most basic features required for fast image viewing | |
| 8 | − | (the ones I want). It has vi key bindings and works nicely with tiling window | |
| 9 | − | managers. Its code base should be kept small and clean to make it easy for you | |
| 10 | − | to dig into it and customize it for your needs. | |
| 6 | + | imlib2, libexif and giflib. The primary goal for writing sxiv is to create an | |
| 7 | + | image viewer, which only has the most basic features required for fast image | |
| 8 | + | viewing (the ones I want). It has vi key bindings and works nicely with tiling | |
| 9 | + | window managers. Its code base should be kept small and clean to make it easy | |
| 10 | + | for you to dig into it and customize it for your needs. | |
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | 13 | Features | |
| @@ -1,141 +0,0 @@ | |||
| 1 | − | /* Copyright 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 | − | ||
| 19 | − | #define _POSIX_C_SOURCE 200112L | |
| 20 | − | ||
| 21 | − | #include <stdlib.h> | |
| 22 | − | #include <fcntl.h> | |
| 23 | − | #include <sys/stat.h> | |
| 24 | − | #include <unistd.h> | |
| 25 | − | ||
| 26 | − | #include "exif.h" | |
| 27 | − | #include "util.h" | |
| 28 | − | ||
| 29 | − | ssize_t s_read(int fd, const char *fn, void *buf, size_t n) | |
| 30 | − | { | |
| 31 | − | ssize_t ret; | |
| 32 | − | ||
| 33 | − | ret = read(fd, buf, n); | |
| 34 | − | if (ret < n) { | |
| 35 | − | warn("unexpected end-of-file: %s", fn); | |
| 36 | − | return -1; | |
| 37 | − | } else { | |
| 38 | − | return ret; | |
| 39 | − | } | |
| 40 | − | } | |
| 41 | − | ||
| 42 | − | unsigned short btous(unsigned char *buf, byteorder_t order) | |
| 43 | − | { | |
| 44 | − | if (buf == NULL) | |
| 45 | − | return 0; | |
| 46 | − | if (order == BO_BIG_ENDIAN) | |
| 47 | − | return buf[0] << 8 | buf[1]; | |
| 48 | − | else | |
| 49 | − | return buf[1] << 8 | buf[0]; | |
| 50 | − | } | |
| 51 | − | ||
| 52 | − | unsigned int btoui(unsigned char *buf, byteorder_t order) | |
| 53 | − | { | |
| 54 | − | if (buf == NULL) | |
| 55 | − | return 0; | |
| 56 | − | if (order == BO_BIG_ENDIAN) | |
| 57 | − | return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; | |
| 58 | − | else | |
| 59 | − | return buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]; | |
| 60 | − | } | |
| 61 | − | ||
| 62 | − | int exif_orientation(const fileinfo_t *file) | |
| 63 | − | { | |
| 64 | − | int fd; | |
| 65 | − | unsigned char data[EXIF_MAX_LEN]; | |
| 66 | − | byteorder_t order = BO_BIG_ENDIAN; | |
| 67 | − | unsigned int cnt, len, idx, val; | |
| 68 | − | ||
| 69 | − | if (file == NULL || file->path == NULL) | |
| 70 | − | return -1; | |
| 71 | − | ||
| 72 | − | fd = open(file->path, O_RDONLY); | |
| 73 | − | if (fd < 0) | |
| 74 | − | return -1; | |
| 75 | − | ||
| 76 | − | if (s_read(fd, file->name, data, 2) < 0) | |
| 77 | − | goto abort; | |
| 78 | − | if (btous(data, order) != JPEG_MARKER_SOI) | |
| 79 | − | goto abort; | |
| 80 | − | if (s_read(fd, file->name, data, 4) < 0) | |
| 81 | − | goto abort; | |
| 82 | − | ||
| 83 | − | if (btous(data, order) == JPEG_MARKER_APP0) { | |
| 84 | − | len = btous(data + 2, order); | |
| 85 | − | if (lseek(fd, len - 2, SEEK_CUR) == (off_t) -1) | |
| 86 | − | goto abort; | |
| 87 | − | if (s_read(fd, file->name, data, 4) < 0) | |
| 88 | − | goto abort; | |
| 89 | − | } | |
| 90 | − | if (btous(data, order) != JPEG_MARKER_APP1) | |
| 91 | − | goto abort; | |
| 92 | − | len = btous(data + 2, order); | |
| 93 | − | if (len < 8) | |
| 94 | − | goto abort; | |
| 95 | − | ||
| 96 | − | if (s_read(fd, file->name, data, 6) < 0) | |
| 97 | − | goto abort; | |
| 98 | − | if (btoui(data, order) != EXIF_HEAD) | |
| 99 | − | goto abort; | |
| 100 | − | ||
| 101 | − | len -= 8; | |
| 102 | − | if (len < 12 || len > EXIF_MAX_LEN) | |
| 103 | − | goto abort; | |
| 104 | − | if (s_read(fd, file->name, data, len) < 0) | |
| 105 | − | goto abort; | |
| 106 | − | ||
| 107 | − | switch (btous(data, order)) { | |
| 108 | − | case EXIF_BO_BIG_ENDIAN: | |
| 109 | − | order = BO_BIG_ENDIAN; | |
| 110 | − | break; | |
| 111 | − | case EXIF_BO_LITTLE_ENDIAN: | |
| 112 | − | order = BO_LITTLE_ENDIAN; | |
| 113 | − | break; | |
| 114 | − | default: | |
| 115 | − | goto abort; | |
| 116 | − | break; | |
| 117 | − | } | |
| 118 | − | ||
| 119 | − | if (btous(data + 2, order) != EXIF_TAG_MARK) | |
| 120 | − | goto abort; | |
| 121 | − | idx = btoui(data + 4, order); | |
| 122 | − | if (idx > len - 2) | |
| 123 | − | goto abort; | |
| 124 | − | ||
| 125 | − | val = 0; | |
| 126 | − | cnt = btous(data + idx, order); | |
| 127 | − | ||
| 128 | − | for (idx += 2; cnt > 0 && idx < len - 12; cnt--, idx += 12) { | |
| 129 | − | if (btous(data + idx, order) == EXIF_TAG_ORIENTATION) { | |
| 130 | − | val = btous(data + idx + 8, order); | |
| 131 | − | break; | |
| 132 | − | } | |
| 133 | − | } | |
| 134 | − | ||
| 135 | − | close(fd); | |
| 136 | − | return val; | |
| 137 | − | ||
| 138 | − | abort: | |
| 139 | − | close(fd); | |
| 140 | − | return -1; | |
| 141 | − | } | |
| @@ -1,42 +0,0 @@ | |||
| 1 | − | /* Copyright 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 | − | ||
| 19 | − | #ifndef EXIF_H | |
| 20 | − | #define EXIF_H | |
| 21 | − | ||
| 22 | − | #include "types.h" | |
| 23 | − | ||
| 24 | − | enum { | |
| 25 | − | JPEG_MARKER_SOI = 0xFFD8, | |
| 26 | − | JPEG_MARKER_APP0 = 0xFFE0, | |
| 27 | − | JPEG_MARKER_APP1 = 0xFFE1 | |
| 28 | − | }; | |
| 29 | − | ||
| 30 | − | enum { | |
| 31 | − | EXIF_MAX_LEN = 0x10000, | |
| 32 | − | EXIF_HEAD = 0x45786966, | |
| 33 | − | EXIF_BO_BIG_ENDIAN = 0x4D4D, | |
| 34 | − | EXIF_BO_LITTLE_ENDIAN = 0x4949, | |
| 35 | − | EXIF_TAG_MARK = 0x002A, | |
| 36 | − | EXIF_TAG_ORIENTATION = 0x0112 | |
| 37 | − | }; | |
| 38 | − | ||
| 39 | − | int exif_orientation(const fileinfo_t*); | |
| 40 | − | void exif_auto_orientate(const fileinfo_t*); /* in image.c */ | |
| 41 | − | ||
| 42 | − | #endif /* EXIF_H */ | |
| @@ -24,17 +24,20 @@ | |||
| 24 | 24 | #include <sys/types.h> | |
| 25 | 25 | #include <unistd.h> | |
| 26 | 26 | ||
| 27 | − | #if HAVE_GIFLIB | |
| 28 | − | #include <gif_lib.h> | |
| 29 | − | enum { MIN_GIF_DELAY = 25 }; | |
| 30 | − | #endif | |
| 31 | − | ||
| 32 | − | #include "exif.h" | |
| 33 | 27 | #include "image.h" | |
| 34 | 28 | #include "options.h" | |
| 35 | 29 | #include "util.h" | |
| 36 | 30 | #include "config.h" | |
| 37 | 31 | ||
| 32 | + | #if HAVE_LIBEXIF | |
| 33 | + | #include <libexif/exif-data.h> | |
| 34 | + | #endif | |
| 35 | + | ||
| 36 | + | #if HAVE_GIFLIB | |
| 37 | + | #include <gif_lib.h> | |
| 38 | + | enum { MIN_GIF_DELAY = 25 }; | |
| 39 | + | #endif | |
| 40 | + | ||
| 38 | 41 | float zoom_min; | |
| 39 | 42 | float zoom_max; | |
| 40 | 43 | ||
| @@ -92,9 +95,22 @@ void img_init(img_t *img, win_t *win) | |||
| 92 | 95 | img->ss.delay = options->slideshow > 0 ? options->slideshow : SLIDESHOW_DELAY; | |
| 93 | 96 | } | |
| 94 | 97 | ||
| 98 | + | #if HAVE_LIBEXIF | |
| 95 | 99 | void exif_auto_orientate(const fileinfo_t *file) | |
| 96 | 100 | { | |
| 97 | − | switch (exif_orientation(file)) { | |
| 101 | + | ExifData *ed; | |
| 102 | + | ExifEntry *entry; | |
| 103 | + | int byte_order, orientation = 0; | |
| 104 | + | ||
| 105 | + | if ((ed = exif_data_new_from_file(file->path)) == NULL) | |
| 106 | + | return; | |
| 107 | + | byte_order = exif_data_get_byte_order(ed); | |
| 108 | + | entry = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION); | |
| 109 | + | if (entry != NULL) | |
| 110 | + | orientation = exif_get_short(entry->data, byte_order); | |
| 111 | + | exif_data_unref(ed); | |
| 112 | + | ||
| 113 | + | switch (orientation) { | |
| 98 | 114 | case 5: | |
| 99 | 115 | imlib_image_orientate(1); | |
| 100 | 116 | case 2: | |
| @@ -116,6 +132,7 @@ void exif_auto_orientate(const fileinfo_t *file) | |||
| 116 | 132 | break; | |
| 117 | 133 | } | |
| 118 | 134 | } | |
| 135 | + | #endif | |
| 119 | 136 | ||
| 120 | 137 | #if HAVE_GIFLIB | |
| 121 | 138 | bool img_load_gif(img_t *img, const fileinfo_t *file) | |
| @@ -322,17 +339,16 @@ bool img_load(img_t *img, const fileinfo_t *file) | |||
| 322 | 339 | imlib_context_set_image(img->im); | |
| 323 | 340 | imlib_image_set_changes_on_disk(); | |
| 324 | 341 | ||
| 325 | − | if ((fmt = imlib_image_format()) == NULL) { | |
| 326 | − | warn("could not open image: %s", file->name); | |
| 327 | − | return false; | |
| 328 | − | } | |
| 329 | − | if (STREQ(fmt, "jpeg")) | |
| 330 | − | exif_auto_orientate(file); | |
| 331 | − | #if HAVE_GIFLIB | |
| 332 | − | if (STREQ(fmt, "gif")) | |
| 333 | − | img_load_gif(img, file); | |
| 342 | + | #if HAVE_LIBEXIF | |
| 343 | + | exif_auto_orientate(file); | |
| 334 | 344 | #endif | |
| 335 | 345 | ||
| 346 | + | if ((fmt = imlib_image_format()) != NULL) { | |
| 347 | + | #if HAVE_GIFLIB | |
| 348 | + | if (STREQ(fmt, "gif")) | |
| 349 | + | img_load_gif(img, file); | |
| 350 | + | #endif | |
| 351 | + | } | |
| 336 | 352 | img_apply_gamma(img); | |
| 337 | 353 | ||
| 338 | 354 | img->w = imlib_image_get_width(); | |
| @@ -19,6 +19,7 @@ | |||
| 19 | 19 | #define _POSIX_C_SOURCE 200112L | |
| 20 | 20 | #define _THUMBS_CONFIG | |
| 21 | 21 | ||
| 22 | + | #include <stdio.h> | |
| 22 | 23 | #include <stdlib.h> | |
| 23 | 24 | #include <string.h> | |
| 24 | 25 | #include <sys/types.h> | |
| @@ -26,11 +27,15 @@ | |||
| 26 | 27 | #include <unistd.h> | |
| 27 | 28 | #include <utime.h> | |
| 28 | 29 | ||
| 29 | − | #include "exif.h" | |
| 30 | 30 | #include "thumbs.h" | |
| 31 | 31 | #include "util.h" | |
| 32 | 32 | #include "config.h" | |
| 33 | 33 | ||
| 34 | + | #if HAVE_LIBEXIF | |
| 35 | + | #include <libexif/exif-data.h> | |
| 36 | + | void exif_auto_orientate(const fileinfo_t*); | |
| 37 | + | #endif | |
| 38 | + | ||
| 34 | 39 | static const int thumb_dim = THUMB_SIZE + 10; | |
| 35 | 40 | ||
| 36 | 41 | static char *cache_dir = NULL; | |
| @@ -224,8 +229,7 @@ bool tns_load(tns_t *tns, int n, const fileinfo_t *file, | |||
| 224 | 229 | bool use_cache, cache_hit = false; | |
| 225 | 230 | float z, zw, zh; | |
| 226 | 231 | thumb_t *t; | |
| 227 | − | Imlib_Image im; | |
| 228 | − | const char *fmt; | |
| 232 | + | Imlib_Image im = NULL; | |
| 229 | 233 | ||
| 230 | 234 | if (tns == NULL || tns->thumbs == NULL) | |
| 231 | 235 | return false; | |
| @@ -248,26 +252,75 @@ bool tns_load(tns_t *tns, int n, const fileinfo_t *file, | |||
| 248 | 252 | } | |
| 249 | 253 | ||
| 250 | 254 | if (!cache_hit) { | |
| 251 | − | if (access(file->path, R_OK) < 0 || | |
| 252 | − | (im = imlib_load_image(file->path)) == NULL) | |
| 255 | + | #if HAVE_LIBEXIF | |
| 256 | + | int pw = 0, ph = 0, x = 0, y = 0; | |
| 257 | + | bool err; | |
| 258 | + | ExifData *ed; | |
| 259 | + | ExifEntry *entry; | |
| 260 | + | ExifContent *ifd; | |
| 261 | + | ExifByteOrder byte_order; | |
| 262 | + | int tmpfd; | |
| 263 | + | char tmppath[] = "/tmp/sxiv-XXXXXX"; | |
| 264 | + | Imlib_Image tmpim; | |
| 265 | + | ||
| 266 | + | if ((ed = exif_data_new_from_file(file->path)) != NULL && | |
| 267 | + | ed->data != NULL && ed->size > 0) | |
| 268 | + | { | |
| 269 | + | if ((tmpfd = mkstemp(tmppath)) >= 0) { | |
| 270 | + | err = write(tmpfd, ed->data, ed->size) != ed->size; | |
| 271 | + | close(tmpfd); | |
| 272 | + | ||
| 273 | + | if (!err && (tmpim = imlib_load_image(tmppath)) != NULL) { | |
| 274 | + | byte_order = exif_data_get_byte_order(ed); | |
| 275 | + | ifd = ed->ifd[EXIF_IFD_EXIF]; | |
| 276 | + | entry = exif_content_get_entry(ifd, EXIF_TAG_PIXEL_X_DIMENSION); | |
| 277 | + | if (entry != NULL) | |
| 278 | + | pw = exif_get_long(entry->data, byte_order); | |
| 279 | + | entry = exif_content_get_entry(ifd, EXIF_TAG_PIXEL_Y_DIMENSION); | |
| 280 | + | if (entry != NULL) | |
| 281 | + | ph = exif_get_long(entry->data, byte_order); | |
| 282 | + | ||
| 283 | + | imlib_context_set_image(tmpim); | |
| 284 | + | w = imlib_image_get_width(); | |
| 285 | + | h = imlib_image_get_height(); | |
| 286 | + | ||
| 287 | + | if (pw > w && ph > h) { | |
| 288 | + | zw = (float) pw / (float) w; | |
| 289 | + | zh = (float) ph / (float) h; | |
| 290 | + | if (zw < zh) { | |
| 291 | + | pw /= zh; | |
| 292 | + | x = (w - pw) / 2; | |
| 293 | + | w = pw; | |
| 294 | + | } else if (zw > zh) { | |
| 295 | + | ph /= zw; | |
| 296 | + | y = (h - ph) / 2; | |
| 297 | + | h = ph; | |
| 298 | + | } | |
| 299 | + | } | |
| 300 | + | if ((im = imlib_create_cropped_image(x, y, w, h)) == NULL) | |
| 301 | + | die("could not allocate memory"); | |
| 302 | + | imlib_free_image_and_decache(); | |
| 303 | + | } | |
| 304 | + | unlink(tmppath); | |
| 305 | + | } | |
| 306 | + | exif_data_unref(ed); | |
| 307 | + | } | |
| 308 | + | #endif | |
| 309 | + | if (im == NULL && (access(file->path, R_OK) < 0 || | |
| 310 | + | (im = imlib_load_image(file->path)) == NULL)) | |
| 253 | 311 | { | |
| 254 | 312 | if (!silent) | |
| 255 | 313 | warn("could not open image: %s", file->name); | |
| 256 | 314 | return false; | |
| 257 | 315 | } | |
| 258 | 316 | } | |
| 259 | − | ||
| 260 | 317 | imlib_context_set_image(im); | |
| 261 | 318 | imlib_context_set_anti_alias(1); | |
| 262 | 319 | ||
| 263 | − | if ((fmt = imlib_image_format()) == NULL) { | |
| 264 | − | if (!silent) | |
| 265 | − | warn("could not open image: %s", file->name); | |
| 266 | − | imlib_free_image_and_decache(); | |
| 267 | − | return false; | |
| 268 | − | } | |
| 269 | − | if (STREQ(fmt, "jpeg")) | |
| 320 | + | #if HAVE_LIBEXIF | |
| 321 | + | if (!cache_hit) | |
| 270 | 322 | exif_auto_orientate(file); | |
| 323 | + | #endif | |
| 271 | 324 | ||
| 272 | 325 | w = imlib_image_get_width(); | |
| 273 | 326 | h = imlib_image_get_height(); | |