<div dir="ltr"><div>My thanks to Jean-Baptiste and Francois for kindly and patiently addressing my ignorance.<br></div>I humbly offer a new version of the patch.<br><br>[PATCH] Automatically orient JPEG image display according to<br> embedded EXIF orientation tag, if set<br><br>---<br> modules/codec/jpeg.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++-<br> 1 file changed, 246 insertions(+), 4 deletions(-)<br><br>diff --git a/modules/codec/jpeg.c b/modules/codec/jpeg.c<br>index 0e3ae9d..d3b513d 100644<br>--- a/modules/codec/jpeg.c<br>+++ b/modules/codec/jpeg.c<br>@@ -1,7 +1,7 @@<br> /*****************************************************************************<br>  * jpeg.c: jpeg decoder module making use of libjpeg.<br>  *****************************************************************************<br>- * Copyright (C) 2013-2014 VLC authors and VideoLAN<br>+ * Copyright (C) 2013-2014,2016 VLC authors and VideoLAN<br>  *<br>  * Authors: Maxim Bublis <<a href="mailto:b@codemonkey.ru">b@codemonkey.ru</a>><br>  *<br>@@ -50,6 +50,22 @@<br> #define ENC_QUALITY_LONGTEXT N_( "Quality level " \<br>                                  "for encoding (this can enlarge or reduce output image size)." )<br> <br>+#define DEC_CFG_PREFIX "jpeg-"<br>+#define DEC_AUTOORIENT_TEXT N_( "Automatic Orientation" )<br>+#define DEC_AUTOORIENT_LONGTEXT N_( "Automatic orientation based on EXIF orientation flag." )<br>+<br>+const char leth[] = {<br>+    0x49, 0x49, 0x2a, 0x00<br>+};                                              // Little endian TIFF header<br>+const char beth[] = {<br>+    0x4d, 0x4d, 0x00, 0x2a<br>+};                                              // Big endian TIFF header<br>+<br>+#define G_LITTLE_ENDIAN     1234<br>+#define G_BIG_ENDIAN        4321<br>+<br>+#define EXIF_JPEG_MARKER    0xE1<br>+#define EXIF_IDENT_STRING   "Exif\000\000"<br> <br> /*<br>  * jpeg common descriptor<br>@@ -107,6 +123,8 @@ set_description( N_( "JPEG image decoder" ) )<br> set_capability( "decoder", 1000 )<br> set_callbacks( OpenDecoder, CloseDecoder )<br> add_shortcut( "jpeg" )<br>+add_bool( DEC_CFG_PREFIX "autoorient", true,<br>+          DEC_AUTOORIENT_TEXT, DEC_AUTOORIENT_LONGTEXT, true )<br> <br> /* encoder submodule */<br> add_submodule()<br>@@ -178,6 +196,191 @@ static int OpenDecoder ( vlc_object_t * p_this )<br>     return VLC_SUCCESS;<br> }<br> <br>+LOCAL( unsigned short )<br>+de_get16( void * ptr, uint endian ) {<br>+    unsigned short val;<br>+<br>+    memcpy( &val, ptr, sizeof( val ) );<br>+    if ( endian == G_BIG_ENDIAN )<br>+    {<br>+        #ifndef WORDS_BIGENDIAN<br>+        val = bswap16( val );<br>+        #endif<br>+    }<br>+    else<br>+    {<br>+        #ifdef WORDS_BIGENDIAN<br>+        val = bswap16( val );<br>+        #endif<br>+    }<br>+    return val;<br>+}<br>+<br>+LOCAL( unsigned int )<br>+de_get32( void * ptr, uint endian ) {<br>+    unsigned int val;<br>+<br>+    memcpy( &val, ptr, sizeof( val ) );<br>+    if ( endian == G_BIG_ENDIAN )<br>+    {<br>+        #ifndef WORDS_BIGENDIAN<br>+        val = bswap32( val );<br>+        #endif<br>+    }<br>+    else<br>+    {<br>+        #ifdef WORDS_BIGENDIAN<br>+        val = bswap32( val );<br>+        #endif<br>+    }<br>+    return val;<br>+}<br>+<br>+/* jpeg_GetOrientation function based on code from GdkPixbuf library - JPEG image loader<br>+ * <a href="http://src.gnu-darwin.org/ports/x11-toolkits/gtk20/work/gtk+-2.12.3/gdk-pixbuf/io-jpeg.c">http://src.gnu-darwin.org/ports/x11-toolkits/gtk20/work/gtk+-2.12.3/gdk-pixbuf/io-jpeg.c</a><br>+ *<br>+ * Copyright (C) 1999 Michael Zucchi<br>+ * Copyright (C) 1999 The Free Software Foundation<br>+ */<br>+<br>+LOCAL( int )<br>+jpeg_GetOrientation( j_decompress_ptr cinfo ) {<br>+    /* This function looks through the meta data in the libjpeg decompress structure to<br>+       determine if an EXIF Orientation tag is present and if so return its value (1-8).<br>+       If no EXIF Orientation tag is found 0 (zero) is returned. */<br>+<br>+    uint i;                    /* index into working buffer */<br>+    ushort tag_type;           /* endianed tag type extracted from tiff header */<br>+    uint ret;                  /* Return value */<br>+    uint offset;               /* de-endianed offset in various situations */<br>+    uint tags;                 /* number of tags in current ifd */<br>+    uint type;                 /* de-endianed type of tag used as index into types[] */<br>+    uint count;                /* de-endianed count of elements in a tag */<br>+    uint tiff = 0;             /* offset to active tiff header */<br>+    uint endian = 0;           /* detected endian of data */<br>+<br>+    jpeg_saved_marker_ptr exif_marker;      /* Location of the Exif APP1 marker */<br>+    jpeg_saved_marker_ptr cmarker;          /* Location to check for Exif APP1 marker */<br>+<br>+    /* check for Exif marker (also called the APP1 marker) */<br>+    exif_marker = NULL;<br>+    cmarker = cinfo->marker_list;<br>+<br>+    while ( cmarker )<br>+    {<br>+        if ( cmarker->marker == EXIF_JPEG_MARKER )<br>+        {<br>+            /* The Exif APP1 marker should contain a unique<br>+               identification string ("Exif\0\0"). Check for it. */<br>+            if ( !memcmp( cmarker->data, EXIF_IDENT_STRING, 6 ) )<br>+            {<br>+                exif_marker = cmarker;<br>+            }<br>+        }<br>+        cmarker = cmarker->next;<br>+    }<br>+<br>+    /* Did we find the Exif APP1 marker? */<br>+    if ( exif_marker == NULL )<br>+        return 0;<br>+<br>+    /* Do we have enough data? */<br>+    if ( exif_marker->data_length < 32 )<br>+        return 0;<br>+<br>+    /* Check for TIFF header and catch endianess */<br>+    i = 0;<br>+<br>+    /* Just skip data until TIFF header - it should be within 16 bytes from marker start.<br>+       Normal structure relative to APP1 marker -<br>+            0x0000: APP1 marker entry = 2 bytes<br>+            0x0002: APP1 length entry = 2 bytes<br>+            0x0004: Exif Identifier entry = 6 bytes<br>+            0x000A: Start of TIFF header (Byte order entry) - 4 bytes<br>+                    - This is what we look for, to determine endianess.<br>+            0x000E: 0th IFD offset pointer - 4 bytes<br>+<br>+            exif_marker->data points to the first data after the APP1 marker<br>+            and length entries, which is the exif identification string.<br>+            The TIFF header should thus normally be found at i=6, below,<br>+            and the pointer to IFD0 will be at 6+4 = 10.<br>+    */<br>+<br>+    while ( i < 16 )<br>+    {<br>+        /* Little endian TIFF header */<br>+        if ( memcmp( &exif_marker->data[i], leth, 4 ) == 0 )<br>+        {<br>+            endian = G_LITTLE_ENDIAN;<br>+        }<br>+        /* Big endian TIFF header */<br>+        else<br>+        if ( memcmp( &exif_marker->data[i], beth, 4 ) == 0 )<br>+        {<br>+            endian = G_BIG_ENDIAN;<br>+        }<br>+        /* Keep looking through buffer */<br>+        else<br>+        {<br>+            i++;<br>+            continue;<br>+        }<br>+        /* We have found either big or little endian TIFF header */<br>+        tiff = i;<br>+        break;<br>+    }<br>+<br>+    /* So did we find a TIFF header or did we just hit end of buffer? */<br>+    if ( tiff == 0 )<br>+        return 0;<br>+<br>+    /* Read out the offset pointer to IFD0 */<br>+    offset = de_get32( &exif_marker->data[i] + 4, endian );<br>+    i = i + offset;<br>+<br>+    /* Check that we still are within the buffer and can read the tag count */<br>+<br>+    if ( ( i + 2 ) > exif_marker->data_length )<br>+        return 0;<br>+<br>+    /* Find out how many tags we have in IFD0. As per the TIFF spec, the first<br>+       two bytes of the IFD contain a count of the number of tags. */<br>+    tags = de_get16( &exif_marker->data[i], endian );<br>+    i = i + 2;<br>+<br>+    /* Check that we still have enough data for all tags to check. The tags<br>+       are listed in consecutive 12-byte blocks. The tag ID, type, size, and<br>+       a pointer to the actual value, are packed into these 12 byte entries. */<br>+    if ( ( i + tags * 12 ) > exif_marker->data_length )<br>+        return 0;<br>+<br>+    /* Check through IFD0 for tags of interest */<br>+    while ( tags-- )<br>+    {<br>+        tag_type = de_get16( &exif_marker->data[i], endian );<br>+        /* Is this the orientation tag? */<br>+        if ( tag_type == 0x112 )<br>+        {<br>+            type = de_get16( &exif_marker->data[i + 2], endian );<br>+            count = de_get32( &exif_marker->data[i + 4], endian );<br>+<br>+            /* Check that type and count fields are OK. The orientation field<br>+               will consist of a single (count=1) 2-byte integer (type=3). */<br>+            if ( type != 3 || count != 1 )<br>+                return 0;<br>+<br>+            /* Return the orientation value. Within the 12-byte block, the<br>+               pointer to the actual data is at offset 8. */<br>+            ret = de_get16( &exif_marker->data[i + 8], endian );<br>+            return ret <= 8 ? ret : 0;<br>+        }<br>+        /* move the pointer to the next 12-byte tag field. */<br>+        i = i + 12;<br>+    }<br>+<br>+    return 0;     /* No EXIF Orientation tag found */<br>+}<br>+<br> /*<br>  * This function must be fed with a complete compressed frame.<br>  */<br>@@ -186,8 +389,9 @@ static picture_t * DecodeBlock ( decoder_t * p_dec, block_t * * pp_block )<br>     decoder_sys_t * p_sys = p_dec->p_sys;<br>     block_t * p_block;<br>     picture_t * p_pic = 0;<br>-<br>     JSAMPARRAY p_row_pointers = NULL;<br>+    bool b_autoorient;<br>+    int i_otag;<br> <br>     if ( !pp_block || !*pp_block )<br>     {<br>@@ -211,10 +415,49 @@ static picture_t * DecodeBlock ( decoder_t * p_dec, block_t * * pp_block )<br> <br>     jpeg_create_decompress( &p_sys->p_jpeg );<br>     jpeg_mem_src( &p_sys->p_jpeg, p_block->p_buffer, p_block->i_buffer );<br>-    jpeg_read_header( &p_sys->p_jpeg, TRUE );<br>+    b_autoorient = var_CreateGetBool( p_dec, "jpeg-autoorient" );<br>+<br>+    if ( b_autoorient )<br>+    {<br>+        jpeg_save_markers( &p_sys->p_jpeg, EXIF_JPEG_MARKER, 0xffff );<br>+    }<br> <br>+    jpeg_read_header( &p_sys->p_jpeg, TRUE );<br>     p_sys->p_jpeg.out_color_space = JCS_RGB;<br> <br>+    if ( b_autoorient )<br>+    {<br>+        i_otag = jpeg_GetOrientation( &p_sys->p_jpeg );<br>+        if ( i_otag > 1 )<br>+        {<br>+            msg_Info( p_dec, "Orientation is %d", i_otag );<br>+            switch ( i_otag )<br>+            {<br>+            case 2:<br>+                p_dec->fmt_out.video.orientation = ORIENT_TOP_RIGHT;<br>+                break;<br>+            case 3:<br>+                p_dec->fmt_out.video.orientation = ORIENT_BOTTOM_RIGHT;<br>+                break;<br>+            case 4:<br>+                p_dec->fmt_out.video.orientation = ORIENT_BOTTOM_LEFT;<br>+                break;<br>+            case 5:<br>+                p_dec->fmt_out.video.orientation = ORIENT_LEFT_TOP;<br>+                break;<br>+            case 6:<br>+                p_dec->fmt_out.video.orientation = ORIENT_ROTATED_90;<br>+                break;<br>+            case 7:<br>+                p_dec->fmt_out.video.orientation = ORIENT_RIGHT_BOTTOM;<br>+                break;<br>+            case 8:<br>+            default:<br>+                p_dec->fmt_out.video.orientation = ORIENT_ROTATED_270;<br>+            }<br>+        }<br>+    }<br>+<br>     jpeg_start_decompress( &p_sys->p_jpeg );<br> <br>     /* Set output properties */<br>@@ -265,7 +508,6 @@ static picture_t * DecodeBlock ( decoder_t * p_dec, block_t * * pp_block )<br> <br>     jpeg_destroy_decompress( &p_sys->p_jpeg );<br>     free( p_row_pointers );<br>-<br>     block_Release( p_block );<br>     return NULL;<br> }<br>-- <br>2.7.4<br><br></div><div class="gmail_extra"><br><div class="gmail_quote">On 16 April 2016 at 21:39, Francois Cartegnie <span dir="ltr"><<a href="mailto:fcvlcdev@free.fr" target="_blank">fcvlcdev@free.fr</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">Le 16/04/2016 11:19, Wayne McDougall a écrit :<br>
> Great. Slideshow runs - I can even export to a movie. *BUT* at the moment, some<br>
> images are displayed incorrectly. The JPEG encodes the orientation in the<br>
> header, but currently VLC ignores that.<br>
><br>
> Feel free to tell me that there is some better way of doing it. I have learnt a<br>
> lot, even if is thrown away. ;-)<br>
<br>
</span>As mentioned, video output has support for rotation.<br>
<br>
fmt->video.orientation<br>
<span class="HOEnZb"><font color="#888888"><br>
Francois<br>
</font></span><div class="HOEnZb"><div class="h5">_______________________________________________<br>
vlc-devel mailing list<br>
To unsubscribe or modify your subscription options:<br>
<a href="https://mailman.videolan.org/listinfo/vlc-devel" rel="noreferrer" target="_blank">https://mailman.videolan.org/listinfo/vlc-devel</a><br>
</div></div></blockquote></div><br></div>