From d7790c494e9df04b54a350e18658fe63b1162b0d Mon Sep 17 00:00:00 2001
From: Bernie Purcell <bitmap@videolan.org>
Date: Wed, 5 Sep 2007 05:14:33 +0000
Subject: [PATCH] Move more of the code for USF subtitles decoding out into
 separate module. It has almost no code in common with normal subtitles and
 ASS subtitle decoding, and this split further simplifies what belongs to
 what.

---
 configure.ac                       |   2 +-
 modules/codec/subtitles/Modules.am |   3 +-
 modules/codec/subtitles/subsass.c  |   3 +-
 modules/codec/subtitles/subsdec.c  | 476 +-----------------
 modules/codec/subtitles/subsdec.h  |  14 +-
 modules/codec/subtitles/subsusf.c  | 746 ++++++++++++++++++++++++++++-
 6 files changed, 745 insertions(+), 499 deletions(-)

diff --git a/configure.ac b/configure.ac
index d82f2c4340a5..ff9438b6fc3b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1193,7 +1193,7 @@ dnl  default modules
 dnl
 VLC_ADD_PLUGINS([dummy logger memcpy])
 VLC_ADD_PLUGINS([mpgv mpga m4v m4a h264 vc1 ps pva avi asf mp4 rawdv rawvid nsv real aiff mjpeg demuxdump flacsys tta])
-VLC_ADD_PLUGINS([cvdsub svcdsub spudec subsdec dvbsub mpeg_audio lpcm a52 dts cinepak flac])
+VLC_ADD_PLUGINS([cvdsub svcdsub spudec subsdec subsusf dvbsub mpeg_audio lpcm a52 dts cinepak flac])
 VLC_ADD_PLUGINS([deinterlace invert adjust transform wave ripple psychedelic gradient motionblur rv32 rotate noise grain extract sharpen seamcarving])
 VLC_ADD_PLUGINS([converter_fixed mono])
 VLC_ADD_PLUGINS([trivial_resampler ugly_resampler])
diff --git a/modules/codec/subtitles/Modules.am b/modules/codec/subtitles/Modules.am
index 037a4cf450af..a375df866c17 100644
--- a/modules/codec/subtitles/Modules.am
+++ b/modules/codec/subtitles/Modules.am
@@ -1 +1,2 @@
-SOURCES_subsdec = subsass.c subsusf.c subsdec.c subsdec.h
+SOURCES_subsdec = subsass.c subsdec.c subsdec.h
+SOURCES_subsusf = subsusf.c subsdec.h
diff --git a/modules/codec/subtitles/subsass.c b/modules/codec/subtitles/subsass.c
index bbba60470140..fc4a3d101787 100644
--- a/modules/codec/subtitles/subsass.c
+++ b/modules/codec/subtitles/subsass.c
@@ -2,12 +2,11 @@
  * subsass.c : ASS/SSA subtitles decoder
  *****************************************************************************
  * Copyright (C) 2000-2006 the VideoLAN team
- * $Id: subsdec.c 20996 2007-08-05 20:01:21Z jb $
+ * $Id$
  *
  * Authors: Gildas Bazin <gbazin@videolan.org>
  *          Samuel Hocevar <sam@zoy.org>
  *          Derk-Jan Hartman <hartman at videolan dot org>
- *          Bernie Purcell <b dot purcell at adbglobal dot com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/modules/codec/subtitles/subsdec.c b/modules/codec/subtitles/subsdec.c
index 2e38cbff2029..1d64f9839835 100644
--- a/modules/codec/subtitles/subsdec.c
+++ b/modules/codec/subtitles/subsdec.c
@@ -2,12 +2,12 @@
  * subsdec.c : text subtitles decoder
  *****************************************************************************
  * Copyright (C) 2000-2006 the VideoLAN team
- * $Id: subsdec.c 20996 2007-08-05 20:01:21Z jb $
+ * $Id$
  *
  * Authors: Gildas Bazin <gbazin@videolan.org>
  *          Samuel Hocevar <sam@zoy.org>
  *          Derk-Jan Hartman <hartman at videolan dot org>
- *          Bernie Purcell <b dot purcell at adbglobal dot com>
+ *          Bernie Purcell <bitmap@videolan.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -40,9 +40,7 @@ static subpicture_t   *DecodeBlock   ( decoder_t *, block_t ** );
 static subpicture_t   *ParseText     ( decoder_t *, block_t * );
 static char           *StripTags      ( char * );
 static char           *CreateHtmlSubtitle ( char * );
-static int            ParseImageAttachments( decoder_t *p_dec );
 
-static int            ParsePositionAttributeList( char *, int *, int *, int * );
 
 /*****************************************************************************
  * Module descriptor.
@@ -142,7 +140,6 @@ static int OpenDecoder( vlc_object_t *p_this )
     vlc_value_t    val;
 
     if( p_dec->fmt_in.i_codec != VLC_FOURCC('s','u','b','t') &&
-        p_dec->fmt_in.i_codec != VLC_FOURCC('u','s','f',' ') &&
         p_dec->fmt_in.i_codec != VLC_FOURCC('s','s','a',' ') )
     {
         return VLC_EGENERIC;
@@ -231,18 +228,11 @@ static int OpenDecoder( vlc_object_t *p_this )
     var_Get( p_dec, "subsdec-align", &val );
     p_sys->i_align = val.i_int;
 
-    ParseImageAttachments( p_dec );
-
     if( p_dec->fmt_in.i_codec == VLC_FOURCC('s','s','a',' ') && var_CreateGetBool( p_dec, "subsdec-formatted" ) )
     {
         if( p_dec->fmt_in.i_extra > 0 )
             ParseSSAHeader( p_dec );
     }
-    else if( p_dec->fmt_in.i_codec == VLC_FOURCC('u','s','f',' ') && var_CreateGetBool( p_dec, "subsdec-formatted" ) )
-    {
-        if( p_dec->fmt_in.i_extra > 0 )
-            ParseUSFHeader( p_dec );
-    }
 
     return VLC_SUCCESS;
 }
@@ -425,8 +415,7 @@ static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block )
     }
 
     /* Decode and format the subpicture unit */
-    if( p_dec->fmt_in.i_codec != VLC_FOURCC('s','s','a',' ') &&
-        p_dec->fmt_in.i_codec != VLC_FOURCC('u','s','f',' ') )
+    if( p_dec->fmt_in.i_codec != VLC_FOURCC('s','s','a',' ') )
     {
         /* Normal text subs, easy markup */
         p_spu->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align;
@@ -451,11 +440,6 @@ static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block )
         /* Decode SSA/USF strings */
         if( p_dec->fmt_in.i_codec == VLC_FOURCC('s','s','a',' ') )
             ParseSSAString( p_dec, psz_subtitle, p_spu );
-        else
-        {
-            p_spu->pf_destroy_region( VLC_OBJECT(p_dec), p_spu->p_region );
-            p_spu->p_region = ParseUSFString( p_dec, psz_subtitle, p_spu );
-        }
 
         p_spu->i_start = p_block->i_pts;
         p_spu->i_stop = p_block->i_pts + p_block->i_length;
@@ -469,331 +453,6 @@ static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block )
     return p_spu;
 }
 
-char *GrabAttributeValue( const char *psz_attribute,
-                                 const char *psz_tag_start )
-{
-    if( psz_attribute && psz_tag_start )
-    {
-        char *psz_tag_end = strchr( psz_tag_start, '>' );
-        char *psz_found   = strcasestr( psz_tag_start, psz_attribute );
-
-        if( psz_found )
-        {
-            psz_found += strlen( psz_attribute );
-
-            if(( *(psz_found++) == '=' ) &&
-               ( *(psz_found++) == '\"' ))
-            {
-                if( psz_found < psz_tag_end )
-                {
-                    int   i_len = strcspn( psz_found, "\"" );
-                    return strndup( psz_found, i_len );
-                }
-            }
-        }
-    }
-    return NULL;
-}
-
-static ssa_style_t *ParseStyle( decoder_sys_t *p_sys, char *psz_subtitle )
-{
-    ssa_style_t *p_style   = NULL;
-    char        *psz_style = GrabAttributeValue( "style", psz_subtitle );
-
-    if( psz_style )
-    {
-        int i;
-
-        for( i = 0; i < p_sys->i_ssa_styles; i++ )
-        {
-            if( !strcmp( p_sys->pp_ssa_styles[i]->psz_stylename, psz_style ) )
-                p_style = p_sys->pp_ssa_styles[i];
-        }
-        free( psz_style );
-    }
-    return p_style;
-}
-
-static int ParsePositionAttributeList( char *psz_subtitle, int *i_align,
-                                       int *i_x, int *i_y )
-{
-    int   i_mask = 0;
-
-    char *psz_align    = GrabAttributeValue( "alignment", psz_subtitle );
-    char *psz_margin_x = GrabAttributeValue( "horizontal-margin", psz_subtitle );
-    char *psz_margin_y = GrabAttributeValue( "vertical-margin", psz_subtitle );
-    /* -- UNSUPPORTED
-    char *psz_relative = GrabAttributeValue( "relative-to", psz_subtitle );
-    char *psz_rotate_x = GrabAttributeValue( "rotate-x", psz_subtitle );
-    char *psz_rotate_y = GrabAttributeValue( "rotate-y", psz_subtitle );
-    char *psz_rotate_z = GrabAttributeValue( "rotate-z", psz_subtitle );
-    */
-
-    *i_align = SUBPICTURE_ALIGN_BOTTOM;
-    *i_x = 0;
-    *i_y = 0;
-
-    if( psz_align )
-    {
-        if( !strcasecmp( "TopLeft", psz_align ) )
-            *i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
-        else if( !strcasecmp( "TopCenter", psz_align ) )
-            *i_align = SUBPICTURE_ALIGN_TOP;
-        else if( !strcasecmp( "TopRight", psz_align ) )
-            *i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_RIGHT;
-        else if( !strcasecmp( "MiddleLeft", psz_align ) )
-            *i_align = SUBPICTURE_ALIGN_LEFT;
-        else if( !strcasecmp( "MiddleCenter", psz_align ) )
-            *i_align = 0;
-        else if( !strcasecmp( "MiddleRight", psz_align ) )
-            *i_align = SUBPICTURE_ALIGN_RIGHT;
-        else if( !strcasecmp( "BottomLeft", psz_align ) )
-            *i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_LEFT;
-        else if( !strcasecmp( "BottomCenter", psz_align ) )
-            *i_align = SUBPICTURE_ALIGN_BOTTOM;
-        else if( !strcasecmp( "BottomRight", psz_align ) )
-            *i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_RIGHT;
-
-        i_mask |= ATTRIBUTE_ALIGNMENT;
-        free( psz_align );
-    }
-    if( psz_margin_x )
-    {
-        *i_x = atoi( psz_margin_x );
-        if( strchr( psz_margin_x, '%' ) )
-            i_mask |= ATTRIBUTE_X_PERCENT;
-        else
-            i_mask |= ATTRIBUTE_X;
-
-        free( psz_margin_x );
-    }
-    if( psz_margin_y )
-    {
-        *i_y = atoi( psz_margin_y );
-        if( strchr( psz_margin_y, '%' ) )
-            i_mask |= ATTRIBUTE_Y_PERCENT;
-        else
-            i_mask |= ATTRIBUTE_Y;
-
-        free( psz_margin_y );
-    }
-    return i_mask;
-}
-
-void SetupPositions( subpicture_region_t *p_region, char *psz_subtitle )
-{
-    int           i_mask = 0;
-    int           i_align;
-    int           i_x, i_y;
-
-    i_mask = ParsePositionAttributeList( psz_subtitle, &i_align, &i_x, &i_y );
-
-    if( i_mask & ATTRIBUTE_ALIGNMENT )
-        p_region->i_align = i_align;
-
-    /* TODO: Setup % based offsets properly, without adversely affecting
-     *       everything else in vlc. Will address with separate patch, to
-     *       prevent this one being any more complicated.
-     */
-    if( i_mask & ATTRIBUTE_X )
-        p_region->i_x = i_x;
-    else if( i_mask & ATTRIBUTE_X_PERCENT )
-        p_region->i_x = 0;
-
-    if( i_mask & ATTRIBUTE_Y )
-        p_region->i_y = i_y;
-    else if( i_mask & ATTRIBUTE_Y_PERCENT )
-        p_region->i_y = 0;
-}
-
-subpicture_region_t *CreateTextRegion( decoder_t *p_dec,
-                                              subpicture_t *p_spu,
-                                              char *psz_subtitle,
-                                              int i_len,
-                                              int i_sys_align )
-{
-    decoder_sys_t        *p_sys = p_dec->p_sys;
-    subpicture_region_t  *p_text_region;
-    video_format_t        fmt;
-
-    /* Create a new subpicture region */
-    memset( &fmt, 0, sizeof(video_format_t) );
-    fmt.i_chroma = VLC_FOURCC('T','E','X','T');
-    fmt.i_aspect = 0;
-    fmt.i_width = fmt.i_height = 0;
-    fmt.i_x_offset = fmt.i_y_offset = 0;
-    p_text_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
-
-    if( p_text_region != NULL )
-    {
-        ssa_style_t  *p_style = NULL;
-
-        p_text_region->psz_text = NULL;
-        p_text_region->psz_html = strndup( psz_subtitle, i_len );
-        if( ! p_text_region->psz_html )
-        {
-            msg_Err( p_dec, "out of memory" );
-            p_spu->pf_destroy_region( VLC_OBJECT(p_dec), p_text_region );
-            return NULL;
-        }
-
-        p_style = ParseStyle( p_sys, p_text_region->psz_html );
-        if( !p_style )
-        {
-            int i;
-
-            for( i = 0; i < p_sys->i_ssa_styles; i++ )
-            {
-                if( !strcasecmp( p_sys->pp_ssa_styles[i]->psz_stylename, "Default" ) )
-                    p_style = p_sys->pp_ssa_styles[i];
-            }
-        }
-
-        if( p_style )
-        {
-            msg_Dbg( p_dec, "style is: %s", p_style->psz_stylename );
-
-            p_text_region->p_style = &p_style->font_style;
-            p_text_region->i_align = p_style->i_align;
-
-            /* TODO: Setup % based offsets properly, without adversely affecting
-             *       everything else in vlc. Will address with separate patch,
-             *       to prevent this one being any more complicated.
-
-                     * p_style->i_margin_percent_h;
-                     * p_style->i_margin_percent_v;
-             */
-            p_text_region->i_x         = p_style->i_margin_h;
-            p_text_region->i_y         = p_style->i_margin_v;
-
-        }
-        else
-        {
-            p_text_region->i_align = SUBPICTURE_ALIGN_BOTTOM | i_sys_align;
-            p_text_region->i_x = i_sys_align ? 20 : 0;
-            p_text_region->i_y = 10;
-        }
-        /* Look for position arguments which may override the style-based
-         * defaults.
-         */
-        SetupPositions( p_text_region, psz_subtitle );
-
-        p_text_region->p_next = NULL;
-    }
-    return p_text_region;
-}
-
-static int ParseImageAttachments( decoder_t *p_dec )
-{
-    decoder_sys_t        *p_sys = p_dec->p_sys;
-    input_attachment_t  **pp_attachments;
-    int                   i_attachments_cnt;
-    int                   k = 0;
-
-    if( VLC_SUCCESS != decoder_GetInputAttachments( p_dec, &pp_attachments, &i_attachments_cnt ))
-        return VLC_EGENERIC;
-
-    for( k = 0; k < i_attachments_cnt; k++ )
-    {
-        input_attachment_t *p_attach = pp_attachments[k];
-
-        vlc_fourcc_t  type  = 0;
-
-        if( ( !strcmp( p_attach->psz_mime, "image/bmp" ) )      || /* BMP */
-            ( !strcmp( p_attach->psz_mime, "image/x-bmp" ) )    ||
-            ( !strcmp( p_attach->psz_mime, "image/x-bitmap" ) ) ||
-            ( !strcmp( p_attach->psz_mime, "image/x-ms-bmp" ) ) )
-        {
-             type = VLC_FOURCC('b','m','p',' ');
-        }
-        else if( ( !strcmp( p_attach->psz_mime, "image/x-portable-anymap" ) )  || /* PNM */
-                 ( !strcmp( p_attach->psz_mime, "image/x-portable-bitmap" ) )  || /* PBM */
-                 ( !strcmp( p_attach->psz_mime, "image/x-portable-graymap" ) ) || /* PGM */
-                 ( !strcmp( p_attach->psz_mime, "image/x-portable-pixmap" ) ) )   /* PPM */
-        {
-            type = VLC_FOURCC('p','n','m',' ');
-        }
-        else if ( !strcmp( p_attach->psz_mime, "image/gif" ) )         /* GIF */
-            type = VLC_FOURCC('g','i','f',' ');
-        else if ( !strcmp( p_attach->psz_mime, "image/jpeg" ) )        /* JPG, JPEG */
-            type = VLC_FOURCC('j','p','e','g');
-        else if ( !strcmp( p_attach->psz_mime, "image/pcx" ) )         /* PCX */
-            type = VLC_FOURCC('p','c','x',' ');
-        else if ( !strcmp( p_attach->psz_mime, "image/png" ) )         /* PNG */
-            type = VLC_FOURCC('p','n','g',' ');
-        else if ( !strcmp( p_attach->psz_mime, "image/tiff" ) )        /* TIF, TIFF */
-            type = VLC_FOURCC('t','i','f','f');
-        else if ( !strcmp( p_attach->psz_mime, "image/x-tga" ) )       /* TGA */
-            type = VLC_FOURCC('t','g','a',' ');
-        else if ( !strcmp( p_attach->psz_mime, "image/x-xpixmap") )    /* XPM */
-            type = VLC_FOURCC('x','p','m',' ');
-
-        if( ( type != 0 ) &&
-            ( p_attach->i_data > 0 ) &&
-            ( p_attach->p_data != NULL ) )
-        {
-            picture_t         *p_pic = NULL;
-            image_handler_t   *p_image;
-
-            p_image = image_HandlerCreate( p_dec );
-            if( p_image != NULL )
-            {
-                block_t   *p_block;
-
-                p_block = block_New( p_image->p_parent, p_attach->i_data );
-
-                if( p_block != NULL )
-                {
-                    video_format_t     fmt_in;
-                    video_format_t     fmt_out;
-
-                    memcpy( p_block->p_buffer, p_attach->p_data, p_attach->i_data );
-
-                    memset( &fmt_in,  0, sizeof( video_format_t));
-                    memset( &fmt_out, 0, sizeof( video_format_t));
-
-                    fmt_in.i_chroma  = type;
-                    fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
-
-                    /* Find a suitable decoder module */
-                    if( module_Exists( p_dec, "sdl_image" ) )
-                    {
-                        /* ffmpeg thinks it can handle bmp properly but it can't (at least
-                         * not all of them), so use sdl_image if it is available */
-
-                        vlc_value_t val;
-
-                        var_Create( p_dec, "codec", VLC_VAR_MODULE | VLC_VAR_DOINHERIT );
-                        val.psz_string = (char*) "sdl_image";
-                        var_Set( p_dec, "codec", val );
-                    }
-
-                    p_pic = image_Read( p_image, p_block, &fmt_in, &fmt_out );
-                    var_Destroy( p_dec, "codec" );
-                }
-
-                image_HandlerDelete( p_image );
-            }
-            if( p_pic )
-            {
-                image_attach_t *p_picture = malloc( sizeof(image_attach_t) );
-
-                if( p_picture )
-                {
-                    p_picture->psz_filename = strdup( p_attach->psz_name );
-                    p_picture->p_pic = p_pic;
-
-                    TAB_APPEND( p_sys->i_images, p_sys->pp_images, p_picture );
-                }
-            }
-        }
-        vlc_input_attachment_Delete( pp_attachments[ k ] );
-    }
-    free( pp_attachments );
-
-    return VLC_SUCCESS;
-}
-
 char* GotoNextLine( char *psz_text )
 {
     char *p_newline = psz_text;
@@ -1123,132 +782,3 @@ static char *CreateHtmlSubtitle( char *psz_subtitle )
     }
     return psz_html_start;
 }
-
-/* The reverse of the above function - given a HTML subtitle, turn it
- * into a plain-text version, complete with sensible whitespace compaction
- */
-
-char *CreatePlainText( char *psz_subtitle )
-{
-    char *psz_text = StripTags( psz_subtitle );
-    char *s;
-
-    if( !psz_text )
-        return NULL;
-
-    s = strpbrk( psz_text, "\t\r\n " );
-    while( s )
-    {
-        int   k;
-        char  spc = ' ';
-        int   i_whitespace = strspn( s, "\t\r\n " );
-
-        /* Favour '\n' over other whitespaces - if one of these
-         * occurs in the whitespace use a '\n' as our value,
-         * otherwise just use a ' '
-         */
-        for( k = 0; k < i_whitespace; k++ )
-            if( s[k] == '\n' ) spc = '\n';
-
-        if( i_whitespace > 1 )
-        {
-            memmove( &s[1],
-                     &s[i_whitespace],
-                     strlen( s ) - i_whitespace + 1 );
-        }
-        *s++ = spc;
-
-        s = strpbrk( s, "\t\r\n " );
-    }
-    return psz_text;
-}
-
-/****************************************************************************
- * download and resize image located at psz_url
- ***************************************************************************/
-subpicture_region_t *LoadEmbeddedImage( decoder_t *p_dec,
-                                        subpicture_t *p_spu,
-                                        const char *psz_filename,
-                                        int i_transparent_color )
-{
-    decoder_sys_t         *p_sys = p_dec->p_sys;
-    subpicture_region_t   *p_region;
-    video_format_t         fmt_out;
-    int                    k;
-    picture_t             *p_pic = NULL;
-
-    for( k = 0; k < p_sys->i_images; k++ )
-    {
-        if( p_sys->pp_images &&
-            !strcmp( p_sys->pp_images[k]->psz_filename, psz_filename ) )
-        {
-            p_pic = p_sys->pp_images[k]->p_pic;
-            break;
-        }
-    }
-
-    if( !p_pic )
-    {
-        msg_Err( p_dec, "Unable to read image %s", psz_filename );
-        return NULL;
-    }
-
-    /* Display the feed's image */
-    memset( &fmt_out, 0, sizeof( video_format_t));
-
-    fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
-    fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
-    fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
-    fmt_out.i_width =
-        fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
-    fmt_out.i_height =
-        fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
-
-    p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt_out );
-    if( !p_region )
-    {
-        msg_Err( p_dec, "cannot allocate SPU region" );
-        return NULL;
-    }
-    vout_CopyPicture( p_dec, &p_region->picture, p_pic );
-
-    /* This isn't the best way to do this - if you really want transparency, then
-     * you're much better off using an image type that supports it like PNG. The
-     * spec requires this support though.
-     */
-    if( i_transparent_color > 0 )
-    {
-        uint8_t i_r = ( i_transparent_color >> 16 ) & 0xff;
-        uint8_t i_g = ( i_transparent_color >>  8 ) & 0xff;
-        uint8_t i_b = ( i_transparent_color       ) & 0xff;
-        uint8_t i_y = ( ( (  66 * i_r + 129 * i_g +  25 * i_b + 128 ) >> 8 ) + 16 );
-        uint8_t i_u =   ( ( -38 * i_r -  74 * i_g + 112 * i_b + 128 ) >> 8 ) + 128 ;
-        uint8_t i_v =   ( ( 112 * i_r -  94 * i_g -  18 * i_b + 128 ) >> 8 ) + 128 ;
-
-        if( ( p_region->picture.Y_PITCH == p_region->picture.U_PITCH ) &&
-            ( p_region->picture.Y_PITCH == p_region->picture.V_PITCH ) &&
-            ( p_region->picture.Y_PITCH == p_region->picture.A_PITCH ) )
-        {
-            int i_lines = p_region->picture.p[ Y_PLANE ].i_lines;
-            if( i_lines > p_region->picture.p[ U_PLANE ].i_lines )
-                i_lines = p_region->picture.p[ U_PLANE ].i_lines;
-            if( i_lines > p_region->picture.p[ V_PLANE ].i_lines )
-                i_lines = p_region->picture.p[ V_PLANE ].i_lines;
-            if( i_lines > p_region->picture.p[ A_PLANE ].i_lines )
-                i_lines = p_region->picture.p[ A_PLANE ].i_lines;
-
-            int   i;
-
-            for( i = 0; i < p_region->picture.A_PITCH * i_lines; i++ )
-            {
-                if(( p_region->picture.Y_PIXELS[ i ] == i_y ) &&
-                   ( p_region->picture.U_PIXELS[ i ] == i_u ) &&
-                   ( p_region->picture.V_PIXELS[ i ] == i_v ) )
-                {
-                    p_region->picture.A_PIXELS[ i ] = 1;
-                }
-            }
-        }
-    }
-    return p_region;
-}
diff --git a/modules/codec/subtitles/subsdec.h b/modules/codec/subtitles/subsdec.h
index 646378acd824..ff503bf6e13b 100644
--- a/modules/codec/subtitles/subsdec.h
+++ b/modules/codec/subtitles/subsdec.h
@@ -7,7 +7,7 @@
  * Authors: Gildas Bazin <gbazin@videolan.org>
  *          Samuel Hocevar <sam@zoy.org>
  *          Derk-Jan Hartman <hartman at videolan dot org>
- *          Bernie Purcell <b dot purcell at adbglobal dot com>
+ *          Bernie Purcell <bitmap@videolan.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -93,18 +93,6 @@ struct decoder_sys_t
 
 
 char                *GotoNextLine( char *psz_text );
-void                SetupPositions( subpicture_region_t *, char * );
-
-char                *GrabAttributeValue( const char *, const char * );
-subpicture_region_t *LoadEmbeddedImage( decoder_t *p_dec, subpicture_t *p_spu,
-                        const char *psz_filename, int i_transparent_color );
-
-char                *CreatePlainText( char * );
-subpicture_region_t *CreateTextRegion( decoder_t *, subpicture_t *,
-                                        char *, int, int );
-
-void                ParseUSFHeader ( decoder_t * );
-subpicture_region_t *ParseUSFString ( decoder_t *, char *, subpicture_t * );
 
 void                ParseSSAHeader ( decoder_t * );
 void                ParseSSAString ( decoder_t *, char *, subpicture_t * );
diff --git a/modules/codec/subtitles/subsusf.c b/modules/codec/subtitles/subsusf.c
index fb3b42921496..82ad2d10a13d 100644
--- a/modules/codec/subtitles/subsusf.c
+++ b/modules/codec/subtitles/subsusf.c
@@ -2,12 +2,9 @@
  * subsusf.c : USF subtitles decoder
  *****************************************************************************
  * Copyright (C) 2000-2006 the VideoLAN team
- * $Id: subsdec.c 20996 2007-08-05 20:01:21Z jb $
+ * $Id$
  *
- * Authors: Gildas Bazin <gbazin@videolan.org>
- *          Samuel Hocevar <sam@zoy.org>
- *          Derk-Jan Hartman <hartman at videolan dot org>
- *          Bernie Purcell <b dot purcell at adbglobal dot com>
+ * Authors: Bernie Purcell <bitmap@videolan.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,6 +23,543 @@
 
 #include "subsdec.h"
 
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int  OpenDecoder   ( vlc_object_t * );
+static void CloseDecoder  ( vlc_object_t * );
+
+static subpicture_t *DecodeBlock   ( decoder_t *, block_t ** );
+static char         *CreatePlainText( char * );
+static int           ParseImageAttachments( decoder_t *p_dec );
+
+static subpicture_t        *ParseText     ( decoder_t *, block_t * );
+static void                 ParseUSFHeader( decoder_t * );
+static subpicture_region_t *ParseUSFString( decoder_t *, char *, subpicture_t * );
+static subpicture_region_t *LoadEmbeddedImage( decoder_t *p_dec, subpicture_t *p_spu, const char *psz_filename, int i_transparent_color );
+
+/*****************************************************************************
+ * Module descriptor.
+ *****************************************************************************/
+
+vlc_module_begin();
+    set_capability( "decoder", 40 );
+    set_callbacks( OpenDecoder, CloseDecoder );
+    set_category( CAT_INPUT );
+    set_subcategory( SUBCAT_INPUT_SCODEC );
+    /* We inherit subsdec-align and subsdec-formatted from subsdec.c */
+vlc_module_end();
+
+/*****************************************************************************
+ * OpenDecoder: probe the decoder and return score
+ *****************************************************************************
+ * Tries to launch a decoder and return score so that the interface is able
+ * to chose.
+ *****************************************************************************/
+static int OpenDecoder( vlc_object_t *p_this )
+{
+    decoder_t     *p_dec = (decoder_t*)p_this;
+    decoder_sys_t *p_sys;
+    vlc_value_t    val;
+
+    if( p_dec->fmt_in.i_codec != VLC_FOURCC('u','s','f',' ') )
+    {
+        return VLC_EGENERIC;
+    }
+
+    p_dec->pf_decode_sub = DecodeBlock;
+
+    /* Allocate the memory needed to store the decoder's structure */
+    if( ( p_dec->p_sys = p_sys =
+          (decoder_sys_t *)calloc(1, sizeof(decoder_sys_t)) ) == NULL )
+    {
+        msg_Err( p_dec, "out of memory" );
+        return VLC_ENOMEM;
+    }
+
+    /* Unused fields of p_sys - not needed for USF decoding */
+    p_sys->b_ass = VLC_FALSE;
+    p_sys->iconv_handle = (vlc_iconv_t)-1;
+    p_sys->b_autodetect_utf8 = VLC_FALSE;
+
+    /* init of p_sys */
+    p_sys->i_align = 0;
+    p_sys->i_original_height = -1;
+    p_sys->i_original_width = -1;
+    TAB_INIT( p_sys->i_ssa_styles, p_sys->pp_ssa_styles );
+    TAB_INIT( p_sys->i_images, p_sys->pp_images );
+
+    /* USF subtitles are mandated to be UTF-8, so don't need vlc_iconv */
+
+    var_Create( p_dec, "subsdec-align", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+    var_Get( p_dec, "subsdec-align", &val );
+    p_sys->i_align = val.i_int;
+
+    ParseImageAttachments( p_dec );
+
+    if( var_CreateGetBool( p_dec, "subsdec-formatted" ) )
+    {
+        if( p_dec->fmt_in.i_extra > 0 )
+            ParseUSFHeader( p_dec );
+    }
+
+    return VLC_SUCCESS;
+}
+
+/****************************************************************************
+ * DecodeBlock: the whole thing
+ ****************************************************************************
+ * This function must be fed with complete subtitles units.
+ ****************************************************************************/
+static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
+{
+    subpicture_t *p_spu = NULL;
+
+    if( !pp_block || *pp_block == NULL ) return NULL;
+
+    p_spu = ParseText( p_dec, *pp_block );
+
+    block_Release( *pp_block );
+    *pp_block = NULL;
+
+    return p_spu;
+}
+
+/*****************************************************************************
+ * CloseDecoder: clean up the decoder
+ *****************************************************************************/
+static void CloseDecoder( vlc_object_t *p_this )
+{
+    decoder_t *p_dec = (decoder_t *)p_this;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    if( p_sys->pp_ssa_styles )
+    {
+        int i;
+        for( i = 0; i < p_sys->i_ssa_styles; i++ )
+        {
+            if( !p_sys->pp_ssa_styles[i] )
+                continue;
+
+            if( p_sys->pp_ssa_styles[i]->psz_stylename )
+                free( p_sys->pp_ssa_styles[i]->psz_stylename );
+            if( p_sys->pp_ssa_styles[i]->font_style.psz_fontname )
+                free( p_sys->pp_ssa_styles[i]->font_style.psz_fontname );
+            if( p_sys->pp_ssa_styles[i] )
+                free( p_sys->pp_ssa_styles[i] );
+        }
+        TAB_CLEAN( p_sys->i_ssa_styles, p_sys->pp_ssa_styles );
+    }
+    if( p_sys->pp_images )
+    {
+        int i;
+        for( i = 0; i < p_sys->i_images; i++ )
+        {
+            if( !p_sys->pp_images[i] )
+                continue;
+
+            if( p_sys->pp_images[i]->p_pic )
+                p_sys->pp_images[i]->p_pic->pf_release( p_sys->pp_images[i]->p_pic );
+            if( p_sys->pp_images[i]->psz_filename )
+                free( p_sys->pp_images[i]->psz_filename );
+
+            free( p_sys->pp_images[i] );
+        }
+        TAB_CLEAN( p_sys->i_images, p_sys->pp_images );
+    }
+
+    free( p_sys );
+}
+
+/*****************************************************************************
+ * ParseText: parse an text subtitle packet and send it to the video output
+ *****************************************************************************/
+static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    subpicture_t *p_spu = NULL;
+    char *psz_subtitle = NULL;
+
+    /* We cannot display a subpicture with no date */
+    if( p_block->i_pts == 0 )
+    {
+        msg_Warn( p_dec, "subtitle without a date" );
+        return NULL;
+    }
+
+    /* Check validity of packet data */
+    /* An "empty" line containing only \0 can be used to force
+       and ephemer picture from the screen */
+    if( p_block->i_buffer < 1 )
+    {
+        msg_Warn( p_dec, "no subtitle data" );
+        return NULL;
+    }
+
+    /* Should be resiliant against bad subtitles */
+    psz_subtitle = strndup( (const char *)p_block->p_buffer,
+                            p_block->i_buffer );
+    if( psz_subtitle == NULL )
+        return NULL;
+
+    /* USF Subtitles are mandated to be UTF-8 -- make sure it is */
+    if (EnsureUTF8( psz_subtitle ) == NULL)
+    {
+        msg_Err( p_dec, _("USF subtitles must be in UTF-8 format.\n"
+                 "This stream contains USF subtitles which aren't.") );
+    }
+
+    /* Create the subpicture unit */
+    p_spu = p_dec->pf_spu_buffer_new( p_dec );
+    if( !p_spu )
+    {
+        msg_Warn( p_dec, "can't get spu buffer" );
+        if( psz_subtitle ) free( psz_subtitle );
+        return NULL;
+    }
+
+    p_spu->b_pausable = VLC_TRUE;
+
+    /* Decode USF strings */
+    p_spu->p_region = ParseUSFString( p_dec, psz_subtitle, p_spu );
+
+    p_spu->i_start = p_block->i_pts;
+    p_spu->i_stop = p_block->i_pts + p_block->i_length;
+    p_spu->b_ephemer = (p_block->i_length == 0);
+    p_spu->b_absolute = VLC_FALSE;
+    p_spu->i_original_picture_width = p_sys->i_original_width;
+    p_spu->i_original_picture_height = p_sys->i_original_height;
+
+    if( psz_subtitle ) free( psz_subtitle );
+
+    return p_spu;
+}
+
+static char *GrabAttributeValue( const char *psz_attribute,
+                                 const char *psz_tag_start )
+{
+    if( psz_attribute && psz_tag_start )
+    {
+        char *psz_tag_end = strchr( psz_tag_start, '>' );
+        char *psz_found   = strcasestr( psz_tag_start, psz_attribute );
+
+        if( psz_found )
+        {
+            psz_found += strlen( psz_attribute );
+
+            if(( *(psz_found++) == '=' ) &&
+               ( *(psz_found++) == '\"' ))
+            {
+                if( psz_found < psz_tag_end )
+                {
+                    int   i_len = strcspn( psz_found, "\"" );
+                    return strndup( psz_found, i_len );
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+static ssa_style_t *ParseStyle( decoder_sys_t *p_sys, char *psz_subtitle )
+{
+    ssa_style_t *p_style   = NULL;
+    char        *psz_style = GrabAttributeValue( "style", psz_subtitle );
+
+    if( psz_style )
+    {
+        int i;
+
+        for( i = 0; i < p_sys->i_ssa_styles; i++ )
+        {
+            if( !strcmp( p_sys->pp_ssa_styles[i]->psz_stylename, psz_style ) )
+                p_style = p_sys->pp_ssa_styles[i];
+        }
+        free( psz_style );
+    }
+    return p_style;
+}
+
+static int ParsePositionAttributeList( char *psz_subtitle, int *i_align,
+                                       int *i_x, int *i_y )
+{
+    int   i_mask = 0;
+
+    char *psz_align    = GrabAttributeValue( "alignment", psz_subtitle );
+    char *psz_margin_x = GrabAttributeValue( "horizontal-margin", psz_subtitle );
+    char *psz_margin_y = GrabAttributeValue( "vertical-margin", psz_subtitle );
+    /* -- UNSUPPORTED
+    char *psz_relative = GrabAttributeValue( "relative-to", psz_subtitle );
+    char *psz_rotate_x = GrabAttributeValue( "rotate-x", psz_subtitle );
+    char *psz_rotate_y = GrabAttributeValue( "rotate-y", psz_subtitle );
+    char *psz_rotate_z = GrabAttributeValue( "rotate-z", psz_subtitle );
+    */
+
+    *i_align = SUBPICTURE_ALIGN_BOTTOM;
+    *i_x = 0;
+    *i_y = 0;
+
+    if( psz_align )
+    {
+        if( !strcasecmp( "TopLeft", psz_align ) )
+            *i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
+        else if( !strcasecmp( "TopCenter", psz_align ) )
+            *i_align = SUBPICTURE_ALIGN_TOP;
+        else if( !strcasecmp( "TopRight", psz_align ) )
+            *i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_RIGHT;
+        else if( !strcasecmp( "MiddleLeft", psz_align ) )
+            *i_align = SUBPICTURE_ALIGN_LEFT;
+        else if( !strcasecmp( "MiddleCenter", psz_align ) )
+            *i_align = 0;
+        else if( !strcasecmp( "MiddleRight", psz_align ) )
+            *i_align = SUBPICTURE_ALIGN_RIGHT;
+        else if( !strcasecmp( "BottomLeft", psz_align ) )
+            *i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_LEFT;
+        else if( !strcasecmp( "BottomCenter", psz_align ) )
+            *i_align = SUBPICTURE_ALIGN_BOTTOM;
+        else if( !strcasecmp( "BottomRight", psz_align ) )
+            *i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_RIGHT;
+
+        i_mask |= ATTRIBUTE_ALIGNMENT;
+        free( psz_align );
+    }
+    if( psz_margin_x )
+    {
+        *i_x = atoi( psz_margin_x );
+        if( strchr( psz_margin_x, '%' ) )
+            i_mask |= ATTRIBUTE_X_PERCENT;
+        else
+            i_mask |= ATTRIBUTE_X;
+
+        free( psz_margin_x );
+    }
+    if( psz_margin_y )
+    {
+        *i_y = atoi( psz_margin_y );
+        if( strchr( psz_margin_y, '%' ) )
+            i_mask |= ATTRIBUTE_Y_PERCENT;
+        else
+            i_mask |= ATTRIBUTE_Y;
+
+        free( psz_margin_y );
+    }
+    return i_mask;
+}
+
+static void SetupPositions( subpicture_region_t *p_region, char *psz_subtitle )
+{
+    int           i_mask = 0;
+    int           i_align;
+    int           i_x, i_y;
+
+    i_mask = ParsePositionAttributeList( psz_subtitle, &i_align, &i_x, &i_y );
+
+    if( i_mask & ATTRIBUTE_ALIGNMENT )
+        p_region->i_align = i_align;
+
+    /* TODO: Setup % based offsets properly, without adversely affecting
+     *       everything else in vlc. Will address with separate patch, to
+     *       prevent this one being any more complicated.
+     */
+    if( i_mask & ATTRIBUTE_X )
+        p_region->i_x = i_x;
+    else if( i_mask & ATTRIBUTE_X_PERCENT )
+        p_region->i_x = 0;
+
+    if( i_mask & ATTRIBUTE_Y )
+        p_region->i_y = i_y;
+    else if( i_mask & ATTRIBUTE_Y_PERCENT )
+        p_region->i_y = 0;
+}
+
+static subpicture_region_t *CreateTextRegion( decoder_t *p_dec,
+                                              subpicture_t *p_spu,
+                                              char *psz_subtitle,
+                                              int i_len,
+                                              int i_sys_align )
+{
+    decoder_sys_t        *p_sys = p_dec->p_sys;
+    subpicture_region_t  *p_text_region;
+    video_format_t        fmt;
+
+    /* Create a new subpicture region */
+    memset( &fmt, 0, sizeof(video_format_t) );
+    fmt.i_chroma = VLC_FOURCC('T','E','X','T');
+    fmt.i_aspect = 0;
+    fmt.i_width = fmt.i_height = 0;
+    fmt.i_x_offset = fmt.i_y_offset = 0;
+    p_text_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
+
+    if( p_text_region != NULL )
+    {
+        ssa_style_t  *p_style = NULL;
+
+        p_text_region->psz_text = NULL;
+        p_text_region->psz_html = strndup( psz_subtitle, i_len );
+        if( ! p_text_region->psz_html )
+        {
+            msg_Err( p_dec, "out of memory" );
+            p_spu->pf_destroy_region( VLC_OBJECT(p_dec), p_text_region );
+            return NULL;
+        }
+
+        p_style = ParseStyle( p_sys, p_text_region->psz_html );
+        if( !p_style )
+        {
+            int i;
+
+            for( i = 0; i < p_sys->i_ssa_styles; i++ )
+            {
+                if( !strcasecmp( p_sys->pp_ssa_styles[i]->psz_stylename, "Default" ) )
+                    p_style = p_sys->pp_ssa_styles[i];
+            }
+        }
+
+        if( p_style )
+        {
+            msg_Dbg( p_dec, "style is: %s", p_style->psz_stylename );
+
+            p_text_region->p_style = &p_style->font_style;
+            p_text_region->i_align = p_style->i_align;
+
+            /* TODO: Setup % based offsets properly, without adversely affecting
+             *       everything else in vlc. Will address with separate patch,
+             *       to prevent this one being any more complicated.
+
+                     * p_style->i_margin_percent_h;
+                     * p_style->i_margin_percent_v;
+             */
+            p_text_region->i_x         = p_style->i_margin_h;
+            p_text_region->i_y         = p_style->i_margin_v;
+
+        }
+        else
+        {
+            p_text_region->i_align = SUBPICTURE_ALIGN_BOTTOM | i_sys_align;
+            p_text_region->i_x = i_sys_align ? 20 : 0;
+            p_text_region->i_y = 10;
+        }
+        /* Look for position arguments which may override the style-based
+         * defaults.
+         */
+        SetupPositions( p_text_region, psz_subtitle );
+
+        p_text_region->p_next = NULL;
+    }
+    return p_text_region;
+}
+
+static int ParseImageAttachments( decoder_t *p_dec )
+{
+    decoder_sys_t        *p_sys = p_dec->p_sys;
+    input_attachment_t  **pp_attachments;
+    int                   i_attachments_cnt;
+    int                   k = 0;
+
+    if( VLC_SUCCESS != decoder_GetInputAttachments( p_dec, &pp_attachments, &i_attachments_cnt ))
+        return VLC_EGENERIC;
+
+    for( k = 0; k < i_attachments_cnt; k++ )
+    {
+        input_attachment_t *p_attach = pp_attachments[k];
+
+        vlc_fourcc_t  type  = 0;
+
+        if( ( !strcmp( p_attach->psz_mime, "image/bmp" ) )      || /* BMP */
+            ( !strcmp( p_attach->psz_mime, "image/x-bmp" ) )    ||
+            ( !strcmp( p_attach->psz_mime, "image/x-bitmap" ) ) ||
+            ( !strcmp( p_attach->psz_mime, "image/x-ms-bmp" ) ) )
+        {
+             type = VLC_FOURCC('b','m','p',' ');
+        }
+        else if( ( !strcmp( p_attach->psz_mime, "image/x-portable-anymap" ) )  || /* PNM */
+                 ( !strcmp( p_attach->psz_mime, "image/x-portable-bitmap" ) )  || /* PBM */
+                 ( !strcmp( p_attach->psz_mime, "image/x-portable-graymap" ) ) || /* PGM */
+                 ( !strcmp( p_attach->psz_mime, "image/x-portable-pixmap" ) ) )   /* PPM */
+        {
+            type = VLC_FOURCC('p','n','m',' ');
+        }
+        else if ( !strcmp( p_attach->psz_mime, "image/gif" ) )         /* GIF */
+            type = VLC_FOURCC('g','i','f',' ');
+        else if ( !strcmp( p_attach->psz_mime, "image/jpeg" ) )        /* JPG, JPEG */
+            type = VLC_FOURCC('j','p','e','g');
+        else if ( !strcmp( p_attach->psz_mime, "image/pcx" ) )         /* PCX */
+            type = VLC_FOURCC('p','c','x',' ');
+        else if ( !strcmp( p_attach->psz_mime, "image/png" ) )         /* PNG */
+            type = VLC_FOURCC('p','n','g',' ');
+        else if ( !strcmp( p_attach->psz_mime, "image/tiff" ) )        /* TIF, TIFF */
+            type = VLC_FOURCC('t','i','f','f');
+        else if ( !strcmp( p_attach->psz_mime, "image/x-tga" ) )       /* TGA */
+            type = VLC_FOURCC('t','g','a',' ');
+        else if ( !strcmp( p_attach->psz_mime, "image/x-xpixmap") )    /* XPM */
+            type = VLC_FOURCC('x','p','m',' ');
+
+        if( ( type != 0 ) &&
+            ( p_attach->i_data > 0 ) &&
+            ( p_attach->p_data != NULL ) )
+        {
+            picture_t         *p_pic = NULL;
+            image_handler_t   *p_image;
+
+            p_image = image_HandlerCreate( p_dec );
+            if( p_image != NULL )
+            {
+                block_t   *p_block;
+
+                p_block = block_New( p_image->p_parent, p_attach->i_data );
+
+                if( p_block != NULL )
+                {
+                    video_format_t     fmt_in;
+                    video_format_t     fmt_out;
+
+                    memcpy( p_block->p_buffer, p_attach->p_data, p_attach->i_data );
+
+                    memset( &fmt_in,  0, sizeof( video_format_t));
+                    memset( &fmt_out, 0, sizeof( video_format_t));
+
+                    fmt_in.i_chroma  = type;
+                    fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
+
+                    /* Find a suitable decoder module */
+                    if( module_Exists( p_dec, "sdl_image" ) )
+                    {
+                        /* ffmpeg thinks it can handle bmp properly but it can't (at least
+                         * not all of them), so use sdl_image if it is available */
+
+                        vlc_value_t val;
+
+                        var_Create( p_dec, "codec", VLC_VAR_MODULE | VLC_VAR_DOINHERIT );
+                        val.psz_string = (char*) "sdl_image";
+                        var_Set( p_dec, "codec", val );
+                    }
+
+                    p_pic = image_Read( p_image, p_block, &fmt_in, &fmt_out );
+                    var_Destroy( p_dec, "codec" );
+                }
+
+                image_HandlerDelete( p_image );
+            }
+            if( p_pic )
+            {
+                image_attach_t *p_picture = malloc( sizeof(image_attach_t) );
+
+                if( p_picture )
+                {
+                    p_picture->psz_filename = strdup( p_attach->psz_name );
+                    p_picture->p_pic = p_pic;
+
+                    TAB_APPEND( p_sys->i_images, p_sys->pp_images, p_picture );
+                }
+            }
+        }
+        vlc_input_attachment_Delete( pp_attachments[ k ] );
+    }
+    free( pp_attachments );
+
+    return VLC_SUCCESS;
+}
+
 static void ParseUSFHeaderTags( decoder_t *p_dec, xml_reader_t *p_xml_reader )
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
@@ -326,9 +860,9 @@ static void ParseUSFHeaderTags( decoder_t *p_dec, xml_reader_t *p_xml_reader )
 
 
 
-subpicture_region_t *ParseUSFString( decoder_t *p_dec,
-                                     char *psz_subtitle,
-                                     subpicture_t *p_spu_in )
+static subpicture_region_t *ParseUSFString( decoder_t *p_dec,
+                                            char *psz_subtitle,
+                                            subpicture_t *p_spu_in )
 {
     decoder_sys_t        *p_sys = p_dec->p_sys;
     subpicture_t         *p_spu = p_spu_in;
@@ -488,7 +1022,7 @@ subpicture_region_t *ParseUSFString( decoder_t *p_dec,
 /*****************************************************************************
  * ParseUSFHeader: Retrieve global formatting information etc
  *****************************************************************************/
-void ParseUSFHeader( decoder_t *p_dec )
+static void ParseUSFHeader( decoder_t *p_dec )
 {
     stream_t      *p_sub = NULL;
     xml_t         *p_xml = NULL;
@@ -525,4 +1059,198 @@ void ParseUSFHeader( decoder_t *p_dec )
     stream_Delete( p_sub );
 }
 
+/* Function now handles tags which has attribute values, and tries
+ * to deal with &' commands too. It no longer modifies the string
+ * in place, so that the original text can be reused
+ */
+static char *StripTags( char *psz_subtitle )
+{
+    char *psz_text_start;
+    char *psz_text;
+
+    psz_text = psz_text_start = malloc( strlen( psz_subtitle ) + 1 );
+    if( !psz_text_start )
+        return NULL;
+
+    while( *psz_subtitle )
+    {
+        /* Mask out any pre-existing LFs in the subtitle */
+        if( *psz_subtitle == '\n' )
+            *psz_subtitle = ' ';
 
+        if( *psz_subtitle == '<' )
+        {
+            if( strncasecmp( psz_subtitle, "<br/>", 5 ) == 0 )
+                *psz_text++ = '\n';
+
+            psz_subtitle += strcspn( psz_subtitle, ">" );
+        }
+        else if( *psz_subtitle == '&' )
+        {
+            if( !strncasecmp( psz_subtitle, "&lt;", 4 ))
+            {
+                *psz_text++ = '<';
+                psz_subtitle += strcspn( psz_subtitle, ";" );
+            }
+            else if( !strncasecmp( psz_subtitle, "&gt;", 4 ))
+            {
+                *psz_text++ = '>';
+                psz_subtitle += strcspn( psz_subtitle, ";" );
+            }
+            else if( !strncasecmp( psz_subtitle, "&amp;", 5 ))
+            {
+                *psz_text++ = '&';
+                psz_subtitle += strcspn( psz_subtitle, ";" );
+            }
+            else if( !strncasecmp( psz_subtitle, "&quot;", 6 ))
+            {
+                *psz_text++ = '\"';
+                psz_subtitle += strcspn( psz_subtitle, ";" );
+            }
+            else
+            {
+                /* Assume it is just a normal ampersand */
+                *psz_text++ = '&';
+            }
+        }
+        else
+        {
+            *psz_text++ = *psz_subtitle;
+        }
+
+        psz_subtitle++;
+    }
+    *psz_text = '\0';
+    psz_text_start = realloc( psz_text_start, strlen( psz_text_start ) + 1 );
+
+    return psz_text_start;
+}
+
+/* Turn a HTML subtitle, turn into a plain-text version,
+ *  complete with sensible whitespace compaction
+ */
+
+static char *CreatePlainText( char *psz_subtitle )
+{
+    char *psz_text = StripTags( psz_subtitle );
+    char *s;
+
+    if( !psz_text )
+        return NULL;
+
+    s = strpbrk( psz_text, "\t\r\n " );
+    while( s )
+    {
+        int   k;
+        char  spc = ' ';
+        int   i_whitespace = strspn( s, "\t\r\n " );
+
+        /* Favour '\n' over other whitespaces - if one of these
+         * occurs in the whitespace use a '\n' as our value,
+         * otherwise just use a ' '
+         */
+        for( k = 0; k < i_whitespace; k++ )
+            if( s[k] == '\n' ) spc = '\n';
+
+        if( i_whitespace > 1 )
+        {
+            memmove( &s[1],
+                     &s[i_whitespace],
+                     strlen( s ) - i_whitespace + 1 );
+        }
+        *s++ = spc;
+
+        s = strpbrk( s, "\t\r\n " );
+    }
+    return psz_text;
+}
+
+/****************************************************************************
+ * download and resize image located at psz_url
+ ***************************************************************************/
+static subpicture_region_t *LoadEmbeddedImage( decoder_t *p_dec,
+                                               subpicture_t *p_spu,
+                                               const char *psz_filename,
+                                               int i_transparent_color )
+{
+    decoder_sys_t         *p_sys = p_dec->p_sys;
+    subpicture_region_t   *p_region;
+    video_format_t         fmt_out;
+    int                    k;
+    picture_t             *p_pic = NULL;
+
+    for( k = 0; k < p_sys->i_images; k++ )
+    {
+        if( p_sys->pp_images &&
+            !strcmp( p_sys->pp_images[k]->psz_filename, psz_filename ) )
+        {
+            p_pic = p_sys->pp_images[k]->p_pic;
+            break;
+        }
+    }
+
+    if( !p_pic )
+    {
+        msg_Err( p_dec, "Unable to read image %s", psz_filename );
+        return NULL;
+    }
+
+    /* Display the feed's image */
+    memset( &fmt_out, 0, sizeof( video_format_t));
+
+    fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
+    fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
+    fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
+    fmt_out.i_width =
+        fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
+    fmt_out.i_height =
+        fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
+
+    p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt_out );
+    if( !p_region )
+    {
+        msg_Err( p_dec, "cannot allocate SPU region" );
+        return NULL;
+    }
+    vout_CopyPicture( p_dec, &p_region->picture, p_pic );
+
+    /* This isn't the best way to do this - if you really want transparency, then
+     * you're much better off using an image type that supports it like PNG. The
+     * spec requires this support though.
+     */
+    if( i_transparent_color > 0 )
+    {
+        uint8_t i_r = ( i_transparent_color >> 16 ) & 0xff;
+        uint8_t i_g = ( i_transparent_color >>  8 ) & 0xff;
+        uint8_t i_b = ( i_transparent_color       ) & 0xff;
+        uint8_t i_y = ( ( (  66 * i_r + 129 * i_g +  25 * i_b + 128 ) >> 8 ) + 16 );
+        uint8_t i_u =   ( ( -38 * i_r -  74 * i_g + 112 * i_b + 128 ) >> 8 ) + 128 ;
+        uint8_t i_v =   ( ( 112 * i_r -  94 * i_g -  18 * i_b + 128 ) >> 8 ) + 128 ;
+
+        if( ( p_region->picture.Y_PITCH == p_region->picture.U_PITCH ) &&
+            ( p_region->picture.Y_PITCH == p_region->picture.V_PITCH ) &&
+            ( p_region->picture.Y_PITCH == p_region->picture.A_PITCH ) )
+        {
+            int i_lines = p_region->picture.p[ Y_PLANE ].i_lines;
+            if( i_lines > p_region->picture.p[ U_PLANE ].i_lines )
+                i_lines = p_region->picture.p[ U_PLANE ].i_lines;
+            if( i_lines > p_region->picture.p[ V_PLANE ].i_lines )
+                i_lines = p_region->picture.p[ V_PLANE ].i_lines;
+            if( i_lines > p_region->picture.p[ A_PLANE ].i_lines )
+                i_lines = p_region->picture.p[ A_PLANE ].i_lines;
+
+            int   i;
+
+            for( i = 0; i < p_region->picture.A_PITCH * i_lines; i++ )
+            {
+                if(( p_region->picture.Y_PIXELS[ i ] == i_y ) &&
+                   ( p_region->picture.U_PIXELS[ i ] == i_u ) &&
+                   ( p_region->picture.V_PIXELS[ i ] == i_v ) )
+                {
+                    p_region->picture.A_PIXELS[ i ] = 1;
+                }
+            }
+        }
+    }
+    return p_region;
+}
-- 
GitLab