+ bs_write( s, 8, i ); /* Clut entry id */
+ bs_write( s, 1, p_pal->i_entries == 4 ); /* 2bit/entry flag */
+ bs_write( s, 1, p_pal->i_entries == 16 ); /* 4bit/entry flag */
+ bs_write( s, 1, p_pal->i_entries == 256 ); /* 8bit/entry flag */
+ bs_write( s, 4, 0 ); /* Reserved */
+ bs_write( s, 1, 1 ); /* Full range flag */
+ bs_write( s, 8, p_pal->palette[i][3] ? /* Y value */
+ (p_pal->palette[i][0] ? p_pal->palette[i][0] : 16) : 0 );
+ bs_write( s, 8, p_pal->palette[i][1] ); /* Cr value */
+ bs_write( s, 8, p_pal->palette[i][2] ); /* Cb value */
+ bs_write( s, 8, 0xff - p_pal->palette[i][3] ); /* T value */
+ }
+}
+
+static void encode_region_composition( encoder_t *p_enc, bs_t *s,
+ subpicture_t *p_subpic )
+{
+ encoder_sys_t *p_sys = p_enc->p_sys;
+ subpicture_region_t *p_region;
+ int i_region;
+
+ for( i_region = 0, p_region = p_subpic->p_region; p_region;
+ p_region = p_region->p_next, i_region++ )
+ {
+ int i_entries = 4, i_depth = 0x1, i_bg = 0;
+ bool b_text =
+ ( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') );
+
+ if( !b_text )
+ {
+ video_palette_t *p_pal = p_region->fmt.p_palette;
+
+ if( !p_pal )
+ {
+ msg_Err( p_enc, "subpicture has no palette - ignoring it" );
+ break;
+ }
+
+ i_entries = p_pal->i_entries;
+ i_depth = i_entries == 4 ? 0x1 : i_entries == 16 ? 0x2 : 0x3;
+
+ for( i_bg = 0; i_bg < p_pal->i_entries; i_bg++ )
+ {
+ if( !p_pal->palette[i_bg][3] ) break;
+ }
+ }
+
+ bs_write( s, 8, 0x0f ); /* Sync byte */
+ bs_write( s, 8, DVBSUB_ST_REGION_COMPOSITION ); /* Segment type */
+ bs_write( s, 16, 1 ); /* Page id */
+
+ bs_write( s, 16, 10 + 6 + (b_text ? 2 : 0) ); /* Segment length */
+ bs_write( s, 8, i_region );
+ bs_write( s, 4, p_sys->i_region_ver++ );
+
+ /* Region attributes */
+ bs_write( s, 1, i_bg < i_entries ); /* Fill */
+ bs_write( s, 3, 0 ); /* Reserved */
+ bs_write( s, 16, p_sys->p_regions[i_region].i_width );
+ bs_write( s, 16, p_sys->p_regions[i_region].i_height );
+ bs_write( s, 3, i_depth ); /* Region level of compatibility */
+ bs_write( s, 3, i_depth ); /* Region depth */
+ bs_write( s, 2, 0 ); /* Reserved */
+ bs_write( s, 8, 1 ); /* Clut id */
+ bs_write( s, 8, i_bg ); /* region 8bit pixel code */
+ bs_write( s, 4, i_bg ); /* region 4bit pixel code */
+ bs_write( s, 2, i_bg ); /* region 2bit pixel code */
+ bs_write( s, 2, 0 ); /* Reserved */
+
+ /* In our implementation we only have 1 object per region */
+ bs_write( s, 16, i_region );
+ bs_write( s, 2, b_text ? DVBSUB_OT_BASIC_CHAR:DVBSUB_OT_BASIC_BITMAP );
+ bs_write( s, 2, 0 ); /* object provider flag */
+ bs_write( s, 12, 0 );/* object horizontal position */
+ bs_write( s, 4, 0 ); /* Reserved */
+ bs_write( s, 12, 0 );/* object vertical position */
+
+ if( b_text )
+ {
+ bs_write( s, 8, 1 ); /* foreground pixel code */
+ bs_write( s, 8, 0 ); /* background pixel code */
+ }
+ }
+}
+
+static void encode_pixel_data( encoder_t *p_enc, bs_t *s,
+ subpicture_region_t *p_region,
+ bool b_top );
+
+static void encode_object( encoder_t *p_enc, bs_t *s, subpicture_t *p_subpic )
+{
+ encoder_sys_t *p_sys = p_enc->p_sys;
+ subpicture_region_t *p_region;
+ int i_region;
+
+ int i_length_pos, i_update_pos, i_pixel_data_pos;
+
+ for( i_region = 0, p_region = p_subpic->p_region; p_region;
+ p_region = p_region->p_next, i_region++ )
+ {
+ bs_write( s, 8, 0x0f ); /* Sync byte */
+ bs_write( s, 8, DVBSUB_ST_OBJECT_DATA ); /* Segment type */
+ bs_write( s, 16, 1 ); /* Page id */
+
+ i_length_pos = bs_pos( s );
+ bs_write( s, 16, 0 ); /* Segment length */
+ bs_write( s, 16, i_region ); /* Object id */
+ bs_write( s, 4, p_sys->i_region_ver++ );
+
+ /* object coding method */
+ switch( p_region->fmt.i_chroma )
+ {
+ case VLC_FOURCC( 'Y','U','V','P' ):
+ bs_write( s, 2, 0 );
+ break;
+ case VLC_FOURCC( 'T','E','X','T' ):
+ bs_write( s, 2, 1 );
+ break;
+ default:
+ msg_Err( p_enc, "FOURCC %d not supported by encoder.", p_region->fmt.i_chroma );
+ continue;
+ }
+
+ bs_write( s, 1, 0 ); /* non modifying color flag */
+ bs_write( s, 1, 0 ); /* Reserved */
+
+ if( p_region->fmt.i_chroma == VLC_FOURCC( 'T','E','X','T' ) )
+ {
+ int i_size, i;
+
+ if( !p_region->psz_text ) continue;
+
+ i_size = __MIN( strlen( p_region->psz_text ), 256 );
+
+ bs_write( s, 8, i_size ); /* number of characters in string */
+ for( i = 0; i < i_size; i++ )
+ {
+ bs_write( s, 16, p_region->psz_text[i] );
+ }
+
+ /* Update segment length */
+ SetWBE( &s->p_start[i_length_pos/8],
+ (bs_pos(s) - i_length_pos)/8 -2 );
+ continue;
+ }
+
+ /* Coding of a bitmap object */
+ i_update_pos = bs_pos( s );
+ bs_write( s, 16, 0 ); /* topfield data block length */
+ bs_write( s, 16, 0 ); /* bottomfield data block length */
+
+ /* Top field */
+ i_pixel_data_pos = bs_pos( s );
+ encode_pixel_data( p_enc, s, p_region, true );
+ i_pixel_data_pos = ( bs_pos( s ) - i_pixel_data_pos ) / 8;
+ SetWBE( &s->p_start[i_update_pos/8], i_pixel_data_pos );
+
+ /* Bottom field */
+ i_pixel_data_pos = bs_pos( s );
+ encode_pixel_data( p_enc, s, p_region, false );
+ i_pixel_data_pos = ( bs_pos( s ) - i_pixel_data_pos ) / 8;
+ SetWBE( &s->p_start[i_update_pos/8+2], i_pixel_data_pos );
+
+ /* Stuffing for word alignment */
+ bs_align_0( s );
+ if( bs_pos( s ) % 16 ) bs_write( s, 8, 0 );
+
+ /* Update segment length */
+ SetWBE( &s->p_start[i_length_pos/8], (bs_pos(s) - i_length_pos)/8 -2 );
+ }
+}
+
+static void encode_pixel_line_2bp( bs_t *s, subpicture_region_t *p_region,
+ int i_line );
+static void encode_pixel_line_4bp( bs_t *s, subpicture_region_t *p_region,
+ int i_line );
+static void encode_pixel_line_8bp( bs_t *s, subpicture_region_t *p_region,
+ int i_line );
+static void encode_pixel_data( encoder_t *p_enc, bs_t *s,
+ subpicture_region_t *p_region,
+ bool b_top )
+{
+ unsigned int i_line;
+
+ /* Sanity check */
+ if( p_region->fmt.i_chroma != VLC_FOURCC('Y','U','V','P') ) return;
+
+ /* Encode line by line */
+ for( i_line = !b_top; i_line < p_region->fmt.i_visible_height;
+ i_line += 2 )
+ {
+ switch( p_region->fmt.p_palette->i_entries )
+ {
+ case 0: