Commit 3ea1b0e9 authored by Gildas Bazin's avatar Gildas Bazin

* ALL: Major rework of the subpictures architecture.

  (expect some breakage).
* modules/video_filter/blend.c: new alpha-blending module.
parent 327a8a92
......@@ -98,6 +98,7 @@ HEADERS_include = \
include/vlc_error.h \
include/vlc_es.h \
include/vlc_es_out.h \
include/vlc_filter.h \
include/vlc_help.h \
include/vlc_httpd.h \
include/vlc_input.h \
......
......@@ -952,7 +952,7 @@ VLC_ADD_PLUGINS([trivial_mixer spdif_mixer float32_mixer])
VLC_ADD_PLUGINS([aout_file equalizer])
VLC_ADD_PLUGINS([i420_rgb i420_yuy2 i422_yuy2 i420_ymga])
VLC_ADD_PLUGINS([m3u id3 playlist export sgimb])
VLC_ADD_PLUGINS([rawvideo])
VLC_ADD_PLUGINS([rawvideo blend])
VLC_ADD_PLUGINS([wav araw subtitle adpcm a52sys dtssys au])
VLC_ADD_PLUGINS([access_file access_udp access_tcp access_http ipv4 access_mms])
VLC_ADD_PLUGINS([access_ftp access_directory sap http])
......
......@@ -53,7 +53,7 @@ struct text_style_t
};
static const text_style_t default_text_style = { 22, 0xffffff, VLC_FALSE, VLC_FALSE, VLC_FALSE };
VLC_EXPORT( subpicture_t *, vout_ShowTextRelative, ( vout_thread_t *, int, char *, text_style_t *, int, int, int, mtime_t ) );
VLC_EXPORT( int, vout_ShowTextRelative, ( vout_thread_t *, int, char *, text_style_t *, int, int, int, mtime_t ) );
VLC_EXPORT( int, vout_ShowTextAbsolute, ( vout_thread_t *, int, char *, text_style_t *, int, int, int, mtime_t, mtime_t ) );
VLC_EXPORT( void, __vout_OSDMessage, ( vlc_object_t *, int, char *, ... ) );
/**
......
......@@ -123,14 +123,26 @@ struct vout_thread_t
vout_chroma_t chroma; /**< translation tables */
/**@}*/
/* Picture and subpicture heaps */
/* Picture heap */
picture_t p_picture[2*VOUT_MAX_PICTURES+1]; /**< pictures */
/* Subpicture properties */
subpicture_t p_subpicture[VOUT_MAX_PICTURES]; /**< subpictures */
subpicture_t *p_default_channel; /**< subpicture in the default
channel */
int i_channel_count; /**< index of last subpicture
channel registered */
filter_t *p_blend; /**< alpha blending module */
filter_t *p_text; /**< text renderer module */
vlc_bool_t b_force_crop; /**< force cropping of subpicture */
int i_crop_x, i_crop_y, i_crop_width, i_crop_height; /**< cropping */
vlc_bool_t b_force_alpha; /**< force alpha palette of subpicture */
uint8_t pi_alpha[4]; /**< forced alpha palette */
/* Statistics */
count_t c_loops;
count_t c_pictures, c_late_pictures;
......@@ -144,15 +156,6 @@ struct vout_thread_t
/* Filter chain */
char *psz_filter_chain;
vlc_bool_t b_filter_change;
/* text renderer data */
text_renderer_sys_t * p_text_renderer_data; /**< private data for
the text renderer */
module_t * p_text_renderer_module; /**< text renderer module */
/** callback used when a new string needs to be shown on the vout */
subpicture_t * ( *pf_add_string ) ( vout_thread_t *, int, char *,
text_style_t *, int, int, int, mtime_t,
mtime_t );
};
#define I_OUTPUTPICTURES p_vout->output.i_pictures
......@@ -268,10 +271,16 @@ VLC_EXPORT( void, vout_DestroySubPicture, ( vout_thread_t *, subpict
VLC_EXPORT( void, vout_DisplaySubPicture, ( vout_thread_t *, subpicture_t * ) );
VLC_EXPORT( int, vout_RegisterOSDChannel, ( vout_thread_t * ) );
VLC_EXPORT( void, vout_ClearOSDChannel, ( vout_thread_t *, int ) );
subpicture_t * vout_SortSubPictures ( vout_thread_t *, mtime_t );
void vout_RenderSubPictures ( vout_thread_t *, picture_t *,
subpicture_t * );
#define spu_CreateRegion(a,b) __spu_CreateRegion(VLC_OBJECT(a),b)
VLC_EXPORT( subpicture_region_t *,__spu_CreateRegion, ( vlc_object_t *, video_format_t * ) );
#define spu_DestroyRegion(a,b) __spu_DestroyRegion(VLC_OBJECT(a),b)
VLC_EXPORT( void, __spu_DestroyRegion, ( vlc_object_t *, subpicture_region_t * ) );
void vout_InitSPU( vout_thread_t * );
void vout_DestroySPU( vout_thread_t * );
subpicture_t * vout_SortSubPictures ( vout_thread_t *, mtime_t );
void vout_RenderSubPictures( vout_thread_t *, picture_t *,
subpicture_t * );
/** @}*/
/**
* @}
......
......@@ -48,7 +48,7 @@ struct decoder_t
picture_t * ( * pf_decode_video )( decoder_t *, block_t ** );
aout_buffer_t * ( * pf_decode_audio )( decoder_t *, block_t ** );
void ( * pf_decode_sub) ( decoder_t *, block_t ** );
subpicture_t * ( * pf_decode_sub) ( decoder_t *, block_t ** );
block_t * ( * pf_packetize ) ( decoder_t *, block_t ** );
/* Some decoders only accept packetized data (ie. not truncated) */
......@@ -74,6 +74,9 @@ struct decoder_t
void ( * pf_picture_link) ( decoder_t *, picture_t * );
void ( * pf_picture_unlink) ( decoder_t *, picture_t * );
/* SPU output callbacks */
subpicture_t * ( * pf_spu_buffer_new) ( decoder_t * );
void ( * pf_spu_buffer_del) ( decoder_t *, subpicture_t * );
/* Private structure for the owner of the decoder */
decoder_owner_sys_t *p_owner;
......
......@@ -277,6 +277,7 @@ typedef struct picture_sys_t picture_sys_t;
typedef struct picture_heap_t picture_heap_t;
typedef struct subpicture_t subpicture_t;
typedef struct subpicture_sys_t subpicture_sys_t;
typedef struct subpicture_region_t subpicture_region_t;
typedef struct vout_synchro_t vout_synchro_t;
typedef struct text_renderer_sys_t text_renderer_sys_t;
typedef struct text_style_t text_style_t;
......@@ -314,6 +315,10 @@ typedef struct decoder_sys_t decoder_sys_t;
typedef struct encoder_t encoder_t;
typedef struct encoder_sys_t encoder_sys_t;
/* Filters */
typedef struct filter_t filter_t;
typedef struct filter_sys_t filter_sys_t;
/* Misc */
typedef struct data_packet_t data_packet_t;
typedef struct data_buffer_t data_buffer_t;
......
......@@ -36,10 +36,8 @@
*/
struct video_palette_t
{
int i_dummy; /**< to keep the compatibility with ffmpeg's palette */
uint32_t palette[256]; /**< 4-byte ARGB palette entries, stored in native
* byte order */
int i_dummy; /**< to keep the compatibility with ffmpeg's palette */
uint8_t palette[256][4]; /**< 4-byte RGBA/YUVA palette */
};
/**
......@@ -96,6 +94,7 @@ struct video_format_t
unsigned int i_frame_rate; /**< frame rate numerator */
unsigned int i_frame_rate_base; /**< frame rate denominator */
int i_rmask, i_rgmask, i_bmask; /**< color masks for RGB chroma */
video_palette_t *p_palette; /**< video palette from demuxer */
};
......
/*****************************************************************************
* vlc_codec.h: codec related structures
*****************************************************************************
* Copyright (C) 1999-2003 VideoLAN
* $Id$
*
* Authors: Gildas Bazin <gbazin@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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#ifndef _VLC_FILTER_H
#define _VLC_FILTER_H 1
/**
* \file
* This file defines the structure and types used by video and audio filters
*/
typedef struct filter_owner_sys_t filter_owner_sys_t;
/**
* \defgroup filter Filter
*
* The structure describing a filter
*
* @{
*/
struct filter_t
{
VLC_COMMON_MEMBERS
/* Module properties */
module_t * p_module;
filter_sys_t * p_sys;
void ( * pf_video_blend ) ( filter_t *, picture_t *,
picture_t *, picture_t *,
int, int );
picture_t * ( * pf_video_filter ) ( filter_t *, picture_t * );
subpicture_t * ( *pf_render_string ) ( filter_t *, block_t * );
/* Input format */
es_format_t fmt_in;
/* Output format of filter */
es_format_t fmt_out;
/*
* Buffers allocation
*/
/* Audio output callbacks */
aout_buffer_t * ( * pf_aout_buffer_new) ( filter_t *, int );
void ( * pf_aout_buffer_del) ( filter_t *, aout_buffer_t * );
/* Video output callbacks */
picture_t * ( * pf_vout_buffer_new) ( filter_t * );
void ( * pf_vout_buffer_del) ( filter_t *, picture_t * );
void ( * pf_picture_link) ( filter_t *, picture_t * );
void ( * pf_picture_unlink) ( filter_t *, picture_t * );
/* SPU output callbacks */
subpicture_t * ( * pf_spu_buffer_new) ( filter_t * );
void ( * pf_spu_buffer_del) ( filter_t *, subpicture_t * );
/* Private structure for the owner of the decoder */
filter_owner_sys_t *p_owner;
};
/**
* @}
*/
#endif /* _VLC_FILTER_H */
......@@ -175,6 +175,8 @@ struct picture_heap_t
#define U_PITCH p[U_PLANE].i_pitch
#define V_PIXELS p[V_PLANE].p_pixels
#define V_PITCH p[V_PLANE].i_pitch
#define A_PIXELS p[A_PLANE].p_pixels
#define A_PITCH p[A_PLANE].i_pitch
/**
* \defgroup subpicture Video Subpictures
......@@ -184,6 +186,28 @@ struct picture_heap_t
* @{
*/
/**
* Video subtitle region
*
* A subtitle region is defined by a picture (graphic) and its rendering
* coordinates.
* Subtitles contain a list of regions.
*/
struct subpicture_region_t
{
/** \name Region properties */
/**@{*/
video_format_t fmt; /**< format of the picture */
picture_t picture; /**< picture comprising this region */
int i_x; /**< position of region */
int i_y; /**< position of region */
subpicture_region_t *p_next; /**< next region in the list */
/**@}*/
};
/**
* Video subtitle
*
......@@ -216,6 +240,8 @@ struct subpicture_t
untill the next one appear */
/**@}*/
subpicture_region_t *p_region; /**< region list composing this subtitle */
/** \name Display properties
* These properties are only indicative and may be
* changed by the video output thread, or simply ignored depending of the
......@@ -225,13 +251,20 @@ struct subpicture_t
int i_y; /**< offset from alignment position */
int i_width; /**< picture width */
int i_height; /**< picture height */
/**@}*/
int b_absolute; /**< position is absolute */
int i_flags; /**< position flags */
/**@}*/
/** Pointer to function that renders this subtitle in a picture */
void ( *pf_render ) ( vout_thread_t *, picture_t *, const subpicture_t * );
/** Pointer to function that cleans up the private data of this subtitle */
void ( *pf_destroy ) ( subpicture_t * );
/** Pointer to functions for region management */
subpicture_region_t * ( *pf_create_region ) ( vlc_object_t *,
video_format_t * );
void ( *pf_destroy_region ) ( vlc_object_t *, subpicture_region_t * );
/** Private data - the subtitle plugin might want to put stuff here to
* keep track of the subpicture */
subpicture_sys_t *p_sys; /* subpicture data */
......
......@@ -37,8 +37,10 @@
/*****************************************************************************
* Module descriptor.
*****************************************************************************/
static int Open ( vlc_object_t *p_this );
static void Close( vlc_object_t *p_this );
static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
static subpicture_t *Decode( decoder_t *, block_t ** );
vlc_module_begin();
set_description( _("DVB subtitles decoder") );
......@@ -173,33 +175,14 @@ typedef struct
} dvbsub_clut_t;
typedef struct dvbsub_render_s
{
uint16_t i_x;
uint16_t i_y;
dvbsub_image_t *p_rle_top;
dvbsub_image_t *p_rle_bot;
struct dvbsub_render_s *p_next;
} dvbsub_render_t;
struct subpicture_sys_t
{
dvbsub_render_t *p_objects; /* Linked list of objects to render */
};
struct decoder_sys_t
{
vout_thread_t *p_vout;
bs_t bs;
/* Decoder internal data */
int i_id;
int i_ancillary_id;
mtime_t i_pts;
int i_id;
int i_ancillary_id;
mtime_t i_pts;
dvbsub_page_t *p_page;
dvbsub_region_t *p_regions;
......@@ -207,9 +190,6 @@ struct decoder_sys_t
dvbsub_clut_t *p_clut[256];
dvbsub_clut_t default_clut;
subpicture_t *p_spu;
int i_subpic_channel;
};
......@@ -243,8 +223,6 @@ struct decoder_sys_t
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static void Decode( decoder_t *, block_t ** );
static void decode_segment( decoder_t *, bs_t * );
static void decode_page_composition( decoder_t *, bs_t * );
static void decode_region_composition( decoder_t *, bs_t * );
......@@ -254,7 +232,8 @@ static void decode_clut( decoder_t *, bs_t * );
static void free_objects( decoder_t * );
static void free_all( decoder_t * );
static void render( decoder_t *, vout_thread_t * );
static subpicture_t *render( decoder_t * );
static void default_clut_init( decoder_t * );
/*****************************************************************************
......@@ -283,8 +262,6 @@ static int Open( vlc_object_t *p_this )
p_sys->p_page = NULL;
p_sys->p_regions = NULL;
p_sys->p_objects = NULL;
p_sys->p_vout = NULL;
p_sys->p_spu = NULL;
for( i = 0; i < 256; i++ ) p_sys->p_clut[i] = NULL;
es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 'd','v','b','s' ) );
......@@ -302,22 +279,6 @@ static void Close( 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->p_vout && p_sys->p_vout->p_subpicture != NULL )
{
subpicture_t *p_subpic;
int i_subpic;
for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
{
p_subpic = &p_sys->p_vout->p_subpicture[i_subpic];
if( p_subpic != NULL &&
( p_subpic->i_status == RESERVED_SUBPICTURE ||
p_subpic->i_status == READY_SUBPICTURE ) )
{
vout_DestroySubPicture( p_sys->p_vout, p_subpic );
}
}
}
free_all( p_dec );
free( p_sys );
}
......@@ -325,16 +286,13 @@ static void Close( vlc_object_t *p_this )
/*****************************************************************************
* Decode:
*****************************************************************************/
static void Decode( decoder_t *p_dec, block_t **pp_block )
static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_block;
vout_thread_t *p_last_vout;
subpicture_t *p_spu = NULL;
if( pp_block == NULL || *pp_block == NULL )
{
return;
}
if( pp_block == NULL || *pp_block == NULL ) return NULL;
p_block = *pp_block;
*pp_block = NULL;
......@@ -343,7 +301,7 @@ static void Decode( decoder_t *p_dec, block_t **pp_block )
{
msg_Warn( p_dec, "non dated subtitle" );
block_Release( p_block );
return;
return NULL;
}
bs_init( &p_sys->bs, p_block->p_buffer, p_block->i_buffer );
......@@ -352,14 +310,14 @@ static void Decode( decoder_t *p_dec, block_t **pp_block )
{
msg_Dbg( p_dec, "invalid data identifier" );
block_Release( p_block );
return;
return NULL;
}
if( bs_read( &p_sys->bs, 8 ) != 0x20 && 0 ) /* Subtitle stream id */
{
msg_Dbg( p_dec, "invalid subtitle stream id" );
block_Release( p_block );
return;
return NULL;
}
while( bs_show( &p_sys->bs, 8 ) == 0x0f ) /* Sync byte */
......@@ -371,29 +329,15 @@ static void Decode( decoder_t *p_dec, block_t **pp_block )
{
msg_Warn( p_dec, "end marker not found (corrupted subtitle ?)" );
block_Release( p_block );
return;
return NULL;
}
p_last_vout = p_sys->p_vout;
if( ( p_sys->p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT,
FIND_ANYWHERE ) ) )
{
if( p_last_vout != p_sys->p_vout )
{
p_sys->i_subpic_channel =
vout_RegisterOSDChannel( p_sys->p_vout );
}
/* Check if the page is to be displayed */
if( p_sys->p_page ) render( p_dec, p_sys->p_vout );
vlc_object_release( p_sys->p_vout );
}
#ifdef DEBUG_DVBSUB
else if( p_sys->p_page ) render( p_dec, NULL );
#endif
/* Check if the page is to be displayed */
if( p_sys->p_page ) p_spu = render( p_dec );
block_Release( p_block );
return p_spu;
}
/* following functions are local */
......@@ -1174,27 +1118,6 @@ static uint16_t dvbsub_pdata8bpp( bs_t *s, uint16_t* p,
return ( i_processed + 7 ) / 8 ;
}
static dvbsub_image_t *dup_image( dvbsub_image_t *p_i )
{
dvbsub_image_t *p_image = malloc( sizeof(dvbsub_image_t) );
dvbsub_rle_t *p_rle = p_i->p_codes;
*p_image = *p_i;
p_image->p_last = NULL;
while( p_rle )
{
dvbsub_rle_t *p_last = p_image->p_last;
p_image->p_last = malloc( sizeof(dvbsub_rle_t) );
if( !p_last ) p_image->p_codes = p_image->p_last;
if( p_last ) p_last->p_next = p_image->p_last;
*p_image->p_last = *p_rle;
p_rle = p_rle->p_next;
}
return p_image;
}
static void free_image( dvbsub_image_t *p_i )
{
dvbsub_rle_t *p1;
......@@ -1210,21 +1133,6 @@ static void free_image( dvbsub_image_t *p_i )
free( p_i );
}
static void free_spu( subpicture_t *p_spu )
{
dvbsub_render_t *p_obj, *p_obj_next;
for( p_obj = p_spu->p_sys->p_objects; p_obj != NULL; p_obj = p_obj_next )
{
p_obj_next = p_obj->p_next;
free_image( p_obj->p_rle_top );
free_image( p_obj->p_rle_bot );
free( p_obj );
}
free( p_spu->p_sys );
p_spu->p_sys = NULL;
}
static void free_objects( decoder_t *p_dec )
{
decoder_sys_t *p_sys = p_dec->p_sys;
......@@ -1271,201 +1179,20 @@ static void free_all( decoder_t *p_dec )
free_objects( p_dec );
}
static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
dvbsub_render_t *p_r )
{
/* Common variables */
uint8_t *p_desty;
uint16_t i,j;
uint16_t i_cnt;
uint16_t x, y;
dvbsub_rle_t* p_c;
dvbsub_image_t* p_im = p_r->p_rle_top;
i=0;
j=0;
p_desty = p_pic->Y_PIXELS;
//let's render the 1st frame
for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
{
//if( p_c->y != 0 && p_c->t < 0x20)
if( p_c->y != 0 && p_c->t < 0x20)
{
x = j+ p_r->i_x;
y = 2*i+p_r->i_y;
//memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
// In YUY2 we have to set pixel per pixel
for( i_cnt = 0; i_cnt < p_c->i_num; i_cnt+=2 )
{
memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt, p_c->y, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+1, p_c->cr, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+2, p_c->y, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+3, p_c->cb, 1);
}
}
j += p_c->i_num;
if(j >= p_im->i_cols[i])
{
i++; j=0;
}
if( i>= p_im->i_rows) break;
}
//idem for the second frame
p_im = p_r->p_rle_bot; i=0; j=0;
for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
{
if( p_c->y != 0 && p_c->t < 0x20)
{
x = j+ p_r->i_x;
y = 2*i+1+p_r->i_y;
//memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
// In YUY2 we have to set pixel per pixel
for( i_cnt = 0; i_cnt < p_c->i_num; i_cnt+=2 )
{
memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt, p_c->y, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+1, p_c->cr, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+2, p_c->y, 1);
//memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+3, p_c->cb, 1);
}
}
j += p_c->i_num;
if(j >= p_im->i_cols[i])
{
i++; j=0;
}
if( i>= p_im->i_rows) break;
}
}
static void RenderI42x( vout_thread_t *p_vout, picture_t *p_pic,
dvbsub_render_t *p_r )
{
/* Common variables */
uint8_t *p_desty = p_pic->Y_PIXELS;
uint8_t *p_destu = p_pic->U_PIXELS;
uint8_t *p_destv = p_pic->V_PIXELS;
dvbsub_image_t* p_im = p_r->p_rle_top;
dvbsub_rle_t* p_c;
uint16_t i, j, x, y;
int i_x_subsampling =
p_vout->output.i_chroma == VLC_FOURCC('I','4','2','2') ? 1 : 2;
/* Let's render the top field */
p_im = p_r->p_rle_bot; i = 0; j = 0;
for( p_c = p_im->p_codes; p_c->p_next != NULL; p_c = p_c->p_next )
{
if( p_c->y != 0 && p_c->t != 0xFF )
{
x = j + p_r->i_x;
y = 2 * i + p_r->i_y;
memset( p_desty + y * p_pic->Y_PITCH + x, p_c->y, p_c->i_num );
memset( p_destu + y/2 * p_pic->U_PITCH + x/i_x_subsampling,
p_c->cr, p_c->i_num/i_x_subsampling );
memset( p_destv + y/2 * p_pic->V_PITCH + x/i_x_subsampling,
p_c->cb, p_c->i_num/i_x_subsampling );
}
j += p_c->i_num;
if( j >= p_im->i_cols[i] )
{
i++; j=0;
}
if( i >= p_im->i_rows) break;
}
/* Idem for the bottom field */
p_im = p_r->p_rle_bot; i = 0; j = 0;
for( p_c = p_im->p_codes; p_c->p_next != NULL; p_c = p_c->p_next )
{
if( p_c->y != 0 && p_c->t != 0xFF )
{
x = j + p_r->i_x;
y = 2*i + 1 + p_r->i_y;
memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
/* No U or V (decimation) */
}
j += p_c->i_num;
if( j >= p_im->i_cols[i] )
{
i++; j=0;
}
if( i >= p_im->i_rows ) break;
}
}
static void RenderDVBSUB( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu )
{
dvbsub_render_t* p_render;
if( p_spu->p_sys == NULL ) return;
p_render = p_spu->p_sys->p_objects;
while( p_render )
{
switch( p_vout->output.i_chroma )
{
/* I420 target, no scaling */
case VLC_FOURCC('I','4','2','2'):
case VLC_FOURCC('I','4','2','0'):
case VLC_FOURCC('I','Y','U','V'):
case VLC_FOURCC('Y','V','1','2'):
/* As long as we just use Y info, I422 and YV12 are just equivalent
* to I420. Remember to change it the day we'll take into account
* U and V info. */
RenderI42x( p_vout, p_pic, p_render );
break;
/* RV16 target, scaling */
case VLC_FOURCC('R','V','1','6'):
msg_Err(p_vout, "unimplemented chroma: RV16");
/* RenderRV16( p_vout, p_pic, p_spu ); */
break;
/* RV32 target, scaling */
case VLC_FOURCC('R','V','2','4'):
case VLC_FOURCC('R','V','3','2'):
msg_Err(p_vout, "unimplemented chroma: RV32");
/* RenderRV32( p_vout, p_pic, p_spu ); */
break;
/* NVidia overlay, no scaling */
case VLC_FOURCC('Y','U','Y','2'):
RenderYUY2( p_vout, p_pic, p_render );
break;
default:
msg_Err( p_vout, "unknown chroma, can't render SPU" );
break;
}
p_render = p_render->p_next;
}
}
static void render( decoder_t *p_dec, vout_thread_t *p_vout )
static subpicture_t *render( decoder_t *p_dec )
{
decoder_sys_t *p_sys = p_dec->p_sys;
dvbsub_render_t *p_render = NULL, *p_current, *p_last = NULL;
dvbsub_clut_t *p_clut;
dvbsub_rle_t *p_c;
subpicture_t *p_spu;
subpicture_region_t **pp_spu_region;
int i, j = 0, i_timeout = 0;
/* Allocate the subpicture internal data. */
#ifdef DEBUG_DVBSUB
if( !p_vout ) p_sys->p_spu = malloc( sizeof(subpicture_t) );
else
#endif
p_sys->p_spu =
vout_CreateSubPicture( p_vout, p_sys->i_subpic_channel,
MEMORY_SUBPICTURE );
if( p_sys->p_spu == NULL ) return;
p_spu = p_dec->pf_spu_buffer_new( p_dec );
if( !p_spu ) return NULL;
pp_spu_region = &p_spu->p_region;
/* Loop on region definitions */
#ifdef DEBUG_DVBSUB
......@@ -1475,8 +1202,12 @@ static void render( decoder_t *p_dec, vout_thread_t *p_vout )
for( i = 0; p_sys->p_page && i < p_sys->p_page->i_region_defs; i++ )
{
dvbsub_region_t *p_region;
dvbsub_regiondef_t *p_regiondef;
dvbsub_region_t *p_region;
dvbsub_regiondef_t *p_regiondef;
subpicture_region_t *p_spu_region;
uint8_t *p_y, *p_u, *p_v, *p_a;
video_format_t fmt;
int i_pitch;
i_timeout = p_sys->p_page->i_timeout;
......@@ -1500,11 +1231,37 @@ static void render( decoder_t *p_dec, vout_thread_t *p_vout )
continue;
}
/* Create new SPU region */
memset( &fmt, 0, sizeof(video_format_t) );
fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
fmt.i_aspect = VOUT_ASPECT_FACTOR;
fmt.i_width = fmt.i_visible_width = p_region->i_width;
fmt.i_height = fmt.i_visible_height = p_region->i_height;
fmt.i_x_offset = fmt.i_y_offset = 0;
p_spu_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
if( !p_region )
{
msg_Err( p_dec, "cannot allocate SPU region" );
continue;
}
p_spu_region->i_x = p_regiondef->i_x;
p_spu_region->i_y = p_regiondef->i_y;
*pp_spu_region = p_spu_region;
pp_spu_region = &p_spu_region->p_next;
p_y = p_spu_region->picture.Y_PIXELS;
p_u = p_spu_region->picture.U_PIXELS;
p_v = p_spu_region->picture.V_PIXELS;
p_a = p_spu_region->picture.A_PIXELS;
i_pitch = p_spu_region->picture.Y_PITCH;
memset( p_a, 0, i_pitch * p_region->i_height );
/* Loop on object definitions */
for( j = 0; j < p_region->i_object_defs; j++ )
{
dvbsub_object_t *p_object;
dvbsub_objectdef_t *p_objectdef;
uint16_t k, l, x, y;
p_objectdef = &p_region->p_object_defs[j];
......@@ -1526,65 +1283,64 @@ static void render( decoder_t *p_dec, vout_thread_t *p_vout )
continue;
}
/* Allocate the render structure */
p_current = malloc( sizeof(dvbsub_render_t) );
p_current->p_next = NULL;
p_current->i_x = p_regiondef->i_x + p_objectdef->i_x;
p_current->i_y = p_regiondef->i_y + p_objectdef->i_y;
p_current->p_rle_top = dup_image( p_object->topfield );
p_current->p_rle_bot = dup_image( p_object->bottomfield );
if( !p_render ) p_render = p_current;
if( p_last ) p_last->p_next = p_current;
p_last = p_current;
/* Draw SPU region */
p_clut = p_sys->p_clut[p_region->i_clut];
if( !p_clut ) p_clut = &p_sys->default_clut;
/* Compute the color datas according to the appropriate CLUT */
for( p_c = p_current->p_rle_top->p_codes;
p_c->p_next != NULL; p_c = p_c->p_next )
for( k = 0, l = 0, p_c = p_object->topfield->p_codes;
p_c->p_next; p_c = p_c->p_next )
{
/* Compute the color data according to the appropriate CLUT */
dvbsub_color_t *p_color = (p_c->i_bpp == 2) ? p_clut->c_2b :
(p_c->i_bpp == 4) ? p_clut->c_4b : p_clut->c_8b;
p_c->y = p_color[p_c->i_color_code].Y;
p_c->cr = p_color[p_c->i_color_code].Cr;
p_c->cb = p_color[p_c->i_color_code].Cb;
p_c->t = p_color[p_c->i_color_code].T;
x = l + p_objectdef->i_x;
y = 2 * k + p_objectdef->i_y;
memset( p_y + y * i_pitch + x, p_color[p_c->i_color_code].Y,
p_c->i_num );
memset( p_u + y * i_pitch + x, p_color[p_c->i_color_code].Cr,
p_c->i_num );
memset( p_v + y * i_pitch + x, p_color[p_c->i_color_code].Cb,
p_c->i_num );
memset( p_a + y * i_pitch + x,
255 - p_color[p_c->i_color_code].T, p_c->i_num );
l += p_c->i_num;
if( l >= p_object->topfield->i_cols[k] ) { k++; l = 0; }
if( k >= p_object->topfield->i_rows) break;
}
for( p_c = p_current->p_rle_bot->p_codes; p_c->p_next != NULL;
p_c = p_c->p_next )
for( k = 0, l = 0, p_c = p_object->bottomfield->p_codes;
p_c->p_next; p_c = p_c->p_next )
{
/* Compute the color data according to the appropriate CLUT */
dvbsub_color_t *p_color = (p_c->i_bpp == 2) ? p_clut->c_2b :
(p_c->i_bpp == 4) ? p_clut->c_4b : p_clut->c_8b;
p_c->y = p_color[p_c->i_color_code].Y;
p_c->cr = p_color[p_c->i_color_code].Cr;
p_c->cb = p_color[p_c->i_color_code].Cb;
p_c->t = p_color[p_c->i_color_code].T;
x = l + p_objectdef->i_x;
y = 2 * k + 1 + p_objectdef->i_y;
memset( p_y + y * i_pitch + x, p_color[p_c->i_color_code].Y,
p_c->i_num );
memset( p_u + y * i_pitch + x, p_color[p_c->i_color_code].Cr,
p_c->i_num );
memset( p_v + y * i_pitch + x, p_color[p_c->i_color_code].Cb,
p_c->i_num );
memset( p_a + y * i_pitch + x,
255 - p_color[p_c->i_color_code].T, p_c->i_num );
l += p_c->i_num;
if( l >= p_object->bottomfield->i_cols[k] ) { k++; l = 0; }
if( k >= p_object->bottomfield->i_rows) break;
}
}
}
/* Set the pf_render callback */
p_sys->p_spu->pf_render = RenderDVBSUB;
p_sys->p_spu->p_sys = malloc( sizeof(subpicture_sys_t) );
p_sys->p_spu->p_sys->p_objects = p_render;
p_sys->p_spu->pf_destroy = free_spu;
p_sys->p_spu->i_start = p_sys->i_pts;
p_sys->p_spu->i_stop = p_sys->p_spu->i_start + i_timeout * 1000000;
p_sys->p_spu->b_ephemer = VLC_TRUE;
#ifdef DEBUG_DVBSUB
if( !p_vout )
{
free_spu( p_sys->p_spu );
free( p_sys->p_spu );
p_sys->p_spu = NULL;
return;
}
#endif
p_spu->i_start = p_sys->i_pts;
p_spu->i_stop = p_spu->i_start + i_timeout * 1000000;
p_spu->b_ephemer = VLC_TRUE;
vout_DisplaySubPicture( p_vout, p_sys->p_spu );
return p_spu;
}
SOURCES_spudec = \
spudec.c \
parse.c \
render.c \
spudec.h \
$(NULL)
......@@ -6,6 +6,7 @@
*
* Authors: Samuel Hocevar <sam@zoy.org>
* Laurent Aimar <fenrir@via.ecp.fr>
* Gildas Bazin <gbazin@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
......@@ -34,14 +35,9 @@
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
static int ParseControlSeq ( decoder_t *, subpicture_t * );
static int ParseRLE ( decoder_t *, subpicture_t * );
static void DestroySPU ( subpicture_t * );
static void UpdateSPU ( subpicture_t *, vlc_object_t * );
static int CropCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int ParseControlSeq( decoder_t *, subpicture_t *, subpicture_data_t *);
static int ParseRLE ( decoder_t *, subpicture_t *, subpicture_data_t *);
static void Render ( decoder_t *, subpicture_t *, subpicture_data_t *);
/*****************************************************************************
* AddNibble: read a nibble from a source packet and add it to our integer.
......@@ -65,89 +61,56 @@ static inline unsigned int AddNibble( unsigned int i_code,
* This function parses the SPU packet and, if valid, sends it to the
* video output.
*****************************************************************************/
void E_(ParsePacket)( decoder_t *p_dec)
subpicture_t * E_(ParsePacket)( decoder_t *p_dec )
{
decoder_sys_t *p_sys = p_dec->p_sys;
subpicture_t *p_spu;
int i_spu_channel;
subpicture_data_t *p_spu_data;
subpicture_t *p_spu;
/* Allocate the subpicture internal data. */
p_spu = vout_CreateSubPicture( p_sys->p_vout, p_sys->i_subpic_channel,
MEMORY_SUBPICTURE );
if( p_spu == NULL )
{
return;
}
p_spu = p_dec->pf_spu_buffer_new( p_dec );
if( !p_spu ) return NULL;
/* Rationale for the "p_spudec->i_rle_size * 4": we are going to
* expand the RLE stuff so that we won't need to read nibbles later
* on. This will speed things up a lot. Plus, we'll only need to do
* this stupid interlacing stuff once. */
p_spu->p_sys = malloc( sizeof( subpicture_sys_t ) + 4*p_sys->i_rle_size );
/* Fill the p_spu structure */
vlc_mutex_init( p_dec, &p_spu->p_sys->lock );
p_spu->pf_render = E_(RenderSPU);
p_spu->pf_destroy = DestroySPU;
p_spu->p_sys->p_data = (uint8_t*)p_spu->p_sys + sizeof( subpicture_sys_t );
p_spu->p_sys->b_palette = VLC_FALSE;
p_spu_data = malloc( sizeof(subpicture_data_t) + 4 * p_sys->i_rle_size );
p_spu_data->p_data = (uint8_t *)p_spu_data + sizeof(subpicture_data_t);
p_spu_data->b_palette = VLC_FALSE;
p_spu->p_sys->pi_alpha[0] = 0x00;
p_spu->p_sys->pi_alpha[1] = 0x0f;
p_spu->p_sys->pi_alpha[2] = 0x0f;
p_spu->p_sys->pi_alpha[3] = 0x0f;
p_spu->p_sys->b_crop = VLC_FALSE;
p_spu_data->pi_alpha[0] = 0x00;
p_spu_data->pi_alpha[1] = 0x0f;
p_spu_data->pi_alpha[2] = 0x0f;
p_spu_data->pi_alpha[3] = 0x0f;
/* Get display time now. If we do it later, we may miss the PTS. */
p_spu->p_sys->i_pts = p_sys->i_pts;
/* Attach to our input thread */
p_spu->p_sys->p_input = vlc_object_find( p_dec,
VLC_OBJECT_INPUT, FIND_PARENT );
if( p_spu->p_sys->p_input )
{
vlc_value_t val;
if( !var_Get( p_spu->p_sys->p_input, "highlight-mutex", &val ) )
{
vlc_mutex_t *p_mutex = val.p_address;
vlc_mutex_lock( p_mutex );
UpdateSPU( p_spu, VLC_OBJECT(p_spu->p_sys->p_input) );
var_AddCallback( p_spu->p_sys->p_input,
"highlight", CropCallback, p_spu );
vlc_mutex_unlock( p_mutex );
}
}
p_spu_data->i_pts = p_sys->i_pts;
/* Getting the control part */
if( ParseControlSeq( p_dec, p_spu ) )
if( ParseControlSeq( p_dec, p_spu, p_spu_data ) )
{
/* There was a parse error, delete the subpicture */
vout_DestroySubPicture( p_sys->p_vout, p_spu );
return;
p_dec->pf_spu_buffer_del( p_dec, p_spu );
return NULL;
}
/* We try to display it */
if( ParseRLE( p_dec, p_spu ) )
if( ParseRLE( p_dec, p_spu, p_spu_data ) )
{
/* There was a parse error, delete the subpicture */
vout_DestroySubPicture( p_sys->p_vout, p_spu );
return;
p_dec->pf_spu_buffer_del( p_dec, p_spu );
return NULL;
}
msg_Dbg( p_dec, "total size: 0x%x, RLE offsets: 0x%x 0x%x",
p_sys->i_spu_size,
p_spu->p_sys->pi_offset[0], p_spu->p_sys->pi_offset[1] );
p_spu_data->pi_offset[0], p_spu_data->pi_offset[1] );
/* SPU is finished - we can ask the video output to display it */
vout_DisplaySubPicture( p_sys->p_vout, p_spu );
Render( p_dec, p_spu, p_spu_data );
free( p_spu_data );
/* TODO: do stuff! */
return p_spu;
}
/*****************************************************************************
......@@ -157,7 +120,8 @@ void E_(ParsePacket)( decoder_t *p_dec)
* information, coordinates, and so on. For more information on the
* subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
*****************************************************************************/
static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
subpicture_data_t *p_spu_data )
{
decoder_sys_t *p_sys = p_dec->p_sys;
......@@ -211,17 +175,17 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
switch( i_command )
{
case SPU_CMD_FORCE_DISPLAY: /* 00 (force displaying) */
p_spu->i_start = p_spu->p_sys->i_pts + date;
p_spu->i_start = p_spu_data->i_pts + date;
p_spu->b_ephemer = VLC_TRUE;
break;
/* Convert the dates in seconds to PTS values */
case SPU_CMD_START_DISPLAY: /* 01 (start displaying) */
p_spu->i_start = p_spu->p_sys->i_pts + date;
p_spu->i_start = p_spu_data->i_pts + date;
break;
case SPU_CMD_STOP_DISPLAY: /* 02 (stop displaying) */
p_spu->i_stop = p_spu->p_sys->i_pts + date;
p_spu->i_stop = p_spu_data->i_pts + date;
break;
case SPU_CMD_SET_PALETTE:
......@@ -231,7 +195,7 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
{
unsigned int idx[4];
p_spu->p_sys->b_palette = VLC_TRUE;
p_spu_data->b_palette = VLC_TRUE;
idx[0] = (p_sys->buffer[i_index+0]>>4)&0x0f;
idx[1] = (p_sys->buffer[i_index+0])&0x0f;
......@@ -243,9 +207,9 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
uint32_t i_color = p_dec->fmt_in.subs.spu.palette[1+idx[i]];
/* FIXME: this job should be done sooner */
p_spu->p_sys->pi_yuv[3-i][0] = (i_color>>16) & 0xff;
p_spu->p_sys->pi_yuv[3-i][1] = (i_color>>0) & 0xff;
p_spu->p_sys->pi_yuv[3-i][2] = (i_color>>8) & 0xff;
p_spu_data->pi_yuv[3-i][0] = (i_color>>16) & 0xff;
p_spu_data->pi_yuv[3-i][1] = (i_color>>0) & 0xff;
p_spu_data->pi_yuv[3-i][2] = (i_color>>8) & 0xff;
}
}
i_index += 2;
......@@ -262,10 +226,10 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
* alpha palettes are present - dunno why. */
if( pi_alpha[0] | pi_alpha[1] | pi_alpha[2] | pi_alpha[3] )
{
p_spu->p_sys->pi_alpha[0] = pi_alpha[0];
p_spu->p_sys->pi_alpha[1] = pi_alpha[1];
p_spu->p_sys->pi_alpha[2] = pi_alpha[2];
p_spu->p_sys->pi_alpha[3] = pi_alpha[3];
p_spu_data->pi_alpha[0] = pi_alpha[0];
p_spu_data->pi_alpha[1] = pi_alpha[1];
p_spu_data->pi_alpha[2] = pi_alpha[2];
p_spu_data->pi_alpha[3] = pi_alpha[3];
}
else
{
......@@ -290,8 +254,8 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
break;
case SPU_CMD_SET_OFFSETS: /* 06xxxxyyyy (byte offsets) */
p_spu->p_sys->pi_offset[0] = GetWBE(&p_sys->buffer[i_index+0]) - 4;
p_spu->p_sys->pi_offset[1] = GetWBE(&p_sys->buffer[i_index+2]) - 4;
p_spu_data->pi_offset[0] = GetWBE(&p_sys->buffer[i_index+0]) - 4;
p_spu_data->pi_offset[1] = GetWBE(&p_sys->buffer[i_index+2]) - 4;
i_index += 4;
break;
......@@ -335,9 +299,6 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
{
/* This subtitle will live for 5 seconds or until the next subtitle */
p_spu->i_stop = p_spu->i_start + (mtime_t)500 * 11000;
/* FIXME how to access i_rate ?
* p_spudec->bit_stream.p_pes->i_rate / DEFAULT_RATE;
*/
p_spu->b_ephemer = VLC_TRUE;
}
......@@ -362,7 +323,8 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t * p_spu )
* convenient structure for later decoding. For more information on the
* subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
*****************************************************************************/
static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu )
static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu,
subpicture_data_t *p_spu_data )
{
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p_src = &p_sys->buffer[4];
......@@ -373,26 +335,19 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu )
unsigned int i_height = p_spu->i_height;
unsigned int i_x, i_y;
uint16_t *p_dest = (uint16_t *)p_spu->p_sys->p_data;
uint16_t *p_dest = (uint16_t *)p_spu_data->p_data;
/* The subtitles are interlaced, we need two offsets */
unsigned int i_id = 0; /* Start on the even SPU layer */
unsigned int pi_table[ 2 ];
unsigned int *pi_offset;
#if 0 /* cropping */
vlc_bool_t b_empty_top = VLC_TRUE,
b_empty_bottom = VLC_FALSE;
unsigned int i_skipped_top = 0,
i_skipped_bottom = 0;
#endif
/* Colormap statistics */
int i_border = -1;
int stats[4]; stats[0] = stats[1] = stats[2] = stats[3] = 0;
pi_table[ 0 ] = p_spu->p_sys->pi_offset[ 0 ] << 1;
pi_table[ 1 ] = p_spu->p_sys->pi_offset[ 1 ] << 1;
pi_table[ 0 ] = p_spu_data->pi_offset[ 0 ] << 1;
pi_table[ 1 ] = p_spu_data->pi_offset[ 1 ] << 1;
for( i_y = 0 ; i_y < i_height ; i_y++ )
{
......@@ -436,58 +391,25 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu )
if( ( (i_code >> 2) + i_x + i_y * i_width ) > i_height * i_width )
{
msg_Err( p_dec,
"out of bounds, %i at (%i,%i) is out of %ix%i",
msg_Err( p_dec, "out of bounds, %i at (%i,%i) is out of %ix%i",
i_code >> 2, i_x, i_y, i_width, i_height );
return VLC_EGENERIC;
}
/* Try to find the border color */
if( p_spu->p_sys->pi_alpha[ i_code & 0x3 ] != 0x00 )
if( p_spu_data->pi_alpha[ i_code & 0x3 ] != 0x00 )
{
i_border = i_code & 0x3;
stats[i_border] += i_code >> 2;
}
#if 0 /* cropping */
if( (i_code >> 2) == i_width
&& p_spu->p_sys->pi_alpha[ i_code & 0x3 ] == 0x00 )
{
if( b_empty_top )
{
/* This is a blank top line, we skip it */
i_skipped_top++;
}
else
{
/* We can't be sure the current lines will be skipped,
* so we store the code just in case. */
*p_dest++ = i_code;
b_empty_bottom = VLC_TRUE;
i_skipped_bottom++;
}
}
else
{
/* We got a valid code, store it */
*p_dest++ = i_code;
/* Valid code means no blank line */
b_empty_top = VLC_FALSE;
b_empty_bottom = VLC_FALSE;
i_skipped_bottom = 0;
}
#else
*p_dest++ = i_code;
#endif
}
/* Check that we didn't go too far */
if( i_x > i_width )
{
msg_Err( p_dec, "i_x overflowed, %i > %i",
i_x, i_width );
msg_Err( p_dec, "i_x overflowed, %i > %i", i_x, i_width );
return VLC_EGENERIC;
}
......@@ -521,27 +443,15 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu )
msg_Dbg( p_dec, "valid subtitle, size: %ix%i, position: %i,%i",
p_spu->i_width, p_spu->i_height, p_spu->i_x, p_spu->i_y );
#if 0 /* cropping */
/* Crop if necessary */
if( i_skipped_top || i_skipped_bottom )
{
p_spu->i_y += i_skipped_top;
p_spu->i_height -= i_skipped_top + i_skipped_bottom;
msg_Dbg( p_spudec->p_fifo, "cropped to: %ix%i, position: %i,%i",
p_spu->i_width, p_spu->i_height, p_spu->i_x, p_spu->i_y );
}
#endif
/* Handle color if no palette was found */
if( !p_spu->p_sys->b_palette )
if( !p_spu_data->b_palette )
{
int i, i_inner = -1, i_shade = -1;
/* Set the border color */
p_spu->p_sys->pi_yuv[i_border][0] = 0x00;
p_spu->p_sys->pi_yuv[i_border][1] = 0x80;
p_spu->p_sys->pi_yuv[i_border][2] = 0x80;
p_spu_data->pi_yuv[i_border][0] = 0x00;
p_spu_data->pi_yuv[i_border][1] = 0x80;
p_spu_data->pi_yuv[i_border][2] = 0x80;
stats[i_border] = 0;
/* Find the inner colors */
......@@ -572,103 +482,73 @@ static int ParseRLE( decoder_t *p_dec, subpicture_t * p_spu )
/* Set the inner color */
if( i_inner != -1 )
{
p_spu->p_sys->pi_yuv[i_inner][0] = 0xff;
p_spu->p_sys->pi_yuv[i_inner][1] = 0x80;
p_spu->p_sys->pi_yuv[i_inner][2] = 0x80;
p_spu_data->pi_yuv[i_inner][0] = 0xff;
p_spu_data->pi_yuv[i_inner][1] = 0x80;
p_spu_data->pi_yuv[i_inner][2] = 0x80;
}
/* Set the anti-aliasing color */
if( i_shade != -1 )
{
p_spu->p_sys->pi_yuv[i_shade][0] = 0x80;
p_spu->p_sys->pi_yuv[i_shade][1] = 0x80;
p_spu->p_sys->pi_yuv[i_shade][2] = 0x80;
p_spu_data->pi_yuv[i_shade][0] = 0x80;
p_spu_data->pi_yuv[i_shade][1] = 0x80;
p_spu_data->pi_yuv[i_shade][2] = 0x80;
}
msg_Dbg( p_dec,
"using custom palette (border %i, inner %i, shade %i)",
msg_Dbg( p_dec, "using custom palette (border %i, inner %i, shade %i)",
i_border, i_inner, i_shade );
}
return VLC_SUCCESS;
}
/*****************************************************************************
* DestroySPU: subpicture destructor
*****************************************************************************/
static void DestroySPU( subpicture_t *p_spu )
static void Render( decoder_t *p_dec, subpicture_t *p_spu,
subpicture_data_t *p_spu_data )
{
if( p_spu->p_sys->p_input )
{
/* Detach from our input thread */
var_DelCallback( p_spu->p_sys->p_input, "highlight",
CropCallback, p_spu );
vlc_object_release( p_spu->p_sys->p_input );
}
vlc_mutex_destroy( &p_spu->p_sys->lock );
free( p_spu->p_sys );
}
/*****************************************************************************
* UpdateSPU: update subpicture settings
*****************************************************************************
* This function is called from CropCallback and at initialization time, to
* retrieve crop information from the input.
*****************************************************************************/
static void UpdateSPU( subpicture_t *p_spu, vlc_object_t *p_object )
{
vlc_value_t val;
if( var_Get( p_object, "highlight", &val ) )
uint8_t *p_p;
int i_x, i_y, i_len, i_color, i_pitch;
uint16_t *p_source = (uint16_t *)p_spu_data->p_data;
video_format_t fmt;
/* Create a new subpicture region */
memset( &fmt, 0, sizeof(video_format_t) );
fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
fmt.i_aspect = VOUT_ASPECT_FACTOR;
fmt.i_width = fmt.i_visible_width = p_spu->i_width;
fmt.i_height = fmt.i_visible_height = p_spu->i_height;
fmt.i_x_offset = fmt.i_y_offset = 0;
p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
if( !p_spu->p_region )
{
msg_Err( p_dec, "cannot allocate SPU region" );
return;
}
p_spu->p_sys->b_crop = val.b_bool;
if( !p_spu->p_sys->b_crop )
{
return;
}
p_spu->p_region->i_x = p_spu->p_region->i_y = 0;
p_p = p_spu->p_region->picture.p->p_pixels;
i_pitch = p_spu->p_region->picture.p->i_pitch;
var_Get( p_object, "x-start", &val );
p_spu->p_sys->i_x_start = val.i_int;
var_Get( p_object, "y-start", &val );
p_spu->p_sys->i_y_start = val.i_int;
var_Get( p_object, "x-end", &val );
p_spu->p_sys->i_x_end = val.i_int;
var_Get( p_object, "y-end", &val );
p_spu->p_sys->i_y_end = val.i_int;
#if 0
if( var_Get( p_object, "color", &val ) == VLC_SUCCESS )
/* Build palette */
for( i_x = 0; i_x < 4; i_x++ )
{
p_spu->p_sys->pi_color[0] = ((uint8_t *)val.p_address)[0];
p_spu->p_sys->pi_color[1] = ((uint8_t *)val.p_address)[1];
p_spu->p_sys->pi_color[2] = ((uint8_t *)val.p_address)[2];
p_spu->p_sys->pi_color[3] = ((uint8_t *)val.p_address)[3];
fmt.p_palette->palette[i_x][0] = p_spu_data->pi_yuv[i_x][0];
fmt.p_palette->palette[i_x][1] = p_spu_data->pi_yuv[i_x][1];
fmt.p_palette->palette[i_x][2] = p_spu_data->pi_yuv[i_x][2];
fmt.p_palette->palette[i_x][3] =
p_spu_data->pi_alpha[i_x] == 0xf ? 0xff :
p_spu_data->pi_alpha[i_x] << 4;
}
#endif
if( var_Get( p_object, "contrast", &val ) == VLC_SUCCESS )
/* Draw until we reach the bottom of the subtitle */
for( i_y = 0; i_y < p_spu->i_height * i_pitch; i_y += i_pitch )
{
p_spu->p_sys->pi_alpha[0] = ((uint8_t *)val.p_address)[0];
p_spu->p_sys->pi_alpha[1] = ((uint8_t *)val.p_address)[1];
p_spu->p_sys->pi_alpha[2] = ((uint8_t *)val.p_address)[2];
p_spu->p_sys->pi_alpha[3] = ((uint8_t *)val.p_address)[3];
/* Draw until we reach the end of the line */
for( i_x = 0 ; i_x < p_spu->i_width; i_x += i_len )
{
/* Get the RLE part, then draw the line */
i_color = *p_source & 0x3;
i_len = *p_source++ >> 2;
memset( p_p + i_x + i_y, i_color, i_len );
}
}
}
/*****************************************************************************
* CropCallback: called when the highlight properties are changed
*****************************************************************************
* This callback is called from the input thread when we need cropping
*****************************************************************************/
static int CropCallback( vlc_object_t *p_object, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
UpdateSPU( (subpicture_t *)p_data, p_object );
return VLC_SUCCESS;
}
/*****************************************************************************
* render.c : SPU renderer
*****************************************************************************
* Copyright (C) 2000-2001 VideoLAN
* $Id$
*
* Authors: Samuel Hocevar <sam@zoy.org>
* Rudolf Cornelissen <rag.cornelissen@inter.nl.net>
* Roine Gustafsson <roine@popstar.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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#include "spudec.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static void RenderI420( vout_thread_t *, picture_t *, const subpicture_t *,
vlc_bool_t );
static void RenderRV16( vout_thread_t *, picture_t *, const subpicture_t *,
vlc_bool_t );
static void RenderRV32( vout_thread_t *, picture_t *, const subpicture_t *,
vlc_bool_t );
static void RenderYUY2( vout_thread_t *, picture_t *, const subpicture_t *,
vlc_bool_t );
/*****************************************************************************
* RenderSPU: draw an SPU on a picture
*****************************************************************************
* This is a fast implementation of the subpicture drawing code. The data
* has been preprocessed once, so we don't need to parse the RLE buffer again
* and again. Most sanity checks are already done so that this routine can be
* as fast as possible.
*****************************************************************************/
void E_(RenderSPU)( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu )
{
switch( p_vout->output.i_chroma )
{
/* I420 target, no scaling */
case VLC_FOURCC('I','4','2','0'):
case VLC_FOURCC('I','Y','U','V'):
case VLC_FOURCC('Y','V','1','2'):
RenderI420( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
break;
/* RV16 target, scaling */
case VLC_FOURCC('R','V','1','6'):
RenderRV16( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
break;
/* RV32 target, scaling */
case VLC_FOURCC('R','V','2','4'):
case VLC_FOURCC('R','V','3','2'):
RenderRV32( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
break;
/* NVidia overlay, no scaling */
case VLC_FOURCC('Y','U','Y','2'):
RenderYUY2( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
break;
default:
msg_Err( p_vout, "unknown chroma, can't render SPU" );
break;
}
}
/* Following functions are local */
static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
/* Common variables */
uint8_t *p_dest, *p_dest_u, *p_dest_v;
uint8_t *p_destptr;
uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
int i_x, i_y, i_y_u, i_y_v;
int i_len, i_color, i_subsample;
uint16_t i_colprecomp, i_destalpha;
/* Crop-specific */
int i_x_start, i_y_start, i_x_end, i_y_end;
p_dest = p_pic->Y_PIXELS + p_spu->i_x + p_spu->i_width
+ p_pic->Y_PITCH * ( p_spu->i_y + p_spu->i_height );
p_dest_u = p_pic->U_PIXELS + p_spu->i_x / 2 + p_spu->i_width / 2
+ p_pic->U_PITCH * ( p_spu->i_y + p_spu->i_height ) / 2;
p_dest_v = p_pic->V_PIXELS + p_spu->i_x / 2 + p_spu->i_width / 2
+ p_pic->V_PITCH * ( p_spu->i_y + p_spu->i_height ) / 2;
#if 0 /* Fix vouts to handle YV12 properly */
if( p_vout->output.i_chroma == VLC_FOURCC('Y','V','1','2') )
{
/* Assumes U_PITCH == V_PITCH */
uint8_t *p_tmp = p_dest_u;
p_dest_u = p_dest_v;
p_dest_v = p_tmp;
}
#endif
i_subsample = (p_spu->i_y + p_spu->i_height) & 0x1 ? 0 : 1;
i_x_start = p_spu->i_width - p_spu->p_sys->i_x_end;
i_y_start = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_end );
i_x_end = p_spu->i_width - p_spu->p_sys->i_x_start;
i_y_end = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_start );
/* Draw until we reach the bottom of the subtitle */
for( i_y = p_spu->i_height * p_pic->Y_PITCH,
i_y_u = (p_spu->i_height + i_subsample) * p_pic->U_PITCH / 2,
i_y_v = (p_spu->i_height + i_subsample) * p_pic->V_PITCH / 2 ;
i_y ;
i_y -= p_pic->Y_PITCH,
i_y_u -= (p_pic->U_PITCH / 2),
i_y_v -= (p_pic->V_PITCH / 2), i_subsample++ )
{
/* Draw until we reach the end of the line */
for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw the line */
i_color = *p_source & 0x3;
i_len = *p_source++ >> 2;
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[i_color] )
{
case 0x00:
break;
case 0x0f:
memset( p_dest - i_x - i_y,
p_spu->p_sys->pi_yuv[i_color][0], i_len );
if ( i_subsample & 0x1 ) break;
/* U and V */
memset( p_dest_u - i_x/2 - i_y_u,
p_spu->p_sys->pi_yuv[i_color][1], (i_len+1)/2 );
memset( p_dest_v - i_x/2 - i_y_v,
p_spu->p_sys->pi_yuv[i_color][2], (i_len+1)/2 );
break;
default:
/* To be able to divide by 16 (>>4) we add 1 to the alpha.
* This means Alpha 0 won't be completely transparent, but
* that's handled in a special case above anyway. */
i_colprecomp = (uint16_t)p_spu->p_sys->pi_yuv[i_color][0]
* (uint16_t)(p_spu->p_sys->pi_alpha[i_color] + 1);
i_destalpha = 15 - p_spu->p_sys->pi_alpha[i_color];
for ( p_destptr = p_dest - i_x - i_y;
p_destptr < p_dest - i_x - i_y + i_len;
p_destptr++ )
{
*p_destptr = ( i_colprecomp +
(uint16_t)*p_destptr * i_destalpha ) >> 4;
}
if ( i_subsample & 0x1 ) break;
/* U and V */
i_colprecomp = (uint16_t)p_spu->p_sys->pi_yuv[i_color][1]
* (uint16_t)(p_spu->p_sys->pi_alpha[i_color] + 1);
i_destalpha = 15 - p_spu->p_sys->pi_alpha[i_color];
for ( p_destptr = p_dest_u - i_x/2 - i_y_u;
p_destptr < p_dest_u - i_x/2 - i_y_u + (i_len+1)/2;
p_destptr++ )
{
*p_destptr = ( i_colprecomp +
(uint16_t)*p_destptr * i_destalpha ) >> 4;
}
i_colprecomp = (uint16_t)p_spu->p_sys->pi_yuv[i_color][2]
* (uint16_t)(p_spu->p_sys->pi_alpha[i_color] + 1);
i_destalpha = 15 - p_spu->p_sys->pi_alpha[i_color];
for ( p_destptr = p_dest_v - i_x/2 - i_y_v;
p_destptr < p_dest_v - i_x/2 - i_y_v + (i_len+1)/2;
p_destptr++ )
{
*p_destptr = ( i_colprecomp +
(uint16_t)*p_destptr * i_destalpha ) >> 4;
}
break;
}
}
}
}
static void RenderRV16( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
/* Common variables */
uint16_t p_clut16[4];
uint8_t *p_dest;
uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
int i_x, i_y;
int i_len, i_color;
/* RGB-specific */
int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
/* Crop-specific */
int i_x_start, i_y_start, i_x_end, i_y_end;
/* XXX: this is a COMPLETE HACK, memcpy is unable to do u16s anyway */
/* FIXME: get this from the DVD */
for( i_color = 0; i_color < 4; i_color++ )
{
p_clut16[i_color] = 0x1111
* ( (uint16_t)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
}
i_xscale = ( p_vout->output.i_width << 6 ) / p_vout->render.i_width;
i_yscale = ( p_vout->output.i_height << 6 ) / p_vout->render.i_height;
i_width = p_spu->i_width * i_xscale;
i_height = p_spu->i_height * i_yscale;
p_dest = p_pic->p->p_pixels + ( i_width >> 6 ) * 2
/* Add the picture coordinates and the SPU coordinates */
+ ( (p_spu->i_x * i_xscale) >> 6 ) * 2
+ ( (p_spu->i_y * i_yscale) >> 6 ) * p_pic->p->i_pitch;
i_x_start = i_width - i_xscale * p_spu->p_sys->i_x_end;
i_y_start = i_yscale * p_spu->p_sys->i_y_start;
i_x_end = i_width - i_xscale * p_spu->p_sys->i_x_start;
i_y_end = i_yscale * p_spu->p_sys->i_y_end;
/* Draw until we reach the bottom of the subtitle */
for( i_y = 0 ; i_y < i_height ; )
{
i_ytmp = i_y >> 6;
i_y += i_yscale;
/* Check whether we need to draw one line or more than one */
if( i_ytmp + 1 >= ( i_y >> 6 ) )
{
/* Just one line : we precalculate i_y >> 6 */
i_yreal = p_pic->p->i_pitch * i_ytmp;
/* Draw until we reach the end of the line */
for( i_x = i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw the line */
i_color = *p_source & 0x3;
i_len = i_xscale * ( *p_source++ >> 2 );
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[ i_color ] )
{
case 0x00:
break;
case 0x0f:
memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
p_clut16[ i_color ],
2 * ( ( i_len >> 6 ) + 1 ) );
break;
default:
/* FIXME: we should do transparency */
memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
p_clut16[ i_color ],
2 * ( ( i_len >> 6 ) + 1 ) );
break;
}
}
}
else
{
i_yreal = p_pic->p->i_pitch * i_ytmp;
i_ynext = p_pic->p->i_pitch * i_y >> 6;
/* Draw until we reach the end of the line */
for( i_x = i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw as many lines as needed */
i_color = *p_source & 0x3;
i_len = i_xscale * ( *p_source++ >> 2 );
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[ i_color ] )
{
case 0x00:
break;
case 0x0f:
for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
i_ytmp += p_pic->p->i_pitch )
{
memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
p_clut16[ i_color ],
2 * ( ( i_len >> 6 ) + 1 ) );
}
break;
default:
/* FIXME: we should do transparency */
for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
i_ytmp += p_pic->p->i_pitch )
{
memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
p_clut16[ i_color ],
2 * ( ( i_len >> 6 ) + 1 ) );
}
break;
}
}
}
}
}
static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
/* Common variables */
uint32_t p_clut32[4];
uint8_t *p_dest;
uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
int i_x, i_y;
int i_len, i_color;
/* RGB-specific */
int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
/* Crop-specific */
int i_x_start, i_y_start, i_x_end, i_y_end;
/* XXX: this is a COMPLETE HACK, memcpy is unable to do u32s anyway */
/* FIXME: get this from the DVD */
for( i_color = 0; i_color < 4; i_color++ )
{
p_clut32[i_color] = 0x11111111
* ( (uint16_t)p_spu->p_sys->pi_yuv[i_color][0] >> 4 );
}
i_xscale = ( p_vout->output.i_width << 6 ) / p_vout->render.i_width;
i_yscale = ( p_vout->output.i_height << 6 ) / p_vout->render.i_height;
i_width = p_spu->i_width * i_xscale;
i_height = p_spu->i_height * i_yscale;
i_x_start = i_width - i_xscale * p_spu->p_sys->i_x_end;
i_y_start = i_yscale * p_spu->p_sys->i_y_start;
i_x_end = i_width - i_xscale * p_spu->p_sys->i_x_start;
i_y_end = i_yscale * p_spu->p_sys->i_y_end;
p_dest = p_pic->p->p_pixels + ( i_width >> 6 ) * 4
/* Add the picture coordinates and the SPU coordinates */
+ ( (p_spu->i_x * i_xscale) >> 6 ) * 4
+ ( (p_spu->i_y * i_yscale) >> 6 ) * p_pic->p->i_pitch;
/* Draw until we reach the bottom of the subtitle */
for( i_y = 0 ; i_y < i_height ; )
{
i_ytmp = i_y >> 6;
i_y += i_yscale;
/* Check whether we need to draw one line or more than one */
if( i_ytmp + 1 >= ( i_y >> 6 ) )
{
/* Just one line : we precalculate i_y >> 6 */
i_yreal = p_pic->p->i_pitch * i_ytmp;
/* Draw until we reach the end of the line */
for( i_x = i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw the line */
i_color = *p_source & 0x3;
i_len = i_xscale * ( *p_source++ >> 2 );
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[ i_color ] )
{
case 0x00:
break;
case 0x0f:
memset( p_dest - 4 * ( i_x >> 6 ) + i_yreal,
p_clut32[ i_color ], 4 * ( ( i_len >> 6 ) + 1 ) );
break;
default:
/* FIXME: we should do transparency */
memset( p_dest - 4 * ( i_x >> 6 ) + i_yreal,
p_clut32[ i_color ], 4 * ( ( i_len >> 6 ) + 1 ) );
break;
}
}
}
else
{
i_yreal = p_pic->p->i_pitch * i_ytmp;
i_ynext = p_pic->p->i_pitch * i_y >> 6;
/* Draw until we reach the end of the line */
for( i_x = i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw as many lines as needed */
i_color = *p_source & 0x3;
i_len = i_xscale * ( *p_source++ >> 2 );
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[ i_color ] )
{
case 0x00:
break;
case 0x0f:
for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
i_ytmp += p_pic->p->i_pitch )
{
memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
p_clut32[ i_color ],
4 * ( ( i_len >> 6 ) + 1 ) );
}
break;
default:
/* FIXME: we should do transparency */
for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
i_ytmp += p_pic->p->i_pitch )
{
memset( p_dest - 4 * ( i_x >> 6 ) + i_ytmp,
p_clut32[ i_color ],
4 * ( ( i_len >> 6 ) + 1 ) );
}
break;
}
}
}
}
}
static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
/* Common variables */
uint8_t *p_dest;
uint16_t *p_source = (uint16_t *)p_spu->p_sys->p_data;
int i_x, i_y;
int i_len, i_color;
uint8_t i_cnt;
/* Crop-specific */
int i_x_start, i_y_start, i_x_end, i_y_end;
p_dest = p_pic->p->p_pixels +
+ ( p_spu->i_y + p_spu->i_height ) * p_pic->p->i_pitch // * bytes per line
+ ( p_spu->i_x + p_spu->i_width ) * 2; // * bytes per pixel
i_x_start = p_spu->i_width - p_spu->p_sys->i_x_end;
i_y_start = (p_spu->i_height - p_spu->p_sys->i_y_end)
* p_pic->p->i_pitch / 2;
i_x_end = p_spu->i_width - p_spu->p_sys->i_x_start;
i_y_end = (p_spu->i_height - p_spu->p_sys->i_y_start)
* p_pic->p->i_pitch / 2;
/* Draw until we reach the bottom of the subtitle */
for( i_y = p_spu->i_height * p_pic->p->i_pitch / 2;
i_y ;
i_y -= p_pic->p->i_pitch / 2 )
{
/* Draw until we reach the end of the line */
for( i_x = p_spu->i_width ; i_x ; i_x -= i_len )
{
/* Get the RLE part, then draw the line */
i_color = *p_source & 0x3;
i_len = *p_source++ >> 2;
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
switch( p_spu->p_sys->pi_alpha[ i_color ] )
{
case 0x00:
break;
case 0x0f:
for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
{
/* draw a pixel */
/* Y */
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
p_spu->p_sys->pi_yuv[i_color][0], 1);
if (!(i_cnt & 0x01))
{
/* U and V */
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
0x80, 1);
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,
0x80, 1);
}
}
break;
default:
/* FIXME: we should do transparency */
for( i_cnt = 0; i_cnt < i_len; i_cnt++ )
{
/* draw a pixel */
/* Y */
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2,
p_spu->p_sys->pi_yuv[i_color][0], 1);
if (!(i_cnt & 0x01))
{
/* U and V */
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 1,
0x80, 1);
memset( p_dest - i_x * 2 - i_y * 2 + i_cnt * 2 + 3,
0x80, 1);
}
}
break;
}
}
}
}
......@@ -26,7 +26,6 @@
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#include "spudec.h"
......@@ -36,8 +35,7 @@
*****************************************************************************/
static int DecoderOpen ( vlc_object_t * );
static int PacketizerOpen( vlc_object_t * );
static void Close ( vlc_object_t * );
static void Close ( vlc_object_t * );
vlc_module_begin();
set_description( _("DVD subtitles decoder") );
......@@ -53,12 +51,9 @@ vlc_module_end();
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static vout_thread_t *FindVout( decoder_t *);
static block_t *Reassemble( decoder_t *, block_t ** );
static void Decode ( decoder_t *, block_t ** );
static block_t *Packetize( decoder_t *, block_t ** );
static block_t * Reassemble( decoder_t *, block_t ** );
static subpicture_t * Decode ( decoder_t *, block_t ** );
static block_t * Packetize ( decoder_t *, block_t ** );
/*****************************************************************************
* DecoderOpen
......@@ -83,7 +78,6 @@ static int DecoderOpen( vlc_object_t *p_this )
p_sys->i_spu_size = 0;
p_sys->i_spu = 0;
p_sys->p_block = NULL;
p_sys->p_vout = NULL;
es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
......@@ -120,71 +114,39 @@ static void Close( 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->b_packetizer )
{
/* FIXME check if it's ok to not lock vout */
if( p_sys->p_vout != NULL && p_sys->p_vout->p_subpicture != NULL )
{
subpicture_t * p_subpic;
int i_subpic;
for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
{
p_subpic = &p_sys->p_vout->p_subpicture[i_subpic];
if( p_subpic != NULL &&
( ( p_subpic->i_status == RESERVED_SUBPICTURE ) ||
( p_subpic->i_status == READY_SUBPICTURE ) ) )
{
vout_DestroySubPicture( p_sys->p_vout, p_subpic );
}
}
}
}
if( p_sys->p_block )
{
block_ChainRelease( p_sys->p_block );
}
if( p_sys->p_block ) block_ChainRelease( p_sys->p_block );
free( p_sys );
}
/*****************************************************************************
* Decode:
*****************************************************************************/
static void Decode( decoder_t *p_dec, block_t **pp_block )
static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_spu = Reassemble( p_dec, pp_block );
vout_thread_t *p_last_vout = p_dec->p_sys->p_vout;
block_t *p_spu_block;
if( p_spu )
if( (p_spu_block = Reassemble( p_dec, pp_block )) )
{
p_sys->i_spu = block_ChainExtract( p_spu, p_sys->buffer, 65536 );
p_sys->i_pts = p_spu->i_pts;
block_ChainRelease( p_spu );
subpicture_t *p_spu;
if( ( p_sys->p_vout = FindVout( p_dec ) ) )
{
if( p_last_vout != p_sys->p_vout )
{
p_sys->i_subpic_channel =
vout_RegisterOSDChannel( p_sys->p_vout );
}
p_sys->i_spu = block_ChainExtract( p_spu_block, p_sys->buffer, 65536 );
p_sys->i_pts = p_spu_block->i_pts;
block_ChainRelease( p_spu_block );
/* Parse and decode */
E_(ParsePacket)( p_dec );
vlc_object_release( p_sys->p_vout );
}
/* Parse and decode */
p_spu = E_(ParsePacket)( p_dec );
/* reinit context */
p_sys->i_spu_size = 0;
p_sys->i_rle_size = 0;
p_sys->i_spu = 0;
p_sys->p_block = NULL;
return p_spu;
}
return NULL;
}
/*****************************************************************************
......@@ -208,6 +170,7 @@ static block_t *Packetize( decoder_t *p_dec, block_t **pp_block )
return block_ChainGather( p_spu );
}
return NULL;
}
......@@ -219,17 +182,16 @@ static block_t *Reassemble( decoder_t *p_dec, block_t **pp_block )
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_block;
if( pp_block == NULL || *pp_block == NULL )
{
return NULL;
}
if( pp_block == NULL || *pp_block == NULL ) return NULL;
p_block = *pp_block;
*pp_block = NULL;
if( p_sys->i_spu_size <= 0 && ( p_block->i_pts <= 0 || p_block->i_buffer < 4 ) )
if( p_sys->i_spu_size <= 0 &&
( p_block->i_pts <= 0 || p_block->i_buffer < 4 ) )
{
msg_Dbg( p_dec, "invalid starting packet (size < 4 or pts <=0)" );
msg_Dbg( p_dec, "spu size: %d, i_pts: "I64Fd" i_buffer: %d", p_sys->i_spu_size, p_block->i_pts, p_block->i_buffer );
msg_Dbg( p_dec, "spu size: %d, i_pts: "I64Fd" i_buffer: %d",
p_sys->i_spu_size, p_block->i_pts, p_block->i_buffer );
block_Release( p_block );
return NULL;
}
......@@ -239,11 +201,14 @@ static block_t *Reassemble( decoder_t *p_dec, block_t **pp_block )
if( p_sys->i_spu_size <= 0 )
{
p_sys->i_spu_size = ( p_block->p_buffer[0] << 8 )| p_block->p_buffer[1];
p_sys->i_rle_size = ( ( p_block->p_buffer[2] << 8 )| p_block->p_buffer[3] ) - 4;
p_sys->i_spu_size = ( p_block->p_buffer[0] << 8 )|
p_block->p_buffer[1];
p_sys->i_rle_size = ( ( p_block->p_buffer[2] << 8 )|
p_block->p_buffer[3] ) - 4;
/* msg_Dbg( p_dec, "i_spu_size=%d i_rle=%d",
p_sys->i_spu_size, p_sys->i_rle_size ); */
if( p_sys->i_spu_size <= 0 || p_sys->i_rle_size >= p_sys->i_spu_size )
{
p_sys->i_spu_size = 0;
......@@ -266,34 +231,3 @@ static block_t *Reassemble( decoder_t *p_dec, block_t **pp_block )
}
return NULL;
}
/* following functions are local */
/*****************************************************************************
* FindVout: Find a vout or wait for one to be created.
*****************************************************************************/
static vout_thread_t *FindVout( decoder_t *p_dec )
{
vout_thread_t *p_vout = NULL;
/* Find an available video output */
do
{
if( p_dec->b_die || p_dec->b_error )
{
break;
}
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout )
{
break;
}
msleep( VOUT_OUTMEM_SLEEP );
}
while( 1 );
return p_vout;
}
......@@ -30,16 +30,13 @@ struct decoder_sys_t
int i_rle_size;
int i_spu;
int i_subpic_channel;
block_t *p_block;
uint8_t buffer[65536 + 20 ]; /* we will never overflow more than 11 bytes if I'm right */
vout_thread_t *p_vout;
/* We will never overflow more than 11 bytes if I'm right */
uint8_t buffer[65536 + 20 ];
};
struct subpicture_sys_t
typedef struct subpicture_data_t
{
mtime_t i_pts; /* presentation timestamp */
......@@ -51,14 +48,7 @@ struct subpicture_sys_t
uint8_t pi_alpha[4];
uint8_t pi_yuv[4][3];
/* Link to our input */
vlc_object_t * p_input;
/* Cropping properties */
vlc_mutex_t lock;
vlc_bool_t b_crop;
unsigned int i_x_start, i_y_start, i_x_end, i_y_end;
};
} subpicture_data_t;
/*****************************************************************************
* Amount of bytes we GetChunk() in one go
......@@ -80,7 +70,4 @@ struct subpicture_sys_t
/*****************************************************************************
* Prototypes
*****************************************************************************/
void E_(ParsePacket)( decoder_t * );
void E_(RenderSPU) ( vout_thread_t *, picture_t *, const subpicture_t * );
subpicture_t * E_(ParsePacket)( decoder_t * );
......@@ -29,7 +29,8 @@
#include <vlc/vout.h>
#include <vlc/decoder.h>
#include <osd.h>
#include "osd.h"
#include "vlc_filter.h"
#if defined(HAVE_ICONV)
#include <iconv.h>
......@@ -43,13 +44,12 @@
struct decoder_sys_t
{
int i_align; /* Subtitles alignment on the vout */
int i_subpic_channel; /* Subpic channel for subtitles */
vout_thread_t *p_vout; /* last vout used */
#if defined(HAVE_ICONV)
iconv_t iconv_handle; /* handle to iconv instance */
#endif
filter_t *p_render; /* text renderer filter */
};
/*****************************************************************************
......@@ -58,10 +58,12 @@ struct decoder_sys_t
static int OpenDecoder ( vlc_object_t * );
static void CloseDecoder ( vlc_object_t * );
static void DecodeBlock ( decoder_t *, block_t ** );
static subpicture_t *DecodeBlock ( decoder_t *, block_t ** );
static subpicture_t *ParseText ( decoder_t *, block_t * );
static void StripTags ( char * );
static void ParseText ( decoder_t *, block_t *, vout_thread_t * );
static void StripTags ( char * );
static subpicture_t *spu_new_buffer( filter_t * );
static void spu_del_buffer( filter_t *, subpicture_t * );
#define DEFAULT_NAME "System Default"
......@@ -152,8 +154,8 @@ static int OpenDecoder( vlc_object_t *p_this )
{
msg_Dbg( p_dec, "using character encoding: %s",
p_dec->fmt_in.subs.psz_encoding );
p_sys->iconv_handle = iconv_open( "UTF-8",
p_dec->fmt_in.subs.psz_encoding );
p_sys->iconv_handle =
iconv_open( "UTF-8", p_dec->fmt_in.subs.psz_encoding );
}
else
{
......@@ -185,8 +187,22 @@ static int OpenDecoder( vlc_object_t *p_this )
msg_Dbg( p_dec, "no iconv support available" );
#endif
p_dec->p_sys->p_vout = NULL;
/* Load the text rendering module */
p_sys->p_render = vlc_object_create( p_dec, sizeof(filter_t) );
p_sys->p_render->pf_spu_buffer_new = spu_new_buffer;
p_sys->p_render->pf_spu_buffer_del = spu_del_buffer;
p_sys->p_render->p_owner = (filter_owner_sys_t *)p_dec;
vlc_object_attach( p_sys->p_render, p_dec );
p_sys->p_render->p_module =
module_Need( p_sys->p_render, "text renderer", 0, 0 );
if( p_sys->p_render->p_module == NULL )
{
msg_Warn( p_dec, "no suitable text renderer module" );
vlc_object_detach( p_sys->p_render );
vlc_object_destroy( p_sys->p_render );
p_sys->p_render = NULL;
}
return VLC_SUCCESS;
}
......@@ -196,34 +212,18 @@ static int OpenDecoder( vlc_object_t *p_this )
****************************************************************************
* This function must be fed with complete subtitles units.
****************************************************************************/
static void DecodeBlock( decoder_t *p_dec, block_t **pp_block )
static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
{
vout_thread_t *p_vout;
subpicture_t *p_spu;
if( !pp_block || *pp_block == NULL )
{
return;
}
if( !pp_block || *pp_block == NULL ) return NULL;
/* Here we are dealing with text subtitles */
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout )
{
if( p_dec->p_sys->p_vout != p_vout )
{
p_dec->p_sys->i_subpic_channel = vout_RegisterOSDChannel( p_vout );
}
ParseText( p_dec, *pp_block, p_vout );
vlc_object_release( p_vout );
}
else
{
msg_Warn( p_dec, "couldn't find a video output, trashing subtitle" );
}
p_dec->p_sys->p_vout = p_vout;
p_spu = ParseText( p_dec, *pp_block );
block_Release( *pp_block );
*pp_block = NULL;
return p_spu;
}
/*****************************************************************************
......@@ -233,27 +233,6 @@ 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;
vout_thread_t *p_vout;
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout != NULL && p_vout->p_subpicture != NULL )
{
subpicture_t *p_subpic;
int i_subpic;
for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
{
p_subpic = &p_vout->p_subpicture[i_subpic];
if( p_subpic != NULL &&
( p_subpic->i_status == RESERVED_SUBPICTURE
|| p_subpic->i_status == READY_SUBPICTURE ) )
{
vout_DestroySubPicture( p_vout, p_subpic );
}
}
}
if( p_vout ) vlc_object_release( p_vout );
#if defined(HAVE_ICONV)
if( p_sys->iconv_handle != (iconv_t)-1 )
......@@ -262,16 +241,25 @@ static void CloseDecoder( vlc_object_t *p_this )
}
#endif
if( p_sys->p_render )
{
if( p_sys->p_render->p_module )
module_Unneed( p_sys->p_render, p_sys->p_render->p_module );
vlc_object_detach( p_sys->p_render );
vlc_object_destroy( p_sys->p_render );
}
free( p_sys );
}
/*****************************************************************************
* ParseText: parse an text subtitle packet and send it to the video output
*****************************************************************************/
static void ParseText( decoder_t *p_dec, block_t *p_block,
vout_thread_t *p_vout )
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 = 0;
char *psz_subtitle;
int i_align_h, i_align_v;
......@@ -279,14 +267,14 @@ static void ParseText( decoder_t *p_dec, block_t *p_block,
if( p_block->i_pts == 0 )
{
msg_Warn( p_dec, "subtitle without a date" );
return;
return NULL;
}
/* Check validity of packet data */
if( p_block->i_buffer <= 1 || p_block->p_buffer[0] == '\0' )
{
msg_Warn( p_dec, "empty subtitle" );
return;
return NULL;
}
/* Should be resiliant against bad subtitles */
......@@ -314,9 +302,11 @@ static void ParseText( decoder_t *p_dec, block_t *p_block,
if( inbytes_left )
{
msg_Warn( p_dec, "Failed to convert subtitle encoding, dropping subtitle.\nTry setting a different character-encoding for the subtitle." );
msg_Warn( p_dec, "Failed to convert subtitle encoding, "
"dropping subtitle.\nTry setting a different "
"character-encoding for the subtitle." );
free( psz_subtitle );
return;
return NULL;
}
else
{
......@@ -329,7 +319,8 @@ static void ParseText( decoder_t *p_dec, block_t *p_block,
if( p_dec->fmt_in.i_codec == VLC_FOURCC('s','s','a',' ') )
{
/* Decode SSA strings */
/* We expect: ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text */
/* We expect: ReadOrder, Layer, Style, Name, MarginL, MarginR,
* MarginV, Effect, Text */
char *psz_new_subtitle;
char *psz_buffer_sub;
int i_comma;
......@@ -352,16 +343,19 @@ static void ParseText( decoder_t *p_dec, block_t *p_block,
i_text = 0;
while( psz_buffer_sub[0] != '\0' )
{
if( psz_buffer_sub[0] == '\\' && ( psz_buffer_sub[1] =='n' || psz_buffer_sub[1] =='N' ) )
if( psz_buffer_sub[0] == '\\' && ( psz_buffer_sub[1] == 'n' ||
psz_buffer_sub[1] == 'N' ) )
{
psz_new_subtitle[i_text] = '\n';
i_text++;
psz_buffer_sub += 2;
}
else if( psz_buffer_sub[0] == '{' && psz_buffer_sub[1] == '\\' )
else if( psz_buffer_sub[0] == '{' &&
psz_buffer_sub[1] == '\\' )
{
/* SSA control code */
while( psz_buffer_sub[0] != '\0' && psz_buffer_sub[0] != '}' )
while( psz_buffer_sub[0] != '\0' &&
psz_buffer_sub[0] != '}' )
{
psz_buffer_sub++;
}
......@@ -380,13 +374,33 @@ static void ParseText( decoder_t *p_dec, block_t *p_block,
break;
}
}
StripTags( psz_subtitle );
vout_ShowTextAbsolute( p_vout, p_sys->i_subpic_channel, psz_subtitle, NULL,
OSD_ALIGN_BOTTOM | p_sys->i_align, i_align_h,
i_align_v, p_block->i_pts,
p_block->i_length ? p_block->i_pts + p_block->i_length : 0 );
if( p_sys->p_render && p_sys->p_render->p_module &&
p_sys->p_render->pf_render_string )
{
block_t *p_new_block = block_New( p_dec, strlen(psz_subtitle) + 1 );
if( p_new_block )
{
memcpy( p_new_block->p_buffer, psz_subtitle,
p_new_block->i_buffer );
p_new_block->i_pts = p_new_block->i_dts = p_block->i_pts;
p_new_block->i_length = p_block->i_length;
p_spu = p_sys->p_render->pf_render_string( p_sys->p_render,
p_new_block );
}
}
if( p_spu )
{
p_spu->i_flags = OSD_ALIGN_BOTTOM | p_sys->i_align;
p_spu->i_x = i_align_h;
p_spu->i_y = i_align_v;
}
free( psz_subtitle );
return p_spu;
}
static void StripTags( char *psz_text )
......@@ -431,3 +445,18 @@ static void StripTags( char *psz_text )
}
psz_text[ i - i_left_moves ] = '\0';
}
/*****************************************************************************
* Buffers allocation callbacks for the filters
*****************************************************************************/
static subpicture_t *spu_new_buffer( filter_t *p_filter )
{
decoder_t *p_dec = (decoder_t *)p_filter->p_owner;
return p_dec->pf_spu_buffer_new( p_dec );
}
static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_spu )
{
decoder_t *p_dec = (decoder_t *)p_filter->p_owner;
p_dec->pf_spu_buffer_del( p_dec, p_spu );
}
......@@ -23,22 +23,19 @@
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include "vlc_block.h"
#include "vlc_filter.h"
static subpicture_t * AddText( vout_thread_t *, int, char *, text_style_t *,
int, int, int, mtime_t, mtime_t );
static subpicture_t *RenderText( filter_t *, block_t * );
int E_(OpenRenderer)( vlc_object_t *p_this )
{
vout_thread_t *p_vout = (vout_thread_t *)p_this;
p_vout->pf_add_string = AddText;
filter_t *p_filter = (filter_t *)p_this;
p_filter->pf_render_string = RenderText;
return VLC_SUCCESS;
}
static subpicture_t * AddText( vout_thread_t *p_vout, int i_channel,
char *psz_string, text_style_t *p_style , int i_flags,
int i_x_margin, int i_y_margin, mtime_t i_start,
mtime_t i_stop )
static subpicture_t *RenderText( filter_t *p_filter, block_t *p_block )
{
return VLC_SUCCESS;
return NULL;
}
......@@ -5,6 +5,7 @@
* $Id$
*
* Authors: Sigmund Augdal <sigmunau@idi.ntnu.no>
* Gildas Bazin <gbazin@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
......@@ -33,7 +34,10 @@
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <osd.h>
#include "osd.h"
#include "vlc_block.h"
#include "vlc_filter.h"
#include <math.h>
#ifdef HAVE_ERRNO_H
......@@ -66,21 +70,10 @@ typedef struct line_desc_t line_desc_t;
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Create ( vlc_object_t * );
static void Destroy ( vlc_object_t * );
static void Render ( vout_thread_t *, picture_t *,
const subpicture_t * );
static void RenderI420( vout_thread_t *, picture_t *,
const subpicture_t * );
static void RenderYUY2( vout_thread_t *, picture_t *,
const subpicture_t * );
static void RenderRV32( vout_thread_t *, picture_t *,
const subpicture_t * );
static subpicture_t *AddText ( vout_thread_t *, int, char *, text_style_t *,
int, int, int, mtime_t, mtime_t );
static void FreeString( subpicture_t * );
static int Create ( vlc_object_t * );
static void Destroy( vlc_object_t * );
static subpicture_t *RenderText( filter_t *, block_t * );
#if !defined(HAVE_ICONV)
static int GetUnicodeCharFromUTF8( byte_t ** );
......@@ -123,17 +116,15 @@ vlc_module_end();
/**
* Private data in a subpicture. Describes a string.
*/
struct subpicture_sys_t
typedef struct subpicture_data_t
{
int i_x_margin;
int i_y_margin;
int i_width;
int i_height;
int i_flags;
/** The string associated with this subpicture */
byte_t *psz_text;
line_desc_t *p_lines;
};
} subpicture_data_t;
struct line_desc_t
{
......@@ -146,17 +137,19 @@ struct line_desc_t
line_desc_t *p_next;
};
static void Render ( filter_t *, subpicture_t *, subpicture_data_t * );
static void FreeString( subpicture_data_t * );
/*****************************************************************************
* text_renderer_sys_t: freetype local data
* filter_sys_t: freetype local data
*****************************************************************************
* This structure is part of the video output thread descriptor.
* It describes the freetype specific properties of an output thread.
*****************************************************************************/
struct text_renderer_sys_t
struct filter_sys_t
{
FT_Library p_library; /* handle to library */
FT_Face p_face; /* handle to face object */
vlc_mutex_t *lock;
vlc_bool_t i_use_kerning;
uint8_t pi_gamma[256];
};
......@@ -169,36 +162,39 @@ struct text_renderer_sys_t
#define gamma_value 2.0
static int Create( vlc_object_t *p_this )
{
vout_thread_t *p_vout = (vout_thread_t *)p_this;
char *psz_fontfile;
filter_t *p_filter = (filter_t *)p_this;
filter_sys_t *p_sys;
char *psz_fontfile = NULL;
int i, i_error;
int i_fontsize = 0;
double gamma_inv = 1.0f / gamma_value;
vlc_value_t val;
/* Allocate structure */
p_vout->p_text_renderer_data = malloc( sizeof( text_renderer_sys_t ) );
if( p_vout->p_text_renderer_data == NULL )
p_sys = malloc( sizeof( filter_sys_t ) );
if( !p_sys )
{
msg_Err( p_vout, "out of memory" );
msg_Err( p_filter, "out of memory" );
return VLC_ENOMEM;
}
p_sys->p_face = 0;
p_sys->p_library = 0;
for (i = 0; i < 256; i++) {
p_vout->p_text_renderer_data->pi_gamma[i] =
for( i = 0; i < 256; i++ )
{
p_sys->pi_gamma[i] =
(uint8_t)( pow( (double)i / 255.0f, gamma_inv) * 255.0f );
}
var_Create( p_vout, "freetype-font", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_vout, "freetype-fontsize",
var_Create( p_filter, "freetype-font", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_filter, "freetype-fontsize",
VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_vout, "freetype-rel-fontsize",
var_Create( p_filter, "freetype-rel-fontsize",
VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
/* Look what method was requested */
var_Get( p_vout, "freetype-font", &val );
var_Get( p_filter, "freetype-font", &val );
psz_fontfile = val.psz_string;
if( !psz_fontfile || !*psz_fontfile )
{
if( psz_fontfile ) free( psz_fontfile );
......@@ -209,78 +205,71 @@ static int Create( vlc_object_t *p_this )
#elif SYS_DARWIN
strcpy( psz_fontfile, DEFAULT_FONT );
#else
msg_Err( p_vout, "user didn't specify a font" );
free( p_vout->p_text_renderer_data );
return VLC_EGENERIC;
msg_Err( p_filter, "user didn't specify a font" );
goto error;
#endif
}
i_error = FT_Init_FreeType( &p_vout->p_text_renderer_data->p_library );
i_error = FT_Init_FreeType( &p_sys->p_library );
if( i_error )
{
msg_Err( p_vout, "couldn't initialize freetype" );
free( p_vout->p_text_renderer_data );
return VLC_EGENERIC;
msg_Err( p_filter, "couldn't initialize freetype" );
goto error;
}
i_error = FT_New_Face( p_vout->p_text_renderer_data->p_library,
psz_fontfile ? psz_fontfile : "", 0,
&p_vout->p_text_renderer_data->p_face );
i_error = FT_New_Face( p_sys->p_library, psz_fontfile ? psz_fontfile : "",
0, &p_sys->p_face );
if( i_error == FT_Err_Unknown_File_Format )
{
msg_Err( p_vout, "file %s have unknown format", psz_fontfile );
FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
free( p_vout->p_text_renderer_data );
if( psz_fontfile ) free( psz_fontfile );
return VLC_EGENERIC;
msg_Err( p_filter, "file %s have unknown format", psz_fontfile );
goto error;
}
else if( i_error )
{
msg_Err( p_vout, "failed to load font file %s", psz_fontfile );
FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
free( p_vout->p_text_renderer_data );
if( psz_fontfile ) free( psz_fontfile );
return VLC_EGENERIC;
msg_Err( p_filter, "failed to load font file %s", psz_fontfile );
goto error;
}
if( psz_fontfile ) free( psz_fontfile );
i_error = FT_Select_Charmap( p_vout->p_text_renderer_data->p_face,
ft_encoding_unicode );
i_error = FT_Select_Charmap( p_sys->p_face, ft_encoding_unicode );
if( i_error )
{
msg_Err( p_vout, "Font has no unicode translation table" );
FT_Done_Face( p_vout->p_text_renderer_data->p_face );
FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
free( p_vout->p_text_renderer_data );
return VLC_EGENERIC;
msg_Err( p_filter, "Font has no unicode translation table" );
goto error;
}
p_vout->p_text_renderer_data->i_use_kerning =
FT_HAS_KERNING(p_vout->p_text_renderer_data->p_face);
var_Get( p_vout, "freetype-fontsize", &val );
p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
var_Get( p_filter, "freetype-fontsize", &val );
if( val.i_int )
{
i_fontsize = val.i_int;
}
else
{
var_Get( p_vout, "freetype-rel-fontsize", &val );
i_fontsize = (int) p_vout->render.i_height / val.i_int;
var_Get( p_filter, "freetype-rel-fontsize", &val );
msg_Err( p_filter, "freetype-rel-font currenlty broken, FIXME" );
i_fontsize = (int) /*p_filter->render.i_height*/ 400 / val.i_int;
}
msg_Dbg( p_vout, "Using fontsize: %i", i_fontsize);
msg_Dbg( p_filter, "Using fontsize: %i", i_fontsize);
i_error = FT_Set_Pixel_Sizes( p_vout->p_text_renderer_data->p_face, 0,
i_fontsize );
i_error = FT_Set_Pixel_Sizes( p_sys->p_face, 0, i_fontsize );
if( i_error )
{
msg_Err( p_vout, "couldn't set font size to %d", i_fontsize );
free( p_vout->p_text_renderer_data );
return VLC_EGENERIC;
msg_Err( p_filter, "couldn't set font size to %d", i_fontsize );
goto error;
}
p_vout->pf_add_string = AddText;
if( psz_fontfile ) free( psz_fontfile );
p_filter->pf_render_string = RenderText;
p_filter->p_sys = p_sys;
return VLC_SUCCESS;
error:
if( p_sys->p_face ) FT_Done_Face( p_sys->p_face );
if( p_sys->p_library ) FT_Done_FreeType( p_sys->p_library );
if( psz_fontfile ) free( psz_fontfile );
free( p_sys );
return VLC_EGENERIC;
}
/*****************************************************************************
......@@ -290,10 +279,11 @@ static int Create( vlc_object_t *p_this )
*****************************************************************************/
static void Destroy( vlc_object_t *p_this )
{
vout_thread_t *p_vout = (vout_thread_t *)p_this;
FT_Done_Face( p_vout->p_text_renderer_data->p_face );
FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
free( p_vout->p_text_renderer_data );
filter_t *p_filter = (filter_t *)p_this;
filter_sys_t *p_sys = p_filter->p_sys;
FT_Done_Face( p_sys->p_face );
FT_Done_FreeType( p_sys->p_library );
free( p_sys );
}
/*****************************************************************************
......@@ -301,362 +291,101 @@ static void Destroy( vlc_object_t *p_this )
*****************************************************************************
* This function merges the previously rendered freetype glyphs into a picture
*****************************************************************************/
static void Render( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_subpic )
static void Render( filter_t *p_filter, subpicture_t *p_spu,
subpicture_data_t *p_string )
{
switch( p_vout->output.i_chroma )
{
/* I420 target, no scaling */
case VLC_FOURCC('I','4','2','0'):
case VLC_FOURCC('I','Y','U','V'):
case VLC_FOURCC('Y','V','1','2'):
RenderI420( p_vout, p_pic, p_subpic );
break;
#if 0
/* RV16 target, scaling */
case VLC_FOURCC('R','V','1','6'):
RenderRV16( p_vout, p_pic, p_subpic );
break;
#endif
/* RV32 target, scaling */
case VLC_FOURCC('R','V','2','4'):
case VLC_FOURCC('R','V','3','2'):
RenderRV32( p_vout, p_pic, p_subpic );
break;
/* NVidia or BeOS overlay, no scaling */
case VLC_FOURCC('Y','U','Y','2'):
RenderYUY2( p_vout, p_pic, p_subpic );
break;
default:
msg_Err( p_vout, "unknown chroma, can't render SPU" );
break;
}
}
/**
* Draw a string on a i420 (or similar) picture
*/
static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_subpic )
{
subpicture_sys_t *p_string = p_subpic->p_sys;
int i_plane, x, y, pen_x, pen_y;
unsigned int i;
filter_sys_t *p_sys = p_filter->p_sys;
line_desc_t *p_line;
for( p_line = p_subpic->p_sys->p_lines; p_line != NULL;
p_line = p_line->p_next )
{
for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
{
uint8_t *p_in = p_pic->p[ i_plane ].p_pixels;
int i_pic_pitch = p_pic->p[ i_plane ].i_pitch;
int i_pic_width = p_pic->p[ i_plane ].i_visible_pitch;
if( i_plane == 0 )
{
if( p_string->i_flags & OSD_ALIGN_BOTTOM )
{
pen_y = p_pic->p[ i_plane ].i_lines - p_string->i_height -
p_string->i_y_margin;
}
else
{
pen_y = p_string->i_y_margin;
}
pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
if( p_string->i_flags & OSD_ALIGN_RIGHT )
{
pen_x = i_pic_width - p_line->i_width
- p_string->i_x_margin;
}
else if( p_string->i_flags & OSD_ALIGN_LEFT )
{
pen_x = p_string->i_x_margin;
}
else
{
pen_x = i_pic_width / 2 - p_line->i_width / 2
+ p_string->i_x_margin;
}
for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
{
FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
int i_alpha_offset = -1;
int i_offset = ( p_line->p_glyph_pos[ i ].y + pen_y -
p_glyph->top ) * i_pic_pitch +
p_line->p_glyph_pos[ i ].x + p_glyph->left;
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ i_alpha_offset ] ]
#define pixel p_in[ i_offset + x + pen_x ]
for( y = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
{
i_alpha_offset++;
if( alpha == 0 ) continue;
pen_y--; i_offset -= i_pic_pitch;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y++; i_offset += i_pic_pitch; pen_x--;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_x += 2;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y++; i_offset += i_pic_pitch; pen_x--;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y--; i_offset -= i_pic_pitch;
}
i_offset += i_pic_pitch;
}
i_alpha_offset = -1;
i_offset = ( p_line->p_glyph_pos[ i ].y + pen_y -
p_glyph->top ) * i_pic_pitch +
p_line->p_glyph_pos[ i ].x + p_glyph->left;
for( y = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
{
i_alpha_offset++;
if( alpha == 0 ) continue;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
( 255 * alpha >> 8 );
}
i_offset += i_pic_pitch;
}
#undef alpha
#undef pixel
}
}
else
{
if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
{
pen_y = p_pic->p[i_plane].i_lines - (p_string->i_height>>1)
- (p_string->i_y_margin>>1);
}
else
{
pen_y = p_string->i_y_margin >> 1;
}
pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 7;
if ( p_string->i_flags & OSD_ALIGN_RIGHT )
{
pen_x = i_pic_width - ( p_line->i_width >> 1 )
- ( p_string->i_x_margin >> 1 );
}
else if ( p_string->i_flags & OSD_ALIGN_LEFT )
{
pen_x = p_string->i_x_margin >> 1;
}
else
{
pen_x = i_pic_width / 2 - p_line->i_width / 4
+ p_string->i_x_margin / 2;
}
for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
{
FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ ( x + y * p_glyph->bitmap.width ) ] ]
#define pixel p_in[ ( ( p_line->p_glyph_pos[ i ].y >> 1 ) + pen_y + ( y >> 1 ) - ( p_glyph->top >> 1 ) ) * i_pic_pitch + ( x >> 1 ) + pen_x + ( p_line->p_glyph_pos[ i ].x >> 1 ) + ( p_glyph->left >>1) ]
for( y = 0; y < p_glyph->bitmap.rows; y+=2 )
{
for( x = 0; x < p_glyph->bitmap.width; x+=2 )
{
if( alpha == 0 ) continue;
pixel = ( ( pixel * ( 0xFF - alpha ) ) >> 8 ) +
( 0x80 * alpha >> 8 );
#undef alpha
#undef pixel
}
}
}
}
}
}
}
/**
* Draw a string on a YUY2 picture
*/
static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_subpic )
{
subpicture_sys_t *p_string = p_subpic->p_sys;
int x, y, pen_x, pen_y;
unsigned int i;
line_desc_t *p_line;
for( p_line = p_subpic->p_sys->p_lines; p_line != NULL;
p_line = p_line->p_next )
{
uint8_t *p_in;
int i_pic_pitch = p_pic->p[0].i_pitch;
int i_pic_width = p_pic->p[0].i_visible_pitch;
p_in = p_pic->p[0].p_pixels;
if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
{
pen_y = p_pic->p[0].i_lines - p_string->i_height -
p_string->i_y_margin;
}
else
{
pen_y = p_string->i_y_margin;
}
pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
if ( p_string->i_flags & OSD_ALIGN_RIGHT )
{
pen_x = i_pic_width - p_line->i_width
- p_string->i_x_margin;
}
else if ( p_string->i_flags & OSD_ALIGN_LEFT )
{
pen_x = p_string->i_x_margin;
}
else
{
pen_x = i_pic_width / 2 /2 - p_line->i_width / 2 + p_string->i_x_margin;
}
uint8_t *p_y, *p_u, *p_v, *p_a;
video_format_t fmt;
int i, x, y, i_pitch;
/* Create a new subpicture region */
memset( &fmt, 0, sizeof(video_format_t) );
fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
fmt.i_aspect = VOUT_ASPECT_FACTOR;
fmt.i_width = fmt.i_visible_width = p_string->i_width + 2;
fmt.i_height = fmt.i_visible_height = p_string->i_height + 2;
fmt.i_x_offset = fmt.i_y_offset = 0;
p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
if( !p_spu->p_region )
{
msg_Err( p_filter, "cannot allocate SPU region" );
return;
}
p_spu->p_region->i_x = p_spu->p_region->i_y = 0;
p_y = p_spu->p_region->picture.Y_PIXELS;
p_u = p_spu->p_region->picture.U_PIXELS;
p_v = p_spu->p_region->picture.V_PIXELS;
p_a = p_spu->p_region->picture.A_PIXELS;
i_pitch = p_spu->p_region->picture.Y_PITCH;
/* Initialize the region pixels (only the alpha will be changed later) */
memset( p_y, 0x00, i_pitch * p_spu->p_region->fmt.i_height );
memset( p_u, 0x80, i_pitch * p_spu->p_region->fmt.i_height );
memset( p_v, 0x80, i_pitch * p_spu->p_region->fmt.i_height );
memset( p_a, 0x00, i_pitch * p_spu->p_region->fmt.i_height );
#define pi_gamma p_sys->pi_gamma
for( p_line = p_string->p_lines; p_line != NULL; p_line = p_line->p_next )
{
int i_glyph_tmax = 0;
int i_bitmap_offset, i_offset;
for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
{
FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
#define pixel p_in[ ( p_line->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pic_pitch + 2 * ( x + pen_x + p_line->p_glyph_pos[ i ].x + p_glyph->left ) ]
for(y = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
{
if( alpha == 0 ) continue;
pen_y--;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y++; pen_x--;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_x += 2;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y++; pen_x--;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 );
pen_y--;
}
}
for(y = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
{
if( alpha == 0 ) continue;
pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
( 255 * alpha >> 8 );
}
}
#undef alpha
#undef pixel
}
}
}
/**
* Draw a string on a RV32 picture
*/
static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_subpic )
{
subpicture_sys_t *p_string = p_subpic->p_sys;
int i_plane, x, y, pen_x, pen_y;
unsigned int i;
line_desc_t *p_line;
i_plane = 0;
for( p_line = p_subpic->p_sys->p_lines; p_line != NULL; p_line = p_line->p_next )
{
uint8_t *p_in;
int i_pic_pitch = p_pic->p[ i_plane ].i_pitch;
int i_pic_width = p_pic->p[ i_plane ].i_visible_pitch;
p_in = p_pic->p[ i_plane ].p_pixels;
if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
{
pen_y = p_pic->p[ i_plane ].i_lines - p_string->i_height -
p_string->i_y_margin;
}
else
{
pen_y = p_string->i_y_margin;
}
pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
if ( p_string->i_flags & OSD_ALIGN_RIGHT )
{
pen_x = i_pic_width - p_line->i_width
- p_string->i_x_margin;
}
else if ( p_string->i_flags & OSD_ALIGN_LEFT )
{
pen_x = p_string->i_x_margin;
}
else
{
pen_x = i_pic_width / 2 / 4 - p_line->i_width / 2
+ p_string->i_x_margin;
i_glyph_tmax = __MAX( i_glyph_tmax, p_glyph->top );
}
for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
{
FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
#define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
#define pixel( c ) p_in[ ( p_line->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pic_pitch + ( x + pen_x + p_line->p_glyph_pos[ i ].x + p_glyph->left ) * 4 + c ]
for(y = 0; y < p_glyph->bitmap.rows; y++ )
i_offset = ( p_line->p_glyph_pos[ i ].y +
i_glyph_tmax - p_glyph->top + 1 ) *
i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 1;
for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
{
if( alpha == 0 ) continue;
pen_y--;
pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
pen_y++; pen_x--;
pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
pen_x += 2;
pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
pen_y++; pen_x--;
pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 );
pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 );
pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 );
pen_y--;
if( !pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]] )
continue;
i_offset -= i_pitch;
p_a[i_offset + x] = ((uint16_t)p_a[i_offset + x] +
pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]])/2;
i_offset += i_pitch; x--;
p_a[i_offset + x] = ((uint16_t)p_a[i_offset + x] +
pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]])/2;
x += 2;
p_a[i_offset + x] = ((uint16_t)p_a[i_offset + x] +
pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]])/2;
i_offset += i_pitch; x--;
p_a[i_offset + x] = ((uint16_t)p_a[i_offset + x] +
pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]])/2;
i_offset -= i_pitch;
}
i_offset += i_pitch;
}
for(y = 0; y < p_glyph->bitmap.rows; y++ )
i_offset = ( p_line->p_glyph_pos[ i ].y +
i_glyph_tmax - p_glyph->top ) *
i_pitch + p_line->p_glyph_pos[ i ].x + p_glyph->left + 1;
for( y = 0, i_bitmap_offset = 0; y < p_glyph->bitmap.rows; y++ )
{
for( x = 0; x < p_glyph->bitmap.width; x++ )
{
if( alpha == 0 ) continue;
pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 ) +
( 255 * alpha >> 8 );
pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 ) +
( 255 * alpha >> 8 );
pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 ) +
( 255 * alpha >> 8 );
}
for( x = 0; x < p_glyph->bitmap.width; x++, i_bitmap_offset++ )
{
p_y[i_offset + x] =
pi_gamma[p_glyph->bitmap.buffer[i_bitmap_offset]];
}
i_offset += i_pitch;
}
#undef alpha
#undef pixel
#undef pi_gamma
}
}
}
......@@ -667,17 +396,16 @@ static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
* needed glyphs into memory. It is used as pf_add_string callback in
* the vout method by this module
*/
static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
char *psz_string, text_style_t *p_style, int i_flags,
int i_hmargin, int i_vmargin, mtime_t i_start,
mtime_t i_stop )
static subpicture_t *RenderText( filter_t *p_filter, block_t *p_block )
{
subpicture_sys_t *p_string;
filter_sys_t *p_sys = p_filter->p_sys;
subpicture_t *p_subpic = 0;
subpicture_data_t *p_string = 0;
line_desc_t *p_line = 0, *p_next = 0;
int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous;
subpicture_t *p_subpic;
line_desc_t *p_line, *p_next;
uint32_t *p_unicode_string, i_char;
int i_string_length;
char *psz_string;
#if defined(HAVE_ICONV)
iconv_t iconv_handle;
......@@ -689,10 +417,9 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
FT_Glyph tmp_glyph;
/* Sanity check */
if ( !psz_string || !*psz_string )
{
return NULL;
}
if( !p_block ) return NULL;
psz_string = p_block->p_buffer;
if( !psz_string || !*psz_string ) goto error;
result.x = 0;
result.y = 0;
......@@ -701,41 +428,22 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
line.yMin = 0;
line.yMax = 0;
p_line = 0;
p_string = 0;
p_subpic = 0;
/* Create and initialize a subpicture */
p_subpic = vout_CreateSubPicture( p_vout, i_channel, MEMORY_SUBPICTURE );
if ( p_subpic == NULL )
{
return NULL;
}
p_subpic->p_sys = 0;
p_subpic->pf_render = Render;
p_subpic->pf_destroy = FreeString;
p_subpic->i_start = i_start;
p_subpic->i_stop = i_stop;
if( i_stop == 0 )
{
p_subpic->b_ephemer = VLC_TRUE;
}
else
{
p_subpic->b_ephemer = VLC_FALSE;
}
p_subpic = p_filter->pf_spu_buffer_new( p_filter );
if( !p_subpic ) goto error;
p_subpic->i_start = p_block->i_pts;
p_subpic->i_stop = p_block->i_pts + p_block->i_length;
p_subpic->b_ephemer = (p_block->i_length == 0);
p_subpic->b_absolute = VLC_FALSE;
/* Create and initialize private data for the subpicture */
p_string = malloc( sizeof(subpicture_sys_t) );
if ( p_string == NULL )
p_string = malloc( sizeof(subpicture_data_t) );
if( !p_string )
{
msg_Err( p_vout, "Out of memory" );
msg_Err( p_filter, "Out of memory" );
goto error;
}
p_subpic->p_sys = p_string;
p_string->i_flags = i_flags;
p_string->i_x_margin = i_hmargin;
p_string->i_y_margin = i_vmargin;
p_string->p_lines = 0;
p_string->psz_text = strdup( psz_string );
......@@ -743,7 +451,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
p_unicode_string = malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
if( p_unicode_string == NULL )
{
msg_Err( p_vout, "Out of memory" );
msg_Err( p_filter, "Out of memory" );
goto error;
}
#if defined(WORDS_BIGENDIAN)
......@@ -753,7 +461,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
#endif
if( iconv_handle == (iconv_t)-1 )
{
msg_Warn( p_vout, "Unable to do convertion" );
msg_Warn( p_filter, "Unable to do convertion" );
goto error;
}
......@@ -765,24 +473,25 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
i_out_bytes_left = i_out_bytes;
p_in_buffer = psz_string;
p_out_buffer = (char *)p_unicode_string;
i_ret = iconv( iconv_handle, &p_in_buffer, &i_in_bytes, &p_out_buffer, &i_out_bytes_left );
i_ret = iconv( iconv_handle, &p_in_buffer, &i_in_bytes,
&p_out_buffer, &i_out_bytes_left );
if( i_in_bytes )
{
msg_Warn( p_vout, "Failed to convert string to unicode (%s), bytes left %d", strerror(errno), i_in_bytes );
msg_Warn( p_filter, "Failed to convert string to unicode (%s), "
"bytes left %d", strerror(errno), i_in_bytes );
goto error;
}
*(uint32_t*)p_out_buffer = 0;
i_string_length = ( i_out_bytes - i_out_bytes_left ) / sizeof(uint32_t);
i_string_length = (i_out_bytes - i_out_bytes_left) / sizeof(uint32_t);
}
#if defined(HAVE_FRIBIDI)
{
uint32_t *p_fribidi_string;
FriBidiCharType base_dir = FRIBIDI_TYPE_ON;
p_fribidi_string = malloc( ( i_string_length + 1 ) * sizeof(uint32_t) );
p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
fribidi_log2vis( (FriBidiChar*)p_unicode_string, i_string_length,
&base_dir, (FriBidiChar*)p_fribidi_string, NULL, NULL,
NULL );
&base_dir, (FriBidiChar*)p_fribidi_string, 0, 0, 0 );
free( p_unicode_string );
p_unicode_string = p_fribidi_string;
p_fribidi_string[ i_string_length ] = 0;
......@@ -795,7 +504,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
p_line = NewLine( psz_string );
if( p_line == NULL )
{
msg_Err( p_vout, "Out of memory" );
msg_Err( p_filter, "Out of memory" );
goto error;
}
p_string->p_lines = p_line;
......@@ -804,23 +513,23 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
i_previous = 0;
i = 0;
#define face p_vout->p_text_renderer_data->p_face
#define face p_sys->p_face
#define glyph face->glyph
while( *p_unicode_string )
{
i_char = *p_unicode_string++;
if ( i_char == '\r' ) /* ignore CR chars wherever they may be */
if( i_char == '\r' ) /* ignore CR chars wherever they may be */
{
continue;
}
if ( i_char == '\n' )
if( i_char == '\n' )
{
p_next = NewLine( psz_string );
if( p_next == NULL )
{
msg_Err( p_vout, "Out of memory" );
msg_Err( p_filter, "Out of memory" );
goto error;
}
p_line->p_next = p_next;
......@@ -836,13 +545,15 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
line.yMin = 0;
line.yMax = 0;
i_pen_y += face->size->metrics.height >> 6;
msg_Dbg( p_vout, "Creating new line, i is %d", i );
#if 0
msg_Dbg( p_filter, "Creating new line, i is %d", i );
#endif
i = 0;
continue;
}
i_glyph_index = FT_Get_Char_Index( face, i_char );
if ( p_vout->p_text_renderer_data->i_use_kerning && i_glyph_index
if( p_sys->i_use_kerning && i_glyph_index
&& i_previous )
{
FT_Vector delta;
......@@ -854,21 +565,21 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
p_line->p_glyph_pos[ i ].x = i_pen_x;
p_line->p_glyph_pos[ i ].y = i_pen_y;
i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
if ( i_error )
if( i_error )
{
msg_Err( p_vout, "FT_Load_Glyph returned %d", i_error );
msg_Err( p_filter, "FT_Load_Glyph returned %d", i_error );
goto error;
}
i_error = FT_Get_Glyph( glyph, &tmp_glyph );
if ( i_error )
if( i_error )
{
msg_Err( p_vout, "FT_Get_Glyph returned %d", i_error );
msg_Err( p_filter, "FT_Get_Glyph returned %d", i_error );
goto error;
}
FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal,
NULL, 1 );
if ( i_error ) continue;
if( i_error ) continue;
p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
/* Do rest */
......@@ -880,6 +591,7 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
i_pen_x += glyph->advance.x >> 6;
i++;
}
p_line->i_width = line.xMax;
p_line->i_height = face->size->metrics.height >> 6;
p_line->pp_glyphs[ i ] = NULL;
......@@ -887,25 +599,28 @@ static subpicture_t *AddText ( vout_thread_t *p_vout, int i_channel,
result.y += line.yMax - line.yMin;
p_string->i_height = result.y;
p_string->i_width = result.x;
vout_DisplaySubPicture( p_vout, p_subpic );
return p_subpic;
#undef face
#undef glyph
Render( p_filter, p_subpic, p_string );
FreeString( p_string );
block_Release( p_block );
return p_subpic;
error:
FreeString( p_subpic );
vout_DestroySubPicture( p_vout, p_subpic );
FreeString( p_string );
if( p_subpic ) p_filter->pf_spu_buffer_del( p_filter, p_subpic );
block_Release( p_block );
return NULL;
}
static void FreeString( subpicture_t *p_subpic )
static void FreeString( subpicture_data_t *p_string )
{
unsigned int i;
subpicture_sys_t *p_string = p_subpic->p_sys;
line_desc_t *p_line, *p_next;
if( p_subpic->p_sys == NULL ) return;
if( !p_string ) return;
for( p_line = p_string->p_lines; p_line != NULL; p_line = p_next )
{
......
......@@ -8,4 +8,5 @@ SOURCES_crop = crop.c
SOURCES_motionblur = motionblur.c
SOURCES_logo = logo.c
SOURCES_deinterlace = deinterlace.c
SOURCES_blend = blend.c
noinst_HEADERS += filter_common.h
/*****************************************************************************
* blend.c: alpha blend 2 pictures together
*****************************************************************************
* Copyright (C) 2003 VideoLAN
* $Id$
*
* Author: Gildas Bazin <gbazin@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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/decoder.h>
#include "vlc_filter.h"
/*****************************************************************************
* filter_sys_t : filter descriptor
*****************************************************************************/
struct filter_sys_t
{
};
/****************************************************************************
* Local prototypes
****************************************************************************/
static int OpenFilter ( vlc_object_t * );
static void CloseFilter( vlc_object_t * );
static void Blend( filter_t *, picture_t *, picture_t *, picture_t *,
int, int );
static void BlendI420( filter_t *, picture_t *, picture_t *, picture_t *,
int, int );
static void BlendR16( filter_t *, picture_t *, picture_t *, picture_t *,
int, int );
static void BlendPalette( filter_t *, picture_t *, picture_t *, picture_t *,
int, int );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("Video pictures blending") );
set_capability( "video blending", 100 );
set_callbacks( OpenFilter, CloseFilter );
vlc_module_end();
/*****************************************************************************
* OpenFilter: probe the filter and return score
*****************************************************************************/
static int OpenFilter( vlc_object_t *p_this )
{
filter_t *p_filter = (filter_t*)p_this;
filter_sys_t *p_sys;
/* Check if we can handle that format.
* We could try to use a chroma filter if we can't. */
if( ( p_filter->fmt_in.video.i_chroma != VLC_FOURCC('Y','U','V','A') &&
p_filter->fmt_in.video.i_chroma != VLC_FOURCC('Y','U','V','P') ) ||
( p_filter->fmt_out.video.i_chroma != VLC_FOURCC('I','4','2','0') &&
p_filter->fmt_out.video.i_chroma != VLC_FOURCC('Y','V','1','2') &&
p_filter->fmt_out.video.i_chroma != VLC_FOURCC('R','V','1','6') ) )
{
return VLC_EGENERIC;
}
/* Allocate the memory needed to store the decoder's structure */
if( ( p_filter->p_sys = p_sys =
(filter_sys_t *)malloc(sizeof(filter_sys_t)) ) == NULL )
{
msg_Err( p_filter, "out of memory" );
return VLC_EGENERIC;
}
/* Misc init */
p_filter->pf_video_blend = Blend;
msg_Dbg( p_filter, "chroma: %4.4s -> %4.4s",
(char *)&p_filter->fmt_in.video.i_chroma,
(char *)&p_filter->fmt_out.video.i_chroma );
return VLC_SUCCESS;
}
/****************************************************************************
* Blend: the whole thing
****************************************************************************
* This function is called just after the thread is launched.
****************************************************************************/
static void Blend( filter_t *p_filter, picture_t *p_dst,
picture_t *p_dst_orig, picture_t *p_src,
int i_x_offset, int i_y_offset )
{
if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','A') &&
( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('I','4','2','0') ||
p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','1','2') ) )
{
BlendI420( p_filter, p_dst, p_dst_orig, p_src,
i_x_offset, i_y_offset );
return;
}
if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','A') &&
p_filter->fmt_out.video.i_chroma == VLC_FOURCC('R','V','1','6') )
{
BlendR16( p_filter, p_dst, p_dst_orig, p_src,
i_x_offset, i_y_offset );
return;
}
if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','U','V','P') &&
( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('I','4','2','0') ||
p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','1','2') ) )
{
BlendPalette( p_filter, p_dst, p_dst_orig, p_src,
i_x_offset, i_y_offset );
return;
}
msg_Dbg( p_filter, "no matching alpha blending routine" );
}
static void BlendI420( filter_t *p_filter, picture_t *p_dst,
picture_t *p_dst_orig, picture_t *p_src,
int i_x_offset, int i_y_offset )
{
filter_sys_t *p_sys = p_filter->p_sys;
int i_src1_pitch, i_src2_pitch, i_dst_pitch;
uint8_t *p_src1_y, *p_src2_y, *p_dst_y;
uint8_t *p_src1_u, *p_src2_u, *p_dst_u;
uint8_t *p_src1_v, *p_src2_v, *p_dst_v;
uint8_t *p_trans;
int i_width, i_height, i_x, i_y;
vlc_bool_t b_even_scanline = i_y_offset % 2;
i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst->p[Y_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst->p[U_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst->p[V_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
p_src1_y = p_dst_orig->p[Y_PLANE].p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst_orig->p[Y_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
p_src1_u = p_dst_orig->p[U_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst_orig->p[U_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
p_src1_v = p_dst_orig->p[V_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst_orig->p[V_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
i_src2_pitch = p_src->p[Y_PLANE].i_pitch;
p_src2_y = p_src->p[Y_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset +
p_src->p[Y_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
p_src2_u = p_src->p[U_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset/2 +
p_src->p[U_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
p_src2_v = p_src->p[V_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset/2 +
p_src->p[V_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
p_trans = p_src->p[A_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset +
p_src->p[A_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
p_filter->fmt_in.video.i_visible_width );
i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
p_filter->fmt_in.video.i_visible_height );
#define MAX_TRANS 255
#define TRANS_BITS 8
/* Draw until we reach the bottom of the subtitle */
for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
p_src2_y += i_src2_pitch,
p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
p_src1_u += b_even_scanline ? i_src1_pitch/2 : 0,
p_src2_u += i_src2_pitch,
p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
p_src1_v += b_even_scanline ? i_src1_pitch/2 : 0,
p_src2_v += i_src2_pitch )
{
b_even_scanline = !b_even_scanline;
/* Draw until we reach the end of the line */
for( i_x = 0; i_x < i_width; i_x++ )
{
if( !p_trans[i_x] )
{
/* Completely transparent. Don't change pixel */
continue;
}
else if( p_trans[i_x] == MAX_TRANS )
{
/* Completely opaque. Completely overwrite underlying pixel */
p_dst_y[i_x] = p_src2_y[i_x];
if( b_even_scanline && i_x % 2 == 0 )
{
p_dst_u[i_x/2] = p_src2_u[i_x];
p_dst_v[i_x/2] = p_src2_v[i_x];
}
continue;
}
/* Blending */
p_dst_y[i_x] = ( (uint16_t)p_src2_y[i_x] * p_trans[i_x] +
(uint16_t)p_src1_y[i_x] * (MAX_TRANS - p_trans[i_x]) )
>> TRANS_BITS;
if( b_even_scanline && i_x % 2 == 0 )
{
p_dst_u[i_x/2] = ( (uint16_t)p_src2_u[i_x] * p_trans[i_x] +
(uint16_t)p_src1_u[i_x/2] * (MAX_TRANS - p_trans[i_x]) )
>> TRANS_BITS;
p_dst_v[i_x/2] = ( (uint16_t)p_src2_v[i_x] * p_trans[i_x] +
(uint16_t)p_src1_v[i_x/2] * (MAX_TRANS - p_trans[i_x]) )
>> TRANS_BITS;
}
}
}
#undef MAX_TRANS
#undef TRANS_BITS
return;
}
static inline void yuv_to_rgb( int *r, int *g, int *b,
uint8_t y1, uint8_t u1, uint8_t v1 )
{
/* macros used for YUV pixel conversions */
# define SCALEBITS 10
# define ONE_HALF (1 << (SCALEBITS - 1))
# define FIX(x) ((int) ((x) * (1<<SCALEBITS) + 0.5))
# define CLAMP( x ) (((x) > 255) ? 255 : ((x) < 0) ? 0 : (x));
int y, cb, cr, r_add, g_add, b_add;
cb = u1 - 128;
cr = v1 - 128;
r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;
g_add = - FIX(0.34414*255.0/224.0) * cb
- FIX(0.71414*255.0/224.0) * cr + ONE_HALF;
b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;
y = (y1 - 16) * FIX(255.0/219.0);
*r = CLAMP((y + r_add) >> SCALEBITS);
*g = CLAMP((y + g_add) >> SCALEBITS);
*b = CLAMP((y + b_add) >> SCALEBITS);
}
static void BlendR16( filter_t *p_filter, picture_t *p_dst_pic,
picture_t *p_dst_orig, picture_t *p_src,
int i_x_offset, int i_y_offset )
{
filter_sys_t *p_sys = p_filter->p_sys;
int i_src1_pitch, i_src2_pitch, i_dst_pitch;
uint8_t *p_dst, *p_src1, *p_src2_y;
uint8_t *p_src2_u, *p_src2_v;
uint8_t *p_trans;
int i_width, i_height, i_x, i_y, i_pix_pitch;
int r, g, b;
i_pix_pitch = p_dst_pic->p->i_pixel_pitch;
i_dst_pitch = p_dst_pic->p->i_pitch;
p_dst = p_dst_pic->p->p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst_pic->p->i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
p_src1 = p_dst_orig->p->p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst_orig->p->i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
i_src2_pitch = p_src->p[Y_PLANE].i_pitch;
p_src2_y = p_src->p[Y_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset +
p_src->p[Y_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
p_src2_u = p_src->p[U_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset/2 +
p_src->p[U_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
p_src2_v = p_src->p[V_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset/2 +
p_src->p[V_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset/2;
p_trans = p_src->p[A_PLANE].p_pixels +
p_filter->fmt_in.video.i_x_offset +
p_src->p[A_PLANE].i_pitch * p_filter->fmt_in.video.i_y_offset;
i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
p_filter->fmt_in.video.i_visible_width );
i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
p_filter->fmt_in.video.i_visible_height );
#define MAX_TRANS 255
#define TRANS_BITS 8
/* Draw until we reach the bottom of the subtitle */
for( i_y = 0; i_y < i_height; i_y++, p_trans += i_src2_pitch,
p_dst += i_dst_pitch, p_src1 += i_src1_pitch,
p_src2_y += i_src2_pitch, p_src2_u += i_src2_pitch,
p_src2_v += i_src2_pitch )
{
/* Draw until we reach the end of the line */
for( i_x = 0; i_x < i_width; i_x++ )
{
if( !p_trans[i_x] )
{
/* Completely transparent. Don't change pixel */
continue;
}
else if( p_trans[i_x] == MAX_TRANS )
{
/* Completely opaque. Completely overwrite underlying pixel */
yuv_to_rgb( &r, &g, &b,
p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
((uint16_t *)(&p_dst[i_x * i_pix_pitch]))[0] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
continue;
}
/* Blending */
yuv_to_rgb( &r, &g, &b,
p_src2_y[i_x], p_src2_u[i_x], p_src2_v[i_x] );
((uint16_t *)(&p_dst[i_x * i_pix_pitch]))[0] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
}
}
#undef MAX_TRANS
#undef TRANS_BITS
return;
}
static void BlendPalette( filter_t *p_filter, picture_t *p_dst,
picture_t *p_dst_orig, picture_t *p_src,
int i_x_offset, int i_y_offset )
{
filter_sys_t *p_sys = p_filter->p_sys;
int i_src1_pitch, i_src2_pitch, i_dst_pitch;
uint8_t *p_src1_y, *p_src2, *p_dst_y;
uint8_t *p_src1_u, *p_dst_u;
uint8_t *p_src1_v, *p_dst_v;
int i_width, i_height, i_x, i_y;
vlc_bool_t b_even_scanline = i_y_offset % 2;
i_dst_pitch = p_dst->p[Y_PLANE].i_pitch;
p_dst_y = p_dst->p[Y_PLANE].p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst->p[Y_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
p_dst_u = p_dst->p[U_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst->p[U_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
p_dst_v = p_dst->p[V_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst->p[V_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
i_src1_pitch = p_dst_orig->p[Y_PLANE].i_pitch;
p_src1_y = p_dst_orig->p[Y_PLANE].p_pixels + i_x_offset +
p_filter->fmt_out.video.i_x_offset +
p_dst_orig->p[Y_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset );
p_src1_u = p_dst_orig->p[U_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst_orig->p[U_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
p_src1_v = p_dst_orig->p[V_PLANE].p_pixels + i_x_offset/2 +
p_filter->fmt_out.video.i_x_offset/2 +
p_dst_orig->p[V_PLANE].i_pitch *
( i_y_offset + p_filter->fmt_out.video.i_y_offset )/2;
i_src2_pitch = p_src->p->i_pitch;
p_src2 = p_src->p->p_pixels + p_filter->fmt_in.video.i_x_offset +
i_src2_pitch * p_filter->fmt_in.video.i_y_offset;
i_width = __MIN( p_filter->fmt_out.video.i_visible_width - i_x_offset,
p_filter->fmt_in.video.i_visible_width );
i_height = __MIN( p_filter->fmt_out.video.i_visible_height - i_y_offset,
p_filter->fmt_in.video.i_visible_height );
#define MAX_TRANS 255
#define TRANS_BITS 8
#define p_trans p_src2
#define p_pal p_filter->fmt_in.video.p_palette->palette
/* Draw until we reach the bottom of the subtitle */
for( i_y = 0; i_y < i_height; i_y++,
p_dst_y += i_dst_pitch, p_src1_y += i_src1_pitch,
p_src2 += i_src2_pitch,
p_dst_u += b_even_scanline ? i_dst_pitch/2 : 0,
p_src1_u += b_even_scanline ? i_src1_pitch/2 : 0,
p_dst_v += b_even_scanline ? i_dst_pitch/2 : 0,
p_src1_v += b_even_scanline ? i_src1_pitch/2 : 0 )
{
b_even_scanline = !b_even_scanline;
/* Draw until we reach the end of the line */
for( i_x = 0; i_x < i_width; i_x++ )
{
if( !p_pal[p_trans[i_x]][3] )
{
/* Completely transparent. Don't change pixel */
continue;
}
else if( p_pal[p_trans[i_x]][3] == MAX_TRANS )
{
/* Completely opaque. Completely overwrite underlying pixel */
p_dst_y[i_x] = p_pal[p_src2[i_x]][0];
if( b_even_scanline && i_x % 2 == 0 )
{
p_dst_u[i_x/2] = p_pal[p_src2[i_x]][1];
p_dst_v[i_x/2] = p_pal[p_src2[i_x]][2];
}
continue;
}
/* Blending */
p_dst_y[i_x] = ( (uint16_t)p_pal[p_src2[i_x]][0] *
p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_y[i_x] *
(MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
if( b_even_scanline && i_x % 2 == 0 )
{
p_dst_u[i_x/2] = ( (uint16_t)p_pal[p_src2[i_x]][1] *
p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_u[i_x/2] *
(MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
p_dst_v[i_x/2] = ( (uint16_t)p_pal[p_src2[i_x]][2] *
p_pal[p_trans[i_x]][3] + (uint16_t)p_src1_v[i_x/2] *
(MAX_TRANS - p_pal[p_trans[i_x]][3]) ) >> TRANS_BITS;
}
}
}
#undef MAX_TRANS
#undef TRANS_BITS
#undef p_trans
#undef p_pal
return;
}
/*****************************************************************************
* CloseFilter: clean up the filter
*****************************************************************************/
static void CloseFilter( vlc_object_t *p_this )
{
filter_t *p_filter = (filter_t*)p_this;
filter_sys_t *p_sys = p_filter->p_sys;
free( p_sys );
}
......@@ -51,6 +51,9 @@ static void vout_del_buffer( decoder_t *, picture_t * );
static void vout_link_picture( decoder_t *, picture_t * );
static void vout_unlink_picture( decoder_t *, picture_t * );
static subpicture_t *spu_new_buffer( decoder_t * );
static void spu_del_buffer( decoder_t *, subpicture_t * );
static es_format_t null_es_format = {0};
struct decoder_owner_sys_t
......@@ -64,6 +67,9 @@ struct decoder_owner_sys_t
vout_thread_t *p_vout;
vout_thread_t *p_spu_vout;
int i_spu_channel;
sout_instance_t *p_sout;
sout_packetizer_input_t *p_sout_input;
......@@ -387,17 +393,19 @@ static decoder_t * CreateDecoder( input_thread_t *p_input,
p_dec->p_owner->p_aout = NULL;
p_dec->p_owner->p_aout_input = NULL;
p_dec->p_owner->p_vout = NULL;
p_dec->p_owner->p_spu_vout = NULL;
p_dec->p_owner->i_spu_channel = 0;
p_dec->p_owner->p_sout = p_input->p_sout;
p_dec->p_owner->p_sout_input = NULL;
p_dec->p_owner->p_packetizer = NULL;
/* decoder fifo */
if( ( p_dec->p_owner->p_fifo = block_FifoNew( p_dec ) ) == NULL )
{
msg_Err( p_dec, "out of memory" );
return NULL;
}
/* Set buffers allocation callbacks for the decoders */
p_dec->pf_aout_buffer_new = aout_new_buffer;
p_dec->pf_aout_buffer_del = aout_del_buffer;
......@@ -405,6 +413,8 @@ static decoder_t * CreateDecoder( input_thread_t *p_input,
p_dec->pf_vout_buffer_del = vout_del_buffer;
p_dec->pf_picture_link = vout_link_picture;
p_dec->pf_picture_unlink = vout_unlink_picture;
p_dec->pf_spu_buffer_new = spu_new_buffer;
p_dec->pf_spu_buffer_del = spu_del_buffer;
vlc_object_attach( p_dec, p_input );
......@@ -618,7 +628,17 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block )
}
else if( p_dec->fmt_in.i_cat == SPU_ES )
{
p_dec->pf_decode_sub( p_dec, &p_block );
vout_thread_t *p_vout;
subpicture_t *p_spu;
while( (p_spu = p_dec->pf_decode_sub( p_dec, &p_block ) ) )
{
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout )
{
vout_DisplaySubPicture( p_vout, p_spu );
vlc_object_release( p_vout );
}
}
}
else
{
......@@ -677,6 +697,18 @@ static void DeleteDecoder( decoder_t * p_dec )
es_format_Clean( &p_dec->p_owner->sout );
}
if( p_dec->fmt_in.i_cat == SPU_ES )
{
vout_thread_t *p_vout;
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout )
{
vout_ClearOSDChannel( p_vout, p_dec->p_owner->i_spu_channel );
vlc_object_release( p_vout );
}
}
es_format_Clean( &p_dec->fmt_in );
es_format_Clean( &p_dec->fmt_out );
......@@ -840,3 +872,32 @@ static void vout_unlink_picture( decoder_t *p_dec, picture_t *p_pic )
{
vout_UnlinkPicture( p_dec->p_owner->p_vout, p_pic );
}
static subpicture_t *spu_new_buffer( decoder_t *p_dec )
{
decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner;
vout_thread_t *p_vout;
subpicture_t *p_spu;
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( !p_vout ) return NULL;
if( p_sys->p_spu_vout != p_vout )
{
p_sys->i_spu_channel =
vout_RegisterOSDChannel( p_vout );
p_sys->p_spu_vout = p_vout;
}
p_spu = vout_CreateSubPicture( p_vout, p_sys->i_spu_channel,
MEMORY_SUBPICTURE );
vlc_object_release( p_vout );
return p_spu;
}
static void spu_del_buffer( decoder_t *p_dec, subpicture_t *p_spu )
{
vout_DestroySubPicture( p_dec->p_owner->p_vout, p_spu );
}
......@@ -1210,7 +1210,8 @@ char *stream_ReadLine( stream_t *s )
}
/* Remove trailing LF/CR */
while( i_line > 0 && ( p_line[i_line-1] == '\r' || p_line[i_line-1] == '\n') ) i_line--;
while( i_line > 0 && ( p_line[i_line-1] == '\r' ||
p_line[i_line-1] == '\n') ) i_line--;
if( i_read > 0 )
{
......@@ -1218,7 +1219,7 @@ char *stream_ReadLine( stream_t *s )
return p_line;
}
/* We failed to read any data, probably EOF*/
/* We failed to read any data, probably EOF */
if( p_line ) free( p_line );
return NULL;
}
......@@ -232,7 +232,7 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent,
return NULL;
}
/* Initialize pictures and subpictures - translation tables and functions
/* Initialize pictures - translation tables and functions
* will be initialized later in InitThread */
for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
{
......@@ -243,12 +243,6 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent,
p_vout->p_picture[i_index].b_slow = 0;
}
for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
{
p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
p_vout->p_subpicture[i_index].i_type = EMPTY_SUBPICTURE;
}
/* No images in the heap */
p_vout->i_heap_size = 0;
......@@ -416,13 +410,8 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent,
return NULL;
}
p_vout->p_text_renderer_module =
module_Need( p_vout, "text renderer", NULL, 0 );
if( p_vout->p_text_renderer_module == NULL )
{
msg_Warn( p_vout, "no suitable text renderer module" );
p_vout->pf_add_string = NULL;
}
/* Initialize subpicture unit */
vout_InitSPU( p_vout );
/* Create a few object variables for interface interaction */
var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
......@@ -489,10 +478,6 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent,
/* Make sure the thread is destroyed */
p_vout->b_die = VLC_TRUE;
if( p_vout->p_text_renderer_module )
{
module_Unneed( p_vout, p_vout->p_text_renderer_module );
}
vlc_thread_join( p_vout );
vlc_object_detach( p_vout );
......@@ -1127,18 +1112,8 @@ static void EndThread( vout_thread_t *p_vout )
}
}
/* Destroy all remaining subpictures */
for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
{
if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
{
vout_DestroySubPicture( p_vout,
&p_vout->p_subpicture[i_index] );
}
}
if( p_vout->p_text_renderer_module )
module_Unneed( p_vout, p_vout->p_text_renderer_module );
/* Destroy subpicture unit */
vout_DestroySPU( p_vout );
/* Destroy translation tables */
p_vout->pf_end( p_vout );
......
......@@ -21,7 +21,9 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#include <vlc/vout.h>
#include <osd.h>
#include "vlc_block.h"
#include "vlc_filter.h"
#include "osd.h"
/**
* \brief Show text on the video for some time
......@@ -34,25 +36,44 @@
* \param i_vmargin vertical margin in pixels
* \param i_duration Amount of time the text is to be shown.
*/
subpicture_t *vout_ShowTextRelative( vout_thread_t *p_vout, int i_channel,
char *psz_string, text_style_t *p_style,
int i_flags, int i_hmargin, int i_vmargin,
mtime_t i_duration )
int vout_ShowTextRelative( vout_thread_t *p_vout, int i_channel,
char *psz_string, text_style_t *p_style,
int i_flags, int i_hmargin, int i_vmargin,
mtime_t i_duration )
{
subpicture_t *p_subpic = NULL;
mtime_t i_now = mdate();
if ( p_vout->pf_add_string )
if( p_vout->p_text && p_vout->p_text->p_module &&
p_vout->p_text->pf_render_string )
{
p_subpic = p_vout->pf_add_string( p_vout, i_channel, psz_string,
p_style, i_flags, i_hmargin, i_vmargin, i_now, i_now + i_duration );
block_t *p_block = block_New( p_vout, strlen(psz_string) + 1 );
if( p_block )
{
memcpy( p_block->p_buffer, psz_string, p_block->i_buffer );
p_block->i_pts = p_block->i_dts = i_now;
p_block->i_length = i_duration;
p_subpic = p_vout->p_text->pf_render_string( p_vout->p_text,
p_block );
if( p_subpic )
{
p_subpic->i_x = i_hmargin;
p_subpic->i_y = i_vmargin;
p_subpic->i_flags = i_flags;
p_subpic->i_channel = i_channel;
vout_DisplaySubPicture( p_vout, p_subpic );
return VLC_SUCCESS;
}
}
return VLC_EGENERIC;
}
else
{
msg_Warn( p_vout, "No text renderer found" );
return VLC_EGENERIC;
}
return p_subpic;
}
/**
......@@ -74,11 +95,32 @@ int vout_ShowTextAbsolute( vout_thread_t *p_vout, int i_channel,
int i_flags, int i_hmargin, int i_vmargin,
mtime_t i_start, mtime_t i_stop )
{
if ( p_vout->pf_add_string )
subpicture_t *p_subpic = NULL;
if( p_vout->p_text && p_vout->p_text->p_module &&
p_vout->p_text->pf_render_string )
{
p_vout->pf_add_string( p_vout, i_channel, psz_string, p_style, i_flags,
i_hmargin, i_vmargin, i_start, i_stop );
return VLC_SUCCESS;
block_t *p_block = block_New( p_vout, strlen(psz_string) + 1 );
if( p_block )
{
memcpy( p_block->p_buffer, psz_string, p_block->i_buffer );
p_block->i_pts = p_block->i_dts = i_start;
p_block->i_length = i_stop - i_start;
p_subpic = p_vout->p_text->pf_render_string( p_vout->p_text,
p_block );
if( p_subpic )
{
p_subpic->i_x = i_hmargin;
p_subpic->i_y = i_vmargin;
p_subpic->i_flags = i_flags;
p_subpic->i_channel = i_channel;
vout_DisplaySubPicture( p_vout, p_subpic );
return VLC_SUCCESS;
}
}
return VLC_EGENERIC;
}
else
{
......
......@@ -586,6 +586,9 @@ void vout_InitFormat( video_frame_format_t *p_format, vlc_fourcc_t i_chroma,
switch( i_chroma )
{
case FOURCC_YUVA:
p_format->i_bits_per_pixel = 32;
break;
case FOURCC_I444:
p_format->i_bits_per_pixel = 24;
break;
......@@ -608,6 +611,10 @@ void vout_InitFormat( video_frame_format_t *p_format, vlc_fourcc_t i_chroma,
case FOURCC_Y211:
p_format->i_bits_per_pixel = 8;
break;
case FOURCC_YUVP:
p_format->i_bits_per_pixel = 8;
break;
case FOURCC_RV32:
p_format->i_bits_per_pixel = 32;
break;
......@@ -731,6 +738,30 @@ void vout_InitPicture( vlc_object_t *p_this, picture_t *p_pic,
p_pic->i_planes = 3;
break;
case FOURCC_YUVA:
p_pic->p[ Y_PLANE ].i_lines = i_height;
p_pic->p[ Y_PLANE ].i_pitch = i_width;
p_pic->p[ Y_PLANE ].i_visible_pitch = p_pic->p[ Y_PLANE ].i_pitch;
p_pic->p[ U_PLANE ].i_lines = i_height;
p_pic->p[ U_PLANE ].i_pitch = i_width;
p_pic->p[ U_PLANE ].i_visible_pitch = p_pic->p[ U_PLANE ].i_pitch;
p_pic->p[ V_PLANE ].i_lines = i_height;
p_pic->p[ V_PLANE ].i_pitch = i_width;
p_pic->p[ V_PLANE ].i_visible_pitch = p_pic->p[ V_PLANE ].i_pitch;
p_pic->p[ A_PLANE ].i_lines = i_height;
p_pic->p[ A_PLANE ].i_pitch = i_width;
p_pic->p[ A_PLANE ].i_visible_pitch = p_pic->p[ A_PLANE ].i_pitch;
p_pic->i_planes = 4;
break;
case FOURCC_YUVP:
p_pic->p->i_lines = i_height;
p_pic->p->i_pitch = i_width;
p_pic->p->i_visible_pitch = p_pic->p->i_pitch;
p_pic->p->i_pixel_pitch = 8;
p_pic->i_planes = 1;
break;
case FOURCC_Y211:
p_pic->p->i_lines = i_height;
p_pic->p->i_pitch = i_width;
......@@ -811,20 +842,6 @@ void vout_InitPicture( vlc_object_t *p_this, picture_t *p_pic,
p_pic->p_heap->i_bmask = 0x0000ff; */
p_pic->i_planes = 1;
break;
case FOURCC_YUVA:
p_pic->p[ Y_PLANE ].i_lines = i_height;
p_pic->p[ Y_PLANE ].i_pitch = i_width;
p_pic->p[ Y_PLANE ].i_visible_pitch = p_pic->p[ Y_PLANE ].i_pitch;
p_pic->p[ U_PLANE ].i_lines = i_height;
p_pic->p[ U_PLANE ].i_pitch = i_width;
p_pic->p[ U_PLANE ].i_visible_pitch = p_pic->p[ U_PLANE ].i_pitch;
p_pic->p[ V_PLANE ].i_lines = i_height;
p_pic->p[ V_PLANE ].i_pitch = i_width;
p_pic->p[ V_PLANE ].i_visible_pitch = p_pic->p[ V_PLANE ].i_pitch;
p_pic->p[ A_PLANE ].i_lines = i_height;
p_pic->p[ A_PLANE ].i_pitch = i_width;
p_pic->p[ A_PLANE ].i_visible_pitch = p_pic->p[ A_PLANE ].i_pitch;
p_pic->i_planes = 4;
default:
msg_Err( p_this, "unknown chroma type 0x%.8x (%4.4s)",
......
......@@ -90,3 +90,6 @@
/* Planar 4:4:4:4 Y:U:V:A */
#define FOURCC_YUVA VLC_FOURCC('Y','U','V','A')
/* Palettized YUV with palette element Y:U:V:A */
#define FOURCC_YUVP VLC_FOURCC('Y','U','V','P')
......@@ -6,6 +6,7 @@
*
* Authors: Vincent Seguin <seguin@via.ecp.fr>
* Samuel Hocevar <sam@zoy.org>
* Gildas Bazin <gbazin@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
......@@ -33,6 +34,156 @@
#include "vlc_video.h"
#include "video_output.h"
#include "vlc_filter.h"
#include "osd.h"
static void UpdateSPU ( vout_thread_t *, vlc_object_t * );
static int CropCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static subpicture_t *spu_new_buffer( filter_t * );
static void spu_del_buffer( filter_t *, subpicture_t * );
/**
* Initialise the subpicture decoder unit
*
* \param p_vout the vout in which to create the subpicture unit
*/
void vout_InitSPU( vout_thread_t *p_vout )
{
vlc_object_t *p_input;
int i_index;
for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
{
p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
p_vout->p_subpicture[i_index].i_type = EMPTY_SUBPICTURE;
}
/* Load the text rendering module */
p_vout->p_text = vlc_object_create( p_vout, sizeof(filter_t) );
p_vout->p_text->pf_spu_buffer_new = spu_new_buffer;
p_vout->p_text->pf_spu_buffer_del = spu_del_buffer;
p_vout->p_text->p_owner = (filter_owner_sys_t *)p_vout;
vlc_object_attach( p_vout->p_text, p_vout );
p_vout->p_text->p_module =
module_Need( p_vout->p_text, "text renderer", 0, 0 );
if( p_vout->p_text->p_module == NULL )
{
msg_Warn( p_vout, "no suitable text renderer module" );
vlc_object_detach( p_vout->p_text );
vlc_object_destroy( p_vout->p_text );
p_vout->p_text = NULL;
}
p_vout->p_blend = NULL;
p_vout->i_crop_x = p_vout->i_crop_y =
p_vout->i_crop_width = p_vout->i_crop_height = 0;
p_vout->b_force_alpha = VLC_FALSE;
p_vout->b_force_crop = VLC_FALSE;
/* Create callback */
p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
if( p_input )
{
UpdateSPU( p_vout, VLC_OBJECT(p_input) );
var_AddCallback( p_input, "highlight", CropCallback, p_vout );
vlc_object_release( p_input );
}
}
/**
* Destroy the subpicture decoder unit
*
* \param p_vout the vout in which to destroy the subpicture unit
*/
void vout_DestroySPU( vout_thread_t *p_vout )
{
vlc_object_t *p_input;
int i_index;
/* Destroy all remaining subpictures */
for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
{
if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
{
vout_DestroySubPicture( p_vout,
&p_vout->p_subpicture[i_index] );
}
}
if( p_vout->p_text )
{
if( p_vout->p_text->p_module )
module_Unneed( p_vout->p_text, p_vout->p_text->p_module );
vlc_object_detach( p_vout->p_text );
vlc_object_destroy( p_vout->p_text );
}
if( p_vout->p_blend )
{
if( p_vout->p_blend->p_module )
module_Unneed( p_vout->p_blend, p_vout->p_blend->p_module );
vlc_object_detach( p_vout->p_blend );
vlc_object_destroy( p_vout->p_blend );
}
/* Delete callback */
p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT );
if( p_input )
{
var_DelCallback( p_input, "highlight", CropCallback, p_vout );
vlc_object_release( p_input );
}
}
/**
* Create a subpicture region
*
* \param p_this vlc_object_t
* \param p_fmt the format that this subpicture region should have
*/
subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
video_format_t *p_fmt )
{
subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
memset( p_region, 0, sizeof(subpicture_region_t) );
p_region->p_next = 0;
p_region->fmt = *p_fmt;
vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma,
p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
if( !p_region->picture.i_planes )
{
free( p_region );
return NULL;
}
if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
p_fmt->p_palette = p_region->fmt.p_palette =
malloc( sizeof(video_palette_t) );
else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
return p_region;
}
/**
* Destroy a subpicture region
*
* \param p_this vlc_object_t
* \param p_region the subpicture region to destroy
*/
void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region )
{
if( !p_region ) return;
if( p_region->picture.p_data_orig ) free( p_region->picture.p_data_orig );
if( p_region->fmt.p_palette ) free( p_region->fmt.p_palette );
free( p_region );
}
/**
* Display a subpicture unit
......@@ -134,6 +285,11 @@ subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel,
p_subpic->i_y = 0;
p_subpic->i_width = 0;
p_subpic->i_height = 0;
p_subpic->b_absolute= VLC_TRUE;
p_subpic->i_flags = 0;
p_subpic->pf_render = 0;
p_subpic->pf_destroy= 0;
p_subpic->p_sys = 0;
/* Remain last subpicture displayed in DEFAULT_CHAN */
if( i_channel == DEFAULT_CHAN )
......@@ -143,6 +299,9 @@ subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel,
vlc_mutex_unlock( &p_vout->subpicture_lock );
p_subpic->pf_create_region = __spu_CreateRegion;
p_subpic->pf_destroy_region = __spu_DestroyRegion;
return p_subpic;
}
......@@ -174,6 +333,13 @@ void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
p_subpic, p_subpic->i_status );
}
while( p_subpic->p_region )
{
subpicture_region_t *p_region = p_subpic->p_region;
p_subpic->p_region = p_region->p_next;
spu_DestroyRegion( p_vout, p_region );
}
if( p_subpic->pf_destroy )
{
p_subpic->pf_destroy( p_subpic );
......@@ -192,13 +358,109 @@ void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic,
subpicture_t *p_subpic )
{
/* Load the blending module */
if( !p_vout->p_blend && p_subpic && p_subpic->p_region )
{
p_vout->p_blend = vlc_object_create( p_vout, sizeof(filter_t) );
vlc_object_attach( p_vout->p_blend, p_vout );
p_vout->p_blend->fmt_out.video.i_width =
p_vout->p_blend->fmt_out.video.i_visible_width =
p_vout->render.i_width;
p_vout->p_blend->fmt_out.video.i_height =
p_vout->p_blend->fmt_out.video.i_visible_height =
p_vout->render.i_height;
p_vout->p_blend->fmt_out.video.i_x_offset =
p_vout->p_blend->fmt_out.video.i_y_offset = 0;
p_vout->p_blend->fmt_out.video.i_aspect =
p_vout->render.i_aspect;
p_vout->p_blend->fmt_out.video.i_chroma =
p_vout->output.i_chroma;
p_vout->p_blend->fmt_in.video = p_subpic->p_region->fmt;
p_vout->p_blend->p_module =
module_Need( p_vout->p_blend, "video blending", 0, 0 );
}
/* Get lock */
vlc_mutex_lock( &p_vout->subpicture_lock );
/* Check i_status again to make sure spudec hasn't destroyed the subpic */
while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
{
p_subpic->pf_render( p_vout, p_pic, p_subpic );
subpicture_region_t *p_region = p_subpic->p_region;
if( p_subpic->pf_render )
{
p_subpic->pf_render( p_vout, p_pic, p_subpic );
}
else while( p_region && p_vout->p_blend &&
p_vout->p_blend->pf_video_blend )
{
int i_x_offset = p_region->i_x + p_subpic->i_x;
int i_y_offset = p_region->i_y + p_subpic->i_y;
if( p_subpic->i_flags & OSD_ALIGN_BOTTOM )
{
i_y_offset += p_vout->output.i_height - p_region->fmt.i_height;
}
else if ( !(p_subpic->i_flags & OSD_ALIGN_TOP) )
{
i_y_offset += p_vout->output.i_height / 2 -
p_region->fmt.i_height / 2;
}
if( p_subpic->i_flags & OSD_ALIGN_RIGHT )
{
i_x_offset += p_vout->output.i_width - p_region->fmt.i_width;
}
else if ( !(p_subpic->i_flags & OSD_ALIGN_LEFT) )
{
i_x_offset += p_vout->output.i_width / 2 -
p_region->fmt.i_width / 2;
}
if( p_subpic->b_absolute )
{
i_x_offset = p_region->i_x + p_subpic->i_x;
i_y_offset = p_region->i_y + p_subpic->i_y;
}
p_vout->p_blend->fmt_in.video = p_region->fmt;
/* Force cropping if requested */
if( p_vout->b_force_crop )
{
p_vout->p_blend->fmt_in.video.i_x_offset = p_vout->i_crop_x;
p_vout->p_blend->fmt_in.video.i_y_offset = p_vout->i_crop_y;
p_vout->p_blend->fmt_in.video.i_visible_width =
p_vout->i_crop_width;
p_vout->p_blend->fmt_in.video.i_visible_height =
p_vout->i_crop_height;
i_x_offset += p_vout->i_crop_x;
i_y_offset += p_vout->i_crop_y;
}
/* Force palette if requested */
if( p_vout->b_force_alpha && VLC_FOURCC('Y','U','V','P') ==
p_vout->p_blend->fmt_in.video.i_chroma )
{
p_vout->p_blend->fmt_in.video.p_palette->palette[0][3] =
p_vout->pi_alpha[0];
p_vout->p_blend->fmt_in.video.p_palette->palette[1][3] =
p_vout->pi_alpha[1];
p_vout->p_blend->fmt_in.video.p_palette->palette[2][3] =
p_vout->pi_alpha[2];
p_vout->p_blend->fmt_in.video.p_palette->palette[3][3] =
p_vout->pi_alpha[3];
}
p_vout->p_blend->pf_video_blend( p_vout->p_blend,
p_pic, p_pic, &p_region->picture, i_x_offset, i_y_offset );
p_region = p_region->p_next;
}
p_subpic = p_subpic->p_next;
}
......@@ -325,7 +587,8 @@ subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
*****************************************************************************/
int vout_RegisterOSDChannel( vout_thread_t *p_vout )
{
msg_Dbg( p_vout, "Registering OSD channel, ID: %i", p_vout->i_channel_count + 1 );
msg_Dbg( p_vout, "Registering OSD channel, ID: %i",
p_vout->i_channel_count + 1 );
return ++p_vout->i_channel_count;
}
......@@ -361,8 +624,16 @@ void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel )
{
continue;
}
if( p_subpic->i_channel == i_channel )
{
while( p_subpic->p_region )
{
subpicture_region_t *p_region = p_subpic->p_region;
p_subpic->p_region = p_region->p_next;
spu_DestroyRegion( p_vout, p_region );
}
if( p_subpic->pf_destroy )
{
p_subpic->pf_destroy( p_subpic );
......@@ -373,3 +644,87 @@ void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel )
vlc_mutex_unlock( &p_vout->subpicture_lock );
}
/*****************************************************************************
* UpdateSPU: update subpicture settings
*****************************************************************************
* This function is called from CropCallback and at initialization time, to
* retrieve crop information from the input.
*****************************************************************************/
static void UpdateSPU( vout_thread_t *p_vout, vlc_object_t *p_object )
{
vlc_value_t val;
p_vout->b_force_alpha = VLC_FALSE;
p_vout->b_force_crop = VLC_FALSE;
if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;
p_vout->b_force_crop = VLC_TRUE;
var_Get( p_object, "x-start", &val );
p_vout->i_crop_x = val.i_int;
var_Get( p_object, "y-start", &val );
p_vout->i_crop_y = val.i_int;
var_Get( p_object, "x-end", &val );
p_vout->i_crop_width = val.i_int - p_vout->i_crop_x;
var_Get( p_object, "y-end", &val );
p_vout->i_crop_height = val.i_int - p_vout->i_crop_y;
#if 0
if( var_Get( p_object, "color", &val ) == VLC_SUCCESS )
{
int i;
for( i = 0; i < 4; i++ )
{
p_vout->pi_color[i] = ((uint8_t *)val.p_address)[i];
}
}
#endif
if( var_Get( p_object, "contrast", &val ) == VLC_SUCCESS )
{
int i;
for( i = 0; i < 4; i++ )
{
p_vout->pi_alpha[i] = ((uint8_t *)val.p_address)[i];
p_vout->pi_alpha[i] = p_vout->pi_alpha[i] == 0xf ?
0xff : p_vout->pi_alpha[i] << 4;
}
p_vout->b_force_alpha = VLC_TRUE;
}
msg_Dbg( p_vout, "crop: %i,%i,%i,%i, alpha: %i",
p_vout->i_crop_x, p_vout->i_crop_y,
p_vout->i_crop_width, p_vout->i_crop_height,
p_vout->b_force_alpha );
}
/*****************************************************************************
* CropCallback: called when the highlight properties are changed
*****************************************************************************
* This callback is called from the input thread when we need cropping
*****************************************************************************/
static int CropCallback( vlc_object_t *p_object, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
UpdateSPU( (vout_thread_t *)p_data, p_object );
return VLC_SUCCESS;
}
/*****************************************************************************
* Buffers allocation callbacks for the filters
*****************************************************************************/
static subpicture_t *spu_new_buffer( filter_t *p_filter )
{
vout_thread_t *p_vout = (vout_thread_t *)p_filter->p_owner;
subpicture_t *p_spu;
p_spu = vout_CreateSubPicture( p_vout, !DEFAULT_CHAN, MEMORY_SUBPICTURE );
return p_spu;
}
static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_spu )
{
vout_thread_t *p_vout = (vout_thread_t *)p_filter->p_owner;
vout_DestroySubPicture( p_vout, p_spu );
}
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