Commit d7790c49 authored by Bernie Purcell's avatar Bernie Purcell

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.
parent a27798f8
......@@ -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])
......
SOURCES_subsdec = subsass.c subsusf.c subsdec.c subsdec.h
SOURCES_subsdec = subsass.c subsdec.c subsdec.h
SOURCES_subsusf = subsusf.c subsdec.h
......@@ -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
......
......@@ -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;
}
......@@ -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 * );
......
......@@ -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,7 +860,7 @@ static void ParseUSFHeaderTags( decoder_t *p_dec, xml_reader_t *p_xml_reader )
subpicture_region_t *ParseUSFString( decoder_t *p_dec,
static subpicture_region_t *ParseUSFString( decoder_t *p_dec,
char *psz_subtitle,
subpicture_t *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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment