diff --git a/Makefile b/Makefile index 9c7afcd..a8320bb 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,15 @@ -VERSION = git-20140531 +VERSION = git-20140609 PREFIX = /usr/local MANPREFIX = $(PREFIX)/share/man CC = gcc CFLAGS = -std=c99 -Wall -pedantic -O2 -CPPFLAGS = -I$(PREFIX)/include -D_XOPEN_SOURCE=500 -DHAVE_GIFLIB +CPPFLAGS = -I$(PREFIX)/include -D_XOPEN_SOURCE=500 -DHAVE_LIBEXIF -DHAVE_GIFLIB LDFLAGS = -L$(PREFIX)/lib -LIBS = -lX11 -lImlib2 -lgif +LIBS = -lX11 -lImlib2 -lexif -lgif -SRC = commands.c exif.c image.c main.c options.c thumbs.c util.c window.c +SRC = commands.c image.c main.c options.c thumbs.c util.c window.c OBJ = $(SRC:.c=.o) all: sxiv diff --git a/README.md b/README.md index 6c3a9c0..97d43c1 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,11 @@ **Simple X Image Viewer** sxiv is an alternative to feh and qiv. Its only dependencies besides xlib are -imlib2 and giflib. The primary goal for writing sxiv is to create an image -viewer, which only has the most basic features required for fast image viewing -(the ones I want). It has vi key bindings and works nicely with tiling window -managers. Its code base should be kept small and clean to make it easy for you -to dig into it and customize it for your needs. +imlib2, libexif and giflib. The primary goal for writing sxiv is to create an +image viewer, which only has the most basic features required for fast image +viewing (the ones I want). It has vi key bindings and works nicely with tiling +window managers. Its code base should be kept small and clean to make it easy +for you to dig into it and customize it for your needs. Features diff --git a/exif.c b/exif.c deleted file mode 100644 index 424d36d..0000000 --- a/exif.c +++ /dev/null @@ -1,141 +0,0 @@ -/* Copyright 2012 Bert Muennich - * - * This file is part of sxiv. - * - * sxiv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * sxiv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with sxiv. If not, see . - */ - -#define _POSIX_C_SOURCE 200112L - -#include -#include -#include -#include - -#include "exif.h" -#include "util.h" - -ssize_t s_read(int fd, const char *fn, void *buf, size_t n) -{ - ssize_t ret; - - ret = read(fd, buf, n); - if (ret < n) { - warn("unexpected end-of-file: %s", fn); - return -1; - } else { - return ret; - } -} - -unsigned short btous(unsigned char *buf, byteorder_t order) -{ - if (buf == NULL) - return 0; - if (order == BO_BIG_ENDIAN) - return buf[0] << 8 | buf[1]; - else - return buf[1] << 8 | buf[0]; -} - -unsigned int btoui(unsigned char *buf, byteorder_t order) -{ - if (buf == NULL) - return 0; - if (order == BO_BIG_ENDIAN) - return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; - else - return buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]; -} - -int exif_orientation(const fileinfo_t *file) -{ - int fd; - unsigned char data[EXIF_MAX_LEN]; - byteorder_t order = BO_BIG_ENDIAN; - unsigned int cnt, len, idx, val; - - if (file == NULL || file->path == NULL) - return -1; - - fd = open(file->path, O_RDONLY); - if (fd < 0) - return -1; - - if (s_read(fd, file->name, data, 2) < 0) - goto abort; - if (btous(data, order) != JPEG_MARKER_SOI) - goto abort; - if (s_read(fd, file->name, data, 4) < 0) - goto abort; - - if (btous(data, order) == JPEG_MARKER_APP0) { - len = btous(data + 2, order); - if (lseek(fd, len - 2, SEEK_CUR) == (off_t) -1) - goto abort; - if (s_read(fd, file->name, data, 4) < 0) - goto abort; - } - if (btous(data, order) != JPEG_MARKER_APP1) - goto abort; - len = btous(data + 2, order); - if (len < 8) - goto abort; - - if (s_read(fd, file->name, data, 6) < 0) - goto abort; - if (btoui(data, order) != EXIF_HEAD) - goto abort; - - len -= 8; - if (len < 12 || len > EXIF_MAX_LEN) - goto abort; - if (s_read(fd, file->name, data, len) < 0) - goto abort; - - switch (btous(data, order)) { - case EXIF_BO_BIG_ENDIAN: - order = BO_BIG_ENDIAN; - break; - case EXIF_BO_LITTLE_ENDIAN: - order = BO_LITTLE_ENDIAN; - break; - default: - goto abort; - break; - } - - if (btous(data + 2, order) != EXIF_TAG_MARK) - goto abort; - idx = btoui(data + 4, order); - if (idx > len - 2) - goto abort; - - val = 0; - cnt = btous(data + idx, order); - - for (idx += 2; cnt > 0 && idx < len - 12; cnt--, idx += 12) { - if (btous(data + idx, order) == EXIF_TAG_ORIENTATION) { - val = btous(data + idx + 8, order); - break; - } - } - - close(fd); - return val; - -abort: - close(fd); - return -1; -} diff --git a/exif.h b/exif.h deleted file mode 100644 index 257f094..0000000 --- a/exif.h +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright 2012 Bert Muennich - * - * This file is part of sxiv. - * - * sxiv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * sxiv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with sxiv. If not, see . - */ - -#ifndef EXIF_H -#define EXIF_H - -#include "types.h" - -enum { - JPEG_MARKER_SOI = 0xFFD8, - JPEG_MARKER_APP0 = 0xFFE0, - JPEG_MARKER_APP1 = 0xFFE1 -}; - -enum { - EXIF_MAX_LEN = 0x10000, - EXIF_HEAD = 0x45786966, - EXIF_BO_BIG_ENDIAN = 0x4D4D, - EXIF_BO_LITTLE_ENDIAN = 0x4949, - EXIF_TAG_MARK = 0x002A, - EXIF_TAG_ORIENTATION = 0x0112 -}; - -int exif_orientation(const fileinfo_t*); -void exif_auto_orientate(const fileinfo_t*); /* in image.c */ - -#endif /* EXIF_H */ diff --git a/image.c b/image.c index 7eec544..7312e73 100644 --- a/image.c +++ b/image.c @@ -24,17 +24,20 @@ #include #include -#if HAVE_GIFLIB -#include -enum { MIN_GIF_DELAY = 25 }; -#endif - -#include "exif.h" #include "image.h" #include "options.h" #include "util.h" #include "config.h" +#if HAVE_LIBEXIF +#include +#endif + +#if HAVE_GIFLIB +#include +enum { MIN_GIF_DELAY = 25 }; +#endif + float zoom_min; float zoom_max; @@ -92,9 +95,22 @@ void img_init(img_t *img, win_t *win) img->ss.delay = options->slideshow > 0 ? options->slideshow : SLIDESHOW_DELAY; } +#if HAVE_LIBEXIF void exif_auto_orientate(const fileinfo_t *file) { - switch (exif_orientation(file)) { + ExifData *ed; + ExifEntry *entry; + int byte_order, orientation = 0; + + if ((ed = exif_data_new_from_file(file->path)) == NULL) + return; + byte_order = exif_data_get_byte_order(ed); + entry = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION); + if (entry != NULL) + orientation = exif_get_short(entry->data, byte_order); + exif_data_unref(ed); + + switch (orientation) { case 5: imlib_image_orientate(1); case 2: @@ -116,6 +132,7 @@ void exif_auto_orientate(const fileinfo_t *file) break; } } +#endif #if HAVE_GIFLIB 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) imlib_context_set_image(img->im); imlib_image_set_changes_on_disk(); - if ((fmt = imlib_image_format()) == NULL) { - warn("could not open image: %s", file->name); - return false; - } - if (STREQ(fmt, "jpeg")) - exif_auto_orientate(file); -#if HAVE_GIFLIB - if (STREQ(fmt, "gif")) - img_load_gif(img, file); +#if HAVE_LIBEXIF + exif_auto_orientate(file); #endif + if ((fmt = imlib_image_format()) != NULL) { +#if HAVE_GIFLIB + if (STREQ(fmt, "gif")) + img_load_gif(img, file); +#endif + } img_apply_gamma(img); img->w = imlib_image_get_width(); diff --git a/thumbs.c b/thumbs.c index 80249d7..0357836 100644 --- a/thumbs.c +++ b/thumbs.c @@ -19,6 +19,7 @@ #define _POSIX_C_SOURCE 200112L #define _THUMBS_CONFIG +#include #include #include #include @@ -26,11 +27,15 @@ #include #include -#include "exif.h" #include "thumbs.h" #include "util.h" #include "config.h" +#if HAVE_LIBEXIF +#include +void exif_auto_orientate(const fileinfo_t*); +#endif + static const int thumb_dim = THUMB_SIZE + 10; static char *cache_dir = NULL; @@ -224,8 +229,7 @@ bool tns_load(tns_t *tns, int n, const fileinfo_t *file, bool use_cache, cache_hit = false; float z, zw, zh; thumb_t *t; - Imlib_Image im; - const char *fmt; + Imlib_Image im = NULL; if (tns == NULL || tns->thumbs == NULL) return false; @@ -248,26 +252,75 @@ bool tns_load(tns_t *tns, int n, const fileinfo_t *file, } if (!cache_hit) { - if (access(file->path, R_OK) < 0 || - (im = imlib_load_image(file->path)) == NULL) +#if HAVE_LIBEXIF + int pw = 0, ph = 0, x = 0, y = 0; + bool err; + ExifData *ed; + ExifEntry *entry; + ExifContent *ifd; + ExifByteOrder byte_order; + int tmpfd; + char tmppath[] = "/tmp/sxiv-XXXXXX"; + Imlib_Image tmpim; + + if ((ed = exif_data_new_from_file(file->path)) != NULL && + ed->data != NULL && ed->size > 0) + { + if ((tmpfd = mkstemp(tmppath)) >= 0) { + err = write(tmpfd, ed->data, ed->size) != ed->size; + close(tmpfd); + + if (!err && (tmpim = imlib_load_image(tmppath)) != NULL) { + byte_order = exif_data_get_byte_order(ed); + ifd = ed->ifd[EXIF_IFD_EXIF]; + entry = exif_content_get_entry(ifd, EXIF_TAG_PIXEL_X_DIMENSION); + if (entry != NULL) + pw = exif_get_long(entry->data, byte_order); + entry = exif_content_get_entry(ifd, EXIF_TAG_PIXEL_Y_DIMENSION); + if (entry != NULL) + ph = exif_get_long(entry->data, byte_order); + + imlib_context_set_image(tmpim); + w = imlib_image_get_width(); + h = imlib_image_get_height(); + + if (pw > w && ph > h) { + zw = (float) pw / (float) w; + zh = (float) ph / (float) h; + if (zw < zh) { + pw /= zh; + x = (w - pw) / 2; + w = pw; + } else if (zw > zh) { + ph /= zw; + y = (h - ph) / 2; + h = ph; + } + } + if ((im = imlib_create_cropped_image(x, y, w, h)) == NULL) + die("could not allocate memory"); + imlib_free_image_and_decache(); + } + unlink(tmppath); + } + exif_data_unref(ed); + } +#endif + if (im == NULL && (access(file->path, R_OK) < 0 || + (im = imlib_load_image(file->path)) == NULL)) { if (!silent) warn("could not open image: %s", file->name); return false; } } - imlib_context_set_image(im); imlib_context_set_anti_alias(1); - if ((fmt = imlib_image_format()) == NULL) { - if (!silent) - warn("could not open image: %s", file->name); - imlib_free_image_and_decache(); - return false; - } - if (STREQ(fmt, "jpeg")) +#if HAVE_LIBEXIF + if (!cache_hit) exif_auto_orientate(file); +#endif w = imlib_image_get_width(); h = imlib_image_get_height();