[vlc-devel] [PATCH] Automatically orient JPEG image based on orientation flag, if set

Jean-Baptiste Kempf jb at videolan.org
Sun Apr 17 07:44:42 CEST 2016


On 17 Apr, Wayne McDougall wrote :
> +#define DEC_CFG_PREFIX "jpeg-"
> +#define DEC_AUTOORIENT_TEXT N_( "Automatic Orientation" )
> +#define DEC_AUTOORIENT_LONGTEXT N_( "Automatic orientation based on EXIF
> orientation flag." )

In what case don't we want it automatic, tbh?

> +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;
> +}

I'm surprised we don't have the right function in vlc_common.h
(GetWBE/GetWLE)

> +/* 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" );

var_InheritBool

> +    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;
> +            }
> +        }
> +    }
> +

This looks good!

With my kindest regards,

-- 
Jean-Baptiste Kempf
http://www.jbkempf.com/ - +33 672 704 734
Sent from my Electronic Device


More information about the vlc-devel mailing list