From e475bec0ed51a684111d1cb41ddc85fc8ae971e7 Mon Sep 17 00:00:00 2001
From: David Maciejak <david.maciejak@gmail.com>
Date: Mon, 2 Jun 2014 18:27:36 +0800
Subject: [PATCH] util/wmiv: add image auto orientation detection

Based on libexif feature, this patch is adding orientation
detection and provides 2 more shortcuts for live
right/left rotation. Internal version was bumped to 0.7.
---
 configure.ac     |   6 ++
 m4/wm_libexif.m4 |  34 ++++++++++
 util/Makefile.am |   2 +-
 util/wmiv.c      | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 4 files changed, 220 insertions(+), 15 deletions(-)
 create mode 100644 m4/wm_libexif.m4

diff --git a/configure.ac b/configure.ac
index 4ea1e32..2b9fe6b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -640,6 +640,12 @@ dnl ===============================================
 dnl 		End of stuff that uses X
 dnl ===============================================
 
+dnl EXIF Support
+dnl ===========
+WM_CHECK_LIBEXIF
+AS_IF([test "x$LIBEXIF" != "x"],
+        [AC_DEFINE(HAVE_EXIF, 1, [Define if EXIF can be used])])
+
 dnl PNG Support
 dnl ===========
 AC_ARG_ENABLE([png],
diff --git a/m4/wm_libexif.m4 b/m4/wm_libexif.m4
new file mode 100644
index 0000000..ac501bb
--- /dev/null
+++ b/m4/wm_libexif.m4
@@ -0,0 +1,34 @@
+# wm_libexif.m4 - Macros to check proper libexif
+#
+# Copyright (c) 2014 Window Maker Team
+#
+# 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.
+
+# WM_CHECK_LIBEXIF
+# -------------
+#
+# Checks the needed library link flags
+# Sets variable LIBEXIF with the appropriates flags
+AC_DEFUN_ONCE([WM_CHECK_LIBEXIF],
+[AC_CHECK_HEADER([libexif/exif-data.h],
+	[AC_CHECK_FUNC(exif_data_new_from_file,
+		[LIBEXIF=],
+    		[AC_CHECK_LIB(exif, [exif_data_new_from_file],
+		[LIBEXIF=-lexif],
+		[AC_MSG_WARN(Could not find EXIF library, you may experience problems)
+		LIBEXIF=] )] )],
+	[AC_MSG_WARN([header for EXIF library not found])])
+AC_SUBST(LIBEXIF) dnl
+])
diff --git a/util/Makefile.am b/util/Makefile.am
index 5cde3ee..b5c9d04 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -73,7 +73,7 @@ wmmenugen_SOURCES = wmmenugen.c wmmenugen.h wmmenugen_misc.c \
 wmiv_LDADD = \
 	$(top_builddir)/wrlib/libwraster.la \
 	@XLFLAGS@ @XLIBS@ \
-	@GFXLIBS@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
+	@GFXLIBS@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) $(LIBEXIF)
 
 wmiv_SOURCES = wmiv.c wmiv.h
 
diff --git a/util/wmiv.c b/util/wmiv.c
index 2eab5d3..9810b5c 100755
--- a/util/wmiv.c
+++ b/util/wmiv.c
@@ -21,6 +21,7 @@
 #if !defined(_GNU_SOURCE)
 #define _GNU_SOURCE
 #endif
+
 #include <X11/keysym.h>
 #include <X11/XKBlib.h>
 #include <X11/Xatom.h>
@@ -34,6 +35,11 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #include "config.h"
+
+#ifdef HAVE_EXIF
+#include <libexif/exif-data.h>
+#endif
+
 #ifdef HAVE_PTHREAD
 #include <pthread.h>
 #endif
@@ -57,7 +63,7 @@ Pixmap pix;
 
 const char *APPNAME = "wmiv";
 int APPVERSION_MAJOR = 0;
-int APPVERSION_MINOR = 6;
+int APPVERSION_MINOR = 7;
 int NEXT = 0;
 int PREV = 1;
 float zoom_factor = 0;
@@ -66,6 +72,7 @@ int max_height = 0;
 
 Bool fullscreen_flag = False;
 Bool focus = False;
+Bool back_from_fullscreen = False;
 
 #ifdef HAVE_PTHREAD
 Bool diaporama_flag = False;
@@ -98,6 +105,90 @@ typedef struct linked_list {
 linked_list_t list;
 link_t *current_link;
 
+
+/*
+	load_oriented_image: used to load an image and optionally
+	get its orientation if libexif is available
+	return the image on success, NULL on failure
+*/
+RImage *load_oriented_image(RContext *context, const char *file, int index)
+{
+	RImage *image;
+#ifdef HAVE_EXIF
+	int orientation = 0;
+#endif
+	image = RLoadImage(context, file, index);
+	if (!image)
+		return NULL;
+#ifdef HAVE_EXIF
+	ExifData *exifData = exif_data_new_from_file(file);
+	if (exifData) {
+		ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
+		ExifEntry *exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
+		if (exifEntry)
+			orientation = exif_get_short(exifEntry->data, byteOrder);
+
+		exif_data_free(exifData);
+	}
+
+/*
+	0th Row      0th Column
+	1  top          left side
+	2  top          right side
+	3  bottom     right side
+	4  bottom     left side
+	5  left side    top
+	6  right side  top
+	7  right side  bottom
+	8  left side    bottom
+*/
+
+	if (image && (orientation > 1)) {
+		RImage *tmp = NULL;
+		switch (orientation) {
+		case 2:
+			tmp = RHorizontalFlipImage(image);
+			break;
+		case 3:
+			tmp = RRotateImage(image, 180);
+			break;
+		case 4:
+			tmp = RVerticalFlipImage(image);
+			break;
+		case 5: {
+				RImage *tmp2;
+				tmp2 = RVerticalFlipImage(image);
+				if (tmp2) {
+					tmp = RRotateImage(tmp2, 90);
+					RReleaseImage(tmp2);
+				}
+			}
+			break;
+		case 6:
+			tmp = RRotateImage(image, 90);
+			break;
+		case 7: {
+				RImage *tmp2;
+				tmp2 = RVerticalFlipImage(image);
+				if (tmp2) {
+					tmp = RRotateImage(tmp2, 270);
+					RReleaseImage(tmp2);
+				}
+			}
+			break;
+		case 8:
+			tmp = RRotateImage(image, 270);
+			break;
+		}
+		if (tmp) {
+			RReleaseImage(image);
+			image = tmp;
+		}
+	}
+#endif
+	return image;
+}
+
 /*
 	change_title: used to change window title
 	return EXIT_SUCCESS on success, 1 on failure
@@ -212,6 +303,59 @@ int merge_with_background(RImage *i)
 }
 
 /*
+	turn_image: rotate the image by the angle passed
+	return EXIT_SUCCESS on success, EXIT_FAILURE on failure
+*/
+int turn_image(float angle)
+{
+	RImage *tmp;
+
+	if (!img)
+		return EXIT_FAILURE;
+
+	tmp = RRotateImage(img, angle);
+	if (!tmp)
+		return EXIT_FAILURE;
+
+	if (!fullscreen_flag) {
+		if (img->width != tmp->width || img->height != tmp->height)
+			XResizeWindow(dpy, win, tmp->width, tmp->height);
+	}
+
+	RReleaseImage(img);
+	img = tmp;
+
+	rescale_image();
+	if (!fullscreen_flag) {
+		XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
+	} else {
+		XClearWindow(dpy, win);
+		XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
+			img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
+	}
+
+	return EXIT_SUCCESS;
+}
+
+/*
+	turn_image_right: rotate the image by 90 degree
+	return EXIT_SUCCESS on success, EXIT_FAILURE on failure
+*/
+int turn_image_right(void)
+{
+	return turn_image(90.0);
+}
+
+/*
+	turn_image_left: rotate the image by -90 degree
+	return EXIT_SUCCESS on success, 1 on failure
+*/
+int turn_image_left(void)
+{
+	return turn_image(-90.0);
+}
+
+/*
 	draw_failed_image: create a red crossed image to indicate an error loading file
 	return the image on success, NULL on failure
 
@@ -250,6 +394,7 @@ int full_screen(void)
 	if (fullscreen_flag) {
 		fullscreen_flag = False;
 		zoom_factor = 0;
+		back_from_fullscreen = True;
 	} else {
 		fullscreen_flag = True;
 		zoom_factor = 1000;
@@ -279,7 +424,7 @@ int full_screen(void)
 int zoom_in_out(int z)
 {
 	RImage *old_img = img;
-	RImage *tmp = RLoadImage(ctx, current_link->data, 0);
+	RImage *tmp = load_oriented_image(ctx, current_link->data, 0);
 	if (!tmp)
 		return EXIT_FAILURE;
 
@@ -368,7 +513,7 @@ int change_image(int way)
 		}
 		if (DEBUG)
 			fprintf(stderr, "current file is> %s\n", (char *)current_link->data);
-		img = RLoadImage(ctx, current_link->data, 0);
+		img = load_oriented_image(ctx, current_link->data, 0);
 
 		if (!img) {
 			fprintf(stderr, "Error: %s %s\n", (char *)current_link->data,
@@ -453,7 +598,7 @@ int linked_list_add(linked_list_t *list, const void *data)
 	/* calloc sets the "next" field to zero. */
 	link = calloc(1, sizeof(link_t));
 	if (!link) {
-		fprintf(stderr, "calloc failed.\n");
+		fprintf(stderr, "Error: memory allocation failed\n");
 		return EXIT_FAILURE;
 	}
 	link->data = data;
@@ -572,7 +717,9 @@ int main(int argc, char **argv)
 #ifdef HAVE_PTHREAD
 			"d: launch diaporama mode\n"
 #endif
+			"l: rotate image on the left\n"
 			"q: quit\n"
+			"r: rotate image on the right\n"
 			"right: next image\n"
 			"left: previous image\n"
 			"up: first image\n"
@@ -618,7 +765,7 @@ int main(int argc, char **argv)
 		}
 	}
 
-	img = RLoadImage(ctx, reading_filename, 0);
+	img = load_oriented_image(ctx, reading_filename, 0);
 
 	if (!img) {
 		fprintf(stderr, "Error: %s %s\n", reading_filename, RMessageForError(RErrorCode));
@@ -703,21 +850,33 @@ int main(int argc, char **argv)
 			XConfigureEvent xce = e.xconfigure;
 			if (xce.width != img->width || xce.height != img->height) {
 				RImage *old_img = img;
-				img = RLoadImage(ctx, current_link->data, 0);
+				img = load_oriented_image(ctx, current_link->data, 0);
 				if (!img) {
 					/* keep the old img and window size */
 					img = old_img;
 					XResizeWindow(dpy, win, img->width, img->height);
 				} else {
-					img = RScaleImage(img, xce.width, xce.height);
-					if (!img) {
-						img = old_img;
-						XResizeWindow(dpy, win, img->width, img->height);
-					} else {
-						merge_with_background(img);
-						if (RConvertImage(ctx, img, &pix))
-							RReleaseImage(old_img);
+					RImage *tmp2;
+					if (!back_from_fullscreen)
+						/* manually resized window */
+						tmp2 = RScaleImage(img, xce.width, xce.height);
+					else {
+						/* back from fullscreen mode, maybe img was rotated */
+						tmp2 = img;
+						back_from_fullscreen = False;
+						XClearWindow(dpy, win);
+					}
+					merge_with_background(tmp2);
+					if (RConvertImage(ctx, tmp2, &pix)) {
+						RReleaseImage(old_img);
+						img = RCloneImage(tmp2);
+						RReleaseImage(tmp2);
+						change_title(&title_property, (char *)current_link->data);
+						XSync(dpy, True);
 						XResizeWindow(dpy, win, img->width, img->height);
+						XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
+									img->width, img->height, 0, 0);
+
 					}
 				}
 			}
@@ -814,6 +973,12 @@ int main(int argc, char **argv)
 			case XK_f:
 				full_screen();
 				break;
+			case XK_r:
+				turn_image_right();
+				break;
+			case XK_l:
+				turn_image_left();
+				break;
 			}
 
 		}
-- 
1.8.3.2

