subsusf.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:45k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * subsusf.c : USF subtitles decoder
  3.  *****************************************************************************
  4.  * Copyright (C) 2000-2006 the VideoLAN team
  5.  * $Id: 330e167ab3c10f21b9cb53c7e77628ee90370e81 $
  6.  *
  7.  * Authors: Bernie Purcell <bitmap@videolan.org>
  8.  *
  9.  * This program is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; either version 2 of the License, or
  12.  * (at your option) any later version.
  13.  *
  14.  * This program is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program; if not, write to the Free Software
  21.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  22.  *****************************************************************************/
  23. #ifdef HAVE_CONFIG_H
  24. # include "config.h"
  25. #endif
  26. #include "subsdec.h"
  27. #include <vlc_plugin.h>
  28. #include <assert.h>
  29. /*****************************************************************************
  30.  * Local prototypes
  31.  *****************************************************************************/
  32. static int  OpenDecoder   ( vlc_object_t * );
  33. static void CloseDecoder  ( vlc_object_t * );
  34. static subpicture_t *DecodeBlock   ( decoder_t *, block_t ** );
  35. static char         *CreatePlainText( char * );
  36. static int           ParseImageAttachments( decoder_t *p_dec );
  37. static subpicture_t        *ParseText     ( decoder_t *, block_t * );
  38. static void                 ParseUSFHeader( decoder_t * );
  39. static subpicture_region_t *ParseUSFString( decoder_t *, char *, subpicture_t * );
  40. static subpicture_region_t *LoadEmbeddedImage( decoder_t *p_dec, subpicture_t *p_spu, const char *psz_filename, int i_transparent_color );
  41. /*****************************************************************************
  42.  * Module descriptor.
  43.  *****************************************************************************/
  44. vlc_module_begin ()
  45.     set_capability( "decoder", 40 )
  46.     set_shortname( N_("USFSubs"))
  47.     set_description( N_("USF subtitles decoder") )
  48.     set_callbacks( OpenDecoder, CloseDecoder )
  49.     set_category( CAT_INPUT )
  50.     set_subcategory( SUBCAT_INPUT_SCODEC )
  51.     /* We inherit subsdec-align and subsdec-formatted from subsdec.c */
  52. vlc_module_end ()
  53. /*****************************************************************************
  54.  * OpenDecoder: probe the decoder and return score
  55.  *****************************************************************************
  56.  * Tries to launch a decoder and return score so that the interface is able
  57.  * to chose.
  58.  *****************************************************************************/
  59. static int OpenDecoder( vlc_object_t *p_this )
  60. {
  61.     decoder_t     *p_dec = (decoder_t*)p_this;
  62.     decoder_sys_t *p_sys;
  63.     if( p_dec->fmt_in.i_codec != VLC_FOURCC('u','s','f',' ') )
  64.         return VLC_EGENERIC;
  65.     /* Allocate the memory needed to store the decoder's structure */
  66.     if( ( p_dec->p_sys = p_sys = calloc(1, sizeof(decoder_sys_t)) ) == NULL )
  67.         return VLC_ENOMEM;
  68.     p_dec->pf_decode_sub = DecodeBlock;
  69.     p_dec->fmt_out.i_cat = SPU_ES;
  70.     p_dec->fmt_out.i_codec = 0;
  71.     /* Unused fields of p_sys - not needed for USF decoding */
  72.     p_sys->b_ass = false;
  73.     p_sys->iconv_handle = (vlc_iconv_t)-1;
  74.     p_sys->b_autodetect_utf8 = false;
  75.     /* init of p_sys */
  76.     p_sys->i_align = 0;
  77.     p_sys->i_original_height = 0;
  78.     p_sys->i_original_width = 0;
  79.     TAB_INIT( p_sys->i_ssa_styles, p_sys->pp_ssa_styles );
  80.     TAB_INIT( p_sys->i_images, p_sys->pp_images );
  81.     /* USF subtitles are mandated to be UTF-8, so don't need vlc_iconv */
  82.     p_sys->i_align = var_CreateGetInteger( p_dec, "subsdec-align" );
  83.     ParseImageAttachments( p_dec );
  84.     if( var_CreateGetBool( p_dec, "subsdec-formatted" ) )
  85.     {
  86.         if( p_dec->fmt_in.i_extra > 0 )
  87.             ParseUSFHeader( p_dec );
  88.     }
  89.     return VLC_SUCCESS;
  90. }
  91. /****************************************************************************
  92.  * DecodeBlock: the whole thing
  93.  ****************************************************************************
  94.  * This function must be fed with complete subtitles units.
  95.  ****************************************************************************/
  96. static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
  97. {
  98.     subpicture_t *p_spu;
  99.     block_t *p_block;
  100.     if( !pp_block || *pp_block == NULL )
  101.         return NULL;
  102.     p_block = *pp_block;
  103.     p_spu = ParseText( p_dec, p_block );
  104.     block_Release( p_block );
  105.     *pp_block = NULL;
  106.     return p_spu;
  107. }
  108. /*****************************************************************************
  109.  * CloseDecoder: clean up the decoder
  110.  *****************************************************************************/
  111. static void CloseDecoder( vlc_object_t *p_this )
  112. {
  113.     decoder_t *p_dec = (decoder_t *)p_this;
  114.     decoder_sys_t *p_sys = p_dec->p_sys;
  115.     if( p_sys->pp_ssa_styles )
  116.     {
  117.         int i;
  118.         for( i = 0; i < p_sys->i_ssa_styles; i++ )
  119.         {
  120.             if( !p_sys->pp_ssa_styles[i] )
  121.                 continue;
  122.             free( p_sys->pp_ssa_styles[i]->psz_stylename );
  123.             free( p_sys->pp_ssa_styles[i]->font_style.psz_fontname );
  124.             free( p_sys->pp_ssa_styles[i] );
  125.         }
  126.         TAB_CLEAN( p_sys->i_ssa_styles, p_sys->pp_ssa_styles );
  127.     }
  128.     if( p_sys->pp_images )
  129.     {
  130.         int i;
  131.         for( i = 0; i < p_sys->i_images; i++ )
  132.         {
  133.             if( !p_sys->pp_images[i] )
  134.                 continue;
  135.             if( p_sys->pp_images[i]->p_pic )
  136.                 picture_Release( p_sys->pp_images[i]->p_pic );
  137.             free( p_sys->pp_images[i]->psz_filename );
  138.             free( p_sys->pp_images[i] );
  139.         }
  140.         TAB_CLEAN( p_sys->i_images, p_sys->pp_images );
  141.     }
  142.     free( p_sys );
  143. }
  144. /*****************************************************************************
  145.  * ParseText: parse an text subtitle packet and send it to the video output
  146.  *****************************************************************************/
  147. static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block )
  148. {
  149.     decoder_sys_t *p_sys = p_dec->p_sys;
  150.     subpicture_t *p_spu = NULL;
  151.     char *psz_subtitle = NULL;
  152.     /* We cannot display a subpicture with no date */
  153.     if( p_block->i_pts == 0 )
  154.     {
  155.         msg_Warn( p_dec, "subtitle without a date" );
  156.         return NULL;
  157.     }
  158.     /* Check validity of packet data */
  159.     /* An "empty" line containing only  can be used to force
  160.        and ephemer picture from the screen */
  161.     if( p_block->i_buffer < 1 )
  162.     {
  163.         msg_Warn( p_dec, "no subtitle data" );
  164.         return NULL;
  165.     }
  166.     /* Should be resiliant against bad subtitles */
  167.     psz_subtitle = strndup( (const char *)p_block->p_buffer,
  168.                             p_block->i_buffer );
  169.     if( psz_subtitle == NULL )
  170.         return NULL;
  171.     /* USF Subtitles are mandated to be UTF-8 -- make sure it is */
  172.     if (EnsureUTF8( psz_subtitle ) == NULL)
  173.     {
  174.         msg_Err( p_dec, "USF subtitles must be in UTF-8 format.n"
  175.                  "This stream contains USF subtitles which aren't." );
  176.     }
  177.     /* Create the subpicture unit */
  178.     p_spu = decoder_NewSubpicture( p_dec );
  179.     if( !p_spu )
  180.     {
  181.         msg_Warn( p_dec, "can't get spu buffer" );
  182.         free( psz_subtitle );
  183.         return NULL;
  184.     }
  185.     /* Decode USF strings */
  186.     p_spu->p_region = ParseUSFString( p_dec, psz_subtitle, p_spu );
  187.     p_spu->i_start = p_block->i_pts;
  188.     p_spu->i_stop = p_block->i_pts + p_block->i_length;
  189.     p_spu->b_ephemer = (p_block->i_length == 0);
  190.     p_spu->b_absolute = false;
  191.     p_spu->i_original_picture_width = p_sys->i_original_width;
  192.     p_spu->i_original_picture_height = p_sys->i_original_height;
  193.     free( psz_subtitle );
  194.     return p_spu;
  195. }
  196. static char *GrabAttributeValue( const char *psz_attribute,
  197.                                  const char *psz_tag_start )
  198. {
  199.     if( psz_attribute && psz_tag_start )
  200.     {
  201.         char *psz_tag_end = strchr( psz_tag_start, '>' );
  202.         char *psz_found   = strcasestr( psz_tag_start, psz_attribute );
  203.         if( psz_found )
  204.         {
  205.             psz_found += strlen( psz_attribute );
  206.             if(( *(psz_found++) == '=' ) &&
  207.                ( *(psz_found++) == '"' ))
  208.             {
  209.                 if( psz_found < psz_tag_end )
  210.                 {
  211.                     int   i_len = strcspn( psz_found, """ );
  212.                     return strndup( psz_found, i_len );
  213.                 }
  214.             }
  215.         }
  216.     }
  217.     return NULL;
  218. }
  219. static ssa_style_t *ParseStyle( decoder_sys_t *p_sys, char *psz_subtitle )
  220. {
  221.     ssa_style_t *p_style   = NULL;
  222.     char        *psz_style = GrabAttributeValue( "style", psz_subtitle );
  223.     if( psz_style )
  224.     {
  225.         int i;
  226.         for( i = 0; i < p_sys->i_ssa_styles; i++ )
  227.         {
  228.             if( !strcmp( p_sys->pp_ssa_styles[i]->psz_stylename, psz_style ) )
  229.                 p_style = p_sys->pp_ssa_styles[i];
  230.         }
  231.         free( psz_style );
  232.     }
  233.     return p_style;
  234. }
  235. static int ParsePositionAttributeList( char *psz_subtitle, int *i_align,
  236.                                        int *i_x, int *i_y )
  237. {
  238.     int   i_mask = 0;
  239.     char *psz_align    = GrabAttributeValue( "alignment", psz_subtitle );
  240.     char *psz_margin_x = GrabAttributeValue( "horizontal-margin", psz_subtitle );
  241.     char *psz_margin_y = GrabAttributeValue( "vertical-margin", psz_subtitle );
  242.     /* -- UNSUPPORTED
  243.     char *psz_relative = GrabAttributeValue( "relative-to", psz_subtitle );
  244.     char *psz_rotate_x = GrabAttributeValue( "rotate-x", psz_subtitle );
  245.     char *psz_rotate_y = GrabAttributeValue( "rotate-y", psz_subtitle );
  246.     char *psz_rotate_z = GrabAttributeValue( "rotate-z", psz_subtitle );
  247.     */
  248.     *i_align = SUBPICTURE_ALIGN_BOTTOM;
  249.     *i_x = 0;
  250.     *i_y = 0;
  251.     if( psz_align )
  252.     {
  253.         if( !strcasecmp( "TopLeft", psz_align ) )
  254.             *i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
  255.         else if( !strcasecmp( "TopCenter", psz_align ) )
  256.             *i_align = SUBPICTURE_ALIGN_TOP;
  257.         else if( !strcasecmp( "TopRight", psz_align ) )
  258.             *i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_RIGHT;
  259.         else if( !strcasecmp( "MiddleLeft", psz_align ) )
  260.             *i_align = SUBPICTURE_ALIGN_LEFT;
  261.         else if( !strcasecmp( "MiddleCenter", psz_align ) )
  262.             *i_align = 0;
  263.         else if( !strcasecmp( "MiddleRight", psz_align ) )
  264.             *i_align = SUBPICTURE_ALIGN_RIGHT;
  265.         else if( !strcasecmp( "BottomLeft", psz_align ) )
  266.             *i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_LEFT;
  267.         else if( !strcasecmp( "BottomCenter", psz_align ) )
  268.             *i_align = SUBPICTURE_ALIGN_BOTTOM;
  269.         else if( !strcasecmp( "BottomRight", psz_align ) )
  270.             *i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_RIGHT;
  271.         i_mask |= ATTRIBUTE_ALIGNMENT;
  272.         free( psz_align );
  273.     }
  274.     if( psz_margin_x )
  275.     {
  276.         *i_x = atoi( psz_margin_x );
  277.         if( strchr( psz_margin_x, '%' ) )
  278.             i_mask |= ATTRIBUTE_X_PERCENT;
  279.         else
  280.             i_mask |= ATTRIBUTE_X;
  281.         free( psz_margin_x );
  282.     }
  283.     if( psz_margin_y )
  284.     {
  285.         *i_y = atoi( psz_margin_y );
  286.         if( strchr( psz_margin_y, '%' ) )
  287.             i_mask |= ATTRIBUTE_Y_PERCENT;
  288.         else
  289.             i_mask |= ATTRIBUTE_Y;
  290.         free( psz_margin_y );
  291.     }
  292.     return i_mask;
  293. }
  294. static void SetupPositions( subpicture_region_t *p_region, char *psz_subtitle )
  295. {
  296.     int           i_mask = 0;
  297.     int           i_align;
  298.     int           i_x, i_y;
  299.     i_mask = ParsePositionAttributeList( psz_subtitle, &i_align, &i_x, &i_y );
  300.     if( i_mask & ATTRIBUTE_ALIGNMENT )
  301.         p_region->i_align = i_align;
  302.     /* TODO: Setup % based offsets properly, without adversely affecting
  303.      *       everything else in vlc. Will address with separate patch, to
  304.      *       prevent this one being any more complicated.
  305.      */
  306.     if( i_mask & ATTRIBUTE_X )
  307.         p_region->i_x = i_x;
  308.     else if( i_mask & ATTRIBUTE_X_PERCENT )
  309.         p_region->i_x = 0;
  310.     if( i_mask & ATTRIBUTE_Y )
  311.         p_region->i_y = i_y;
  312.     else if( i_mask & ATTRIBUTE_Y_PERCENT )
  313.         p_region->i_y = 0;
  314. }
  315. static subpicture_region_t *CreateTextRegion( decoder_t *p_dec,
  316.                                               subpicture_t *p_spu,
  317.                                               char *psz_subtitle,
  318.                                               int i_len,
  319.                                               int i_sys_align )
  320. {
  321.     decoder_sys_t        *p_sys = p_dec->p_sys;
  322.     subpicture_region_t  *p_text_region;
  323.     video_format_t        fmt;
  324.     /* Create a new subpicture region */
  325.     memset( &fmt, 0, sizeof(video_format_t) );
  326.     fmt.i_chroma = VLC_FOURCC('T','E','X','T');
  327.     fmt.i_aspect = 0;
  328.     fmt.i_width = fmt.i_height = 0;
  329.     fmt.i_x_offset = fmt.i_y_offset = 0;
  330.     p_text_region = subpicture_region_New( &fmt );
  331.     if( p_text_region != NULL )
  332.     {
  333.         ssa_style_t  *p_style = NULL;
  334.         p_text_region->psz_text = NULL;
  335.         p_text_region->psz_html = strndup( psz_subtitle, i_len );
  336.         if( ! p_text_region->psz_html )
  337.         {
  338.             subpicture_region_Delete( p_text_region );
  339.             return NULL;
  340.         }
  341.         p_style = ParseStyle( p_sys, p_text_region->psz_html );
  342.         if( !p_style )
  343.         {
  344.             int i;
  345.             for( i = 0; i < p_sys->i_ssa_styles; i++ )
  346.             {
  347.                 if( !strcasecmp( p_sys->pp_ssa_styles[i]->psz_stylename, "Default" ) )
  348.                     p_style = p_sys->pp_ssa_styles[i];
  349.             }
  350.         }
  351.         if( p_style )
  352.         {
  353.             msg_Dbg( p_dec, "style is: %s", p_style->psz_stylename );
  354.             p_text_region->p_style = &p_style->font_style;
  355.             p_text_region->i_align = p_style->i_align;
  356.             /* TODO: Setup % based offsets properly, without adversely affecting
  357.              *       everything else in vlc. Will address with separate patch,
  358.              *       to prevent this one being any more complicated.
  359.                      * p_style->i_margin_percent_h;
  360.                      * p_style->i_margin_percent_v;
  361.              */
  362.             p_text_region->i_x         = p_style->i_margin_h;
  363.             p_text_region->i_y         = p_style->i_margin_v;
  364.         }
  365.         else
  366.         {
  367.             p_text_region->i_align = SUBPICTURE_ALIGN_BOTTOM | i_sys_align;
  368.             p_text_region->i_x = i_sys_align ? 20 : 0;
  369.             p_text_region->i_y = 10;
  370.         }
  371.         /* Look for position arguments which may override the style-based
  372.          * defaults.
  373.          */
  374.         SetupPositions( p_text_region, psz_subtitle );
  375.         p_text_region->p_next = NULL;
  376.     }
  377.     return p_text_region;
  378. }
  379. static int ParseImageAttachments( decoder_t *p_dec )
  380. {
  381.     decoder_sys_t        *p_sys = p_dec->p_sys;
  382.     input_attachment_t  **pp_attachments;
  383.     int                   i_attachments_cnt;
  384.     int                   k = 0;
  385.     if( VLC_SUCCESS != decoder_GetInputAttachments( p_dec, &pp_attachments, &i_attachments_cnt ))
  386.         return VLC_EGENERIC;
  387.     for( k = 0; k < i_attachments_cnt; k++ )
  388.     {
  389.         input_attachment_t *p_attach = pp_attachments[k];
  390.         vlc_fourcc_t type = image_Mime2Fourcc( p_attach->psz_mime );
  391.         if( ( type != 0 ) &&
  392.             ( p_attach->i_data > 0 ) &&
  393.             ( p_attach->p_data != NULL ) )
  394.         {
  395.             picture_t         *p_pic = NULL;
  396.             image_handler_t   *p_image;
  397.             p_image = image_HandlerCreate( p_dec );
  398.             if( p_image != NULL )
  399.             {
  400.                 block_t   *p_block;
  401.                 p_block = block_New( p_image->p_parent, p_attach->i_data );
  402.                 if( p_block != NULL )
  403.                 {
  404.                     video_format_t     fmt_in;
  405.                     video_format_t     fmt_out;
  406.                     memcpy( p_block->p_buffer, p_attach->p_data, p_attach->i_data );
  407.                     memset( &fmt_in,  0, sizeof( video_format_t));
  408.                     memset( &fmt_out, 0, sizeof( video_format_t));
  409.                     fmt_in.i_chroma  = type;
  410.                     fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
  411.                     /* Find a suitable decoder module */
  412.                     if( module_exists( "sdl_image" ) )
  413.                     {
  414.                         /* ffmpeg thinks it can handle bmp properly but it can't (at least
  415.                          * not all of them), so use sdl_image if it is available */
  416.                         vlc_value_t val;
  417.                         var_Create( p_dec, "codec", VLC_VAR_MODULE | VLC_VAR_DOINHERIT );
  418.                         val.psz_string = (char*) "sdl_image";
  419.                         var_Set( p_dec, "codec", val );
  420.                     }
  421.                     p_pic = image_Read( p_image, p_block, &fmt_in, &fmt_out );
  422.                     var_Destroy( p_dec, "codec" );
  423.                 }
  424.                 image_HandlerDelete( p_image );
  425.             }
  426.             if( p_pic )
  427.             {
  428.                 image_attach_t *p_picture = malloc( sizeof(image_attach_t) );
  429.                 if( p_picture )
  430.                 {
  431.                     p_picture->psz_filename = strdup( p_attach->psz_name );
  432.                     p_picture->p_pic = p_pic;
  433.                     TAB_APPEND( p_sys->i_images, p_sys->pp_images, p_picture );
  434.                 }
  435.             }
  436.         }
  437.         vlc_input_attachment_Delete( pp_attachments[ k ] );
  438.     }
  439.     free( pp_attachments );
  440.     return VLC_SUCCESS;
  441. }
  442. static void ParseUSFHeaderTags( decoder_t *p_dec, xml_reader_t *p_xml_reader )
  443. {
  444.     decoder_sys_t *p_sys = p_dec->p_sys;
  445.     char *psz_node;
  446.     ssa_style_t *p_style = NULL;
  447.     int i_style_level = 0;
  448.     int i_metadata_level = 0;
  449.     while ( xml_ReaderRead( p_xml_reader ) == 1 )
  450.     {
  451.         switch ( xml_ReaderNodeType( p_xml_reader ) )
  452.         {
  453.             case XML_READER_TEXT:
  454.             case XML_READER_NONE:
  455.                 break;
  456.             case XML_READER_ENDELEM:
  457.                 psz_node = xml_ReaderName( p_xml_reader );
  458.                 if( !psz_node )
  459.                     break;
  460.                 switch (i_style_level)
  461.                 {
  462.                     case 0:
  463.                         if( !strcasecmp( "metadata", psz_node ) && (i_metadata_level == 1) )
  464.                         {
  465.                             i_metadata_level--;
  466.                         }
  467.                         break;
  468.                     case 1:
  469.                         if( !strcasecmp( "styles", psz_node ) )
  470.                         {
  471.                             i_style_level--;
  472.                         }
  473.                         break;
  474.                     case 2:
  475.                         if( !strcasecmp( "style", psz_node ) )
  476.                         {
  477.                             TAB_APPEND( p_sys->i_ssa_styles, p_sys->pp_ssa_styles, p_style );
  478.                             p_style = NULL;
  479.                             i_style_level--;
  480.                         }
  481.                         break;
  482.                 }
  483.                 free( psz_node );
  484.                 break;
  485.             case XML_READER_STARTELEM:
  486.                 psz_node = xml_ReaderName( p_xml_reader );
  487.                 if( !psz_node )
  488.                     break;
  489.                 if( !strcasecmp( "metadata", psz_node ) && (i_style_level == 0) )
  490.                 {
  491.                     i_metadata_level++;
  492.                 }
  493.                 else if( !strcasecmp( "resolution", psz_node ) &&
  494.                          ( i_metadata_level == 1) )
  495.                 {
  496.                     while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
  497.                     {
  498.                         char *psz_name = xml_ReaderName ( p_xml_reader );
  499.                         char *psz_value = xml_ReaderValue ( p_xml_reader );
  500.                         if( psz_name && psz_value )
  501.                         {
  502.                             if( !strcasecmp( "x", psz_name ) )
  503.                                 p_sys->i_original_width = atoi( psz_value );
  504.                             else if( !strcasecmp( "y", psz_name ) )
  505.                                 p_sys->i_original_height = atoi( psz_value );
  506.                         }
  507.                         free( psz_name );
  508.                         free( psz_value );
  509.                     }
  510.                 }
  511.                 else if( !strcasecmp( "styles", psz_node ) && (i_style_level == 0) )
  512.                 {
  513.                     i_style_level++;
  514.                 }
  515.                 else if( !strcasecmp( "style", psz_node ) && (i_style_level == 1) )
  516.                 {
  517.                     i_style_level++;
  518.                     p_style = calloc( 1, sizeof(ssa_style_t) );
  519.                     if( ! p_style )
  520.                     {
  521.                         free( psz_node );
  522.                         return;
  523.                     }
  524.                     /* All styles are supposed to default to Default, and then
  525.                      * one or more settings are over-ridden.
  526.                      * At the moment this only effects styles defined AFTER
  527.                      * Default in the XML
  528.                      */
  529.                     int i;
  530.                     for( i = 0; i < p_sys->i_ssa_styles; i++ )
  531.                     {
  532.                         if( !strcasecmp( p_sys->pp_ssa_styles[i]->psz_stylename, "Default" ) )
  533.                         {
  534.                             ssa_style_t *p_default_style = p_sys->pp_ssa_styles[i];
  535.                             memcpy( p_style, p_default_style, sizeof( ssa_style_t ) );
  536.                             p_style->font_style.psz_fontname = strdup( p_style->font_style.psz_fontname );
  537.                             p_style->psz_stylename = NULL;
  538.                         }
  539.                     }
  540.                     while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
  541.                     {
  542.                         char *psz_name = xml_ReaderName ( p_xml_reader );
  543.                         char *psz_value = xml_ReaderValue ( p_xml_reader );
  544.                         if( psz_name && psz_value )
  545.                         {
  546.                             if( !strcasecmp( "name", psz_name ) )
  547.                                 p_style->psz_stylename = strdup( psz_value);
  548.                         }
  549.                         free( psz_name );
  550.                         free( psz_value );
  551.                     }
  552.                 }
  553.                 else if( !strcasecmp( "fontstyle", psz_node ) && (i_style_level == 2) )
  554.                 {
  555.                     while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
  556.                     {
  557.                         char *psz_name = xml_ReaderName ( p_xml_reader );
  558.                         char *psz_value = xml_ReaderValue ( p_xml_reader );
  559.                         if( psz_name && psz_value )
  560.                         {
  561.                             if( !strcasecmp( "face", psz_name ) )
  562.                             {
  563.                                 free( p_style->font_style.psz_fontname );
  564.                                 p_style->font_style.psz_fontname = strdup( psz_value );
  565.                             }
  566.                             else if( !strcasecmp( "size", psz_name ) )
  567.                             {
  568.                                 if( ( *psz_value == '+' ) || ( *psz_value == '-' ) )
  569.                                 {
  570.                                     int i_value = atoi( psz_value );
  571.                                     if( ( i_value >= -5 ) && ( i_value <= 5 ) )
  572.                                         p_style->font_style.i_font_size  +=
  573.                                             ( i_value * p_style->font_style.i_font_size ) / 10;
  574.                                     else if( i_value < -5 )
  575.                                         p_style->font_style.i_font_size  = - i_value;
  576.                                     else if( i_value > 5 )
  577.                                         p_style->font_style.i_font_size  = i_value;
  578.                                 }
  579.                                 else
  580.                                     p_style->font_style.i_font_size  = atoi( psz_value );
  581.                             }
  582.                             else if( !strcasecmp( "italic", psz_name ) )
  583.                             {
  584.                                 if( !strcasecmp( "yes", psz_value ))
  585.                                     p_style->font_style.i_style_flags |= STYLE_ITALIC;
  586.                                 else
  587.                                     p_style->font_style.i_style_flags &= ~STYLE_ITALIC;
  588.                             }
  589.                             else if( !strcasecmp( "weight", psz_name ) )
  590.                             {
  591.                                 if( !strcasecmp( "bold", psz_value ))
  592.                                     p_style->font_style.i_style_flags |= STYLE_BOLD;
  593.                                 else
  594.                                     p_style->font_style.i_style_flags &= ~STYLE_BOLD;
  595.                             }
  596.                             else if( !strcasecmp( "underline", psz_name ) )
  597.                             {
  598.                                 if( !strcasecmp( "yes", psz_value ))
  599.                                     p_style->font_style.i_style_flags |= STYLE_UNDERLINE;
  600.                                 else
  601.                                     p_style->font_style.i_style_flags &= ~STYLE_UNDERLINE;
  602.                             }
  603.                             else if( !strcasecmp( "color", psz_name ) )
  604.                             {
  605.                                 if( *psz_value == '#' )
  606.                                 {
  607.                                     unsigned long col = strtol(psz_value+1, NULL, 16);
  608.                                     p_style->font_style.i_font_color = (col & 0x00ffffff);
  609.                                     p_style->font_style.i_font_alpha = (col >> 24) & 0xff;
  610.                                 }
  611.                             }
  612.                             else if( !strcasecmp( "outline-color", psz_name ) )
  613.                             {
  614.                                 if( *psz_value == '#' )
  615.                                 {
  616.                                     unsigned long col = strtol(psz_value+1, NULL, 16);
  617.                                     p_style->font_style.i_outline_color = (col & 0x00ffffff);
  618.                                     p_style->font_style.i_outline_alpha = (col >> 24) & 0xff;
  619.                                 }
  620.                             }
  621.                             else if( !strcasecmp( "outline-level", psz_name ) )
  622.                             {
  623.                                 p_style->font_style.i_outline_width = atoi( psz_value );
  624.                             }
  625.                             else if( !strcasecmp( "shadow-color", psz_name ) )
  626.                             {
  627.                                 if( *psz_value == '#' )
  628.                                 {
  629.                                     unsigned long col = strtol(psz_value+1, NULL, 16);
  630.                                     p_style->font_style.i_shadow_color = (col & 0x00ffffff);
  631.                                     p_style->font_style.i_shadow_alpha = (col >> 24) & 0xff;
  632.                                 }
  633.                             }
  634.                             else if( !strcasecmp( "shadow-level", psz_name ) )
  635.                             {
  636.                                 p_style->font_style.i_shadow_width = atoi( psz_value );
  637.                             }
  638.                             else if( !strcasecmp( "back-color", psz_name ) )
  639.                             {
  640.                                 if( *psz_value == '#' )
  641.                                 {
  642.                                     unsigned long col = strtol(psz_value+1, NULL, 16);
  643.                                     p_style->font_style.i_karaoke_background_color = (col & 0x00ffffff);
  644.                                     p_style->font_style.i_karaoke_background_alpha = (col >> 24) & 0xff;
  645.                                 }
  646.                             }
  647.                             else if( !strcasecmp( "spacing", psz_name ) )
  648.                             {
  649.                                 p_style->font_style.i_spacing = atoi( psz_value );
  650.                             }
  651.                         }
  652.                         free( psz_name );
  653.                         free( psz_value );
  654.                     }
  655.                 }
  656.                 else if( !strcasecmp( "position", psz_node ) && (i_style_level == 2) )
  657.                 {
  658.                     while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
  659.                     {
  660.                         char *psz_name = xml_ReaderName ( p_xml_reader );
  661.                         char *psz_value = xml_ReaderValue ( p_xml_reader );
  662.                         if( psz_name && psz_value )
  663.                         {
  664.                             if( !strcasecmp( "alignment", psz_name ) )
  665.                             {
  666.                                 if( !strcasecmp( "TopLeft", psz_value ) )
  667.                                     p_style->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
  668.                                 else if( !strcasecmp( "TopCenter", psz_value ) )
  669.                                     p_style->i_align = SUBPICTURE_ALIGN_TOP;
  670.                                 else if( !strcasecmp( "TopRight", psz_value ) )
  671.                                     p_style->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_RIGHT;
  672.                                 else if( !strcasecmp( "MiddleLeft", psz_value ) )
  673.                                     p_style->i_align = SUBPICTURE_ALIGN_LEFT;
  674.                                 else if( !strcasecmp( "MiddleCenter", psz_value ) )
  675.                                     p_style->i_align = 0;
  676.                                 else if( !strcasecmp( "MiddleRight", psz_value ) )
  677.                                     p_style->i_align = SUBPICTURE_ALIGN_RIGHT;
  678.                                 else if( !strcasecmp( "BottomLeft", psz_value ) )
  679.                                     p_style->i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_LEFT;
  680.                                 else if( !strcasecmp( "BottomCenter", psz_value ) )
  681.                                     p_style->i_align = SUBPICTURE_ALIGN_BOTTOM;
  682.                                 else if( !strcasecmp( "BottomRight", psz_value ) )
  683.                                     p_style->i_align = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_RIGHT;
  684.                             }
  685.                             else if( !strcasecmp( "horizontal-margin", psz_name ) )
  686.                             {
  687.                                 if( strchr( psz_value, '%' ) )
  688.                                 {
  689.                                     p_style->i_margin_h = 0;
  690.                                     p_style->i_margin_percent_h = atoi( psz_value );
  691.                                 }
  692.                                 else
  693.                                 {
  694.                                     p_style->i_margin_h = atoi( psz_value );
  695.                                     p_style->i_margin_percent_h = 0;
  696.                                 }
  697.                             }
  698.                             else if( !strcasecmp( "vertical-margin", psz_name ) )
  699.                             {
  700.                                 if( strchr( psz_value, '%' ) )
  701.                                 {
  702.                                     p_style->i_margin_v = 0;
  703.                                     p_style->i_margin_percent_v = atoi( psz_value );
  704.                                 }
  705.                                 else
  706.                                 {
  707.                                     p_style->i_margin_v = atoi( psz_value );
  708.                                     p_style->i_margin_percent_v = 0;
  709.                                 }
  710.                             }
  711.                         }
  712.                         free( psz_name );
  713.                         free( psz_value );
  714.                     }
  715.                 }
  716.                 free( psz_node );
  717.                 break;
  718.         }
  719.     }
  720.     free( p_style );
  721. }
  722. static subpicture_region_t *ParseUSFString( decoder_t *p_dec,
  723.                                             char *psz_subtitle,
  724.                                             subpicture_t *p_spu_in )
  725. {
  726.     decoder_sys_t        *p_sys = p_dec->p_sys;
  727.     subpicture_t         *p_spu = p_spu_in;
  728.     subpicture_region_t  *p_region_first = NULL;
  729.     subpicture_region_t  *p_region_upto  = p_region_first;
  730.     while( *psz_subtitle )
  731.     {
  732.         if( *psz_subtitle == '<' )
  733.         {
  734.             char *psz_end = NULL;
  735.             if(( !strncasecmp( psz_subtitle, "<text ", 6 )) ||
  736.                ( !strncasecmp( psz_subtitle, "<text>", 6 )))
  737.             {
  738.                 psz_end = strcasestr( psz_subtitle, "</text>" );
  739.                 if( psz_end )
  740.                 {
  741.                     subpicture_region_t  *p_text_region;
  742.                     psz_end += strcspn( psz_end, ">" ) + 1;
  743.                     p_text_region = CreateTextRegion( p_dec,
  744.                                                       p_spu,
  745.                                                       psz_subtitle,
  746.                                                       psz_end - psz_subtitle,
  747.                                                       p_sys->i_align );
  748.                     if( p_text_region )
  749.                     {
  750.                         p_text_region->psz_text = CreatePlainText( p_text_region->psz_html );
  751.                         if( ! var_CreateGetBool( p_dec, "subsdec-formatted" ) )
  752.                         {
  753.                             free( p_text_region->psz_html );
  754.                             p_text_region->psz_html = NULL;
  755.                         }
  756.                     }
  757.                     if( !p_region_first )
  758.                     {
  759.                         p_region_first = p_region_upto = p_text_region;
  760.                     }
  761.                     else if( p_text_region )
  762.                     {
  763.                         p_region_upto->p_next = p_text_region;
  764.                         p_region_upto = p_region_upto->p_next;
  765.                     }
  766.                 }
  767.             }
  768.             else if(( !strncasecmp( psz_subtitle, "<karaoke ", 9 )) ||
  769.                     ( !strncasecmp( psz_subtitle, "<karaoke>", 9 )))
  770.             {
  771.                 psz_end = strcasestr( psz_subtitle, "</karaoke>" );
  772.                 if( psz_end )
  773.                 {
  774.                     subpicture_region_t  *p_text_region;
  775.                     psz_end += strcspn( psz_end, ">" ) + 1;
  776.                     p_text_region = CreateTextRegion( p_dec,
  777.                                                       p_spu,
  778.                                                       psz_subtitle,
  779.                                                       psz_end - psz_subtitle,
  780.                                                       p_sys->i_align );
  781.                     if( p_text_region )
  782.                     {
  783.                         if( ! var_CreateGetBool( p_dec, "subsdec-formatted" ) )
  784.                         {
  785.                             free( p_text_region->psz_html );
  786.                             p_text_region->psz_html = NULL;
  787.                         }
  788.                     }
  789.                     if( !p_region_first )
  790.                     {
  791.                         p_region_first = p_region_upto = p_text_region;
  792.                     }
  793.                     else if( p_text_region )
  794.                     {
  795.                         p_region_upto->p_next = p_text_region;
  796.                         p_region_upto = p_region_upto->p_next;
  797.                     }
  798.                 }
  799.             }
  800.             else if(( !strncasecmp( psz_subtitle, "<image ", 7 )) ||
  801.                     ( !strncasecmp( psz_subtitle, "<image>", 7 )))
  802.             {
  803.                 subpicture_region_t *p_image_region = NULL;
  804.                 char *psz_end = strcasestr( psz_subtitle, "</image>" );
  805.                 char *psz_content = strchr( psz_subtitle, '>' );
  806.                 int   i_transparent = -1;
  807.                 /* If a colorkey parameter is specified, then we have to map
  808.                  * that index in the picture through as transparent (it is
  809.                  * required by the USF spec but is also recommended that if the
  810.                  * creator really wants a transparent colour that they use a
  811.                  * type like PNG that properly supports it; this goes doubly
  812.                  * for VLC because the pictures are stored internally in YUV
  813.                  * and the resulting colour-matching may not produce the
  814.                  * desired results.)
  815.                  */
  816.                 char *psz_tmp = GrabAttributeValue( "colorkey", psz_subtitle );
  817.                 if( psz_tmp )
  818.                 {
  819.                     if( *psz_tmp == '#' )
  820.                         i_transparent = strtol( psz_tmp + 1, NULL, 16 ) & 0x00ffffff;
  821.                     free( psz_tmp );
  822.                 }
  823.                 if( psz_content && ( psz_content < psz_end ) )
  824.                 {
  825.                     char *psz_filename = strndup( &psz_content[1], psz_end - &psz_content[1] );
  826.                     if( psz_filename )
  827.                     {
  828.                         p_image_region = LoadEmbeddedImage( p_dec, p_spu,
  829.                                             psz_filename, i_transparent );
  830.                         free( psz_filename );
  831.                     }
  832.                 }
  833.                 if( psz_end ) psz_end += strcspn( psz_end, ">" ) + 1;
  834.                 if( p_image_region )
  835.                 {
  836.                     SetupPositions( p_image_region, psz_subtitle );
  837.                     p_image_region->p_next   = NULL;
  838.                     p_image_region->psz_text = NULL;
  839.                     p_image_region->psz_html = NULL;
  840.                 }
  841.                 if( !p_region_first )
  842.                 {
  843.                     p_region_first = p_region_upto = p_image_region;
  844.                 }
  845.                 else if( p_image_region )
  846.                 {
  847.                     p_region_upto->p_next = p_image_region;
  848.                     p_region_upto = p_region_upto->p_next;
  849.                 }
  850.             }
  851.             if( psz_end )
  852.                 psz_subtitle = psz_end - 1;
  853.             psz_subtitle += strcspn( psz_subtitle, ">" );
  854.         }
  855.         psz_subtitle++;
  856.     }
  857.     return p_region_first;
  858. }
  859. /*****************************************************************************
  860.  * ParseUSFHeader: Retrieve global formatting information etc
  861.  *****************************************************************************/
  862. static void ParseUSFHeader( decoder_t *p_dec )
  863. {
  864.     stream_t      *p_sub = NULL;
  865.     xml_t         *p_xml = NULL;
  866.     xml_reader_t  *p_xml_reader = NULL;
  867.     p_sub = stream_MemoryNew( VLC_OBJECT(p_dec),
  868.                               p_dec->fmt_in.p_extra,
  869.                               p_dec->fmt_in.i_extra,
  870.                               true );
  871.     if( !p_sub )
  872.         return;
  873.     p_xml = xml_Create( p_dec );
  874.     if( p_xml )
  875.     {
  876.         p_xml_reader = xml_ReaderCreate( p_xml, p_sub );
  877.         if( p_xml_reader )
  878.         {
  879.             /* Look for Root Node */
  880.             if( xml_ReaderRead( p_xml_reader ) == 1 )
  881.             {
  882.                 char *psz_node = xml_ReaderName( p_xml_reader );
  883.                 if( !strcasecmp( "usfsubtitles", psz_node ) )
  884.                     ParseUSFHeaderTags( p_dec, p_xml_reader );
  885.                 free( psz_node );
  886.             }
  887.             xml_ReaderDelete( p_xml, p_xml_reader );
  888.         }
  889.         xml_Delete( p_xml );
  890.     }
  891.     stream_Delete( p_sub );
  892. }
  893. /* Function now handles tags which has attribute values, and tries
  894.  * to deal with &' commands too. It no longer modifies the string
  895.  * in place, so that the original text can be reused
  896.  */
  897. static char *StripTags( char *psz_subtitle )
  898. {
  899.     char *psz_text_start;
  900.     char *psz_text;
  901.     psz_text = psz_text_start = malloc( strlen( psz_subtitle ) + 1 );
  902.     if( !psz_text_start )
  903.         return NULL;
  904.     while( *psz_subtitle )
  905.     {
  906.         /* Mask out any pre-existing LFs in the subtitle */
  907.         if( *psz_subtitle == 'n' )
  908.             *psz_subtitle = ' ';
  909.         if( *psz_subtitle == '<' )
  910.         {
  911.             if( strncasecmp( psz_subtitle, "<br/>", 5 ) == 0 )
  912.                 *psz_text++ = 'n';
  913.             psz_subtitle += strcspn( psz_subtitle, ">" );
  914.         }
  915.         else if( *psz_subtitle == '&' )
  916.         {
  917.             if( !strncasecmp( psz_subtitle, "&lt;", 4 ))
  918.             {
  919.                 *psz_text++ = '<';
  920.                 psz_subtitle += strcspn( psz_subtitle, ";" );
  921.             }
  922.             else if( !strncasecmp( psz_subtitle, "&gt;", 4 ))
  923.             {
  924.                 *psz_text++ = '>';
  925.                 psz_subtitle += strcspn( psz_subtitle, ";" );
  926.             }
  927.             else if( !strncasecmp( psz_subtitle, "&amp;", 5 ))
  928.             {
  929.                 *psz_text++ = '&';
  930.                 psz_subtitle += strcspn( psz_subtitle, ";" );
  931.             }
  932.             else if( !strncasecmp( psz_subtitle, "&quot;", 6 ))
  933.             {
  934.                 *psz_text++ = '"';
  935.                 psz_subtitle += strcspn( psz_subtitle, ";" );
  936.             }
  937.             else
  938.             {
  939.                 /* Assume it is just a normal ampersand */
  940.                 *psz_text++ = '&';
  941.             }
  942.         }
  943.         else
  944.         {
  945.             *psz_text++ = *psz_subtitle;
  946.         }
  947.         psz_subtitle++;
  948.     }
  949.     *psz_text = '';
  950.     psz_text_start = realloc( psz_text_start, strlen( psz_text_start ) + 1 );
  951.     return psz_text_start;
  952. }
  953. /* Turn a HTML subtitle, turn into a plain-text version,
  954.  *  complete with sensible whitespace compaction
  955.  */
  956. static char *CreatePlainText( char *psz_subtitle )
  957. {
  958.     char *psz_text = StripTags( psz_subtitle );
  959.     char *s;
  960.     if( !psz_text )
  961.         return NULL;
  962.     s = strpbrk( psz_text, "trn " );
  963.     while( s )
  964.     {
  965.         int   k;
  966.         char  spc = ' ';
  967.         int   i_whitespace = strspn( s, "trn " );
  968.         /* Favour 'n' over other whitespaces - if one of these
  969.          * occurs in the whitespace use a 'n' as our value,
  970.          * otherwise just use a ' '
  971.          */
  972.         for( k = 0; k < i_whitespace; k++ )
  973.             if( s[k] == 'n' ) spc = 'n';
  974.         if( i_whitespace > 1 )
  975.         {
  976.             memmove( &s[1],
  977.                      &s[i_whitespace],
  978.                      strlen( s ) - i_whitespace + 1 );
  979.         }
  980.         *s++ = spc;
  981.         s = strpbrk( s, "trn " );
  982.     }
  983.     return psz_text;
  984. }
  985. /****************************************************************************
  986.  * download and resize image located at psz_url
  987.  ***************************************************************************/
  988. static subpicture_region_t *LoadEmbeddedImage( decoder_t *p_dec,
  989.                                                subpicture_t *p_spu,
  990.                                                const char *psz_filename,
  991.                                                int i_transparent_color )
  992. {
  993.     decoder_sys_t         *p_sys = p_dec->p_sys;
  994.     subpicture_region_t   *p_region;
  995.     video_format_t         fmt_out;
  996.     int                    k;
  997.     picture_t             *p_pic = NULL;
  998.     for( k = 0; k < p_sys->i_images; k++ )
  999.     {
  1000.         if( p_sys->pp_images &&
  1001.             !strcmp( p_sys->pp_images[k]->psz_filename, psz_filename ) )
  1002.         {
  1003.             p_pic = p_sys->pp_images[k]->p_pic;
  1004.             break;
  1005.         }
  1006.     }
  1007.     if( !p_pic )
  1008.     {
  1009.         msg_Err( p_dec, "Unable to read image %s", psz_filename );
  1010.         return NULL;
  1011.     }
  1012.     /* Display the feed's image */
  1013.     memset( &fmt_out, 0, sizeof( video_format_t));
  1014.     fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
  1015.     fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
  1016.     fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
  1017.     fmt_out.i_width =
  1018.         fmt_out.i_visible_width = p_pic->format.i_visible_width;
  1019.     fmt_out.i_height =
  1020.         fmt_out.i_visible_height = p_pic->format.i_visible_height;
  1021.     p_region = subpicture_region_New( &fmt_out );
  1022.     if( !p_region )
  1023.     {
  1024.         msg_Err( p_dec, "cannot allocate SPU region" );
  1025.         return NULL;
  1026.     }
  1027.     assert( p_pic->format.i_chroma == VLC_FOURCC('Y','U','V','A') );
  1028.     /* FIXME the copy is probably not needed anymore */
  1029.     picture_CopyPixels( p_region->p_picture, p_pic );
  1030.     /* This isn't the best way to do this - if you really want transparency, then
  1031.      * you're much better off using an image type that supports it like PNG. The
  1032.      * spec requires this support though.
  1033.      */
  1034.     if( i_transparent_color > 0 )
  1035.     {
  1036.         int i_r = ( i_transparent_color >> 16 ) & 0xff;
  1037.         int i_g = ( i_transparent_color >>  8 ) & 0xff;
  1038.         int i_b = ( i_transparent_color       ) & 0xff;
  1039.         /* FIXME it cannot work as the yuv conversion code will probably NOT match
  1040.          * this one  */
  1041.         int i_y = ( ( (  66 * i_r + 129 * i_g +  25 * i_b + 128 ) >> 8 ) + 16 );
  1042.         int i_u =   ( ( -38 * i_r -  74 * i_g + 112 * i_b + 128 ) >> 8 ) + 128 ;
  1043.         int i_v =   ( ( 112 * i_r -  94 * i_g -  18 * i_b + 128 ) >> 8 ) + 128 ;
  1044.         assert( p_region->fmt.i_chroma == VLC_FOURCC('Y','U','V','A') );
  1045.         for( unsigned int y = 0; y < p_region->fmt.i_height; y++ )
  1046.         {
  1047.             for( unsigned int x = 0; x < p_region->fmt.i_width; x++ )
  1048.             {
  1049.                 if( p_region->p_picture->Y_PIXELS[y*p_region->p_picture->Y_PITCH + x] != i_y ||
  1050.                     p_region->p_picture->U_PIXELS[y*p_region->p_picture->U_PITCH + x] != i_u ||
  1051.                     p_region->p_picture->V_PIXELS[y*p_region->p_picture->V_PITCH + x] != i_v )
  1052.                     continue;
  1053.                 p_region->p_picture->A_PIXELS[y*p_region->p_picture->A_PITCH + x] = 0;
  1054.             }
  1055.         }
  1056.     }
  1057.     return p_region;
  1058. }