This patch is adding a RLoadOrientedImage that can be used
to extract the EXIF orientation field of a given image
(only JPEG for now), the resulting image is automatically flipped/rotated.


---
 wrlib/libwraster.map |   2 +
 wrlib/load.c         |  60 +++++++++++++-
 wrlib/misc.c         | 227 ++++++++++++++++++++++++++++++++++++++++++++++++---
 wrlib/wraster.h      |  19 +++++
 4 files changed, 294 insertions(+), 14 deletions(-)

diff --git a/wrlib/libwraster.map b/wrlib/libwraster.map
index cf36401..8dc5fc7 100644
--- a/wrlib/libwraster.map
+++ b/wrlib/libwraster.map
@@ -52,7 +52,9 @@ LIBWRASTER3
     RGetXImage;
     RHSVtoRGB;
     RLightImage;
+    RGetImageOrientation;
     RLoadImage;
+    RLoadOrientedImage;
     RMakeCenteredImage;
     RMakeTiledImage;
     RMessageForError;
diff --git a/wrlib/load.c b/wrlib/load.c
index 7c2e6af..cd09990 100644
--- a/wrlib/load.c
+++ b/wrlib/load.c
@@ -146,7 +146,7 @@ void RReleaseCache(void)
  }
 }

-RImage *RLoadImage(RContext * context, const char *file, int index)
+static RImage *load_image(RContext *context, const char *file, int
index, int orientation)
 {
  RImage *image = NULL;
  int i;
@@ -236,6 +236,49 @@ RImage *RLoadImage(RContext * context, const char
*file, int index)
  return NULL;
  }

+ if (image && (orientation > ROrientationNormal)) {
+ RImage *tmp = NULL;
+ switch (orientation) {
+ case ROrientationFlipHorizontal:
+ tmp = RHorizontalFlipImage(image);
+ break;
+ case ROrientationRotate180:
+ tmp = RRotateImage(image, 180);
+ break;
+ case ROrientationFlipVertical:
+ tmp = RVerticalFlipImage(image);
+ break;
+ case ROrientationTranspose: {
+ RImage *tmp2;
+ tmp2 = RVerticalFlipImage(image);
+ if (tmp2) {
+ tmp = RRotateImage(tmp2, 90);
+ RReleaseImage(tmp2);
+ }
+ }
+ break;
+ case ROrientationRotate90:
+ tmp = RRotateImage(image, 90);
+ break;
+ case ROrientationTransverse: {
+ RImage *tmp2;
+ tmp2 = RVerticalFlipImage(image);
+ if (tmp2) {
+ tmp = RRotateImage(tmp2, 270);
+ RReleaseImage(tmp2);
+ }
+ }
+ break;
+ case ROrientationRotate270:
+ tmp = RRotateImage(image, 270);
+ break;
+ }
+ if (tmp) {
+ RReleaseImage(image);
+ image = tmp;
+ }
+ }
+
  /* store image in cache */
  if (RImageCacheSize > 0 && image &&
     (RImageCacheMaxImage == 0 || RImageCacheMaxImage >= image->width
* image->height)) {
@@ -275,6 +318,17 @@ RImage *RLoadImage(RContext * context, const char
*file, int index)
  return image;
 }

+RImage *RLoadImage(RContext *context, const char *file, int index)
+{
+ return load_image(context, file, index, ROrientationNormal);
+}
+
+RImage *RLoadOrientedImage(RContext *context, const char *file, int index)
+{
+ int orientation = RGetImageOrientation(file);
+ return load_image(context, file, index, orientation);
+}
+
 char *RGetImageFileFormat(const char *file)
 {
  switch (identFile(file)) {
@@ -370,8 +424,8 @@ static WRImgFormat identFile(const char *path)
  return IM_GIF;

  /* check for WEBP */
- if (buffer[ 0] == 'R' && buffer[ 1] == 'I' && buffer[ 2] == 'F' &&
buffer[ 3] == 'F' &&
-    buffer[ 8] == 'W' && buffer[ 9] == 'E' && buffer[10] == 'B' &&
buffer[11] == 'P' &&
+ if (buffer[0] == 'R' && buffer[1] == 'I' && buffer[2] == 'F' &&
buffer[3] == 'F' &&
+    buffer[8] == 'W' && buffer[9] == 'E' && buffer[10] == 'B' &&
buffer[11] == 'P' &&
     buffer[12] == 'V' && buffer[13] == 'P' && buffer[14] == '8' &&
     (buffer[15] == ' '       /* Simple File Format (Lossy) */
      || buffer[15] == 'L'    /* Simple File Format (Lossless) */
diff --git a/wrlib/misc.c b/wrlib/misc.c
index 615777e..d5a63a5 100644
--- a/wrlib/misc.c
+++ b/wrlib/misc.c
@@ -30,7 +30,7 @@
 #include "convert.h"


-void RBevelImage(RImage * image, int bevel_type)
+void RBevelImage(RImage *image, int bevel_type)
 {
  RColor color;
  RColor cdelta;
@@ -83,7 +83,7 @@ void RBevelImage(RImage * image, int bevel_type)
  }
 }

-void RFillImage(RImage * image, const RColor * color)
+void RFillImage(RImage *image, const RColor *color)
 {
  unsigned char *d = image->data;
  unsigned lineSize;
@@ -97,9 +97,8 @@ void RFillImage(RImage * image, const RColor * color)
  *d++ = color->alpha;
  }
  lineSize = image->width * 4;
- for (i = 1; i < image->height; i++, d += lineSize) {
+ for (i = 1; i < image->height; i++, d += lineSize)
  memcpy(d, image->data, lineSize);
- }
  } else {
  for (i = 0; i < image->width; i++) {
  *d++ = color->red;
@@ -107,13 +106,12 @@ void RFillImage(RImage * image, const RColor * color)
  *d++ = color->blue;
  }
  lineSize = image->width * 3;
- for (i = 1; i < image->height; i++, d += lineSize) {
+ for (i = 1; i < image->height; i++, d += lineSize)
  memcpy(d, image->data, lineSize);
- }
  }
 }

-void RClearImage(RImage * image, const RColor * color)
+void RClearImage(RImage *image, const RColor *color)
 {
  unsigned char *d = image->data;
  unsigned lineSize;
@@ -128,9 +126,8 @@ void RClearImage(RImage * image, const RColor * color)
  *d++ = 0xff;
  }
  lineSize = image->width * 4;
- for (i = 1; i < image->height; i++, d += lineSize) {
+ for (i = 1; i < image->height; i++, d += lineSize)
  memcpy(d, image->data, lineSize);
- }
  } else {
  for (i = 0; i < image->width; i++) {
  *d++ = color->red;
@@ -138,9 +135,8 @@ void RClearImage(RImage * image, const RColor * color)
  *d++ = color->blue;
  }
  lineSize = image->width * 3;
- for (i = 1; i < image->height; i++, d += lineSize) {
+ for (i = 1; i < image->height; i++, d += lineSize)
  memcpy(d, image->data, lineSize);
- }
  }
  } else {
  int bytes = image->width * image->height;
@@ -199,6 +195,215 @@ void RLightImage(RImage *image, const RColor *color)
  }
 }

+/*
+ Based on jpegexiforient.c
+ Full src available at
http://ftp.freebsd.org/pub/FreeBSD/distfiles/jpeg8b/jpegexiforient.c
+
+ Tested with img samples from
http://github.com/recurser/exif-orientation-examples
+*/
+int RGetImageOrientation(const char *file)
+{
+ int c1, c2;
+ int set_flag = ROrientationUnknown;
+ unsigned int length, i;
+ /* Flag for byte order */
+ int is_motorola;
+ unsigned int exif_offset, offset, number_of_tags, tagnum;
+ FILE *myfile;
+ unsigned char exif_data[65536L];
+
+ myfile = fopen(file, "rb");
+ if (myfile == NULL)
+ return 0;
+
+ /* Read File head, check for JPEG SOI */
+ for (i = 0; i < 2; i++) {
+ int c;
+ c = getc(myfile);
+ if (c == EOF)
+ goto clean_return;
+ exif_data[i] = (unsigned char) c;
+ }
+ /* Modified from the original code as the marker could be located at
any marker positions */
+ if (exif_data[0] != 0xFF || exif_data[1] != 0xD8)
+ goto clean_return;
+
+ /* search exif data marker APP1 0xFFE1 */
+ exif_offset = 2;
+ while (exif_offset) {
+ for (i = 0; i < 2; i++) {
+ int c;
+ c = getc(myfile);
+ if (c == EOF)
+ exif_offset = 0;
+ exif_data[i] = (unsigned char) c;
+ }
+
+ /* Get the marker parameter length count */
+ c1 = getc(myfile);
+ if (c1 == EOF)
+ exif_offset = 0;
+ c2 = getc(myfile);
+ if (c2 == EOF)
+ exif_offset = 0;
+ length = (((unsigned int) c1) << 8) + ((unsigned int) c2);
+
+ /* Length includes itself, so must be at least 2 */
+ /* Following Exif data length must be at least 6 */
+ if (length < 8)
+ exif_offset = 0;
+
+ exif_offset += 2;
+
+ /* No marker tag. */
+ if (exif_data[0] != 0xFF)
+ exif_offset = 0;
+
+ /* Exif if APP1 is found. */
+ if (exif_data[1] == 0xE1)
+ break;
+
+ exif_offset += length;
+
+ /* Some other marker found, seek to next one. */
+ if (-1 == fseek(myfile, length - 2, SEEK_CUR))
+ /* Can't seek. */
+ exif_offset = 0;
+ }
+
+ /* check if something went wrong */
+ if (!exif_offset)
+ goto clean_return;
+
+ length -= 8;
+ /* Read Exif head, check for "Exif" */
+ for (i = 0; i < 6; i++) {
+ int c;
+ c = getc(myfile);
+ if (c == EOF)
+ goto clean_return;
+ exif_data[i] = (unsigned char) c;
+ }
+
+ if (exif_data[0] != 0x45 || exif_data[1] != 0x78 || exif_data[2] != 0x69 ||
+ exif_data[3] != 0x66 || exif_data[4] != 0 || exif_data[5] != 0)
+ goto clean_return;
+
+ /* Read Exif body */
+ for (i = 0; i < length; i++) {
+ int c;
+ c = getc(myfile);
+ if (c == EOF)
+ goto clean_return;
+ exif_data[i] = (unsigned char) c;
+ }
+
+ /* Length of an IFD entry */
+ if (length < 12)
+ goto clean_return;
+
+ /* Discover byte order */
+ if (exif_data[0] == 0x49 && exif_data[1] == 0x49)
+ is_motorola = 0;
+ else {
+ if (exif_data[0] == 0x4D && exif_data[1] == 0x4D)
+ is_motorola = 1;
+ else
+    goto clean_return;
+ }
+
+ /* Check Tag Mark */
+ if (is_motorola) {
+ if (exif_data[2] != 0)
+ goto clean_return;
+ if (exif_data[3] != 0x2A)
+ goto clean_return;
+ } else {
+ if (exif_data[3] != 0)
+ goto clean_return;
+ if (exif_data[2] != 0x2A)
+ goto clean_return;
+ }
+
+ /* Get first IFD offset (offset to IFD0) */
+ if (is_motorola) {
+ if (exif_data[4] != 0)
+ goto clean_return;
+ if (exif_data[5] != 0)
+ goto clean_return;
+ offset = exif_data[6];
+ offset <<= 8;
+ offset += exif_data[7];
+ } else {
+ if (exif_data[7] != 0)
+ goto clean_return;
+ if (exif_data[6] != 0)
+ goto clean_return;
+ offset = exif_data[5];
+ offset <<= 8;
+ offset += exif_data[4];
+ }
+
+ /* check end of data segment */
+ if (offset > length - 2)
+ goto clean_return;
+
+ /* Get the number of directory entries contained in this IFD */
+ if (is_motorola) {
+ number_of_tags = exif_data[offset];
+ number_of_tags <<= 8;
+ number_of_tags += exif_data[offset+1];
+ } else {
+ number_of_tags = exif_data[offset+1];
+ number_of_tags <<= 8;
+ number_of_tags += exif_data[offset];
+ }
+
+ if (number_of_tags == 0)
+ goto clean_return;
+ offset += 2;
+
+ /* Search for Orientation Tag in IFD0 */
+ for (;;) {
+ /* check end of data segment */
+ if (offset > length - 12)
+ goto clean_return;
+ /* Get Tag number */
+ if (is_motorola) {
+ tagnum = exif_data[offset];
+ tagnum <<= 8;
+ tagnum += exif_data[offset+1];
+ } else {
+ tagnum = exif_data[offset+1];
+ tagnum <<= 8;
+ tagnum += exif_data[offset];
+ }
+
+ /* found Orientation Tag */
+ if (tagnum == 0x0112)
+ break;
+ if (--number_of_tags == 0)
+ goto clean_return;
+ offset += 12;
+ }
+
+ /* Get the Orientation value */
+ if (is_motorola) {
+ if (exif_data[offset+8] != 0)
+ goto clean_return;
+ set_flag = exif_data[offset+9];
+ } else {
+ if (exif_data[offset+9] != 0)
+ goto clean_return;
+ set_flag = exif_data[offset+8];
+ }
+ if (set_flag > 8)
+ return ROrientationUnknown;
+clean_return:
+ fclose(myfile);
+ return set_flag;
+}
+
 const char *RMessageForError(int errorCode)
 {
  switch (errorCode) {
diff --git a/wrlib/wraster.h b/wrlib/wraster.h
index 45cbcc6..ee460bc 100644
--- a/wrlib/wraster.h
+++ b/wrlib/wraster.h
@@ -264,6 +264,19 @@ enum {
     RVerticalGradient = 3,
     RDiagonalGradient = 4
 };
+
+enum {
+ ROrientationUnknown = 0,
+ ROrientationNormal = 1,
+ ROrientationFlipHorizontal = 2,
+ ROrientationRotate180 = 3,
+ ROrientationFlipVertical = 4,
+ ROrientationTranspose = 5,
+ ROrientationRotate90 = 6,
+ ROrientationTransverse = 7,
+ ROrientationRotate270 = 8
+};
+
 /* for backwards compatibility */
 #define RGRD_HORIZONTAL  RHorizontalGradient
 #define RGRD_VERTICAL RVerticalGradient
@@ -326,6 +339,8 @@ RImage *RCreateImageFromDrawable(RContext
*context, Drawable drawable,

 RImage *RLoadImage(RContext *context, const char *file, int index);

+RImage *RLoadOrientedImage(RContext *context, const char *file, int index);
+
 RImage* RRetainImage(RImage *image);

 void RReleaseImage(RImage *image);
@@ -369,6 +384,8 @@ RImage *RScaleImage(RImage *image, unsigned
new_width, unsigned new_height);
 RImage *RSmoothScaleImage(RImage *src, unsigned new_width,
                           unsigned new_height);

+int RGetImageOrientation(const char *file);
+
 RImage *RRotateImage(RImage *image, float angle);

 RImage *RVerticalFlipImage(RImage *image);
@@ -472,6 +489,8 @@ const char *RMessageForError(int errorCode);

 int RBlurImage(RImage *image);

+int RGetImageOrientation(const char *file);
+
 /****** Global Variables *******/

 extern int RErrorCode;
-- 
1.8.3.2

Attachment: 0001-wrlib-add-EXIF-orientation-feature.patch
Description: Binary data

Reply via email to