@@ -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 | |||
@@ -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 | |||
@@ -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 <http://www.gnu.org/licenses/>. | |||
*/ | |||
#define _POSIX_C_SOURCE 200112L | |||
#include <stdlib.h> | |||
#include <fcntl.h> | |||
#include <sys/stat.h> | |||
#include <unistd.h> | |||
#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; | |||
} |
@@ -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 <http://www.gnu.org/licenses/>. | |||
*/ | |||
#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 */ |
@@ -24,17 +24,20 @@ | |||
#include <sys/types.h> | |||
#include <unistd.h> | |||
#if HAVE_GIFLIB | |||
#include <gif_lib.h> | |||
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 <libexif/exif-data.h> | |||
#endif | |||
#if HAVE_GIFLIB | |||
#include <gif_lib.h> | |||
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(); | |||
@@ -19,6 +19,7 @@ | |||
#define _POSIX_C_SOURCE 200112L | |||
#define _THUMBS_CONFIG | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <sys/types.h> | |||
@@ -26,11 +27,15 @@ | |||
#include <unistd.h> | |||
#include <utime.h> | |||
#include "exif.h" | |||
#include "thumbs.h" | |||
#include "util.h" | |||
#include "config.h" | |||
#if HAVE_LIBEXIF | |||
#include <libexif/exif-data.h> | |||
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(); | |||