<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>