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