diff --git a/Makefile b/Makefile index 4b437f2..0991f6c 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ LIBS = -lX11 -lImlib2 -lgif PREFIX = /usr/local MANPREFIX = $(PREFIX)/share/man -SRC = commands.c image.c main.c options.c thumbs.c util.c window.c +SRC = commands.c exif.c image.c main.c options.c thumbs.c util.c window.c OBJ = $(SRC:.c=.o) all: options sxiv diff --git a/exif.c b/exif.c new file mode 100644 index 0000000..4703a66 --- /dev/null +++ b/exif.c @@ -0,0 +1,131 @@ +/* sxiv: exif.c + * Copyright (c) 2011 Bert Muennich + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#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, 4) < 0) + goto abort; + if (btous(data, order) != JPEG_MARKER_SOI) + goto abort; + if (btous(data + 2, order) != JPEG_MARKER_APP1) + goto abort; + + if (s_read(fd, file->name, data, 2) < 0) + goto abort; + len = btous(data, 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); + printf("exif orientation: %s: %u\n", file->name, val); + break; + } + } + + close(fd); + return val; + +abort: + close(fd); + return -1; +} diff --git a/exif.h b/exif.h new file mode 100644 index 0000000..6bb6f9c --- /dev/null +++ b/exif.h @@ -0,0 +1,41 @@ +/* sxiv: exif.h + * Copyright (c) 2011 Bert Muennich + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef EXIF_H +#define EXIF_H + +#include "types.h" + +enum { + JPEG_MARKER_SOI = 0xFFD8, + 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 c9ffc59..6829dae 100644 --- a/image.c +++ b/image.c @@ -25,6 +25,7 @@ #include #include +#include "exif.h" #include "image.h" #include "options.h" #include "util.h" @@ -64,6 +65,34 @@ void img_init(img_t *img, win_t *win) { img->multi.animate = false; } +void exif_auto_orientate(const fileinfo_t *file) { + switch (exif_orientation(file)) { + case 5: + imlib_image_orientate(1); + case 2: + imlib_image_flip_vertical(); + break; + + case 3: + imlib_image_orientate(2); + break; + + case 7: + imlib_image_orientate(1); + case 4: + imlib_image_flip_horizontal(); + break; + + case 6: + imlib_image_orientate(1); + break; + + case 8: + imlib_image_orientate(3); + break; + } +} + bool img_load_gif(img_t *img, const fileinfo_t *file) { GifFileType *gif; GifRowType *rows = NULL; @@ -254,6 +283,8 @@ bool img_load(img_t *img, const fileinfo_t *file) { warn("could not open image: %s", file->name); return false; } + if (STREQ(fmt, "jpeg")) + exif_auto_orientate(file); if (STREQ(fmt, "gif")) img_load_gif(img, file); diff --git a/thumbs.c b/thumbs.c index 31a1f89..420547a 100644 --- a/thumbs.c +++ b/thumbs.c @@ -26,6 +26,7 @@ #include #include +#include "exif.h" #include "thumbs.h" #include "util.h" #include "config.h" @@ -254,6 +255,8 @@ bool tns_load(tns_t *tns, int n, const fileinfo_t *file, imlib_free_image_and_decache(); return false; } + if (STREQ(fmt, "jpeg")) + exif_auto_orientate(file); w = imlib_image_get_width(); h = imlib_image_get_height(); diff --git a/types.h b/types.h index 9228558..1d72c95 100644 --- a/types.h +++ b/types.h @@ -6,6 +6,11 @@ typedef enum { true } bool; +typedef enum { + BO_BIG_ENDIAN, + BO_LITTLE_ENDIAN +} byteorder_t; + typedef enum { MODE_IMAGE, MODE_THUMB