ethumb: automagically orient thumbnails based on:
authorillogict <illogict@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Sun, 19 Sep 2010 17:07:35 +0000 (17:07 +0000)
committerillogict <illogict@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Sun, 19 Sep 2010 17:07:35 +0000 (17:07 +0000)
 - metadata contained in files (EXIF only currently) - default active;
 - orientation given by the caller against pixel data orientation.
 Code is based on els_icon.

 Next is to add that through dbus.

 Open question: if orientation is specified, do we need to save the thumbnail in a different folder?

git-svn-id: https://svn.enlightenment.org/svn/e/trunk/ethumb@52465 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

configure.ac
src/lib/Ethumb.c
src/lib/Ethumb.h
src/lib/Makefile.am
src/lib/client/Ethumb_Client.c
src/lib/client/Ethumb_Client.h
src/lib/ethumb_private.h

index ddb64f6..62b8a11 100644 (file)
@@ -121,6 +121,29 @@ if $USE_MODULE_ETHUMBD ; then
                     ]
                    )
 fi
+AC_ARG_ENABLE([libexif],
+        [AC_HELP_STRING([--disable-libexif], [disable libexif support. Default is enabled.])],
+        [
+         if test "x${enableval}" = "xyes" ; then
+            _iv_enable_libexif="yes"
+         else
+            _iv_enable_libexif="no"
+         fi
+        ],
+        [_iv_enable_libexif="yes"]
+)
+
+AC_MSG_CHECKING([whether libexif is built])
+AC_MSG_RESULT([${_iv_enable_libexif}])
+
+HAVE_LIBEXIF="no"
+
+if test "x${_iv_enable_libexif}" = "xyes" ; then
+        AC_ETH_CHECK_PKG(LIBEXIF, libexif)
+fi
+
+AM_CONDITIONAL(HAVE_LIBEXIF, test $HAVE_LIBEXIF = yes)
+AC_SUBST(HAVE_LIBEXIF)
 
 AC_SUBST(requirement_ethumb)
 AC_SUBST(requirement_ethumb_client)
index 5bee1dd..b592639 100644 (file)
@@ -69,6 +69,10 @@ void *alloca (size_t);
 #include "Ethumb_Plugin.h"
 #include "md5.h"
 
+#ifdef HAVE_LIBEXIF
+  #include <libexif/exif-data.h>
+#endif
+
 static int _log_dom = -1;
 #define DBG(...) EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
 #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
@@ -224,6 +228,7 @@ ethumb_new(void)
    /* IF CHANGED, UPDATE DOCS in (Ethumb.c, Ethumb_Client.c, python...)!!! */
    ethumb->tw = THUMB_SIZE_NORMAL;
    ethumb->th = THUMB_SIZE_NORMAL;
+   ethumb->orientation = ETHUMB_THUMB_ORIENT_ORIGINAL;
    ethumb->crop_x = 0.5;
    ethumb->crop_y = 0.5;
    ethumb->quality = 80;
@@ -345,6 +350,7 @@ ethumb_thumb_fdo_set(Ethumb *e, Ethumb_Thumb_FDO_Size s)
 
    e->format = ETHUMB_THUMB_FDO;
    e->aspect = ETHUMB_THUMB_KEEP_ASPECT;
+   e->orientation = ETHUMB_THUMB_ORIENT_ORIGINAL;
    _ethumb_frame_free(e->frame);
    e->frame = NULL;
    eina_stringshare_del(e->thumb_dir);
@@ -413,6 +419,31 @@ ethumb_thumb_aspect_get(const Ethumb *e)
 }
 
 EAPI void
+ethumb_thumb_orientation_set(Ethumb *e, Ethumb_Thumb_Orientation o)
+{
+   EINA_SAFETY_ON_NULL_RETURN(e);
+   EINA_SAFETY_ON_FALSE_RETURN(o == ETHUMB_THUMB_ORIENT_NONE ||
+                              o == ETHUMB_THUMB_ROTATE_90_CW ||
+                              o == ETHUMB_THUMB_ROTATE_180 ||
+                              o == ETHUMB_THUMB_ROTATE_90_CCW ||
+                              o == ETHUMB_THUMB_FLIP_HORIZONTAL ||
+                              o == ETHUMB_THUMB_FLIP_VERTICAL ||
+                              o == ETHUMB_THUMB_FLIP_TRANSPOSE ||
+                              o == ETHUMB_THUMB_FLIP_TRANSVERSE ||
+                              o == ETHUMB_THUMB_ORIENT_ORIGINAL);
+
+   DBG("ethumb=%p, orientation=%d", e, o);
+   e->orientation = o;
+}
+
+EAPI Ethumb_Thumb_Orientation
+ethumb_thumb_orientation_get(const Ethumb *e)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e, 0);
+   return e->orientation;
+}
+
+EAPI void
 ethumb_thumb_crop_align_set(Ethumb *e, float x, float y)
 {
    EINA_SAFETY_ON_NULL_RETURN(e);
@@ -1171,12 +1202,135 @@ ethumb_image_save(Ethumb *e)
    return EINA_TRUE;
 }
 
+static void
+_ethumb_image_orient(Ethumb *e, int orientation)
+{
+   Evas_Object *img = e->img, *tmp;
+   unsigned int *data, *data2, *to, *from, *p1, *p2, pt;
+   int x, y, w, hw, iw, ih, tw, th;
+   const char *file, *key;
+
+   evas_object_image_size_get(img, &iw, &ih);
+   data = evas_object_image_data_get(img, 1);
+
+   switch (orientation)
+     {
+      case ETHUMB_THUMB_FLIP_HORIZONTAL:
+        for (y = 0; y < ih; y++)
+          {
+             p1 = data + (y * iw);
+             p2 = data + ((y + 1) * iw) - 1;
+             for (x = 0; x < (iw >> 1); x++)
+               {
+                  pt = *p1;
+                  *p1 = *p2;
+                  *p2 = pt;
+                  p1++;
+                  p2--;
+               }
+          }
+        evas_object_image_data_set(img, data);
+        evas_object_image_data_update_add(img, 0, 0, iw, ih);
+        return;
+      case ETHUMB_THUMB_FLIP_VERTICAL:
+        for (y = 0; y < (ih >> 1); y++)
+          {
+             p1 = data + (y * iw);
+             p2 = data + ((ih - 1 - y) * iw);
+             for (x = 0; x < iw; x++)
+               {
+                  pt = *p1;
+                  *p1 = *p2;
+                  *p2 = pt;
+                  p1++;
+                  p2++;
+               }
+          }
+        evas_object_image_data_set(img, data);
+        evas_object_image_data_update_add(img, 0, 0, iw, ih);
+        return;
+      case ETHUMB_THUMB_ROTATE_180:
+        hw = iw * ih;
+        x = (hw / 2);
+        p1 = data;
+        p2 = data + hw - 1;
+        for (; --x > 0;)
+          {
+             pt = *p1;
+             *p1 = *p2;
+             *p2 = pt;
+             p1++;
+             p2--;
+          }
+        evas_object_image_data_set(img, data);
+        evas_object_image_data_update_add(img, 0, 0, iw, ih);
+        return;
+     }
+
+   evas_object_image_load_size_get(img, &tw, &th);
+   evas_object_image_file_get(img, &file, &key);
+   tmp = evas_object_image_add(evas_object_evas_get(img));
+   evas_object_image_load_size_set(tmp, tw, th);
+   evas_object_image_file_set(tmp, file, key);
+   data2 = evas_object_image_data_get(tmp, 0);
+
+   w = ih;
+   ih = iw;
+   iw = w;
+   hw = w * ih;
+
+   evas_object_image_size_set(img, iw, ih);
+   data = evas_object_image_data_get(img, 1);
+
+   switch (orientation)
+     {
+      case ETHUMB_THUMB_FLIP_TRANSPOSE:
+        to = data;
+        hw = -hw + 1;
+        break;
+      case ETHUMB_THUMB_FLIP_TRANSVERSE:
+        to = data + hw - 1;
+        w = -w;
+        hw = hw - 1;
+        break;
+      case ETHUMB_THUMB_ROTATE_90_CW:
+        to = data + w - 1;
+        hw = -hw - 1;
+        break;
+      case ETHUMB_THUMB_ROTATE_90_CCW:
+        to = data + hw - w;
+        w = -w;
+        hw = hw + 1;
+        break;
+      default:
+        ERR("unknown orient %d", orientation);
+        evas_object_del(tmp);
+        evas_object_image_data_set(img, data); // give it back
+        return;
+     }
+   from = data2;
+   for (x = iw; --x >= 0;)
+     {
+        for (y = ih; --y >= 0;)
+          {
+             *to = *from;
+             from++;
+             to += w;
+          }
+        to += hw;
+     }
+   evas_object_del(tmp);
+   evas_object_image_data_set(img, data);
+   evas_object_image_data_update_add(img, 0, 0, iw, ih);
+}
+
 static int
 _ethumb_image_load(Ethumb *e)
 {
    int error;
    Evas_Coord w, h, ww, hh, fx, fy, fw, fh;
    Evas_Object *img;
+   int orientation = ETHUMB_THUMB_ORIENT_NONE;
 
    img = e->img;
 
@@ -1200,6 +1354,54 @@ _ethumb_image_load(Ethumb *e)
        return 0;
      }
 
+   if (e->orientation == ETHUMB_THUMB_ORIENT_ORIGINAL)
+      {
+#ifdef HAVE_LIBEXIF
+        ExifData  *exif = exif_data_new_from_file(e->src_path);
+        ExifEntry *entry = NULL;
+        ExifByteOrder bo;
+        int o;
+
+        if (exif)
+          {
+             entry = exif_data_get_entry(exif, EXIF_TAG_ORIENTATION);
+             if (entry)
+               {
+                  bo = exif_data_get_byte_order(exif);
+                  o = exif_get_short(entry->data, bo);
+               }
+             exif_data_free(exif);
+             switch (o)
+               {
+                case 2:
+                   orientation = ETHUMB_THUMB_FLIP_HORIZONTAL;
+                   break;
+                case 3:
+                   orientation = ETHUMB_THUMB_ROTATE_180;
+                   break;
+                case 4:
+                   orientation = ETHUMB_THUMB_FLIP_VERTICAL;
+                   break;
+                case 5:
+                   orientation = ETHUMB_THUMB_FLIP_TRANSPOSE;
+                   break;
+                case 6:
+                   orientation = ETHUMB_THUMB_ROTATE_90_CW;
+                   break;
+                case 7:
+                   orientation = ETHUMB_THUMB_FLIP_TRANSVERSE;
+                   break;
+                case 8:
+                   orientation = ETHUMB_THUMB_ROTATE_90_CCW;
+                   break;
+               }
+          }
+#endif
+   }
+
+   if (orientation != ETHUMB_THUMB_ORIENT_NONE)
+     _ethumb_image_orient(e, orientation);
+
    evas_object_image_size_get(img, &w, &h);
    if ((w <= 0) || (h <= 0))
      return 0;
index 37e5ba3..b9c0c86 100644 (file)
@@ -119,16 +119,32 @@ typedef enum _Ethumb_Thumb_Aspect
   ETHUMB_THUMB_CROP /**< keep aspect but crop (cut) the largest dimension */
 } Ethumb_Thumb_Aspect;
 
+typedef enum _Ethumb_Thumb_Orientation
+{
+  ETHUMB_THUMB_ORIENT_NONE,     /**< keep orientation as pixel data is */
+  ETHUMB_THUMB_ROTATE_90_CW,    /**< rotate 90° clockwise */
+  ETHUMB_THUMB_ROTATE_180,      /**< rotate 180° */
+  ETHUMB_THUMB_ROTATE_90_CCW,   /**< rotate 90° counter-clockwise */
+  ETHUMB_THUMB_FLIP_HORIZONTAL, /**< flip horizontally */
+  ETHUMB_THUMB_FLIP_VERTICAL,   /**< flip vertically */
+  ETHUMB_THUMB_FLIP_TRANSPOSE,  /**< transpose */
+  ETHUMB_THUMB_FLIP_TRANSVERSE, /**< transverse */
+  ETHUMB_THUMB_ORIENT_ORIGINAL  /**< use orientation from metadata (EXIF-only currently) */
+} Ethumb_Thumb_Orientation;
+
 EAPI void ethumb_thumb_fdo_set(Ethumb *e, Ethumb_Thumb_FDO_Size s) EINA_ARG_NONNULL(1);
 
 EAPI void ethumb_thumb_size_set(Ethumb *e, int tw, int th) EINA_ARG_NONNULL(1);
 EAPI void ethumb_thumb_size_get(const Ethumb *e, int *tw, int *th) EINA_ARG_NONNULL(1);
 
-EAPI void                ethumb_thumb_format_set(Ethumb *e, Ethumb_Thumb_Format f) EINA_ARG_NONNULL(1);
-EAPI Ethumb_Thumb_Format ethumb_thumb_format_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+EAPI void                     ethumb_thumb_format_set(Ethumb *e, Ethumb_Thumb_Format f) EINA_ARG_NONNULL(1);
+EAPI Ethumb_Thumb_Format      ethumb_thumb_format_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+
+EAPI void                     ethumb_thumb_aspect_set(Ethumb *e, Ethumb_Thumb_Aspect a) EINA_ARG_NONNULL(1);
+EAPI Ethumb_Thumb_Aspect      ethumb_thumb_aspect_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
 
-EAPI void                ethumb_thumb_aspect_set(Ethumb *e, Ethumb_Thumb_Aspect a) EINA_ARG_NONNULL(1);
-EAPI Ethumb_Thumb_Aspect ethumb_thumb_aspect_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
+EAPI void                     ethumb_thumb_orientation_set(Ethumb *e, Ethumb_Thumb_Orientation o) EINA_ARG_NONNULL(1);
+EAPI Ethumb_Thumb_Orientation ethumb_thumb_orientation_get(const Ethumb *e) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_PURE;
 
 EAPI void         ethumb_thumb_crop_align_set(Ethumb *e, float x, float y) EINA_ARG_NONNULL(1);
 EAPI void         ethumb_thumb_crop_align_get(const Ethumb *e, float *x, float *y) EINA_ARG_NONNULL(1);
index c521a0b..66796a4 100644 (file)
@@ -9,6 +9,10 @@ AM_CPPFLAGS = \
        @EINA_CFLAGS@ @EVAS_CFLAGS@ @ECORE_EVAS_CFLAGS@ @ECORE_FILE_CFLAGS@ @EDJE_CFLAGS@ \
        @EFL_ETHUMB_BUILD@
 
+if HAVE_LIBEXIF
+AM_CPPFLAGS += @LIBEXIF_CFLAGS@
+endif
+
 includes_HEADERS = Ethumb.h Ethumb_Plugin.h
 includesdir = $(includedir)/ethumb-@VMAJ@
 
@@ -24,6 +28,9 @@ libethumb_la_LIBADD = \
        @EDJE_LIBS@ @ECORE_FILE_LIBS@ @ECORE_EVAS_LIBS@ @EVAS_LIBS@
 libethumb_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@
 
+if HAVE_LIBEXIF
+libethumb_la_LIBADD += @LIBEXIF_LIBS@
+endif
 
 if USE_MODULE_ETHUMBD
 SUBDIRS += client
index 2985e2f..e67fbf2 100644 (file)
@@ -844,7 +844,7 @@ ethumb_client_ethumb_setup(Ethumb_Client *client)
    DBusMessageIter iter, aiter, diter, viter, vaiter;
    Ethumb *e = client->ethumb;
    const char *entry;
-   dbus_int32_t tw, th, format, aspect, quality, compress;
+   dbus_int32_t tw, th, format, aspect, orientation, quality, compress;
    float cx, cy;
    double t;
    const char *theme_file, *group, *swallow;
@@ -900,6 +900,11 @@ ethumb_client_ethumb_setup(Ethumb_Client *client)
    dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &aspect);
    _close_variant_iter(viter);
 
+   _open_variant_iter("orientation", "i", viter);
+   orientation = ethumb_thumb_orientation_get(e);
+   dbus_message_iter_append_basic(&viter, DBUS_TYPE_INT32, &orientation);
+   _close_variant_iter(viter);
+
    _open_variant_iter("crop", "(dd)", viter);
    dbus_message_iter_open_container(&viter, DBUS_TYPE_STRUCT, NULL, &vaiter);
    ethumb_thumb_crop_align_get(e, &cx, &cy);
@@ -1488,6 +1493,48 @@ ethumb_client_aspect_get(const Ethumb_Client *client)
 }
 
 /**
+ * Configure orientation to use for future requests.
+ *
+ * Default value is #ETHUMB_THUMB_ORIENT_ORIGINAL: metadata from the file
+ * will be used to orient pixel data.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ * @param f format identifier to use, either #ETHUMB_THUMB_ORIENT_NONE (0),
+ *        #ETHUMB_THUMB_ROTATE_90_CW (1), #ETHUMB_THUMB_ROTATE_180 (2),
+ *        #ETHUMB_THUMB_ROTATE_90_CCW (3), #ETHUMB_THUMB_FLIP_HORIZONTAL (4),
+ *        #ETHUMB_THUMB_FLIP_VERTICAL (5), #ETHUMB_THUMB_FLIP_TRANSPOSE (6),
+ *        #ETHUMB_THUMB_FLIP_TRANSVERSE (7) or #ETHUMB_THUMB_ORIENT_ORIGINAL
+ *        (8). Default is ORIGINAL.
+ */
+EAPI void
+ethumb_client_orientation_set(Ethumb_Client *client, Ethumb_Thumb_Orientation o)
+{
+   EINA_SAFETY_ON_NULL_RETURN(client);
+
+   client->ethumb_dirty = 1;
+   ethumb_thumb_orientation_set(client->ethumb, o);
+}
+
+/**
+ * Get current orientation in use for requests.
+ *
+ * @param client the client instance to use. Must @b not be @c
+ *        NULL. May be pending connected (can be called before @c
+ *        connected_cb)
+ *
+ * @return orientation in use for future requests.
+ */
+EAPI Ethumb_Thumb_Orientation
+ethumb_client_orientation_get(const Ethumb_Client *client)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(client, 0);
+
+   return ethumb_thumb_orientation_get(client->ethumb);
+}
+
+/**
  * Configure crop alignment in use for future requests.
  *
  * @param client the client instance to use. Must @b not be @c
index 7762480..355ecad 100644 (file)
@@ -122,7 +122,7 @@ EAPI void ethumb_client_on_server_die_callback_set(Ethumb_Client *client, Ethumb
 /**
  * @defgroup Ethumb_Client_Setup Ethumb Client Fine Tune Setup
  *
- * How to fine tune thumbnail generation, setting size, aspect,
+ * How to fine tune thumbnail generation, setting size, aspect, orientation,
  * frames, quality and so on.
  *
  * @{
@@ -135,6 +135,8 @@ EAPI void ethumb_client_format_set(Ethumb_Client *client, Ethumb_Thumb_Format f)
 EAPI Ethumb_Thumb_Format ethumb_client_format_get(const Ethumb_Client *client);
 EAPI void ethumb_client_aspect_set(Ethumb_Client *client, Ethumb_Thumb_Aspect a);
 EAPI Ethumb_Thumb_Aspect ethumb_client_aspect_get(const Ethumb_Client *client);
+EAPI void ethumb_client_orientation_set(Ethumb_Client *client, Ethumb_Thumb_Orientation o);
+EAPI Ethumb_Thumb_Orientation ethumb_client_orientation_get(const Ethumb_Client *client);
 EAPI void ethumb_client_crop_align_set(Ethumb_Client *client, float x, float y);
 EAPI void ethumb_client_crop_align_get(const Ethumb_Client *client, float *x, float *y);
 EAPI void ethumb_client_quality_set(Ethumb_Client *client, int quality);
index a647c5b..873b979 100644 (file)
@@ -20,6 +20,7 @@ struct _Ethumb
    int tw, th;
    int format;
    int aspect;
+   int orientation;
    float crop_x, crop_y;
    int quality;
    int compress;