* ALL: Major rework of the subpictures architecture.
authorGildas Bazin <gbazin@videolan.org>
Tue, 3 Aug 2004 00:11:32 +0000 (00:11 +0000)
committerGildas Bazin <gbazin@videolan.org>
Tue, 3 Aug 2004 00:11:32 +0000 (00:11 +0000)
  (expect some breakage).
* modules/video_filter/blend.c: new alpha-blending module.

27 files changed:
Makefile.am
configure.ac
include/osd.h
include/video_output.h
include/vlc_codec.h
include/vlc_common.h
include/vlc_es.h
include/vlc_filter.h [new file with mode: 0644]
include/vlc_video.h
modules/codec/dvbsub.c
modules/codec/spudec/Modules.am
modules/codec/spudec/parse.c
modules/codec/spudec/render.c [deleted file]
modules/codec/spudec/spudec.c
modules/codec/spudec/spudec.h
modules/codec/subsdec.c
modules/misc/dummy/renderer.c
modules/misc/freetype.c
modules/video_filter/Modules.am
modules/video_filter/blend.c [new file with mode: 0644]
src/input/decoder.c
src/input/stream.c
src/video_output/video_output.c
src/video_output/video_text.c
src/video_output/vout_pictures.c
src/video_output/vout_pictures.h
src/video_output/vout_subpictures.c

index 78ca0ec..34d0527 100644 (file)
@@ -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 \
index eed92b0..b9008fa 100644 (file)
@@ -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])
index 9bd1f49..8be324b 100644 (file)
@@ -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 *, ... ) );
 /**
index cb7ad25..bd9c009 100644 (file)
@@ -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 * );
 /** @}*/
 /**
  * @}
index 4454b77..01a7d93 100644 (file)
@@ -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;
index d1a76da..a55d2be 100644 (file)
@@ -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;
index b3231e7..00a9e56 100644 (file)
  */
 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 */
 };
 
diff --git a/include/vlc_filter.h b/include/vlc_filter.h
new file mode 100644 (file)
index 0000000..ca02903
--- /dev/null
@@ -0,0 +1,87 @@
+/*****************************************************************************
+ * 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 */
index 8267806..db437fa 100644 (file)
@@ -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 */
index 8eeca67..5f21099 100644 (file)
 /*****************************************************************************
  * 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;
 }
index f9103de..371fcfe 100644 (file)
@@ -1,6 +1,5 @@
 SOURCES_spudec = \
        spudec.c \
        parse.c \
-       render.c \
        spudec.h \
        $(NULL)
index 6ada5fd..c8f50ba 100644 (file)
@@ -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
 /*****************************************************************************
  * 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;
-}
-
diff --git a/modules/codec/spudec/render.c b/modules/codec/spudec/render.c
deleted file mode 100644 (file)
index 5cdc4d6..0000000
+++ /dev/null
@@ -1,583 +0,0 @@
-/*****************************************************************************
- * 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;
-            }
-        }
-    }
-}
-
index ca8f481..4801ec8 100644 (file)
@@ -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;
-}
-
index 6f68d2a..157481d 100644 (file)
@@ -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 * );
index 0b1828f..05b3619 100644 (file)
@@ -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>
 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 );
+}
index 16a141a..a1f0a39 100644 (file)
 
 #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;
 }
-
index 1a20783..d0ebd4c 100644 (file)
@@ -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
 
 #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 )
     {
index 525a0b4..888d7d5 100644 (file)
@@ -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
diff --git a/modules/video_filter/blend.c b/modules/video_filter/blend.c
new file mode 100644 (file)
index 0000000..6c6c408
--- /dev/null
@@ -0,0 +1,489 @@
+/*****************************************************************************
+ * 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 );
+}
index 3e40f4c..c7e5f6c 100644 (file)
@@ -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 );
+}
index 1cce7a9..762e0fa 100644 (file)
@@ -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;
 }
index 64c5dc3..633c594 100644 (file)
@@ -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 );
index 104caec..2cfa5e5 100644 (file)
@@ -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
  * \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
     {
index f9a3561..c26d0ce 100644 (file)
@@ -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)",
index 38b91a8..89e3cd0 100644 (file)
@@ -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')
index dc33d72..4b316b6 100644 (file)
@@ -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
 
 #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 );
+}